From ecd56d6e3ab4bf198bd8f1b737e78bc10cae116d Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Tue, 22 Feb 2022 10:01:04 +0100 Subject: Backport WebRTC changes to PipeWire/Wayland screen sharing support --- firefox.spec | 10 +- libwebrtc-screen-cast-sync.patch | 9928 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 9937 insertions(+), 1 deletion(-) create mode 100644 libwebrtc-screen-cast-sync.patch diff --git a/firefox.spec b/firefox.spec index 37bc621..4ed71c2 100644 --- a/firefox.spec +++ b/firefox.spec @@ -163,7 +163,7 @@ ExcludeArch: aarch64 Summary: Mozilla Firefox Web browser Name: firefox Version: 97.0.1 -Release: 1%{?pre_tag}%{?dist} +Release: 2%{?pre_tag}%{?dist} URL: https://www.mozilla.org/firefox/ License: MPLv1.1 or GPLv2+ or LGPLv2+ Source0: https://archive.mozilla.org/pub/firefox/releases/%{version}%{?pre_version}/source/firefox-%{version}%{?pre_version}.source.tar.xz @@ -251,6 +251,9 @@ Patch415: mozilla-1670333.patch Patch600: pgo.patch Patch602: mozilla-1516803.patch +# Backported WebRTC changes for PipeWire/Wayland screen sharing support +Patch1000: libwebrtc-screen-cast-sync.patch + %if %{?system_nss} BuildRequires: pkgconfig(nspr) >= %{nspr_version} BuildRequires: pkgconfig(nss) >= %{nss_version} @@ -496,6 +499,8 @@ This package contains results of tests executed during build. %endif %endif +%patch1000 -p1 -b .libwebrtc-screen-cast-sync + %{__rm} -f .mozconfig %{__cp} %{SOURCE10} .mozconfig echo "ac_add_options --enable-default-toolkit=cairo-gtk3-wayland" >> .mozconfig @@ -1051,6 +1056,9 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : #--------------------------------------------------------------------- %changelog +* Mon Feb 21 2022 Jan Grulich - 97.0.1-2 +- Backport WebRTC changes to PipeWire/Wayland screen sharing support + * Fri Feb 18 2022 Martin Stransky - 97.0.1-1 - Updated to 97.0.1 - GCC 12 build fixes diff --git a/libwebrtc-screen-cast-sync.patch b/libwebrtc-screen-cast-sync.patch new file mode 100644 index 0000000..da909ee --- /dev/null +++ b/libwebrtc-screen-cast-sync.patch @@ -0,0 +1,9928 @@ +From e0e925da71abb97a60d02716b18faa19a29fada6 Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Mon, 21 Feb 2022 15:34:52 +0100 +Subject: WebRTC - screen cast sync + + +diff --git a/dom/media/webrtc/third_party_build/moz.build b/dom/media/webrtc/third_party_build/moz.build +index e4c7ba7..a42f913 100644 +--- a/dom/media/webrtc/third_party_build/moz.build ++++ b/dom/media/webrtc/third_party_build/moz.build +@@ -63,6 +63,8 @@ webrtc_non_unified_sources = [ + + if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + DIRS += ["../../../../third_party/pipewire/libpipewire"] ++ DIRS += ["../../../../third_party/drm/libdrm"] ++ DIRS += ["../../../../third_party/gbm/libgbm"] + + GN_DIRS += ["../../../../third_party/libwebrtc"] + +diff --git a/third_party/drm/README b/third_party/drm/README +new file mode 100644 +index 0000000..f68ed10 +--- /dev/null ++++ b/third_party/drm/README +@@ -0,0 +1,4 @@ ++Libdrm is a drm library wrapper needed to build and run Firefox with ++Pipewire support on Linux (https://gitlab.freedesktop.org/mesa/drm). ++ ++libdrm directory stores headers of libdrm needed for build only. +diff --git a/third_party/drm/drm/drm.h b/third_party/drm/drm/drm.h +new file mode 100644 +index 0000000..5e54c3a +--- /dev/null ++++ b/third_party/drm/drm/drm.h +@@ -0,0 +1,1193 @@ ++/* ++ * Header for the Direct Rendering Manager ++ * ++ * Author: Rickard E. (Rik) Faith ++ * ++ * Acknowledgments: ++ * Dec 1999, Richard Henderson , move to generic cmpxchg. ++ */ ++ ++/* ++ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. ++ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. ++ * All rights reserved. ++ * ++ * 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 ++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. ++ */ ++ ++#ifndef _DRM_H_ ++#define _DRM_H_ ++ ++#if defined(__linux__) ++ ++#include ++#include ++typedef unsigned int drm_handle_t; ++ ++#else /* One of the BSDs */ ++ ++#include ++#include ++#include ++typedef int8_t __s8; ++typedef uint8_t __u8; ++typedef int16_t __s16; ++typedef uint16_t __u16; ++typedef int32_t __s32; ++typedef uint32_t __u32; ++typedef int64_t __s64; ++typedef uint64_t __u64; ++typedef size_t __kernel_size_t; ++typedef unsigned long drm_handle_t; ++ ++#endif ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ ++#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ ++#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ ++#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ ++ ++#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ ++#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ ++#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) ++#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) ++#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) ++ ++typedef unsigned int drm_context_t; ++typedef unsigned int drm_drawable_t; ++typedef unsigned int drm_magic_t; ++ ++/* ++ * Cliprect. ++ * ++ * \warning: If you change this structure, make sure you change ++ * XF86DRIClipRectRec in the server as well ++ * ++ * \note KW: Actually it's illegal to change either for ++ * backwards-compatibility reasons. ++ */ ++struct drm_clip_rect { ++ unsigned short x1; ++ unsigned short y1; ++ unsigned short x2; ++ unsigned short y2; ++}; ++ ++/* ++ * Drawable information. ++ */ ++struct drm_drawable_info { ++ unsigned int num_rects; ++ struct drm_clip_rect *rects; ++}; ++ ++/* ++ * Texture region, ++ */ ++struct drm_tex_region { ++ unsigned char next; ++ unsigned char prev; ++ unsigned char in_use; ++ unsigned char padding; ++ unsigned int age; ++}; ++ ++/* ++ * Hardware lock. ++ * ++ * The lock structure is a simple cache-line aligned integer. To avoid ++ * processor bus contention on a multiprocessor system, there should not be any ++ * other data stored in the same cache line. ++ */ ++struct drm_hw_lock { ++ __volatile__ unsigned int lock; /**< lock variable */ ++ char padding[60]; /**< Pad to cache line */ ++}; ++ ++/* ++ * DRM_IOCTL_VERSION ioctl argument type. ++ * ++ * \sa drmGetVersion(). ++ */ ++struct drm_version { ++ int version_major; /**< Major version */ ++ int version_minor; /**< Minor version */ ++ int version_patchlevel; /**< Patch level */ ++ __kernel_size_t name_len; /**< Length of name buffer */ ++ char *name; /**< Name of driver */ ++ __kernel_size_t date_len; /**< Length of date buffer */ ++ char *date; /**< User-space buffer to hold date */ ++ __kernel_size_t desc_len; /**< Length of desc buffer */ ++ char *desc; /**< User-space buffer to hold desc */ ++}; ++ ++/* ++ * DRM_IOCTL_GET_UNIQUE ioctl argument type. ++ * ++ * \sa drmGetBusid() and drmSetBusId(). ++ */ ++struct drm_unique { ++ __kernel_size_t unique_len; /**< Length of unique */ ++ char *unique; /**< Unique name for driver instantiation */ ++}; ++ ++struct drm_list { ++ int count; /**< Length of user-space structures */ ++ struct drm_version *version; ++}; ++ ++struct drm_block { ++ int unused; ++}; ++ ++/* ++ * DRM_IOCTL_CONTROL ioctl argument type. ++ * ++ * \sa drmCtlInstHandler() and drmCtlUninstHandler(). ++ */ ++struct drm_control { ++ enum { ++ DRM_ADD_COMMAND, ++ DRM_RM_COMMAND, ++ DRM_INST_HANDLER, ++ DRM_UNINST_HANDLER ++ } func; ++ int irq; ++}; ++ ++/* ++ * Type of memory to map. ++ */ ++enum drm_map_type { ++ _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ ++ _DRM_REGISTERS = 1, /**< no caching, no core dump */ ++ _DRM_SHM = 2, /**< shared, cached */ ++ _DRM_AGP = 3, /**< AGP/GART */ ++ _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ ++ _DRM_CONSISTENT = 5 /**< Consistent memory for PCI DMA */ ++}; ++ ++/* ++ * Memory mapping flags. ++ */ ++enum drm_map_flags { ++ _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ ++ _DRM_READ_ONLY = 0x02, ++ _DRM_LOCKED = 0x04, /**< shared, cached, locked */ ++ _DRM_KERNEL = 0x08, /**< kernel requires access */ ++ _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ ++ _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ ++ _DRM_REMOVABLE = 0x40, /**< Removable mapping */ ++ _DRM_DRIVER = 0x80 /**< Managed by driver */ ++}; ++ ++struct drm_ctx_priv_map { ++ unsigned int ctx_id; /**< Context requesting private mapping */ ++ void *handle; /**< Handle of map */ ++}; ++ ++/* ++ * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls ++ * argument type. ++ * ++ * \sa drmAddMap(). ++ */ ++struct drm_map { ++ unsigned long offset; /**< Requested physical address (0 for SAREA)*/ ++ unsigned long size; /**< Requested physical size (bytes) */ ++ enum drm_map_type type; /**< Type of memory to map */ ++ enum drm_map_flags flags; /**< Flags */ ++ void *handle; /**< User-space: "Handle" to pass to mmap() */ ++ /**< Kernel-space: kernel-virtual address */ ++ int mtrr; /**< MTRR slot used */ ++ /* Private data */ ++}; ++ ++/* ++ * DRM_IOCTL_GET_CLIENT ioctl argument type. ++ */ ++struct drm_client { ++ int idx; /**< Which client desired? */ ++ int auth; /**< Is client authenticated? */ ++ unsigned long pid; /**< Process ID */ ++ unsigned long uid; /**< User ID */ ++ unsigned long magic; /**< Magic */ ++ unsigned long iocs; /**< Ioctl count */ ++}; ++ ++enum drm_stat_type { ++ _DRM_STAT_LOCK, ++ _DRM_STAT_OPENS, ++ _DRM_STAT_CLOSES, ++ _DRM_STAT_IOCTLS, ++ _DRM_STAT_LOCKS, ++ _DRM_STAT_UNLOCKS, ++ _DRM_STAT_VALUE, /**< Generic value */ ++ _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ ++ _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ ++ ++ _DRM_STAT_IRQ, /**< IRQ */ ++ _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ ++ _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ ++ _DRM_STAT_DMA, /**< DMA */ ++ _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ ++ _DRM_STAT_MISSED /**< Missed DMA opportunity */ ++ /* Add to the *END* of the list */ ++}; ++ ++/* ++ * DRM_IOCTL_GET_STATS ioctl argument type. ++ */ ++struct drm_stats { ++ unsigned long count; ++ struct { ++ unsigned long value; ++ enum drm_stat_type type; ++ } data[15]; ++}; ++ ++/* ++ * Hardware locking flags. ++ */ ++enum drm_lock_flags { ++ _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ ++ _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ ++ _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ ++ _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ ++ /* These *HALT* flags aren't supported yet ++ -- they will be used to support the ++ full-screen DGA-like mode. */ ++ _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ ++ _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ ++}; ++ ++/* ++ * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. ++ * ++ * \sa drmGetLock() and drmUnlock(). ++ */ ++struct drm_lock { ++ int context; ++ enum drm_lock_flags flags; ++}; ++ ++/* ++ * DMA flags ++ * ++ * \warning ++ * These values \e must match xf86drm.h. ++ * ++ * \sa drm_dma. ++ */ ++enum drm_dma_flags { ++ /* Flags for DMA buffer dispatch */ ++ _DRM_DMA_BLOCK = 0x01, /**< ++ * Block until buffer dispatched. ++ * ++ * \note The buffer may not yet have ++ * been processed by the hardware -- ++ * getting a hardware lock with the ++ * hardware quiescent will ensure ++ * that the buffer has been ++ * processed. ++ */ ++ _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ ++ _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ ++ ++ /* Flags for DMA buffer request */ ++ _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ ++ _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ ++ _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ ++}; ++ ++/* ++ * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. ++ * ++ * \sa drmAddBufs(). ++ */ ++struct drm_buf_desc { ++ int count; /**< Number of buffers of this size */ ++ int size; /**< Size in bytes */ ++ int low_mark; /**< Low water mark */ ++ int high_mark; /**< High water mark */ ++ enum { ++ _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ ++ _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ ++ _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ ++ _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ ++ _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ ++ } flags; ++ unsigned long agp_start; /**< ++ * Start address of where the AGP buffers are ++ * in the AGP aperture ++ */ ++}; ++ ++/* ++ * DRM_IOCTL_INFO_BUFS ioctl argument type. ++ */ ++struct drm_buf_info { ++ int count; /**< Entries in list */ ++ struct drm_buf_desc *list; ++}; ++ ++/* ++ * DRM_IOCTL_FREE_BUFS ioctl argument type. ++ */ ++struct drm_buf_free { ++ int count; ++ int *list; ++}; ++ ++/* ++ * Buffer information ++ * ++ * \sa drm_buf_map. ++ */ ++struct drm_buf_pub { ++ int idx; /**< Index into the master buffer list */ ++ int total; /**< Buffer size */ ++ int used; /**< Amount of buffer in use (for DMA) */ ++ void *address; /**< Address of buffer */ ++}; ++ ++/* ++ * DRM_IOCTL_MAP_BUFS ioctl argument type. ++ */ ++struct drm_buf_map { ++ int count; /**< Length of the buffer list */ ++#ifdef __cplusplus ++ void *virt; ++#else ++ void *virtual; /**< Mmap'd area in user-virtual */ ++#endif ++ struct drm_buf_pub *list; /**< Buffer information */ ++}; ++ ++/* ++ * DRM_IOCTL_DMA ioctl argument type. ++ * ++ * Indices here refer to the offset into the buffer list in drm_buf_get. ++ * ++ * \sa drmDMA(). ++ */ ++struct drm_dma { ++ int context; /**< Context handle */ ++ int send_count; /**< Number of buffers to send */ ++ int *send_indices; /**< List of handles to buffers */ ++ int *send_sizes; /**< Lengths of data to send */ ++ enum drm_dma_flags flags; /**< Flags */ ++ int request_count; /**< Number of buffers requested */ ++ int request_size; /**< Desired size for buffers */ ++ int *request_indices; /**< Buffer information */ ++ int *request_sizes; ++ int granted_count; /**< Number of buffers granted */ ++}; ++ ++enum drm_ctx_flags { ++ _DRM_CONTEXT_PRESERVED = 0x01, ++ _DRM_CONTEXT_2DONLY = 0x02 ++}; ++ ++/* ++ * DRM_IOCTL_ADD_CTX ioctl argument type. ++ * ++ * \sa drmCreateContext() and drmDestroyContext(). ++ */ ++struct drm_ctx { ++ drm_context_t handle; ++ enum drm_ctx_flags flags; ++}; ++ ++/* ++ * DRM_IOCTL_RES_CTX ioctl argument type. ++ */ ++struct drm_ctx_res { ++ int count; ++ struct drm_ctx *contexts; ++}; ++ ++/* ++ * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. ++ */ ++struct drm_draw { ++ drm_drawable_t handle; ++}; ++ ++/* ++ * DRM_IOCTL_UPDATE_DRAW ioctl argument type. ++ */ ++typedef enum { ++ DRM_DRAWABLE_CLIPRECTS ++} drm_drawable_info_type_t; ++ ++struct drm_update_draw { ++ drm_drawable_t handle; ++ unsigned int type; ++ unsigned int num; ++ unsigned long long data; ++}; ++ ++/* ++ * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. ++ */ ++struct drm_auth { ++ drm_magic_t magic; ++}; ++ ++/* ++ * DRM_IOCTL_IRQ_BUSID ioctl argument type. ++ * ++ * \sa drmGetInterruptFromBusID(). ++ */ ++struct drm_irq_busid { ++ int irq; /**< IRQ number */ ++ int busnum; /**< bus number */ ++ int devnum; /**< device number */ ++ int funcnum; /**< function number */ ++}; ++ ++enum drm_vblank_seq_type { ++ _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ ++ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ ++ /* bits 1-6 are reserved for high crtcs */ ++ _DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, ++ _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ ++ _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ ++ _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ ++ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ ++ _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */ ++}; ++#define _DRM_VBLANK_HIGH_CRTC_SHIFT 1 ++ ++#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) ++#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ ++ _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) ++ ++struct drm_wait_vblank_request { ++ enum drm_vblank_seq_type type; ++ unsigned int sequence; ++ unsigned long signal; ++}; ++ ++struct drm_wait_vblank_reply { ++ enum drm_vblank_seq_type type; ++ unsigned int sequence; ++ long tval_sec; ++ long tval_usec; ++}; ++ ++/* ++ * DRM_IOCTL_WAIT_VBLANK ioctl argument type. ++ * ++ * \sa drmWaitVBlank(). ++ */ ++union drm_wait_vblank { ++ struct drm_wait_vblank_request request; ++ struct drm_wait_vblank_reply reply; ++}; ++ ++#define _DRM_PRE_MODESET 1 ++#define _DRM_POST_MODESET 2 ++ ++/* ++ * DRM_IOCTL_MODESET_CTL ioctl argument type ++ * ++ * \sa drmModesetCtl(). ++ */ ++struct drm_modeset_ctl { ++ __u32 crtc; ++ __u32 cmd; ++}; ++ ++/* ++ * DRM_IOCTL_AGP_ENABLE ioctl argument type. ++ * ++ * \sa drmAgpEnable(). ++ */ ++struct drm_agp_mode { ++ unsigned long mode; /**< AGP mode */ ++}; ++ ++/* ++ * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. ++ * ++ * \sa drmAgpAlloc() and drmAgpFree(). ++ */ ++struct drm_agp_buffer { ++ unsigned long size; /**< In bytes -- will round to page boundary */ ++ unsigned long handle; /**< Used for binding / unbinding */ ++ unsigned long type; /**< Type of memory to allocate */ ++ unsigned long physical; /**< Physical used by i810 */ ++}; ++ ++/* ++ * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. ++ * ++ * \sa drmAgpBind() and drmAgpUnbind(). ++ */ ++struct drm_agp_binding { ++ unsigned long handle; /**< From drm_agp_buffer */ ++ unsigned long offset; /**< In bytes -- will round to page boundary */ ++}; ++ ++/* ++ * DRM_IOCTL_AGP_INFO ioctl argument type. ++ * ++ * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), ++ * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), ++ * drmAgpVendorId() and drmAgpDeviceId(). ++ */ ++struct drm_agp_info { ++ int agp_version_major; ++ int agp_version_minor; ++ unsigned long mode; ++ unsigned long aperture_base; /* physical address */ ++ unsigned long aperture_size; /* bytes */ ++ unsigned long memory_allowed; /* bytes */ ++ unsigned long memory_used; ++ ++ /* PCI information */ ++ unsigned short id_vendor; ++ unsigned short id_device; ++}; ++ ++/* ++ * DRM_IOCTL_SG_ALLOC ioctl argument type. ++ */ ++struct drm_scatter_gather { ++ unsigned long size; /**< In bytes -- will round to page boundary */ ++ unsigned long handle; /**< Used for mapping / unmapping */ ++}; ++ ++/* ++ * DRM_IOCTL_SET_VERSION ioctl argument type. ++ */ ++struct drm_set_version { ++ int drm_di_major; ++ int drm_di_minor; ++ int drm_dd_major; ++ int drm_dd_minor; ++}; ++ ++/* DRM_IOCTL_GEM_CLOSE ioctl argument type */ ++struct drm_gem_close { ++ /** Handle of the object to be closed. */ ++ __u32 handle; ++ __u32 pad; ++}; ++ ++/* DRM_IOCTL_GEM_FLINK ioctl argument type */ ++struct drm_gem_flink { ++ /** Handle for the object being named */ ++ __u32 handle; ++ ++ /** Returned global name */ ++ __u32 name; ++}; ++ ++/* DRM_IOCTL_GEM_OPEN ioctl argument type */ ++struct drm_gem_open { ++ /** Name of object being opened */ ++ __u32 name; ++ ++ /** Returned handle for the object */ ++ __u32 handle; ++ ++ /** Returned size of the object */ ++ __u64 size; ++}; ++ ++/** ++ * DRM_CAP_DUMB_BUFFER ++ * ++ * If set to 1, the driver supports creating dumb buffers via the ++ * &DRM_IOCTL_MODE_CREATE_DUMB ioctl. ++ */ ++#define DRM_CAP_DUMB_BUFFER 0x1 ++/** ++ * DRM_CAP_VBLANK_HIGH_CRTC ++ * ++ * If set to 1, the kernel supports specifying a :ref:`CRTC index` ++ * in the high bits of &drm_wait_vblank_request.type. ++ * ++ * Starting kernel version 2.6.39, this capability is always set to 1. ++ */ ++#define DRM_CAP_VBLANK_HIGH_CRTC 0x2 ++/** ++ * DRM_CAP_DUMB_PREFERRED_DEPTH ++ * ++ * The preferred bit depth for dumb buffers. ++ * ++ * The bit depth is the number of bits used to indicate the color of a single ++ * pixel excluding any padding. This is different from the number of bits per ++ * pixel. For instance, XRGB8888 has a bit depth of 24 but has 32 bits per ++ * pixel. ++ * ++ * Note that this preference only applies to dumb buffers, it's irrelevant for ++ * other types of buffers. ++ */ ++#define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 ++/** ++ * DRM_CAP_DUMB_PREFER_SHADOW ++ * ++ * If set to 1, the driver prefers userspace to render to a shadow buffer ++ * instead of directly rendering to a dumb buffer. For best speed, userspace ++ * should do streaming ordered memory copies into the dumb buffer and never ++ * read from it. ++ * ++ * Note that this preference only applies to dumb buffers, it's irrelevant for ++ * other types of buffers. ++ */ ++#define DRM_CAP_DUMB_PREFER_SHADOW 0x4 ++/** ++ * DRM_CAP_PRIME ++ * ++ * Bitfield of supported PRIME sharing capabilities. See &DRM_PRIME_CAP_IMPORT ++ * and &DRM_PRIME_CAP_EXPORT. ++ * ++ * PRIME buffers are exposed as dma-buf file descriptors. See ++ * Documentation/gpu/drm-mm.rst, section "PRIME Buffer Sharing". ++ */ ++#define DRM_CAP_PRIME 0x5 ++/** ++ * DRM_PRIME_CAP_IMPORT ++ * ++ * If this bit is set in &DRM_CAP_PRIME, the driver supports importing PRIME ++ * buffers via the &DRM_IOCTL_PRIME_FD_TO_HANDLE ioctl. ++ */ ++#define DRM_PRIME_CAP_IMPORT 0x1 ++/** ++ * DRM_PRIME_CAP_EXPORT ++ * ++ * If this bit is set in &DRM_CAP_PRIME, the driver supports exporting PRIME ++ * buffers via the &DRM_IOCTL_PRIME_HANDLE_TO_FD ioctl. ++ */ ++#define DRM_PRIME_CAP_EXPORT 0x2 ++/** ++ * DRM_CAP_TIMESTAMP_MONOTONIC ++ * ++ * If set to 0, the kernel will report timestamps with ``CLOCK_REALTIME`` in ++ * struct drm_event_vblank. If set to 1, the kernel will report timestamps with ++ * ``CLOCK_MONOTONIC``. See ``clock_gettime(2)`` for the definition of these ++ * clocks. ++ * ++ * Starting from kernel version 2.6.39, the default value for this capability ++ * is 1. Starting kernel version 4.15, this capability is always set to 1. ++ */ ++#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 ++/** ++ * DRM_CAP_ASYNC_PAGE_FLIP ++ * ++ * If set to 1, the driver supports &DRM_MODE_PAGE_FLIP_ASYNC. ++ */ ++#define DRM_CAP_ASYNC_PAGE_FLIP 0x7 ++/** ++ * DRM_CAP_CURSOR_WIDTH ++ * ++ * The ``CURSOR_WIDTH`` and ``CURSOR_HEIGHT`` capabilities return a valid ++ * width x height combination for the hardware cursor. The intention is that a ++ * hardware agnostic userspace can query a cursor plane size to use. ++ * ++ * Note that the cross-driver contract is to merely return a valid size; ++ * drivers are free to attach another meaning on top, eg. i915 returns the ++ * maximum plane size. ++ */ ++#define DRM_CAP_CURSOR_WIDTH 0x8 ++/** ++ * DRM_CAP_CURSOR_HEIGHT ++ * ++ * See &DRM_CAP_CURSOR_WIDTH. ++ */ ++#define DRM_CAP_CURSOR_HEIGHT 0x9 ++/** ++ * DRM_CAP_ADDFB2_MODIFIERS ++ * ++ * If set to 1, the driver supports supplying modifiers in the ++ * &DRM_IOCTL_MODE_ADDFB2 ioctl. ++ */ ++#define DRM_CAP_ADDFB2_MODIFIERS 0x10 ++/** ++ * DRM_CAP_PAGE_FLIP_TARGET ++ * ++ * If set to 1, the driver supports the &DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE and ++ * &DRM_MODE_PAGE_FLIP_TARGET_RELATIVE flags in ++ * &drm_mode_crtc_page_flip_target.flags for the &DRM_IOCTL_MODE_PAGE_FLIP ++ * ioctl. ++ */ ++#define DRM_CAP_PAGE_FLIP_TARGET 0x11 ++/** ++ * DRM_CAP_CRTC_IN_VBLANK_EVENT ++ * ++ * If set to 1, the kernel supports reporting the CRTC ID in ++ * &drm_event_vblank.crtc_id for the &DRM_EVENT_VBLANK and ++ * &DRM_EVENT_FLIP_COMPLETE events. ++ * ++ * Starting kernel version 4.12, this capability is always set to 1. ++ */ ++#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12 ++/** ++ * DRM_CAP_SYNCOBJ ++ * ++ * If set to 1, the driver supports sync objects. See ++ * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". ++ */ ++#define DRM_CAP_SYNCOBJ 0x13 ++/** ++ * DRM_CAP_SYNCOBJ_TIMELINE ++ * ++ * If set to 1, the driver supports timeline operations on sync objects. See ++ * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". ++ */ ++#define DRM_CAP_SYNCOBJ_TIMELINE 0x14 ++ ++/* DRM_IOCTL_GET_CAP ioctl argument type */ ++struct drm_get_cap { ++ __u64 capability; ++ __u64 value; ++}; ++ ++/** ++ * DRM_CLIENT_CAP_STEREO_3D ++ * ++ * If set to 1, the DRM core will expose the stereo 3D capabilities of the ++ * monitor by advertising the supported 3D layouts in the flags of struct ++ * drm_mode_modeinfo. See ``DRM_MODE_FLAG_3D_*``. ++ * ++ * This capability is always supported for all drivers starting from kernel ++ * version 3.13. ++ */ ++#define DRM_CLIENT_CAP_STEREO_3D 1 ++ ++/** ++ * DRM_CLIENT_CAP_UNIVERSAL_PLANES ++ * ++ * If set to 1, the DRM core will expose all planes (overlay, primary, and ++ * cursor) to userspace. ++ * ++ * This capability has been introduced in kernel version 3.15. Starting from ++ * kernel version 3.17, this capability is always supported for all drivers. ++ */ ++#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 ++ ++/** ++ * DRM_CLIENT_CAP_ATOMIC ++ * ++ * If set to 1, the DRM core will expose atomic properties to userspace. This ++ * implicitly enables &DRM_CLIENT_CAP_UNIVERSAL_PLANES and ++ * &DRM_CLIENT_CAP_ASPECT_RATIO. ++ * ++ * If the driver doesn't support atomic mode-setting, enabling this capability ++ * will fail with -EOPNOTSUPP. ++ * ++ * This capability has been introduced in kernel version 4.0. Starting from ++ * kernel version 4.2, this capability is always supported for atomic-capable ++ * drivers. ++ */ ++#define DRM_CLIENT_CAP_ATOMIC 3 ++ ++/** ++ * DRM_CLIENT_CAP_ASPECT_RATIO ++ * ++ * If set to 1, the DRM core will provide aspect ratio information in modes. ++ * See ``DRM_MODE_FLAG_PIC_AR_*``. ++ * ++ * This capability is always supported for all drivers starting from kernel ++ * version 4.18. ++ */ ++#define DRM_CLIENT_CAP_ASPECT_RATIO 4 ++ ++/** ++ * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS ++ * ++ * If set to 1, the DRM core will expose special connectors to be used for ++ * writing back to memory the scene setup in the commit. The client must enable ++ * &DRM_CLIENT_CAP_ATOMIC first. ++ * ++ * This capability is always supported for atomic-capable drivers starting from ++ * kernel version 4.19. ++ */ ++#define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5 ++ ++/* DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ ++struct drm_set_client_cap { ++ __u64 capability; ++ __u64 value; ++}; ++ ++#define DRM_RDWR O_RDWR ++#define DRM_CLOEXEC O_CLOEXEC ++struct drm_prime_handle { ++ __u32 handle; ++ ++ /** Flags.. only applicable for handle->fd */ ++ __u32 flags; ++ ++ /** Returned dmabuf file descriptor */ ++ __s32 fd; ++}; ++ ++struct drm_syncobj_create { ++ __u32 handle; ++#define DRM_SYNCOBJ_CREATE_SIGNALED (1 << 0) ++ __u32 flags; ++}; ++ ++struct drm_syncobj_destroy { ++ __u32 handle; ++ __u32 pad; ++}; ++ ++#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) ++#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) ++struct drm_syncobj_handle { ++ __u32 handle; ++ __u32 flags; ++ ++ __s32 fd; ++ __u32 pad; ++}; ++ ++struct drm_syncobj_transfer { ++ __u32 src_handle; ++ __u32 dst_handle; ++ __u64 src_point; ++ __u64 dst_point; ++ __u32 flags; ++ __u32 pad; ++}; ++ ++#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0) ++#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1) ++#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2) /* wait for time point to become available */ ++struct drm_syncobj_wait { ++ __u64 handles; ++ /* absolute timeout */ ++ __s64 timeout_nsec; ++ __u32 count_handles; ++ __u32 flags; ++ __u32 first_signaled; /* only valid when not waiting all */ ++ __u32 pad; ++}; ++ ++struct drm_syncobj_timeline_wait { ++ __u64 handles; ++ /* wait on specific timeline point for every handles*/ ++ __u64 points; ++ /* absolute timeout */ ++ __s64 timeout_nsec; ++ __u32 count_handles; ++ __u32 flags; ++ __u32 first_signaled; /* only valid when not waiting all */ ++ __u32 pad; ++}; ++ ++ ++struct drm_syncobj_array { ++ __u64 handles; ++ __u32 count_handles; ++ __u32 pad; ++}; ++ ++#define DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED (1 << 0) /* last available point on timeline syncobj */ ++struct drm_syncobj_timeline_array { ++ __u64 handles; ++ __u64 points; ++ __u32 count_handles; ++ __u32 flags; ++}; ++ ++ ++/* Query current scanout sequence number */ ++struct drm_crtc_get_sequence { ++ __u32 crtc_id; /* requested crtc_id */ ++ __u32 active; /* return: crtc output is active */ ++ __u64 sequence; /* return: most recent vblank sequence */ ++ __s64 sequence_ns; /* return: most recent time of first pixel out */ ++}; ++ ++/* Queue event to be delivered at specified sequence. Time stamp marks ++ * when the first pixel of the refresh cycle leaves the display engine ++ * for the display ++ */ ++#define DRM_CRTC_SEQUENCE_RELATIVE 0x00000001 /* sequence is relative to current */ ++#define DRM_CRTC_SEQUENCE_NEXT_ON_MISS 0x00000002 /* Use next sequence if we've missed */ ++ ++struct drm_crtc_queue_sequence { ++ __u32 crtc_id; ++ __u32 flags; ++ __u64 sequence; /* on input, target sequence. on output, actual sequence */ ++ __u64 user_data; /* user data passed to event */ ++}; ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#include "drm_mode.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++#define DRM_IOCTL_BASE 'd' ++#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) ++#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) ++#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) ++#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) ++ ++#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) ++#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) ++#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) ++#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) ++#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) ++#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) ++#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) ++#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) ++#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) ++#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) ++#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) ++#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) ++#define DRM_IOCTL_GET_CAP DRM_IOWR(0x0c, struct drm_get_cap) ++#define DRM_IOCTL_SET_CLIENT_CAP DRM_IOW( 0x0d, struct drm_set_client_cap) ++ ++#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) ++#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) ++#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) ++#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) ++#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) ++#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) ++#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) ++#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) ++#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) ++#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) ++#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) ++ ++#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) ++ ++#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) ++#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) ++ ++#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) ++#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) ++ ++#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) ++#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) ++#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) ++#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) ++#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) ++#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) ++#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) ++#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) ++#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) ++#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) ++#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) ++#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) ++#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) ++ ++#define DRM_IOCTL_PRIME_HANDLE_TO_FD DRM_IOWR(0x2d, struct drm_prime_handle) ++#define DRM_IOCTL_PRIME_FD_TO_HANDLE DRM_IOWR(0x2e, struct drm_prime_handle) ++ ++#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) ++#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) ++#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) ++#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) ++#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) ++#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) ++#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) ++#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) ++ ++#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) ++#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) ++ ++#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) ++ ++#define DRM_IOCTL_CRTC_GET_SEQUENCE DRM_IOWR(0x3b, struct drm_crtc_get_sequence) ++#define DRM_IOCTL_CRTC_QUEUE_SEQUENCE DRM_IOWR(0x3c, struct drm_crtc_queue_sequence) ++ ++#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) ++ ++#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) ++#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) ++#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) ++#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) ++#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) ++#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) ++#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) ++#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) ++#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) /* deprecated (never worked) */ ++#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) /* deprecated (never worked) */ ++ ++#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) ++#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) ++#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) ++#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) ++#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) ++/** ++ * DRM_IOCTL_MODE_RMFB - Remove a framebuffer. ++ * ++ * This removes a framebuffer previously added via ADDFB/ADDFB2. The IOCTL ++ * argument is a framebuffer object ID. ++ * ++ * Warning: removing a framebuffer currently in-use on an enabled plane will ++ * disable that plane. The CRTC the plane is linked to may also be disabled ++ * (depending on driver capabilities). ++ */ ++#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) ++#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) ++#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd) ++ ++#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) ++#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) ++#define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) ++#define DRM_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xB5, struct drm_mode_get_plane_res) ++#define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) ++#define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) ++#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) ++#define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) ++#define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) ++#define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) ++#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) ++#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob) ++#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob) ++ ++#define DRM_IOCTL_SYNCOBJ_CREATE DRM_IOWR(0xBF, struct drm_syncobj_create) ++#define DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOWR(0xC0, struct drm_syncobj_destroy) ++#define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOWR(0xC1, struct drm_syncobj_handle) ++#define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOWR(0xC2, struct drm_syncobj_handle) ++#define DRM_IOCTL_SYNCOBJ_WAIT DRM_IOWR(0xC3, struct drm_syncobj_wait) ++#define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) ++#define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) ++ ++#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) ++#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) ++#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) ++#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) ++ ++#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) ++#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) ++#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) ++#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) ++ ++#define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2) ++ ++/* ++ * Device specific ioctls should only be in their respective headers ++ * The device specific ioctl range is from 0x40 to 0x9f. ++ * Generic IOCTLS restart at 0xA0. ++ * ++ * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and ++ * drmCommandReadWrite(). ++ */ ++#define DRM_COMMAND_BASE 0x40 ++#define DRM_COMMAND_END 0xA0 ++ ++/* ++ * Header for events written back to userspace on the drm fd. The ++ * type defines the type of event, the length specifies the total ++ * length of the event (including the header), and user_data is ++ * typically a 64 bit value passed with the ioctl that triggered the ++ * event. A read on the drm fd will always only return complete ++ * events, that is, if for example the read buffer is 100 bytes, and ++ * there are two 64 byte events pending, only one will be returned. ++ * ++ * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and ++ * up are chipset specific. ++ */ ++struct drm_event { ++ __u32 type; ++ __u32 length; ++}; ++ ++#define DRM_EVENT_VBLANK 0x01 ++#define DRM_EVENT_FLIP_COMPLETE 0x02 ++#define DRM_EVENT_CRTC_SEQUENCE 0x03 ++ ++struct drm_event_vblank { ++ struct drm_event base; ++ __u64 user_data; ++ __u32 tv_sec; ++ __u32 tv_usec; ++ __u32 sequence; ++ __u32 crtc_id; /* 0 on older kernels that do not support this */ ++}; ++ ++/* Event delivered at sequence. Time stamp marks when the first pixel ++ * of the refresh cycle leaves the display engine for the display ++ */ ++struct drm_event_crtc_sequence { ++ struct drm_event base; ++ __u64 user_data; ++ __s64 time_ns; ++ __u64 sequence; ++}; ++ ++/* typedef area */ ++typedef struct drm_clip_rect drm_clip_rect_t; ++typedef struct drm_drawable_info drm_drawable_info_t; ++typedef struct drm_tex_region drm_tex_region_t; ++typedef struct drm_hw_lock drm_hw_lock_t; ++typedef struct drm_version drm_version_t; ++typedef struct drm_unique drm_unique_t; ++typedef struct drm_list drm_list_t; ++typedef struct drm_block drm_block_t; ++typedef struct drm_control drm_control_t; ++typedef enum drm_map_type drm_map_type_t; ++typedef enum drm_map_flags drm_map_flags_t; ++typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; ++typedef struct drm_map drm_map_t; ++typedef struct drm_client drm_client_t; ++typedef enum drm_stat_type drm_stat_type_t; ++typedef struct drm_stats drm_stats_t; ++typedef enum drm_lock_flags drm_lock_flags_t; ++typedef struct drm_lock drm_lock_t; ++typedef enum drm_dma_flags drm_dma_flags_t; ++typedef struct drm_buf_desc drm_buf_desc_t; ++typedef struct drm_buf_info drm_buf_info_t; ++typedef struct drm_buf_free drm_buf_free_t; ++typedef struct drm_buf_pub drm_buf_pub_t; ++typedef struct drm_buf_map drm_buf_map_t; ++typedef struct drm_dma drm_dma_t; ++typedef union drm_wait_vblank drm_wait_vblank_t; ++typedef struct drm_agp_mode drm_agp_mode_t; ++typedef enum drm_ctx_flags drm_ctx_flags_t; ++typedef struct drm_ctx drm_ctx_t; ++typedef struct drm_ctx_res drm_ctx_res_t; ++typedef struct drm_draw drm_draw_t; ++typedef struct drm_update_draw drm_update_draw_t; ++typedef struct drm_auth drm_auth_t; ++typedef struct drm_irq_busid drm_irq_busid_t; ++typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; ++ ++typedef struct drm_agp_buffer drm_agp_buffer_t; ++typedef struct drm_agp_binding drm_agp_binding_t; ++typedef struct drm_agp_info drm_agp_info_t; ++typedef struct drm_scatter_gather drm_scatter_gather_t; ++typedef struct drm_set_version drm_set_version_t; ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif +diff --git a/third_party/drm/drm/drm_fourcc.h b/third_party/drm/drm/drm_fourcc.h +new file mode 100644 +index 0000000..4ececa8 +--- /dev/null ++++ b/third_party/drm/drm/drm_fourcc.h +@@ -0,0 +1,1377 @@ ++/* ++ * Copyright 2011 Intel Corporation ++ * ++ * 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 ++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. ++ */ ++ ++#ifndef DRM_FOURCC_H ++#define DRM_FOURCC_H ++ ++#include "drm.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/** ++ * DOC: overview ++ * ++ * In the DRM subsystem, framebuffer pixel formats are described using the ++ * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the ++ * fourcc code, a Format Modifier may optionally be provided, in order to ++ * further describe the buffer's format - for example tiling or compression. ++ * ++ * Format Modifiers ++ * ---------------- ++ * ++ * Format modifiers are used in conjunction with a fourcc code, forming a ++ * unique fourcc:modifier pair. This format:modifier pair must fully define the ++ * format and data layout of the buffer, and should be the only way to describe ++ * that particular buffer. ++ * ++ * Having multiple fourcc:modifier pairs which describe the same layout should ++ * be avoided, as such aliases run the risk of different drivers exposing ++ * different names for the same data format, forcing userspace to understand ++ * that they are aliases. ++ * ++ * Format modifiers may change any property of the buffer, including the number ++ * of planes and/or the required allocation size. Format modifiers are ++ * vendor-namespaced, and as such the relationship between a fourcc code and a ++ * modifier is specific to the modifer being used. For example, some modifiers ++ * may preserve meaning - such as number of planes - from the fourcc code, ++ * whereas others may not. ++ * ++ * Modifiers must uniquely encode buffer layout. In other words, a buffer must ++ * match only a single modifier. A modifier must not be a subset of layouts of ++ * another modifier. For instance, it's incorrect to encode pitch alignment in ++ * a modifier: a buffer may match a 64-pixel aligned modifier and a 32-pixel ++ * aligned modifier. That said, modifiers can have implicit minimal ++ * requirements. ++ * ++ * For modifiers where the combination of fourcc code and modifier can alias, ++ * a canonical pair needs to be defined and used by all drivers. Preferred ++ * combinations are also encouraged where all combinations might lead to ++ * confusion and unnecessarily reduced interoperability. An example for the ++ * latter is AFBC, where the ABGR layouts are preferred over ARGB layouts. ++ * ++ * There are two kinds of modifier users: ++ * ++ * - Kernel and user-space drivers: for drivers it's important that modifiers ++ * don't alias, otherwise two drivers might support the same format but use ++ * different aliases, preventing them from sharing buffers in an efficient ++ * format. ++ * - Higher-level programs interfacing with KMS/GBM/EGL/Vulkan/etc: these users ++ * see modifiers as opaque tokens they can check for equality and intersect. ++ * These users musn't need to know to reason about the modifier value ++ * (i.e. they are not expected to extract information out of the modifier). ++ * ++ * Vendors should document their modifier usage in as much detail as ++ * possible, to ensure maximum compatibility across devices, drivers and ++ * applications. ++ * ++ * The authoritative list of format modifier codes is found in ++ * `include/uapi/drm/drm_fourcc.h` ++ */ ++ ++#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ ++ ((__u32)(c) << 16) | ((__u32)(d) << 24)) ++ ++#define DRM_FORMAT_BIG_ENDIAN (1U<<31) /* format is big endian instead of little endian */ ++ ++/* Reserve 0 for the invalid format specifier */ ++#define DRM_FORMAT_INVALID 0 ++ ++/* color index */ ++#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ ++ ++/* 8 bpp Red */ ++#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ ++ ++/* 10 bpp Red */ ++#define DRM_FORMAT_R10 fourcc_code('R', '1', '0', ' ') /* [15:0] x:R 6:10 little endian */ ++ ++/* 12 bpp Red */ ++#define DRM_FORMAT_R12 fourcc_code('R', '1', '2', ' ') /* [15:0] x:R 4:12 little endian */ ++ ++/* 16 bpp Red */ ++#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ ++ ++/* 16 bpp RG */ ++#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ ++#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ ++ ++/* 32 bpp RG */ ++#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ ++#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ ++ ++/* 8 bpp RGB */ ++#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ ++#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ ++ ++/* 16 bpp RGB */ ++#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ ++#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ ++#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ ++#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ ++ ++#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ ++#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ ++#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ ++#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ ++ ++#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ ++#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ ++#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ ++#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ ++ ++#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ ++#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ ++#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ ++#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ ++ ++#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ ++#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ ++ ++/* 24 bpp RGB */ ++#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ ++#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ ++ ++/* 32 bpp RGB */ ++#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ ++#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ ++#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ ++#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ ++ ++#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ ++#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ ++#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ ++#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ ++ ++#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ ++#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ ++#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ ++#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ ++ ++#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ ++#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ ++#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ ++#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ ++ ++/* 64 bpp RGB */ ++#define DRM_FORMAT_XRGB16161616 fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ ++#define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ ++ ++#define DRM_FORMAT_ARGB16161616 fourcc_code('A', 'R', '4', '8') /* [63:0] A:R:G:B 16:16:16:16 little endian */ ++#define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */ ++ ++/* ++ * Floating point 64bpp RGB ++ * IEEE 754-2008 binary16 half-precision float ++ * [15:0] sign:exponent:mantissa 1:5:10 ++ */ ++#define DRM_FORMAT_XRGB16161616F fourcc_code('X', 'R', '4', 'H') /* [63:0] x:R:G:B 16:16:16:16 little endian */ ++#define DRM_FORMAT_XBGR16161616F fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */ ++ ++#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ ++#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ ++ ++/* ++ * RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits ++ * of unused padding per component: ++ */ ++#define DRM_FORMAT_AXBXGXRX106106106106 fourcc_code('A', 'B', '1', '0') /* [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian */ ++ ++/* packed YCbCr */ ++#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ ++#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ ++#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ ++#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ ++ ++#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ ++#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ ++#define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */ ++#define DRM_FORMAT_VUY101010 fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */ ++ ++/* ++ * packed Y2xx indicate for each component, xx valid data occupy msb ++ * 16-xx padding occupy lsb ++ */ ++#define DRM_FORMAT_Y210 fourcc_code('Y', '2', '1', '0') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels */ ++#define DRM_FORMAT_Y212 fourcc_code('Y', '2', '1', '2') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels */ ++#define DRM_FORMAT_Y216 fourcc_code('Y', '2', '1', '6') /* [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels */ ++ ++/* ++ * packed Y4xx indicate for each component, xx valid data occupy msb ++ * 16-xx padding occupy lsb except Y410 ++ */ ++#define DRM_FORMAT_Y410 fourcc_code('Y', '4', '1', '0') /* [31:0] A:Cr:Y:Cb 2:10:10:10 little endian */ ++#define DRM_FORMAT_Y412 fourcc_code('Y', '4', '1', '2') /* [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ ++#define DRM_FORMAT_Y416 fourcc_code('Y', '4', '1', '6') /* [63:0] A:Cr:Y:Cb 16:16:16:16 little endian */ ++ ++#define DRM_FORMAT_XVYU2101010 fourcc_code('X', 'V', '3', '0') /* [31:0] X:Cr:Y:Cb 2:10:10:10 little endian */ ++#define DRM_FORMAT_XVYU12_16161616 fourcc_code('X', 'V', '3', '6') /* [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ ++#define DRM_FORMAT_XVYU16161616 fourcc_code('X', 'V', '4', '8') /* [63:0] X:Cr:Y:Cb 16:16:16:16 little endian */ ++ ++/* ++ * packed YCbCr420 2x2 tiled formats ++ * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile ++ */ ++/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ ++#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') ++/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ ++#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') ++ ++/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ ++#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') ++/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ ++#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') ++ ++/* ++ * 1-plane YUV 4:2:0 ++ * In these formats, the component ordering is specified (Y, followed by U ++ * then V), but the exact Linear layout is undefined. ++ * These formats can only be used with a non-Linear modifier. ++ */ ++#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') ++#define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') ++ ++/* ++ * 2 plane RGB + A ++ * index 0 = RGB plane, same format as the corresponding non _A8 format has ++ * index 1 = A plane, [7:0] A ++ */ ++#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') ++#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') ++#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') ++#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') ++#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') ++#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') ++#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') ++#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') ++ ++/* ++ * 2 plane YCbCr ++ * index 0 = Y plane, [7:0] Y ++ * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian ++ * or ++ * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian ++ */ ++#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ ++#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ ++#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ ++#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ ++#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ ++#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ ++/* ++ * 2 plane YCbCr ++ * index 0 = Y plane, [39:0] Y3:Y2:Y1:Y0 little endian ++ * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian ++ */ ++#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ ++ ++/* ++ * 2 plane YCbCr MSB aligned ++ * index 0 = Y plane, [15:0] Y:x [10:6] little endian ++ * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian ++ */ ++#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') /* 2x1 subsampled Cr:Cb plane, 10 bit per channel */ ++ ++/* ++ * 2 plane YCbCr MSB aligned ++ * index 0 = Y plane, [15:0] Y:x [10:6] little endian ++ * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian ++ */ ++#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */ ++ ++/* ++ * 2 plane YCbCr MSB aligned ++ * index 0 = Y plane, [15:0] Y:x [12:4] little endian ++ * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian ++ */ ++#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane 12 bits per channel */ ++ ++/* ++ * 2 plane YCbCr MSB aligned ++ * index 0 = Y plane, [15:0] Y little endian ++ * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian ++ */ ++#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */ ++ ++/* 3 plane non-subsampled (444) YCbCr ++ * 16 bits per component, but only 10 bits are used and 6 bits are padded ++ * index 0: Y plane, [15:0] Y:x [10:6] little endian ++ * index 1: Cb plane, [15:0] Cb:x [10:6] little endian ++ * index 2: Cr plane, [15:0] Cr:x [10:6] little endian ++ */ ++#define DRM_FORMAT_Q410 fourcc_code('Q', '4', '1', '0') ++ ++/* 3 plane non-subsampled (444) YCrCb ++ * 16 bits per component, but only 10 bits are used and 6 bits are padded ++ * index 0: Y plane, [15:0] Y:x [10:6] little endian ++ * index 1: Cr plane, [15:0] Cr:x [10:6] little endian ++ * index 2: Cb plane, [15:0] Cb:x [10:6] little endian ++ */ ++#define DRM_FORMAT_Q401 fourcc_code('Q', '4', '0', '1') ++ ++/* ++ * 3 plane YCbCr ++ * index 0: Y plane, [7:0] Y ++ * index 1: Cb plane, [7:0] Cb ++ * index 2: Cr plane, [7:0] Cr ++ * or ++ * index 1: Cr plane, [7:0] Cr ++ * index 2: Cb plane, [7:0] Cb ++ */ ++#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ ++#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ ++#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ ++#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ ++#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ ++#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ ++#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ ++#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ ++#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ ++#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ ++ ++ ++/* ++ * Format Modifiers: ++ * ++ * Format modifiers describe, typically, a re-ordering or modification ++ * of the data in a plane of an FB. This can be used to express tiled/ ++ * swizzled formats, or compression, or a combination of the two. ++ * ++ * The upper 8 bits of the format modifier are a vendor-id as assigned ++ * below. The lower 56 bits are assigned as vendor sees fit. ++ */ ++ ++/* Vendor Ids: */ ++#define DRM_FORMAT_MOD_VENDOR_NONE 0 ++#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 ++#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 ++#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 ++#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 ++#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 ++#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 ++#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 ++#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 ++#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 ++#define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a ++ ++/* add more to the end as needed */ ++ ++#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) ++ ++#define fourcc_mod_get_vendor(modifier) \ ++ (((modifier) >> 56) & 0xff) ++ ++#define fourcc_mod_is_vendor(modifier, vendor) \ ++ (fourcc_mod_get_vendor(modifier) == DRM_FORMAT_MOD_VENDOR_## vendor) ++ ++#define fourcc_mod_code(vendor, val) \ ++ ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) ++ ++/* ++ * Format Modifier tokens: ++ * ++ * When adding a new token please document the layout with a code comment, ++ * similar to the fourcc codes above. drm_fourcc.h is considered the ++ * authoritative source for all of these. ++ * ++ * Generic modifier names: ++ * ++ * DRM_FORMAT_MOD_GENERIC_* definitions are used to provide vendor-neutral names ++ * for layouts which are common across multiple vendors. To preserve ++ * compatibility, in cases where a vendor-specific definition already exists and ++ * a generic name for it is desired, the common name is a purely symbolic alias ++ * and must use the same numerical value as the original definition. ++ * ++ * Note that generic names should only be used for modifiers which describe ++ * generic layouts (such as pixel re-ordering), which may have ++ * independently-developed support across multiple vendors. ++ * ++ * In future cases where a generic layout is identified before merging with a ++ * vendor-specific modifier, a new 'GENERIC' vendor or modifier using vendor ++ * 'NONE' could be considered. This should only be for obvious, exceptional ++ * cases to avoid polluting the 'GENERIC' namespace with modifiers which only ++ * apply to a single vendor. ++ * ++ * Generic names should not be used for cases where multiple hardware vendors ++ * have implementations of the same standardised compression scheme (such as ++ * AFBC). In those cases, all implementations should use the same format ++ * modifier(s), reflecting the vendor of the standard. ++ */ ++ ++#define DRM_FORMAT_MOD_GENERIC_16_16_TILE DRM_FORMAT_MOD_SAMSUNG_16_16_TILE ++ ++/* ++ * Invalid Modifier ++ * ++ * This modifier can be used as a sentinel to terminate the format modifiers ++ * list, or to initialize a variable with an invalid modifier. It might also be ++ * used to report an error back to userspace for certain APIs. ++ */ ++#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) ++ ++/* ++ * Linear Layout ++ * ++ * Just plain linear layout. Note that this is different from no specifying any ++ * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), ++ * which tells the driver to also take driver-internal information into account ++ * and so might actually result in a tiled framebuffer. ++ */ ++#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) ++ ++/* ++ * Deprecated: use DRM_FORMAT_MOD_LINEAR instead ++ * ++ * The "none" format modifier doesn't actually mean that the modifier is ++ * implicit, instead it means that the layout is linear. Whether modifiers are ++ * used is out-of-band information carried in an API-specific way (e.g. in a ++ * flag for drm_mode_fb_cmd2). ++ */ ++#define DRM_FORMAT_MOD_NONE 0 ++ ++/* Intel framebuffer modifiers */ ++ ++/* ++ * Intel X-tiling layout ++ * ++ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) ++ * in row-major layout. Within the tile bytes are laid out row-major, with ++ * a platform-dependent stride. On top of that the memory can apply ++ * platform-depending swizzling of some higher address bits into bit6. ++ * ++ * Note that this layout is only accurate on intel gen 8+ or valleyview chipsets. ++ * On earlier platforms the is highly platforms specific and not useful for ++ * cross-driver sharing. It exists since on a given platform it does uniquely ++ * identify the layout in a simple way for i915-specific userspace, which ++ * facilitated conversion of userspace to modifiers. Additionally the exact ++ * format on some really old platforms is not known. ++ */ ++#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) ++ ++/* ++ * Intel Y-tiling layout ++ * ++ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) ++ * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) ++ * chunks column-major, with a platform-dependent height. On top of that the ++ * memory can apply platform-depending swizzling of some higher address bits ++ * into bit6. ++ * ++ * Note that this layout is only accurate on intel gen 8+ or valleyview chipsets. ++ * On earlier platforms the is highly platforms specific and not useful for ++ * cross-driver sharing. It exists since on a given platform it does uniquely ++ * identify the layout in a simple way for i915-specific userspace, which ++ * facilitated conversion of userspace to modifiers. Additionally the exact ++ * format on some really old platforms is not known. ++ */ ++#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) ++ ++/* ++ * Intel Yf-tiling layout ++ * ++ * This is a tiled layout using 4Kb tiles in row-major layout. ++ * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which ++ * are arranged in four groups (two wide, two high) with column-major layout. ++ * Each group therefore consits out of four 256 byte units, which are also laid ++ * out as 2x2 column-major. ++ * 256 byte units are made out of four 64 byte blocks of pixels, producing ++ * either a square block or a 2:1 unit. ++ * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width ++ * in pixel depends on the pixel depth. ++ */ ++#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) ++ ++/* ++ * Intel color control surface (CCS) for render compression ++ * ++ * The framebuffer format must be one of the 8:8:8:8 RGB formats. ++ * The main surface will be plane index 0 and must be Y/Yf-tiled, ++ * the CCS will be plane index 1. ++ * ++ * Each CCS tile matches a 1024x512 pixel area of the main surface. ++ * To match certain aspects of the 3D hardware the CCS is ++ * considered to be made up of normal 128Bx32 Y tiles, Thus ++ * the CCS pitch must be specified in multiples of 128 bytes. ++ * ++ * In reality the CCS tile appears to be a 64Bx64 Y tile, composed ++ * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. ++ * But that fact is not relevant unless the memory is accessed ++ * directly. ++ */ ++#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) ++#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) ++ ++/* ++ * Intel color control surfaces (CCS) for Gen-12 render compression. ++ * ++ * The main surface is Y-tiled and at plane index 0, the CCS is linear and ++ * at index 1. A 64B CCS cache line corresponds to an area of 4x1 tiles in ++ * main surface. In other words, 4 bits in CCS map to a main surface cache ++ * line pair. The main surface pitch is required to be a multiple of four ++ * Y-tile widths. ++ */ ++#define I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS fourcc_mod_code(INTEL, 6) ++ ++/* ++ * Intel color control surfaces (CCS) for Gen-12 media compression ++ * ++ * The main surface is Y-tiled and at plane index 0, the CCS is linear and ++ * at index 1. A 64B CCS cache line corresponds to an area of 4x1 tiles in ++ * main surface. In other words, 4 bits in CCS map to a main surface cache ++ * line pair. The main surface pitch is required to be a multiple of four ++ * Y-tile widths. For semi-planar formats like NV12, CCS planes follow the ++ * Y and UV planes i.e., planes 0 and 1 are used for Y and UV surfaces, ++ * planes 2 and 3 for the respective CCS. ++ */ ++#define I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS fourcc_mod_code(INTEL, 7) ++ ++/* ++ * Intel Color Control Surface with Clear Color (CCS) for Gen-12 render ++ * compression. ++ * ++ * The main surface is Y-tiled and is at plane index 0 whereas CCS is linear ++ * and at index 1. The clear color is stored at index 2, and the pitch should ++ * be ignored. The clear color structure is 256 bits. The first 128 bits ++ * represents Raw Clear Color Red, Green, Blue and Alpha color each represented ++ * by 32 bits. The raw clear color is consumed by the 3d engine and generates ++ * the converted clear color of size 64 bits. The first 32 bits store the Lower ++ * Converted Clear Color value and the next 32 bits store the Higher Converted ++ * Clear Color value when applicable. The Converted Clear Color values are ++ * consumed by the DE. The last 64 bits are used to store Color Discard Enable ++ * and Depth Clear Value Valid which are ignored by the DE. A CCS cache line ++ * corresponds to an area of 4x1 tiles in the main surface. The main surface ++ * pitch is required to be a multiple of 4 tile widths. ++ */ ++#define I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC fourcc_mod_code(INTEL, 8) ++ ++/* ++ * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks ++ * ++ * Macroblocks are laid in a Z-shape, and each pixel data is following the ++ * standard NV12 style. ++ * As for NV12, an image is the result of two frame buffers: one for Y, ++ * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). ++ * Alignment requirements are (for each buffer): ++ * - multiple of 128 pixels for the width ++ * - multiple of 32 pixels for the height ++ * ++ * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html ++ */ ++#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) ++ ++/* ++ * Tiled, 16 (pixels) x 16 (lines) - sized macroblocks ++ * ++ * This is a simple tiled layout using tiles of 16x16 pixels in a row-major ++ * layout. For YCbCr formats Cb/Cr components are taken in such a way that ++ * they correspond to their 16x16 luma block. ++ */ ++#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2) ++ ++/* ++ * Qualcomm Compressed Format ++ * ++ * Refers to a compressed variant of the base format that is compressed. ++ * Implementation may be platform and base-format specific. ++ * ++ * Each macrotile consists of m x n (mostly 4 x 4) tiles. ++ * Pixel data pitch/stride is aligned with macrotile width. ++ * Pixel data height is aligned with macrotile height. ++ * Entire pixel data buffer is aligned with 4k(bytes). ++ */ ++#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) ++ ++/* Vivante framebuffer modifiers */ ++ ++/* ++ * Vivante 4x4 tiling layout ++ * ++ * This is a simple tiled layout using tiles of 4x4 pixels in a row-major ++ * layout. ++ */ ++#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) ++ ++/* ++ * Vivante 64x64 super-tiling layout ++ * ++ * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile ++ * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- ++ * major layout. ++ * ++ * For more information: see ++ * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling ++ */ ++#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) ++ ++/* ++ * Vivante 4x4 tiling layout for dual-pipe ++ * ++ * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a ++ * different base address. Offsets from the base addresses are therefore halved ++ * compared to the non-split tiled layout. ++ */ ++#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) ++ ++/* ++ * Vivante 64x64 super-tiling layout for dual-pipe ++ * ++ * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile ++ * starts at a different base address. Offsets from the base addresses are ++ * therefore halved compared to the non-split super-tiled layout. ++ */ ++#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) ++ ++/* NVIDIA frame buffer modifiers */ ++ ++/* ++ * Tegra Tiled Layout, used by Tegra 2, 3 and 4. ++ * ++ * Pixels are arranged in simple tiles of 16 x 16 bytes. ++ */ ++#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) ++ ++/* ++ * Generalized Block Linear layout, used by desktop GPUs starting with NV50/G80, ++ * and Tegra GPUs starting with Tegra K1. ++ * ++ * Pixels are arranged in Groups of Bytes (GOBs). GOB size and layout varies ++ * based on the architecture generation. GOBs themselves are then arranged in ++ * 3D blocks, with the block dimensions (in terms of GOBs) always being a power ++ * of two, and hence expressible as their log2 equivalent (E.g., "2" represents ++ * a block depth or height of "4"). ++ * ++ * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format ++ * in full detail. ++ * ++ * Macro ++ * Bits Param Description ++ * ---- ----- ----------------------------------------------------------------- ++ * ++ * 3:0 h log2(height) of each block, in GOBs. Placed here for ++ * compatibility with the existing ++ * DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK()-based modifiers. ++ * ++ * 4:4 - Must be 1, to indicate block-linear layout. Necessary for ++ * compatibility with the existing ++ * DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK()-based modifiers. ++ * ++ * 8:5 - Reserved (To support 3D-surfaces with variable log2(depth) block ++ * size). Must be zero. ++ * ++ * Note there is no log2(width) parameter. Some portions of the ++ * hardware support a block width of two gobs, but it is impractical ++ * to use due to lack of support elsewhere, and has no known ++ * benefits. ++ * ++ * 11:9 - Reserved (To support 2D-array textures with variable array stride ++ * in blocks, specified via log2(tile width in blocks)). Must be ++ * zero. ++ * ++ * 19:12 k Page Kind. This value directly maps to a field in the page ++ * tables of all GPUs >= NV50. It affects the exact layout of bits ++ * in memory and can be derived from the tuple ++ * ++ * (format, GPU model, compression type, samples per pixel) ++ * ++ * Where compression type is defined below. If GPU model were ++ * implied by the format modifier, format, or memory buffer, page ++ * kind would not need to be included in the modifier itself, but ++ * since the modifier should define the layout of the associated ++ * memory buffer independent from any device or other context, it ++ * must be included here. ++ * ++ * 21:20 g GOB Height and Page Kind Generation. The height of a GOB changed ++ * starting with Fermi GPUs. Additionally, the mapping between page ++ * kind and bit layout has changed at various points. ++ * ++ * 0 = Gob Height 8, Fermi - Volta, Tegra K1+ Page Kind mapping ++ * 1 = Gob Height 4, G80 - GT2XX Page Kind mapping ++ * 2 = Gob Height 8, Turing+ Page Kind mapping ++ * 3 = Reserved for future use. ++ * ++ * 22:22 s Sector layout. On Tegra GPUs prior to Xavier, there is a further ++ * bit remapping step that occurs at an even lower level than the ++ * page kind and block linear swizzles. This causes the layout of ++ * surfaces mapped in those SOC's GPUs to be incompatible with the ++ * equivalent mapping on other GPUs in the same system. ++ * ++ * 0 = Tegra K1 - Tegra Parker/TX2 Layout. ++ * 1 = Desktop GPU and Tegra Xavier+ Layout ++ * ++ * 25:23 c Lossless Framebuffer Compression type. ++ * ++ * 0 = none ++ * 1 = ROP/3D, layout 1, exact compression format implied by Page ++ * Kind field ++ * 2 = ROP/3D, layout 2, exact compression format implied by Page ++ * Kind field ++ * 3 = CDE horizontal ++ * 4 = CDE vertical ++ * 5 = Reserved for future use ++ * 6 = Reserved for future use ++ * 7 = Reserved for future use ++ * ++ * 55:25 - Reserved for future use. Must be zero. ++ */ ++#define DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(c, s, g, k, h) \ ++ fourcc_mod_code(NVIDIA, (0x10 | \ ++ ((h) & 0xf) | \ ++ (((k) & 0xff) << 12) | \ ++ (((g) & 0x3) << 20) | \ ++ (((s) & 0x1) << 22) | \ ++ (((c) & 0x7) << 23))) ++ ++/* To grandfather in prior block linear format modifiers to the above layout, ++ * the page kind "0", which corresponds to "pitch/linear" and hence is unusable ++ * with block-linear layouts, is remapped within drivers to the value 0xfe, ++ * which corresponds to the "generic" kind used for simple single-sample ++ * uncompressed color formats on Fermi - Volta GPUs. ++ */ ++static __inline__ __u64 ++drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) ++{ ++ if (!(modifier & 0x10) || (modifier & (0xff << 12))) ++ return modifier; ++ else ++ return modifier | (0xfe << 12); ++} ++ ++/* ++ * 16Bx2 Block Linear layout, used by Tegra K1 and later ++ * ++ * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked ++ * vertically by a power of 2 (1 to 32 GOBs) to form a block. ++ * ++ * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. ++ * ++ * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. ++ * Valid values are: ++ * ++ * 0 == ONE_GOB ++ * 1 == TWO_GOBS ++ * 2 == FOUR_GOBS ++ * 3 == EIGHT_GOBS ++ * 4 == SIXTEEN_GOBS ++ * 5 == THIRTYTWO_GOBS ++ * ++ * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format ++ * in full detail. ++ */ ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ ++ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 0, 0, 0, (v)) ++ ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) ++#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ ++ DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) ++ ++/* ++ * Some Broadcom modifiers take parameters, for example the number of ++ * vertical lines in the image. Reserve the lower 32 bits for modifier ++ * type, and the next 24 bits for parameters. Top 8 bits are the ++ * vendor code. ++ */ ++#define __fourcc_mod_broadcom_param_shift 8 ++#define __fourcc_mod_broadcom_param_bits 48 ++#define fourcc_mod_broadcom_code(val, params) \ ++ fourcc_mod_code(BROADCOM, ((((__u64)params) << __fourcc_mod_broadcom_param_shift) | val)) ++#define fourcc_mod_broadcom_param(m) \ ++ ((int)(((m) >> __fourcc_mod_broadcom_param_shift) & \ ++ ((1ULL << __fourcc_mod_broadcom_param_bits) - 1))) ++#define fourcc_mod_broadcom_mod(m) \ ++ ((m) & ~(((1ULL << __fourcc_mod_broadcom_param_bits) - 1) << \ ++ __fourcc_mod_broadcom_param_shift)) ++ ++/* ++ * Broadcom VC4 "T" format ++ * ++ * This is the primary layout that the V3D GPU can texture from (it ++ * can't do linear). The T format has: ++ * ++ * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 ++ * pixels at 32 bit depth. ++ * ++ * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually ++ * 16x16 pixels). ++ * ++ * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On ++ * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows ++ * they're (TR, BR, BL, TL), where bottom left is start of memory. ++ * ++ * - an image made of 4k tiles in rows either left-to-right (even rows of 4k ++ * tiles) or right-to-left (odd rows of 4k tiles). ++ */ ++#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) ++ ++/* ++ * Broadcom SAND format ++ * ++ * This is the native format that the H.264 codec block uses. For VC4 ++ * HVS, it is only valid for H.264 (NV12/21) and RGBA modes. ++ * ++ * The image can be considered to be split into columns, and the ++ * columns are placed consecutively into memory. The width of those ++ * columns can be either 32, 64, 128, or 256 pixels, but in practice ++ * only 128 pixel columns are used. ++ * ++ * The pitch between the start of each column is set to optimally ++ * switch between SDRAM banks. This is passed as the number of lines ++ * of column width in the modifier (we can't use the stride value due ++ * to various core checks that look at it , so you should set the ++ * stride to width*cpp). ++ * ++ * Note that the column height for this format modifier is the same ++ * for all of the planes, assuming that each column contains both Y ++ * and UV. Some SAND-using hardware stores UV in a separate tiled ++ * image from Y to reduce the column height, which is not supported ++ * with these modifiers. ++ */ ++ ++#define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \ ++ fourcc_mod_broadcom_code(2, v) ++#define DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(v) \ ++ fourcc_mod_broadcom_code(3, v) ++#define DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(v) \ ++ fourcc_mod_broadcom_code(4, v) ++#define DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(v) \ ++ fourcc_mod_broadcom_code(5, v) ++ ++#define DRM_FORMAT_MOD_BROADCOM_SAND32 \ ++ DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(0) ++#define DRM_FORMAT_MOD_BROADCOM_SAND64 \ ++ DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(0) ++#define DRM_FORMAT_MOD_BROADCOM_SAND128 \ ++ DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0) ++#define DRM_FORMAT_MOD_BROADCOM_SAND256 \ ++ DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(0) ++ ++/* Broadcom UIF format ++ * ++ * This is the common format for the current Broadcom multimedia ++ * blocks, including V3D 3.x and newer, newer video codecs, and ++ * displays. ++ * ++ * The image consists of utiles (64b blocks), UIF blocks (2x2 utiles), ++ * and macroblocks (4x4 UIF blocks). Those 4x4 UIF block groups are ++ * stored in columns, with padding between the columns to ensure that ++ * moving from one column to the next doesn't hit the same SDRAM page ++ * bank. ++ * ++ * To calculate the padding, it is assumed that each hardware block ++ * and the software driving it knows the platform's SDRAM page size, ++ * number of banks, and XOR address, and that it's identical between ++ * all blocks using the format. This tiling modifier will use XOR as ++ * necessary to reduce the padding. If a hardware block can't do XOR, ++ * the assumption is that a no-XOR tiling modifier will be created. ++ */ ++#define DRM_FORMAT_MOD_BROADCOM_UIF fourcc_mod_code(BROADCOM, 6) ++ ++/* ++ * Arm Framebuffer Compression (AFBC) modifiers ++ * ++ * AFBC is a proprietary lossless image compression protocol and format. ++ * It provides fine-grained random access and minimizes the amount of data ++ * transferred between IP blocks. ++ * ++ * AFBC has several features which may be supported and/or used, which are ++ * represented using bits in the modifier. Not all combinations are valid, ++ * and different devices or use-cases may support different combinations. ++ * ++ * Further information on the use of AFBC modifiers can be found in ++ * Documentation/gpu/afbc.rst ++ */ ++ ++/* ++ * The top 4 bits (out of the 56 bits alloted for specifying vendor specific ++ * modifiers) denote the category for modifiers. Currently we have three ++ * categories of modifiers ie AFBC, MISC and AFRC. We can have a maximum of ++ * sixteen different categories. ++ */ ++#define DRM_FORMAT_MOD_ARM_CODE(__type, __val) \ ++ fourcc_mod_code(ARM, ((__u64)(__type) << 52) | ((__val) & 0x000fffffffffffffULL)) ++ ++#define DRM_FORMAT_MOD_ARM_TYPE_AFBC 0x00 ++#define DRM_FORMAT_MOD_ARM_TYPE_MISC 0x01 ++ ++#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) \ ++ DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_AFBC, __afbc_mode) ++ ++/* ++ * AFBC superblock size ++ * ++ * Indicates the superblock size(s) used for the AFBC buffer. The buffer ++ * size (in pixels) must be aligned to a multiple of the superblock size. ++ * Four lowest significant bits(LSBs) are reserved for block size. ++ * ++ * Where one superblock size is specified, it applies to all planes of the ++ * buffer (e.g. 16x16, 32x8). When multiple superblock sizes are specified, ++ * the first applies to the Luma plane and the second applies to the Chroma ++ * plane(s). e.g. (32x8_64x4 means 32x8 Luma, with 64x4 Chroma). ++ * Multiple superblock sizes are only valid for multi-plane YCbCr formats. ++ */ ++#define AFBC_FORMAT_MOD_BLOCK_SIZE_MASK 0xf ++#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) ++#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 (2ULL) ++#define AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 (3ULL) ++#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4 (4ULL) ++ ++/* ++ * AFBC lossless colorspace transform ++ * ++ * Indicates that the buffer makes use of the AFBC lossless colorspace ++ * transform. ++ */ ++#define AFBC_FORMAT_MOD_YTR (1ULL << 4) ++ ++/* ++ * AFBC block-split ++ * ++ * Indicates that the payload of each superblock is split. The second ++ * half of the payload is positioned at a predefined offset from the start ++ * of the superblock payload. ++ */ ++#define AFBC_FORMAT_MOD_SPLIT (1ULL << 5) ++ ++/* ++ * AFBC sparse layout ++ * ++ * This flag indicates that the payload of each superblock must be stored at a ++ * predefined position relative to the other superblocks in the same AFBC ++ * buffer. This order is the same order used by the header buffer. In this mode ++ * each superblock is given the same amount of space as an uncompressed ++ * superblock of the particular format would require, rounding up to the next ++ * multiple of 128 bytes in size. ++ */ ++#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) ++ ++/* ++ * AFBC copy-block restrict ++ * ++ * Buffers with this flag must obey the copy-block restriction. The restriction ++ * is such that there are no copy-blocks referring across the border of 8x8 ++ * blocks. For the subsampled data the 8x8 limitation is also subsampled. ++ */ ++#define AFBC_FORMAT_MOD_CBR (1ULL << 7) ++ ++/* ++ * AFBC tiled layout ++ * ++ * The tiled layout groups superblocks in 8x8 or 4x4 tiles, where all ++ * superblocks inside a tile are stored together in memory. 8x8 tiles are used ++ * for pixel formats up to and including 32 bpp while 4x4 tiles are used for ++ * larger bpp formats. The order between the tiles is scan line. ++ * When the tiled layout is used, the buffer size (in pixels) must be aligned ++ * to the tile size. ++ */ ++#define AFBC_FORMAT_MOD_TILED (1ULL << 8) ++ ++/* ++ * AFBC solid color blocks ++ * ++ * Indicates that the buffer makes use of solid-color blocks, whereby bandwidth ++ * can be reduced if a whole superblock is a single color. ++ */ ++#define AFBC_FORMAT_MOD_SC (1ULL << 9) ++ ++/* ++ * AFBC double-buffer ++ * ++ * Indicates that the buffer is allocated in a layout safe for front-buffer ++ * rendering. ++ */ ++#define AFBC_FORMAT_MOD_DB (1ULL << 10) ++ ++/* ++ * AFBC buffer content hints ++ * ++ * Indicates that the buffer includes per-superblock content hints. ++ */ ++#define AFBC_FORMAT_MOD_BCH (1ULL << 11) ++ ++/* AFBC uncompressed storage mode ++ * ++ * Indicates that the buffer is using AFBC uncompressed storage mode. ++ * In this mode all superblock payloads in the buffer use the uncompressed ++ * storage mode, which is usually only used for data which cannot be compressed. ++ * The buffer layout is the same as for AFBC buffers without USM set, this only ++ * affects the storage mode of the individual superblocks. Note that even a ++ * buffer without USM set may use uncompressed storage mode for some or all ++ * superblocks, USM just guarantees it for all. ++ */ ++#define AFBC_FORMAT_MOD_USM (1ULL << 12) ++ ++/* ++ * Arm Fixed-Rate Compression (AFRC) modifiers ++ * ++ * AFRC is a proprietary fixed rate image compression protocol and format, ++ * designed to provide guaranteed bandwidth and memory footprint ++ * reductions in graphics and media use-cases. ++ * ++ * AFRC buffers consist of one or more planes, with the same components ++ * and meaning as an uncompressed buffer using the same pixel format. ++ * ++ * Within each plane, the pixel/luma/chroma values are grouped into ++ * "coding unit" blocks which are individually compressed to a ++ * fixed size (in bytes). All coding units within a given plane of a buffer ++ * store the same number of values, and have the same compressed size. ++ * ++ * The coding unit size is configurable, allowing different rates of compression. ++ * ++ * The start of each AFRC buffer plane must be aligned to an alignment granule which ++ * depends on the coding unit size. ++ * ++ * Coding Unit Size Plane Alignment ++ * ---------------- --------------- ++ * 16 bytes 1024 bytes ++ * 24 bytes 512 bytes ++ * 32 bytes 2048 bytes ++ * ++ * Coding units are grouped into paging tiles. AFRC buffer dimensions must be aligned ++ * to a multiple of the paging tile dimensions. ++ * The dimensions of each paging tile depend on whether the buffer is optimised for ++ * scanline (SCAN layout) or rotated (ROT layout) access. ++ * ++ * Layout Paging Tile Width Paging Tile Height ++ * ------ ----------------- ------------------ ++ * SCAN 16 coding units 4 coding units ++ * ROT 8 coding units 8 coding units ++ * ++ * The dimensions of each coding unit depend on the number of components ++ * in the compressed plane and whether the buffer is optimised for ++ * scanline (SCAN layout) or rotated (ROT layout) access. ++ * ++ * Number of Components in Plane Layout Coding Unit Width Coding Unit Height ++ * ----------------------------- --------- ----------------- ------------------ ++ * 1 SCAN 16 samples 4 samples ++ * Example: 16x4 luma samples in a 'Y' plane ++ * 16x4 chroma 'V' values, in the 'V' plane of a fully-planar YUV buffer ++ * ----------------------------- --------- ----------------- ------------------ ++ * 1 ROT 8 samples 8 samples ++ * Example: 8x8 luma samples in a 'Y' plane ++ * 8x8 chroma 'V' values, in the 'V' plane of a fully-planar YUV buffer ++ * ----------------------------- --------- ----------------- ------------------ ++ * 2 DONT CARE 8 samples 4 samples ++ * Example: 8x4 chroma pairs in the 'UV' plane of a semi-planar YUV buffer ++ * ----------------------------- --------- ----------------- ------------------ ++ * 3 DONT CARE 4 samples 4 samples ++ * Example: 4x4 pixels in an RGB buffer without alpha ++ * ----------------------------- --------- ----------------- ------------------ ++ * 4 DONT CARE 4 samples 4 samples ++ * Example: 4x4 pixels in an RGB buffer with alpha ++ */ ++ ++#define DRM_FORMAT_MOD_ARM_TYPE_AFRC 0x02 ++ ++#define DRM_FORMAT_MOD_ARM_AFRC(__afrc_mode) \ ++ DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_AFRC, __afrc_mode) ++ ++/* ++ * AFRC coding unit size modifier. ++ * ++ * Indicates the number of bytes used to store each compressed coding unit for ++ * one or more planes in an AFRC encoded buffer. The coding unit size for chrominance ++ * is the same for both Cb and Cr, which may be stored in separate planes. ++ * ++ * AFRC_FORMAT_MOD_CU_SIZE_P0 indicates the number of bytes used to store ++ * each compressed coding unit in the first plane of the buffer. For RGBA buffers ++ * this is the only plane, while for semi-planar and fully-planar YUV buffers, ++ * this corresponds to the luma plane. ++ * ++ * AFRC_FORMAT_MOD_CU_SIZE_P12 indicates the number of bytes used to store ++ * each compressed coding unit in the second and third planes in the buffer. ++ * For semi-planar and fully-planar YUV buffers, this corresponds to the chroma plane(s). ++ * ++ * For single-plane buffers, AFRC_FORMAT_MOD_CU_SIZE_P0 must be specified ++ * and AFRC_FORMAT_MOD_CU_SIZE_P12 must be zero. ++ * For semi-planar and fully-planar buffers, both AFRC_FORMAT_MOD_CU_SIZE_P0 and ++ * AFRC_FORMAT_MOD_CU_SIZE_P12 must be specified. ++ */ ++#define AFRC_FORMAT_MOD_CU_SIZE_MASK 0xf ++#define AFRC_FORMAT_MOD_CU_SIZE_16 (1ULL) ++#define AFRC_FORMAT_MOD_CU_SIZE_24 (2ULL) ++#define AFRC_FORMAT_MOD_CU_SIZE_32 (3ULL) ++ ++#define AFRC_FORMAT_MOD_CU_SIZE_P0(__afrc_cu_size) (__afrc_cu_size) ++#define AFRC_FORMAT_MOD_CU_SIZE_P12(__afrc_cu_size) ((__afrc_cu_size) << 4) ++ ++/* ++ * AFRC scanline memory layout. ++ * ++ * Indicates if the buffer uses the scanline-optimised layout ++ * for an AFRC encoded buffer, otherwise, it uses the rotation-optimised layout. ++ * The memory layout is the same for all planes. ++ */ ++#define AFRC_FORMAT_MOD_LAYOUT_SCAN (1ULL << 8) ++ ++/* ++ * Arm 16x16 Block U-Interleaved modifier ++ * ++ * This is used by Arm Mali Utgard and Midgard GPUs. It divides the image ++ * into 16x16 pixel blocks. Blocks are stored linearly in order, but pixels ++ * in the block are reordered. ++ */ ++#define DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED \ ++ DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_MISC, 1ULL) ++ ++/* ++ * Allwinner tiled modifier ++ * ++ * This tiling mode is implemented by the VPU found on all Allwinner platforms, ++ * codenamed sunxi. It is associated with a YUV format that uses either 2 or 3 ++ * planes. ++ * ++ * With this tiling, the luminance samples are disposed in tiles representing ++ * 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels. ++ * The pixel order in each tile is linear and the tiles are disposed linearly, ++ * both in row-major order. ++ */ ++#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1) ++ ++/* ++ * Amlogic Video Framebuffer Compression modifiers ++ * ++ * Amlogic uses a proprietary lossless image compression protocol and format ++ * for their hardware video codec accelerators, either video decoders or ++ * video input encoders. ++ * ++ * It considerably reduces memory bandwidth while writing and reading ++ * frames in memory. ++ * ++ * The underlying storage is considered to be 3 components, 8bit or 10-bit ++ * per component YCbCr 420, single plane : ++ * - DRM_FORMAT_YUV420_8BIT ++ * - DRM_FORMAT_YUV420_10BIT ++ * ++ * The first 8 bits of the mode defines the layout, then the following 8 bits ++ * defines the options changing the layout. ++ * ++ * Not all combinations are valid, and different SoCs may support different ++ * combinations of layout and options. ++ */ ++#define __fourcc_mod_amlogic_layout_mask 0xff ++#define __fourcc_mod_amlogic_options_shift 8 ++#define __fourcc_mod_amlogic_options_mask 0xff ++ ++#define DRM_FORMAT_MOD_AMLOGIC_FBC(__layout, __options) \ ++ fourcc_mod_code(AMLOGIC, \ ++ ((__layout) & __fourcc_mod_amlogic_layout_mask) | \ ++ (((__options) & __fourcc_mod_amlogic_options_mask) \ ++ << __fourcc_mod_amlogic_options_shift)) ++ ++/* Amlogic FBC Layouts */ ++ ++/* ++ * Amlogic FBC Basic Layout ++ * ++ * The basic layout is composed of: ++ * - a body content organized in 64x32 superblocks with 4096 bytes per ++ * superblock in default mode. ++ * - a 32 bytes per 128x64 header block ++ * ++ * This layout is transferrable between Amlogic SoCs supporting this modifier. ++ */ ++#define AMLOGIC_FBC_LAYOUT_BASIC (1ULL) ++ ++/* ++ * Amlogic FBC Scatter Memory layout ++ * ++ * Indicates the header contains IOMMU references to the compressed ++ * frames content to optimize memory access and layout. ++ * ++ * In this mode, only the header memory address is needed, thus the ++ * content memory organization is tied to the current producer ++ * execution and cannot be saved/dumped neither transferrable between ++ * Amlogic SoCs supporting this modifier. ++ * ++ * Due to the nature of the layout, these buffers are not expected to ++ * be accessible by the user-space clients, but only accessible by the ++ * hardware producers and consumers. ++ * ++ * The user-space clients should expect a failure while trying to mmap ++ * the DMA-BUF handle returned by the producer. ++ */ ++#define AMLOGIC_FBC_LAYOUT_SCATTER (2ULL) ++ ++/* Amlogic FBC Layout Options Bit Mask */ ++ ++/* ++ * Amlogic FBC Memory Saving mode ++ * ++ * Indicates the storage is packed when pixel size is multiple of word ++ * boudaries, i.e. 8bit should be stored in this mode to save allocation ++ * memory. ++ * ++ * This mode reduces body layout to 3072 bytes per 64x32 superblock with ++ * the basic layout and 3200 bytes per 64x32 superblock combined with ++ * the scatter layout. ++ */ ++#define AMLOGIC_FBC_OPTION_MEM_SAVING (1ULL << 0) ++ ++/* ++ * AMD modifiers ++ * ++ * Memory layout: ++ * ++ * without DCC: ++ * - main surface ++ * ++ * with DCC & without DCC_RETILE: ++ * - main surface in plane 0 ++ * - DCC surface in plane 1 (RB-aligned, pipe-aligned if DCC_PIPE_ALIGN is set) ++ * ++ * with DCC & DCC_RETILE: ++ * - main surface in plane 0 ++ * - displayable DCC surface in plane 1 (not RB-aligned & not pipe-aligned) ++ * - pipe-aligned DCC surface in plane 2 (RB-aligned & pipe-aligned) ++ * ++ * For multi-plane formats the above surfaces get merged into one plane for ++ * each format plane, based on the required alignment only. ++ * ++ * Bits Parameter Notes ++ * ----- ------------------------ --------------------------------------------- ++ * ++ * 7:0 TILE_VERSION Values are AMD_FMT_MOD_TILE_VER_* ++ * 12:8 TILE Values are AMD_FMT_MOD_TILE__* ++ * 13 DCC ++ * 14 DCC_RETILE ++ * 15 DCC_PIPE_ALIGN ++ * 16 DCC_INDEPENDENT_64B ++ * 17 DCC_INDEPENDENT_128B ++ * 19:18 DCC_MAX_COMPRESSED_BLOCK Values are AMD_FMT_MOD_DCC_BLOCK_* ++ * 20 DCC_CONSTANT_ENCODE ++ * 23:21 PIPE_XOR_BITS Only for some chips ++ * 26:24 BANK_XOR_BITS Only for some chips ++ * 29:27 PACKERS Only for some chips ++ * 32:30 RB Only for some chips ++ * 35:33 PIPE Only for some chips ++ * 55:36 - Reserved for future use, must be zero ++ */ ++#define AMD_FMT_MOD fourcc_mod_code(AMD, 0) ++ ++#define IS_AMD_FMT_MOD(val) (((val) >> 56) == DRM_FORMAT_MOD_VENDOR_AMD) ++ ++/* Reserve 0 for GFX8 and older */ ++#define AMD_FMT_MOD_TILE_VER_GFX9 1 ++#define AMD_FMT_MOD_TILE_VER_GFX10 2 ++#define AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS 3 ++ ++/* ++ * 64K_S is the same for GFX9/GFX10/GFX10_RBPLUS and hence has GFX9 as canonical ++ * version. ++ */ ++#define AMD_FMT_MOD_TILE_GFX9_64K_S 9 ++ ++/* ++ * 64K_D for non-32 bpp is the same for GFX9/GFX10/GFX10_RBPLUS and hence has ++ * GFX9 as canonical version. ++ */ ++#define AMD_FMT_MOD_TILE_GFX9_64K_D 10 ++#define AMD_FMT_MOD_TILE_GFX9_64K_S_X 25 ++#define AMD_FMT_MOD_TILE_GFX9_64K_D_X 26 ++#define AMD_FMT_MOD_TILE_GFX9_64K_R_X 27 ++ ++#define AMD_FMT_MOD_DCC_BLOCK_64B 0 ++#define AMD_FMT_MOD_DCC_BLOCK_128B 1 ++#define AMD_FMT_MOD_DCC_BLOCK_256B 2 ++ ++#define AMD_FMT_MOD_TILE_VERSION_SHIFT 0 ++#define AMD_FMT_MOD_TILE_VERSION_MASK 0xFF ++#define AMD_FMT_MOD_TILE_SHIFT 8 ++#define AMD_FMT_MOD_TILE_MASK 0x1F ++ ++/* Whether DCC compression is enabled. */ ++#define AMD_FMT_MOD_DCC_SHIFT 13 ++#define AMD_FMT_MOD_DCC_MASK 0x1 ++ ++/* ++ * Whether to include two DCC surfaces, one which is rb & pipe aligned, and ++ * one which is not-aligned. ++ */ ++#define AMD_FMT_MOD_DCC_RETILE_SHIFT 14 ++#define AMD_FMT_MOD_DCC_RETILE_MASK 0x1 ++ ++/* Only set if DCC_RETILE = false */ ++#define AMD_FMT_MOD_DCC_PIPE_ALIGN_SHIFT 15 ++#define AMD_FMT_MOD_DCC_PIPE_ALIGN_MASK 0x1 ++ ++#define AMD_FMT_MOD_DCC_INDEPENDENT_64B_SHIFT 16 ++#define AMD_FMT_MOD_DCC_INDEPENDENT_64B_MASK 0x1 ++#define AMD_FMT_MOD_DCC_INDEPENDENT_128B_SHIFT 17 ++#define AMD_FMT_MOD_DCC_INDEPENDENT_128B_MASK 0x1 ++#define AMD_FMT_MOD_DCC_MAX_COMPRESSED_BLOCK_SHIFT 18 ++#define AMD_FMT_MOD_DCC_MAX_COMPRESSED_BLOCK_MASK 0x3 ++ ++/* ++ * DCC supports embedding some clear colors directly in the DCC surface. ++ * However, on older GPUs the rendering HW ignores the embedded clear color ++ * and prefers the driver provided color. This necessitates doing a fastclear ++ * eliminate operation before a process transfers control. ++ * ++ * If this bit is set that means the fastclear eliminate is not needed for these ++ * embeddable colors. ++ */ ++#define AMD_FMT_MOD_DCC_CONSTANT_ENCODE_SHIFT 20 ++#define AMD_FMT_MOD_DCC_CONSTANT_ENCODE_MASK 0x1 ++ ++/* ++ * The below fields are for accounting for per GPU differences. These are only ++ * relevant for GFX9 and later and if the tile field is *_X/_T. ++ * ++ * PIPE_XOR_BITS = always needed ++ * BANK_XOR_BITS = only for TILE_VER_GFX9 ++ * PACKERS = only for TILE_VER_GFX10_RBPLUS ++ * RB = only for TILE_VER_GFX9 & DCC ++ * PIPE = only for TILE_VER_GFX9 & DCC & (DCC_RETILE | DCC_PIPE_ALIGN) ++ */ ++#define AMD_FMT_MOD_PIPE_XOR_BITS_SHIFT 21 ++#define AMD_FMT_MOD_PIPE_XOR_BITS_MASK 0x7 ++#define AMD_FMT_MOD_BANK_XOR_BITS_SHIFT 24 ++#define AMD_FMT_MOD_BANK_XOR_BITS_MASK 0x7 ++#define AMD_FMT_MOD_PACKERS_SHIFT 27 ++#define AMD_FMT_MOD_PACKERS_MASK 0x7 ++#define AMD_FMT_MOD_RB_SHIFT 30 ++#define AMD_FMT_MOD_RB_MASK 0x7 ++#define AMD_FMT_MOD_PIPE_SHIFT 33 ++#define AMD_FMT_MOD_PIPE_MASK 0x7 ++ ++#define AMD_FMT_MOD_SET(field, value) \ ++ ((uint64_t)(value) << AMD_FMT_MOD_##field##_SHIFT) ++#define AMD_FMT_MOD_GET(field, value) \ ++ (((value) >> AMD_FMT_MOD_##field##_SHIFT) & AMD_FMT_MOD_##field##_MASK) ++#define AMD_FMT_MOD_CLEAR(field) \ ++ (~((uint64_t)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT)) ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif /* DRM_FOURCC_H */ +diff --git a/third_party/drm/drm/drm_mode.h b/third_party/drm/drm/drm_mode.h +new file mode 100644 +index 0000000..e1e3516 +--- /dev/null ++++ b/third_party/drm/drm/drm_mode.h +@@ -0,0 +1,1217 @@ ++/* ++ * Copyright (c) 2007 Dave Airlie ++ * Copyright (c) 2007 Jakob Bornecrantz ++ * Copyright (c) 2008 Red Hat Inc. ++ * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA ++ * Copyright (c) 2007-2008 Intel Corporation ++ * ++ * 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 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. ++ */ ++ ++#ifndef _DRM_MODE_H ++#define _DRM_MODE_H ++ ++#include "drm.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/** ++ * DOC: overview ++ * ++ * DRM exposes many UAPI and structure definition to have a consistent ++ * and standardized interface with user. ++ * Userspace can refer to these structure definitions and UAPI formats ++ * to communicate to driver ++ */ ++ ++#define DRM_CONNECTOR_NAME_LEN 32 ++#define DRM_DISPLAY_MODE_LEN 32 ++#define DRM_PROP_NAME_LEN 32 ++ ++#define DRM_MODE_TYPE_BUILTIN (1<<0) /* deprecated */ ++#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ ++#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ ++#define DRM_MODE_TYPE_PREFERRED (1<<3) ++#define DRM_MODE_TYPE_DEFAULT (1<<4) /* deprecated */ ++#define DRM_MODE_TYPE_USERDEF (1<<5) ++#define DRM_MODE_TYPE_DRIVER (1<<6) ++ ++#define DRM_MODE_TYPE_ALL (DRM_MODE_TYPE_PREFERRED | \ ++ DRM_MODE_TYPE_USERDEF | \ ++ DRM_MODE_TYPE_DRIVER) ++ ++/* Video mode flags */ ++/* bit compatible with the xrandr RR_ definitions (bits 0-13) ++ * ++ * ABI warning: Existing userspace really expects ++ * the mode flags to match the xrandr definitions. Any ++ * changes that don't match the xrandr definitions will ++ * likely need a new client cap or some other mechanism ++ * to avoid breaking existing userspace. This includes ++ * allocating new flags in the previously unused bits! ++ */ ++#define DRM_MODE_FLAG_PHSYNC (1<<0) ++#define DRM_MODE_FLAG_NHSYNC (1<<1) ++#define DRM_MODE_FLAG_PVSYNC (1<<2) ++#define DRM_MODE_FLAG_NVSYNC (1<<3) ++#define DRM_MODE_FLAG_INTERLACE (1<<4) ++#define DRM_MODE_FLAG_DBLSCAN (1<<5) ++#define DRM_MODE_FLAG_CSYNC (1<<6) ++#define DRM_MODE_FLAG_PCSYNC (1<<7) ++#define DRM_MODE_FLAG_NCSYNC (1<<8) ++#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ ++#define DRM_MODE_FLAG_BCAST (1<<10) /* deprecated */ ++#define DRM_MODE_FLAG_PIXMUX (1<<11) /* deprecated */ ++#define DRM_MODE_FLAG_DBLCLK (1<<12) ++#define DRM_MODE_FLAG_CLKDIV2 (1<<13) ++ /* ++ * When adding a new stereo mode don't forget to adjust DRM_MODE_FLAGS_3D_MAX ++ * (define not exposed to user space). ++ */ ++#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) ++#define DRM_MODE_FLAG_3D_NONE (0<<14) ++#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) ++#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) ++#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) ++#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) ++#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) ++#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) ++#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) ++#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) ++ ++/* Picture aspect ratio options */ ++#define DRM_MODE_PICTURE_ASPECT_NONE 0 ++#define DRM_MODE_PICTURE_ASPECT_4_3 1 ++#define DRM_MODE_PICTURE_ASPECT_16_9 2 ++#define DRM_MODE_PICTURE_ASPECT_64_27 3 ++#define DRM_MODE_PICTURE_ASPECT_256_135 4 ++ ++/* Content type options */ ++#define DRM_MODE_CONTENT_TYPE_NO_DATA 0 ++#define DRM_MODE_CONTENT_TYPE_GRAPHICS 1 ++#define DRM_MODE_CONTENT_TYPE_PHOTO 2 ++#define DRM_MODE_CONTENT_TYPE_CINEMA 3 ++#define DRM_MODE_CONTENT_TYPE_GAME 4 ++ ++/* Aspect ratio flag bitmask (4 bits 22:19) */ ++#define DRM_MODE_FLAG_PIC_AR_MASK (0x0F<<19) ++#define DRM_MODE_FLAG_PIC_AR_NONE \ ++ (DRM_MODE_PICTURE_ASPECT_NONE<<19) ++#define DRM_MODE_FLAG_PIC_AR_4_3 \ ++ (DRM_MODE_PICTURE_ASPECT_4_3<<19) ++#define DRM_MODE_FLAG_PIC_AR_16_9 \ ++ (DRM_MODE_PICTURE_ASPECT_16_9<<19) ++#define DRM_MODE_FLAG_PIC_AR_64_27 \ ++ (DRM_MODE_PICTURE_ASPECT_64_27<<19) ++#define DRM_MODE_FLAG_PIC_AR_256_135 \ ++ (DRM_MODE_PICTURE_ASPECT_256_135<<19) ++ ++#define DRM_MODE_FLAG_ALL (DRM_MODE_FLAG_PHSYNC | \ ++ DRM_MODE_FLAG_NHSYNC | \ ++ DRM_MODE_FLAG_PVSYNC | \ ++ DRM_MODE_FLAG_NVSYNC | \ ++ DRM_MODE_FLAG_INTERLACE | \ ++ DRM_MODE_FLAG_DBLSCAN | \ ++ DRM_MODE_FLAG_CSYNC | \ ++ DRM_MODE_FLAG_PCSYNC | \ ++ DRM_MODE_FLAG_NCSYNC | \ ++ DRM_MODE_FLAG_HSKEW | \ ++ DRM_MODE_FLAG_DBLCLK | \ ++ DRM_MODE_FLAG_CLKDIV2 | \ ++ DRM_MODE_FLAG_3D_MASK) ++ ++/* DPMS flags */ ++/* bit compatible with the xorg definitions. */ ++#define DRM_MODE_DPMS_ON 0 ++#define DRM_MODE_DPMS_STANDBY 1 ++#define DRM_MODE_DPMS_SUSPEND 2 ++#define DRM_MODE_DPMS_OFF 3 ++ ++/* Scaling mode options */ ++#define DRM_MODE_SCALE_NONE 0 /* Unmodified timing (display or ++ software can still scale) */ ++#define DRM_MODE_SCALE_FULLSCREEN 1 /* Full screen, ignore aspect */ ++#define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ ++#define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ ++ ++/* Dithering mode options */ ++#define DRM_MODE_DITHERING_OFF 0 ++#define DRM_MODE_DITHERING_ON 1 ++#define DRM_MODE_DITHERING_AUTO 2 ++ ++/* Dirty info options */ ++#define DRM_MODE_DIRTY_OFF 0 ++#define DRM_MODE_DIRTY_ON 1 ++#define DRM_MODE_DIRTY_ANNOTATE 2 ++ ++/* Link Status options */ ++#define DRM_MODE_LINK_STATUS_GOOD 0 ++#define DRM_MODE_LINK_STATUS_BAD 1 ++ ++/* ++ * DRM_MODE_ROTATE_ ++ * ++ * Signals that a drm plane is been rotated degrees in counter ++ * clockwise direction. ++ * ++ * This define is provided as a convenience, looking up the property id ++ * using the name->prop id lookup is the preferred method. ++ */ ++#define DRM_MODE_ROTATE_0 (1<<0) ++#define DRM_MODE_ROTATE_90 (1<<1) ++#define DRM_MODE_ROTATE_180 (1<<2) ++#define DRM_MODE_ROTATE_270 (1<<3) ++ ++/* ++ * DRM_MODE_ROTATE_MASK ++ * ++ * Bitmask used to look for drm plane rotations. ++ */ ++#define DRM_MODE_ROTATE_MASK (\ ++ DRM_MODE_ROTATE_0 | \ ++ DRM_MODE_ROTATE_90 | \ ++ DRM_MODE_ROTATE_180 | \ ++ DRM_MODE_ROTATE_270) ++ ++/* ++ * DRM_MODE_REFLECT_ ++ * ++ * Signals that the contents of a drm plane is reflected along the axis, ++ * in the same way as mirroring. ++ * See kerneldoc chapter "Plane Composition Properties" for more details. ++ * ++ * This define is provided as a convenience, looking up the property id ++ * using the name->prop id lookup is the preferred method. ++ */ ++#define DRM_MODE_REFLECT_X (1<<4) ++#define DRM_MODE_REFLECT_Y (1<<5) ++ ++/* ++ * DRM_MODE_REFLECT_MASK ++ * ++ * Bitmask used to look for drm plane reflections. ++ */ ++#define DRM_MODE_REFLECT_MASK (\ ++ DRM_MODE_REFLECT_X | \ ++ DRM_MODE_REFLECT_Y) ++ ++/* Content Protection Flags */ ++#define DRM_MODE_CONTENT_PROTECTION_UNDESIRED 0 ++#define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 ++#define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 ++ ++/** ++ * struct drm_mode_modeinfo - Display mode information. ++ * @clock: pixel clock in kHz ++ * @hdisplay: horizontal display size ++ * @hsync_start: horizontal sync start ++ * @hsync_end: horizontal sync end ++ * @htotal: horizontal total size ++ * @hskew: horizontal skew ++ * @vdisplay: vertical display size ++ * @vsync_start: vertical sync start ++ * @vsync_end: vertical sync end ++ * @vtotal: vertical total size ++ * @vscan: vertical scan ++ * @vrefresh: approximate vertical refresh rate in Hz ++ * @flags: bitmask of misc. flags, see DRM_MODE_FLAG_* defines ++ * @type: bitmask of type flags, see DRM_MODE_TYPE_* defines ++ * @name: string describing the mode resolution ++ * ++ * This is the user-space API display mode information structure. For the ++ * kernel version see struct drm_display_mode. ++ */ ++struct drm_mode_modeinfo { ++ __u32 clock; ++ __u16 hdisplay; ++ __u16 hsync_start; ++ __u16 hsync_end; ++ __u16 htotal; ++ __u16 hskew; ++ __u16 vdisplay; ++ __u16 vsync_start; ++ __u16 vsync_end; ++ __u16 vtotal; ++ __u16 vscan; ++ ++ __u32 vrefresh; ++ ++ __u32 flags; ++ __u32 type; ++ char name[DRM_DISPLAY_MODE_LEN]; ++}; ++ ++struct drm_mode_card_res { ++ __u64 fb_id_ptr; ++ __u64 crtc_id_ptr; ++ __u64 connector_id_ptr; ++ __u64 encoder_id_ptr; ++ __u32 count_fbs; ++ __u32 count_crtcs; ++ __u32 count_connectors; ++ __u32 count_encoders; ++ __u32 min_width; ++ __u32 max_width; ++ __u32 min_height; ++ __u32 max_height; ++}; ++ ++struct drm_mode_crtc { ++ __u64 set_connectors_ptr; ++ __u32 count_connectors; ++ ++ __u32 crtc_id; /**< Id */ ++ __u32 fb_id; /**< Id of framebuffer */ ++ ++ __u32 x; /**< x Position on the framebuffer */ ++ __u32 y; /**< y Position on the framebuffer */ ++ ++ __u32 gamma_size; ++ __u32 mode_valid; ++ struct drm_mode_modeinfo mode; ++}; ++ ++#define DRM_MODE_PRESENT_TOP_FIELD (1<<0) ++#define DRM_MODE_PRESENT_BOTTOM_FIELD (1<<1) ++ ++/* Planes blend with or override other bits on the CRTC */ ++struct drm_mode_set_plane { ++ __u32 plane_id; ++ __u32 crtc_id; ++ __u32 fb_id; /* fb object contains surface format type */ ++ __u32 flags; /* see above flags */ ++ ++ /* Signed dest location allows it to be partially off screen */ ++ __s32 crtc_x; ++ __s32 crtc_y; ++ __u32 crtc_w; ++ __u32 crtc_h; ++ ++ /* Source values are 16.16 fixed point */ ++ __u32 src_x; ++ __u32 src_y; ++ __u32 src_h; ++ __u32 src_w; ++}; ++ ++/** ++ * struct drm_mode_get_plane - Get plane metadata. ++ * ++ * Userspace can perform a GETPLANE ioctl to retrieve information about a ++ * plane. ++ * ++ * To retrieve the number of formats supported, set @count_format_types to zero ++ * and call the ioctl. @count_format_types will be updated with the value. ++ * ++ * To retrieve these formats, allocate an array with the memory needed to store ++ * @count_format_types formats. Point @format_type_ptr to this array and call ++ * the ioctl again (with @count_format_types still set to the value returned in ++ * the first ioctl call). ++ */ ++struct drm_mode_get_plane { ++ /** ++ * @plane_id: Object ID of the plane whose information should be ++ * retrieved. Set by caller. ++ */ ++ __u32 plane_id; ++ ++ /** @crtc_id: Object ID of the current CRTC. */ ++ __u32 crtc_id; ++ /** @fb_id: Object ID of the current fb. */ ++ __u32 fb_id; ++ ++ /** ++ * @possible_crtcs: Bitmask of CRTC's compatible with the plane. CRTC's ++ * are created and they receive an index, which corresponds to their ++ * position in the bitmask. Bit N corresponds to ++ * :ref:`CRTC index` N. ++ */ ++ __u32 possible_crtcs; ++ /** @gamma_size: Never used. */ ++ __u32 gamma_size; ++ ++ /** @count_format_types: Number of formats. */ ++ __u32 count_format_types; ++ /** ++ * @format_type_ptr: Pointer to ``__u32`` array of formats that are ++ * supported by the plane. These formats do not require modifiers. ++ */ ++ __u64 format_type_ptr; ++}; ++ ++struct drm_mode_get_plane_res { ++ __u64 plane_id_ptr; ++ __u32 count_planes; ++}; ++ ++#define DRM_MODE_ENCODER_NONE 0 ++#define DRM_MODE_ENCODER_DAC 1 ++#define DRM_MODE_ENCODER_TMDS 2 ++#define DRM_MODE_ENCODER_LVDS 3 ++#define DRM_MODE_ENCODER_TVDAC 4 ++#define DRM_MODE_ENCODER_VIRTUAL 5 ++#define DRM_MODE_ENCODER_DSI 6 ++#define DRM_MODE_ENCODER_DPMST 7 ++#define DRM_MODE_ENCODER_DPI 8 ++ ++struct drm_mode_get_encoder { ++ __u32 encoder_id; ++ __u32 encoder_type; ++ ++ __u32 crtc_id; /**< Id of crtc */ ++ ++ __u32 possible_crtcs; ++ __u32 possible_clones; ++}; ++ ++/* This is for connectors with multiple signal types. */ ++/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ ++enum drm_mode_subconnector { ++ DRM_MODE_SUBCONNECTOR_Automatic = 0, /* DVI-I, TV */ ++ DRM_MODE_SUBCONNECTOR_Unknown = 0, /* DVI-I, TV, DP */ ++ DRM_MODE_SUBCONNECTOR_VGA = 1, /* DP */ ++ DRM_MODE_SUBCONNECTOR_DVID = 3, /* DVI-I DP */ ++ DRM_MODE_SUBCONNECTOR_DVIA = 4, /* DVI-I */ ++ DRM_MODE_SUBCONNECTOR_Composite = 5, /* TV */ ++ DRM_MODE_SUBCONNECTOR_SVIDEO = 6, /* TV */ ++ DRM_MODE_SUBCONNECTOR_Component = 8, /* TV */ ++ DRM_MODE_SUBCONNECTOR_SCART = 9, /* TV */ ++ DRM_MODE_SUBCONNECTOR_DisplayPort = 10, /* DP */ ++ DRM_MODE_SUBCONNECTOR_HDMIA = 11, /* DP */ ++ DRM_MODE_SUBCONNECTOR_Native = 15, /* DP */ ++ DRM_MODE_SUBCONNECTOR_Wireless = 18, /* DP */ ++}; ++ ++#define DRM_MODE_CONNECTOR_Unknown 0 ++#define DRM_MODE_CONNECTOR_VGA 1 ++#define DRM_MODE_CONNECTOR_DVII 2 ++#define DRM_MODE_CONNECTOR_DVID 3 ++#define DRM_MODE_CONNECTOR_DVIA 4 ++#define DRM_MODE_CONNECTOR_Composite 5 ++#define DRM_MODE_CONNECTOR_SVIDEO 6 ++#define DRM_MODE_CONNECTOR_LVDS 7 ++#define DRM_MODE_CONNECTOR_Component 8 ++#define DRM_MODE_CONNECTOR_9PinDIN 9 ++#define DRM_MODE_CONNECTOR_DisplayPort 10 ++#define DRM_MODE_CONNECTOR_HDMIA 11 ++#define DRM_MODE_CONNECTOR_HDMIB 12 ++#define DRM_MODE_CONNECTOR_TV 13 ++#define DRM_MODE_CONNECTOR_eDP 14 ++#define DRM_MODE_CONNECTOR_VIRTUAL 15 ++#define DRM_MODE_CONNECTOR_DSI 16 ++#define DRM_MODE_CONNECTOR_DPI 17 ++#define DRM_MODE_CONNECTOR_WRITEBACK 18 ++#define DRM_MODE_CONNECTOR_SPI 19 ++#define DRM_MODE_CONNECTOR_USB 20 ++ ++/** ++ * struct drm_mode_get_connector - Get connector metadata. ++ * ++ * User-space can perform a GETCONNECTOR ioctl to retrieve information about a ++ * connector. User-space is expected to retrieve encoders, modes and properties ++ * by performing this ioctl at least twice: the first time to retrieve the ++ * number of elements, the second time to retrieve the elements themselves. ++ * ++ * To retrieve the number of elements, set @count_props and @count_encoders to ++ * zero, set @count_modes to 1, and set @modes_ptr to a temporary struct ++ * drm_mode_modeinfo element. ++ * ++ * To retrieve the elements, allocate arrays for @encoders_ptr, @modes_ptr, ++ * @props_ptr and @prop_values_ptr, then set @count_modes, @count_props and ++ * @count_encoders to their capacity. ++ * ++ * Performing the ioctl only twice may be racy: the number of elements may have ++ * changed with a hotplug event in-between the two ioctls. User-space is ++ * expected to retry the last ioctl until the number of elements stabilizes. ++ * The kernel won't fill any array which doesn't have the expected length. ++ * ++ * **Force-probing a connector** ++ * ++ * If the @count_modes field is set to zero and the DRM client is the current ++ * DRM master, the kernel will perform a forced probe on the connector to ++ * refresh the connector status, modes and EDID. A forced-probe can be slow, ++ * might cause flickering and the ioctl will block. ++ * ++ * User-space needs to force-probe connectors to ensure their metadata is ++ * up-to-date at startup and after receiving a hot-plug event. User-space ++ * may perform a forced-probe when the user explicitly requests it. User-space ++ * shouldn't perform a forced-probe in other situations. ++ */ ++struct drm_mode_get_connector { ++ /** @encoders_ptr: Pointer to ``__u32`` array of object IDs. */ ++ __u64 encoders_ptr; ++ /** @modes_ptr: Pointer to struct drm_mode_modeinfo array. */ ++ __u64 modes_ptr; ++ /** @props_ptr: Pointer to ``__u32`` array of property IDs. */ ++ __u64 props_ptr; ++ /** @prop_values_ptr: Pointer to ``__u64`` array of property values. */ ++ __u64 prop_values_ptr; ++ ++ /** @count_modes: Number of modes. */ ++ __u32 count_modes; ++ /** @count_props: Number of properties. */ ++ __u32 count_props; ++ /** @count_encoders: Number of encoders. */ ++ __u32 count_encoders; ++ ++ /** @encoder_id: Object ID of the current encoder. */ ++ __u32 encoder_id; ++ /** @connector_id: Object ID of the connector. */ ++ __u32 connector_id; ++ /** ++ * @connector_type: Type of the connector. ++ * ++ * See DRM_MODE_CONNECTOR_* defines. ++ */ ++ __u32 connector_type; ++ /** ++ * @connector_type_id: Type-specific connector number. ++ * ++ * This is not an object ID. This is a per-type connector number. Each ++ * (type, type_id) combination is unique across all connectors of a DRM ++ * device. ++ */ ++ __u32 connector_type_id; ++ ++ /** ++ * @connection: Status of the connector. ++ * ++ * See enum drm_connector_status. ++ */ ++ __u32 connection; ++ /** @mm_width: Width of the connected sink in millimeters. */ ++ __u32 mm_width; ++ /** @mm_height: Height of the connected sink in millimeters. */ ++ __u32 mm_height; ++ /** ++ * @subpixel: Subpixel order of the connected sink. ++ * ++ * See enum subpixel_order. ++ */ ++ __u32 subpixel; ++ ++ /** @pad: Padding, must be zero. */ ++ __u32 pad; ++}; ++ ++#define DRM_MODE_PROP_PENDING (1<<0) /* deprecated, do not use */ ++#define DRM_MODE_PROP_RANGE (1<<1) ++#define DRM_MODE_PROP_IMMUTABLE (1<<2) ++#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ ++#define DRM_MODE_PROP_BLOB (1<<4) ++#define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */ ++ ++/* non-extended types: legacy bitmask, one bit per type: */ ++#define DRM_MODE_PROP_LEGACY_TYPE ( \ ++ DRM_MODE_PROP_RANGE | \ ++ DRM_MODE_PROP_ENUM | \ ++ DRM_MODE_PROP_BLOB | \ ++ DRM_MODE_PROP_BITMASK) ++ ++/* extended-types: rather than continue to consume a bit per type, ++ * grab a chunk of the bits to use as integer type id. ++ */ ++#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 ++#define DRM_MODE_PROP_TYPE(n) ((n) << 6) ++#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) ++#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) ++ ++/* the PROP_ATOMIC flag is used to hide properties from userspace that ++ * is not aware of atomic properties. This is mostly to work around ++ * older userspace (DDX drivers) that read/write each prop they find, ++ * witout being aware that this could be triggering a lengthy modeset. ++ */ ++#define DRM_MODE_PROP_ATOMIC 0x80000000 ++ ++/** ++ * struct drm_mode_property_enum - Description for an enum/bitfield entry. ++ * @value: numeric value for this enum entry. ++ * @name: symbolic name for this enum entry. ++ * ++ * See struct drm_property_enum for details. ++ */ ++struct drm_mode_property_enum { ++ __u64 value; ++ char name[DRM_PROP_NAME_LEN]; ++}; ++ ++/** ++ * struct drm_mode_get_property - Get property metadata. ++ * ++ * User-space can perform a GETPROPERTY ioctl to retrieve information about a ++ * property. The same property may be attached to multiple objects, see ++ * "Modeset Base Object Abstraction". ++ * ++ * The meaning of the @values_ptr field changes depending on the property type. ++ * See &drm_property.flags for more details. ++ * ++ * The @enum_blob_ptr and @count_enum_blobs fields are only meaningful when the ++ * property has the type &DRM_MODE_PROP_ENUM or &DRM_MODE_PROP_BITMASK. For ++ * backwards compatibility, the kernel will always set @count_enum_blobs to ++ * zero when the property has the type &DRM_MODE_PROP_BLOB. User-space must ++ * ignore these two fields if the property has a different type. ++ * ++ * User-space is expected to retrieve values and enums by performing this ioctl ++ * at least twice: the first time to retrieve the number of elements, the ++ * second time to retrieve the elements themselves. ++ * ++ * To retrieve the number of elements, set @count_values and @count_enum_blobs ++ * to zero, then call the ioctl. @count_values will be updated with the number ++ * of elements. If the property has the type &DRM_MODE_PROP_ENUM or ++ * &DRM_MODE_PROP_BITMASK, @count_enum_blobs will be updated as well. ++ * ++ * To retrieve the elements themselves, allocate an array for @values_ptr and ++ * set @count_values to its capacity. If the property has the type ++ * &DRM_MODE_PROP_ENUM or &DRM_MODE_PROP_BITMASK, allocate an array for ++ * @enum_blob_ptr and set @count_enum_blobs to its capacity. Calling the ioctl ++ * again will fill the arrays. ++ */ ++struct drm_mode_get_property { ++ /** @values_ptr: Pointer to a ``__u64`` array. */ ++ __u64 values_ptr; ++ /** @enum_blob_ptr: Pointer to a struct drm_mode_property_enum array. */ ++ __u64 enum_blob_ptr; ++ ++ /** ++ * @prop_id: Object ID of the property which should be retrieved. Set ++ * by the caller. ++ */ ++ __u32 prop_id; ++ /** ++ * @flags: ``DRM_MODE_PROP_*`` bitfield. See &drm_property.flags for ++ * a definition of the flags. ++ */ ++ __u32 flags; ++ /** ++ * @name: Symbolic property name. User-space should use this field to ++ * recognize properties. ++ */ ++ char name[DRM_PROP_NAME_LEN]; ++ ++ /** @count_values: Number of elements in @values_ptr. */ ++ __u32 count_values; ++ /** @count_enum_blobs: Number of elements in @enum_blob_ptr. */ ++ __u32 count_enum_blobs; ++}; ++ ++struct drm_mode_connector_set_property { ++ __u64 value; ++ __u32 prop_id; ++ __u32 connector_id; ++}; ++ ++#define DRM_MODE_OBJECT_CRTC 0xcccccccc ++#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 ++#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 ++#define DRM_MODE_OBJECT_MODE 0xdededede ++#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 ++#define DRM_MODE_OBJECT_FB 0xfbfbfbfb ++#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb ++#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee ++#define DRM_MODE_OBJECT_ANY 0 ++ ++struct drm_mode_obj_get_properties { ++ __u64 props_ptr; ++ __u64 prop_values_ptr; ++ __u32 count_props; ++ __u32 obj_id; ++ __u32 obj_type; ++}; ++ ++struct drm_mode_obj_set_property { ++ __u64 value; ++ __u32 prop_id; ++ __u32 obj_id; ++ __u32 obj_type; ++}; ++ ++struct drm_mode_get_blob { ++ __u32 blob_id; ++ __u32 length; ++ __u64 data; ++}; ++ ++struct drm_mode_fb_cmd { ++ __u32 fb_id; ++ __u32 width; ++ __u32 height; ++ __u32 pitch; ++ __u32 bpp; ++ __u32 depth; ++ /* driver specific handle */ ++ __u32 handle; ++}; ++ ++#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ ++#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ ++ ++struct drm_mode_fb_cmd2 { ++ __u32 fb_id; ++ __u32 width; ++ __u32 height; ++ __u32 pixel_format; /* fourcc code from drm_fourcc.h */ ++ __u32 flags; /* see above flags */ ++ ++ /* ++ * In case of planar formats, this ioctl allows up to 4 ++ * buffer objects with offsets and pitches per plane. ++ * The pitch and offset order is dictated by the fourcc, ++ * e.g. NV12 (https://fourcc.org/yuv.php#NV12) is described as: ++ * ++ * YUV 4:2:0 image with a plane of 8 bit Y samples ++ * followed by an interleaved U/V plane containing ++ * 8 bit 2x2 subsampled colour difference samples. ++ * ++ * So it would consist of Y as offsets[0] and UV as ++ * offsets[1]. Note that offsets[0] will generally ++ * be 0 (but this is not required). ++ * ++ * To accommodate tiled, compressed, etc formats, a ++ * modifier can be specified. The default value of zero ++ * indicates "native" format as specified by the fourcc. ++ * Vendor specific modifier token. Note that even though ++ * it looks like we have a modifier per-plane, we in fact ++ * do not. The modifier for each plane must be identical. ++ * Thus all combinations of different data layouts for ++ * multi plane formats must be enumerated as separate ++ * modifiers. ++ */ ++ __u32 handles[4]; ++ __u32 pitches[4]; /* pitch for each plane */ ++ __u32 offsets[4]; /* offset of each plane */ ++ __u64 modifier[4]; /* ie, tiling, compress */ ++}; ++ ++#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 ++#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02 ++#define DRM_MODE_FB_DIRTY_FLAGS 0x03 ++ ++#define DRM_MODE_FB_DIRTY_MAX_CLIPS 256 ++ ++/* ++ * Mark a region of a framebuffer as dirty. ++ * ++ * Some hardware does not automatically update display contents ++ * as a hardware or software draw to a framebuffer. This ioctl ++ * allows userspace to tell the kernel and the hardware what ++ * regions of the framebuffer have changed. ++ * ++ * The kernel or hardware is free to update more then just the ++ * region specified by the clip rects. The kernel or hardware ++ * may also delay and/or coalesce several calls to dirty into a ++ * single update. ++ * ++ * Userspace may annotate the updates, the annotates are a ++ * promise made by the caller that the change is either a copy ++ * of pixels or a fill of a single color in the region specified. ++ * ++ * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then ++ * the number of updated regions are half of num_clips given, ++ * where the clip rects are paired in src and dst. The width and ++ * height of each one of the pairs must match. ++ * ++ * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller ++ * promises that the region specified of the clip rects is filled ++ * completely with a single color as given in the color argument. ++ */ ++ ++struct drm_mode_fb_dirty_cmd { ++ __u32 fb_id; ++ __u32 flags; ++ __u32 color; ++ __u32 num_clips; ++ __u64 clips_ptr; ++}; ++ ++struct drm_mode_mode_cmd { ++ __u32 connector_id; ++ struct drm_mode_modeinfo mode; ++}; ++ ++#define DRM_MODE_CURSOR_BO 0x01 ++#define DRM_MODE_CURSOR_MOVE 0x02 ++#define DRM_MODE_CURSOR_FLAGS 0x03 ++ ++/* ++ * depending on the value in flags different members are used. ++ * ++ * CURSOR_BO uses ++ * crtc_id ++ * width ++ * height ++ * handle - if 0 turns the cursor off ++ * ++ * CURSOR_MOVE uses ++ * crtc_id ++ * x ++ * y ++ */ ++struct drm_mode_cursor { ++ __u32 flags; ++ __u32 crtc_id; ++ __s32 x; ++ __s32 y; ++ __u32 width; ++ __u32 height; ++ /* driver specific handle */ ++ __u32 handle; ++}; ++ ++struct drm_mode_cursor2 { ++ __u32 flags; ++ __u32 crtc_id; ++ __s32 x; ++ __s32 y; ++ __u32 width; ++ __u32 height; ++ /* driver specific handle */ ++ __u32 handle; ++ __s32 hot_x; ++ __s32 hot_y; ++}; ++ ++struct drm_mode_crtc_lut { ++ __u32 crtc_id; ++ __u32 gamma_size; ++ ++ /* pointers to arrays */ ++ __u64 red; ++ __u64 green; ++ __u64 blue; ++}; ++ ++struct drm_color_ctm { ++ /* ++ * Conversion matrix in S31.32 sign-magnitude ++ * (not two's complement!) format. ++ */ ++ __u64 matrix[9]; ++}; ++ ++struct drm_color_lut { ++ /* ++ * Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and ++ * 0xffff == 1.0. ++ */ ++ __u16 red; ++ __u16 green; ++ __u16 blue; ++ __u16 reserved; ++}; ++ ++/** ++ * struct hdr_metadata_infoframe - HDR Metadata Infoframe Data. ++ * ++ * HDR Metadata Infoframe as per CTA 861.G spec. This is expected ++ * to match exactly with the spec. ++ * ++ * Userspace is expected to pass the metadata information as per ++ * the format described in this structure. ++ */ ++struct hdr_metadata_infoframe { ++ /** ++ * @eotf: Electro-Optical Transfer Function (EOTF) ++ * used in the stream. ++ */ ++ __u8 eotf; ++ /** ++ * @metadata_type: Static_Metadata_Descriptor_ID. ++ */ ++ __u8 metadata_type; ++ /** ++ * @display_primaries: Color Primaries of the Data. ++ * These are coded as unsigned 16-bit values in units of ++ * 0.00002, where 0x0000 represents zero and 0xC350 ++ * represents 1.0000. ++ * @display_primaries.x: X cordinate of color primary. ++ * @display_primaries.y: Y cordinate of color primary. ++ */ ++ struct { ++ __u16 x, y; ++ } display_primaries[3]; ++ /** ++ * @white_point: White Point of Colorspace Data. ++ * These are coded as unsigned 16-bit values in units of ++ * 0.00002, where 0x0000 represents zero and 0xC350 ++ * represents 1.0000. ++ * @white_point.x: X cordinate of whitepoint of color primary. ++ * @white_point.y: Y cordinate of whitepoint of color primary. ++ */ ++ struct { ++ __u16 x, y; ++ } white_point; ++ /** ++ * @max_display_mastering_luminance: Max Mastering Display Luminance. ++ * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, ++ * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. ++ */ ++ __u16 max_display_mastering_luminance; ++ /** ++ * @min_display_mastering_luminance: Min Mastering Display Luminance. ++ * This value is coded as an unsigned 16-bit value in units of ++ * 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF ++ * represents 6.5535 cd/m2. ++ */ ++ __u16 min_display_mastering_luminance; ++ /** ++ * @max_cll: Max Content Light Level. ++ * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, ++ * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. ++ */ ++ __u16 max_cll; ++ /** ++ * @max_fall: Max Frame Average Light Level. ++ * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, ++ * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. ++ */ ++ __u16 max_fall; ++}; ++ ++/** ++ * struct hdr_output_metadata - HDR output metadata ++ * ++ * Metadata Information to be passed from userspace ++ */ ++struct hdr_output_metadata { ++ /** ++ * @metadata_type: Static_Metadata_Descriptor_ID. ++ */ ++ __u32 metadata_type; ++ /** ++ * @hdmi_metadata_type1: HDR Metadata Infoframe. ++ */ ++ union { ++ struct hdr_metadata_infoframe hdmi_metadata_type1; ++ }; ++}; ++ ++#define DRM_MODE_PAGE_FLIP_EVENT 0x01 ++#define DRM_MODE_PAGE_FLIP_ASYNC 0x02 ++#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4 ++#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8 ++#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \ ++ DRM_MODE_PAGE_FLIP_TARGET_RELATIVE) ++#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \ ++ DRM_MODE_PAGE_FLIP_ASYNC | \ ++ DRM_MODE_PAGE_FLIP_TARGET) ++ ++/* ++ * Request a page flip on the specified crtc. ++ * ++ * This ioctl will ask KMS to schedule a page flip for the specified ++ * crtc. Once any pending rendering targeting the specified fb (as of ++ * ioctl time) has completed, the crtc will be reprogrammed to display ++ * that fb after the next vertical refresh. The ioctl returns ++ * immediately, but subsequent rendering to the current fb will block ++ * in the execbuffer ioctl until the page flip happens. If a page ++ * flip is already pending as the ioctl is called, EBUSY will be ++ * returned. ++ * ++ * Flag DRM_MODE_PAGE_FLIP_EVENT requests that drm sends back a vblank ++ * event (see drm.h: struct drm_event_vblank) when the page flip is ++ * done. The user_data field passed in with this ioctl will be ++ * returned as the user_data field in the vblank event struct. ++ * ++ * Flag DRM_MODE_PAGE_FLIP_ASYNC requests that the flip happen ++ * 'as soon as possible', meaning that it not delay waiting for vblank. ++ * This may cause tearing on the screen. ++ * ++ * The reserved field must be zero. ++ */ ++ ++struct drm_mode_crtc_page_flip { ++ __u32 crtc_id; ++ __u32 fb_id; ++ __u32 flags; ++ __u32 reserved; ++ __u64 user_data; ++}; ++ ++/* ++ * Request a page flip on the specified crtc. ++ * ++ * Same as struct drm_mode_crtc_page_flip, but supports new flags and ++ * re-purposes the reserved field: ++ * ++ * The sequence field must be zero unless either of the ++ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When ++ * the ABSOLUTE flag is specified, the sequence field denotes the absolute ++ * vblank sequence when the flip should take effect. When the RELATIVE ++ * flag is specified, the sequence field denotes the relative (to the ++ * current one when the ioctl is called) vblank sequence when the flip ++ * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to ++ * make sure the vblank sequence before the target one has passed before ++ * calling this ioctl. The purpose of the ++ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify ++ * the target for when code dealing with a page flip runs during a ++ * vertical blank period. ++ */ ++ ++struct drm_mode_crtc_page_flip_target { ++ __u32 crtc_id; ++ __u32 fb_id; ++ __u32 flags; ++ __u32 sequence; ++ __u64 user_data; ++}; ++ ++/* create a dumb scanout buffer */ ++struct drm_mode_create_dumb { ++ __u32 height; ++ __u32 width; ++ __u32 bpp; ++ __u32 flags; ++ /* handle, pitch, size will be returned */ ++ __u32 handle; ++ __u32 pitch; ++ __u64 size; ++}; ++ ++/* set up for mmap of a dumb scanout buffer */ ++struct drm_mode_map_dumb { ++ /** Handle for the object being mapped. */ ++ __u32 handle; ++ __u32 pad; ++ /** ++ * Fake offset to use for subsequent mmap call ++ * ++ * This is a fixed-size type for 32/64 compatibility. ++ */ ++ __u64 offset; ++}; ++ ++struct drm_mode_destroy_dumb { ++ __u32 handle; ++}; ++ ++/* page-flip flags are valid, plus: */ ++#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 ++#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 ++#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 ++ ++#define DRM_MODE_ATOMIC_FLAGS (\ ++ DRM_MODE_PAGE_FLIP_EVENT |\ ++ DRM_MODE_PAGE_FLIP_ASYNC |\ ++ DRM_MODE_ATOMIC_TEST_ONLY |\ ++ DRM_MODE_ATOMIC_NONBLOCK |\ ++ DRM_MODE_ATOMIC_ALLOW_MODESET) ++ ++struct drm_mode_atomic { ++ __u32 flags; ++ __u32 count_objs; ++ __u64 objs_ptr; ++ __u64 count_props_ptr; ++ __u64 props_ptr; ++ __u64 prop_values_ptr; ++ __u64 reserved; ++ __u64 user_data; ++}; ++ ++struct drm_format_modifier_blob { ++#define FORMAT_BLOB_CURRENT 1 ++ /* Version of this blob format */ ++ __u32 version; ++ ++ /* Flags */ ++ __u32 flags; ++ ++ /* Number of fourcc formats supported */ ++ __u32 count_formats; ++ ++ /* Where in this blob the formats exist (in bytes) */ ++ __u32 formats_offset; ++ ++ /* Number of drm_format_modifiers */ ++ __u32 count_modifiers; ++ ++ /* Where in this blob the modifiers exist (in bytes) */ ++ __u32 modifiers_offset; ++ ++ /* __u32 formats[] */ ++ /* struct drm_format_modifier modifiers[] */ ++}; ++ ++struct drm_format_modifier { ++ /* Bitmask of formats in get_plane format list this info applies to. The ++ * offset allows a sliding window of which 64 formats (bits). ++ * ++ * Some examples: ++ * In today's world with < 65 formats, and formats 0, and 2 are ++ * supported ++ * 0x0000000000000005 ++ * ^-offset = 0, formats = 5 ++ * ++ * If the number formats grew to 128, and formats 98-102 are ++ * supported with the modifier: ++ * ++ * 0x0000007c00000000 0000000000000000 ++ * ^ ++ * |__offset = 64, formats = 0x7c00000000 ++ * ++ */ ++ __u64 formats; ++ __u32 offset; ++ __u32 pad; ++ ++ /* The modifier that applies to the >get_plane format list bitmask. */ ++ __u64 modifier; ++}; ++ ++/** ++ * struct drm_mode_create_blob - Create New blob property ++ * ++ * Create a new 'blob' data property, copying length bytes from data pointer, ++ * and returning new blob ID. ++ */ ++struct drm_mode_create_blob { ++ /** @data: Pointer to data to copy. */ ++ __u64 data; ++ /** @length: Length of data to copy. */ ++ __u32 length; ++ /** @blob_id: Return: new property ID. */ ++ __u32 blob_id; ++}; ++ ++/** ++ * struct drm_mode_destroy_blob - Destroy user blob ++ * @blob_id: blob_id to destroy ++ * ++ * Destroy a user-created blob property. ++ * ++ * User-space can release blobs as soon as they do not need to refer to them by ++ * their blob object ID. For instance, if you are using a MODE_ID blob in an ++ * atomic commit and you will not make another commit re-using the same ID, you ++ * can destroy the blob as soon as the commit has been issued, without waiting ++ * for it to complete. ++ */ ++struct drm_mode_destroy_blob { ++ __u32 blob_id; ++}; ++ ++/** ++ * struct drm_mode_create_lease - Create lease ++ * ++ * Lease mode resources, creating another drm_master. ++ * ++ * The @object_ids array must reference at least one CRTC, one connector and ++ * one plane if &DRM_CLIENT_CAP_UNIVERSAL_PLANES is enabled. Alternatively, ++ * the lease can be completely empty. ++ */ ++struct drm_mode_create_lease { ++ /** @object_ids: Pointer to array of object ids (__u32) */ ++ __u64 object_ids; ++ /** @object_count: Number of object ids */ ++ __u32 object_count; ++ /** @flags: flags for new FD (O_CLOEXEC, etc) */ ++ __u32 flags; ++ ++ /** @lessee_id: Return: unique identifier for lessee. */ ++ __u32 lessee_id; ++ /** @fd: Return: file descriptor to new drm_master file */ ++ __u32 fd; ++}; ++ ++/** ++ * struct drm_mode_list_lessees - List lessees ++ * ++ * List lesses from a drm_master. ++ */ ++struct drm_mode_list_lessees { ++ /** ++ * @count_lessees: Number of lessees. ++ * ++ * On input, provides length of the array. ++ * On output, provides total number. No ++ * more than the input number will be written ++ * back, so two calls can be used to get ++ * the size and then the data. ++ */ ++ __u32 count_lessees; ++ /** @pad: Padding. */ ++ __u32 pad; ++ ++ /** ++ * @lessees_ptr: Pointer to lessees. ++ * ++ * Pointer to __u64 array of lessee ids ++ */ ++ __u64 lessees_ptr; ++}; ++ ++/** ++ * struct drm_mode_get_lease - Get Lease ++ * ++ * Get leased objects. ++ */ ++struct drm_mode_get_lease { ++ /** ++ * @count_objects: Number of leased objects. ++ * ++ * On input, provides length of the array. ++ * On output, provides total number. No ++ * more than the input number will be written ++ * back, so two calls can be used to get ++ * the size and then the data. ++ */ ++ __u32 count_objects; ++ /** @pad: Padding. */ ++ __u32 pad; ++ ++ /** ++ * @objects_ptr: Pointer to objects. ++ * ++ * Pointer to __u32 array of object ids. ++ */ ++ __u64 objects_ptr; ++}; ++ ++/** ++ * struct drm_mode_revoke_lease - Revoke lease ++ */ ++struct drm_mode_revoke_lease { ++ /** @lessee_id: Unique ID of lessee */ ++ __u32 lessee_id; ++}; ++ ++/** ++ * struct drm_mode_rect - Two dimensional rectangle. ++ * @x1: Horizontal starting coordinate (inclusive). ++ * @y1: Vertical starting coordinate (inclusive). ++ * @x2: Horizontal ending coordinate (exclusive). ++ * @y2: Vertical ending coordinate (exclusive). ++ * ++ * With drm subsystem using struct drm_rect to manage rectangular area this ++ * export it to user-space. ++ * ++ * Currently used by drm_mode_atomic blob property FB_DAMAGE_CLIPS. ++ */ ++struct drm_mode_rect { ++ __s32 x1; ++ __s32 y1; ++ __s32 x2; ++ __s32 y2; ++}; ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif +diff --git a/third_party/drm/drm/xf86drm.h b/third_party/drm/drm/xf86drm.h +new file mode 100644 +index 0000000..58d66f1 +--- /dev/null ++++ b/third_party/drm/drm/xf86drm.h +@@ -0,0 +1,966 @@ ++/** ++ * \file xf86drm.h ++ * OS-independent header for DRM user-level library interface. ++ * ++ * \author Rickard E. (Rik) Faith ++ */ ++ ++/* ++ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. ++ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. ++ * All Rights Reserved. ++ * ++ * 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 ++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. ++ * ++ */ ++ ++#ifndef _XF86DRM_H_ ++#define _XF86DRM_H_ ++ ++#include ++#include ++#include ++#include ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++#ifndef DRM_MAX_MINOR ++#define DRM_MAX_MINOR 16 ++#endif ++ ++#if defined(__linux__) ++ ++#define DRM_IOCTL_NR(n) _IOC_NR(n) ++#define DRM_IOC_VOID _IOC_NONE ++#define DRM_IOC_READ _IOC_READ ++#define DRM_IOC_WRITE _IOC_WRITE ++#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE ++#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) ++ ++#else /* One of the *BSDs */ ++ ++#include ++#define DRM_IOCTL_NR(n) ((n) & 0xff) ++#define DRM_IOC_VOID IOC_VOID ++#define DRM_IOC_READ IOC_OUT ++#define DRM_IOC_WRITE IOC_IN ++#define DRM_IOC_READWRITE IOC_INOUT ++#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) ++ ++#endif ++ ++ /* Defaults, if nothing set in xf86config */ ++#define DRM_DEV_UID 0 ++#define DRM_DEV_GID 0 ++/* Default /dev/dri directory permissions 0755 */ ++#define DRM_DEV_DIRMODE \ ++ (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) ++#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) ++ ++#ifdef __OpenBSD__ ++#define DRM_DIR_NAME "/dev" ++#define DRM_PRIMARY_MINOR_NAME "drm" ++#define DRM_CONTROL_MINOR_NAME "drmC" ++#define DRM_RENDER_MINOR_NAME "drmR" ++#else ++#define DRM_DIR_NAME "/dev/dri" ++#define DRM_PRIMARY_MINOR_NAME "card" ++#define DRM_CONTROL_MINOR_NAME "controlD" ++#define DRM_RENDER_MINOR_NAME "renderD" ++#define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ ++#endif ++ ++#define DRM_DEV_NAME "%s/" DRM_PRIMARY_MINOR_NAME "%d" ++#define DRM_CONTROL_DEV_NAME "%s/" DRM_CONTROL_MINOR_NAME "%d" ++#define DRM_RENDER_DEV_NAME "%s/" DRM_RENDER_MINOR_NAME "%d" ++ ++#define DRM_NODE_NAME_MAX \ ++ (sizeof(DRM_DIR_NAME) + 1 /* slash */ \ ++ + MAX3(sizeof(DRM_PRIMARY_MINOR_NAME), \ ++ sizeof(DRM_CONTROL_MINOR_NAME), \ ++ sizeof(DRM_RENDER_MINOR_NAME)) \ ++ + sizeof("144") /* highest possible node number */ \ ++ + 1) /* NULL-terminator */ ++ ++#define DRM_ERR_NO_DEVICE (-1001) ++#define DRM_ERR_NO_ACCESS (-1002) ++#define DRM_ERR_NOT_ROOT (-1003) ++#define DRM_ERR_INVALID (-1004) ++#define DRM_ERR_NO_FD (-1005) ++ ++#define DRM_AGP_NO_HANDLE 0 ++ ++typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */ ++typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */ ++ ++#if (__GNUC__ >= 3) ++#define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) ++#else ++#define DRM_PRINTFLIKE(f, a) ++#endif ++ ++typedef struct _drmServerInfo { ++ int (*debug_print)(const char *format, va_list ap) DRM_PRINTFLIKE(1,0); ++ int (*load_module)(const char *name); ++ void (*get_perms)(gid_t *, mode_t *); ++} drmServerInfo, *drmServerInfoPtr; ++ ++typedef struct drmHashEntry { ++ int fd; ++ void (*f)(int, void *, void *); ++ void *tagTable; ++} drmHashEntry; ++ ++extern int drmIoctl(int fd, unsigned long request, void *arg); ++extern void *drmGetHashTable(void); ++extern drmHashEntry *drmGetEntry(int fd); ++ ++/** ++ * Driver version information. ++ * ++ * \sa drmGetVersion() and drmSetVersion(). ++ */ ++typedef struct _drmVersion { ++ int version_major; /**< Major version */ ++ int version_minor; /**< Minor version */ ++ int version_patchlevel; /**< Patch level */ ++ int name_len; /**< Length of name buffer */ ++ char *name; /**< Name of driver */ ++ int date_len; /**< Length of date buffer */ ++ char *date; /**< User-space buffer to hold date */ ++ int desc_len; /**< Length of desc buffer */ ++ char *desc; /**< User-space buffer to hold desc */ ++} drmVersion, *drmVersionPtr; ++ ++typedef struct _drmStats { ++ unsigned long count; /**< Number of data */ ++ struct { ++ unsigned long value; /**< Value from kernel */ ++ const char *long_format; /**< Suggested format for long_name */ ++ const char *long_name; /**< Long name for value */ ++ const char *rate_format; /**< Suggested format for rate_name */ ++ const char *rate_name; /**< Short name for value per second */ ++ int isvalue; /**< True if value (vs. counter) */ ++ const char *mult_names; /**< Multiplier names (e.g., "KGM") */ ++ int mult; /**< Multiplier value (e.g., 1024) */ ++ int verbose; /**< Suggest only in verbose output */ ++ } data[15]; ++} drmStatsT; ++ ++ ++ /* All of these enums *MUST* match with the ++ kernel implementation -- so do *NOT* ++ change them! (The drmlib implementation ++ will just copy the flags instead of ++ translating them.) */ ++typedef enum { ++ DRM_FRAME_BUFFER = 0, /**< WC, no caching, no core dump */ ++ DRM_REGISTERS = 1, /**< no caching, no core dump */ ++ DRM_SHM = 2, /**< shared, cached */ ++ DRM_AGP = 3, /**< AGP/GART */ ++ DRM_SCATTER_GATHER = 4, /**< PCI scatter/gather */ ++ DRM_CONSISTENT = 5 /**< PCI consistent */ ++} drmMapType; ++ ++typedef enum { ++ DRM_RESTRICTED = 0x0001, /**< Cannot be mapped to client-virtual */ ++ DRM_READ_ONLY = 0x0002, /**< Read-only in client-virtual */ ++ DRM_LOCKED = 0x0004, /**< Physical pages locked */ ++ DRM_KERNEL = 0x0008, /**< Kernel requires access */ ++ DRM_WRITE_COMBINING = 0x0010, /**< Use write-combining, if available */ ++ DRM_CONTAINS_LOCK = 0x0020, /**< SHM page that contains lock */ ++ DRM_REMOVABLE = 0x0040 /**< Removable mapping */ ++} drmMapFlags; ++ ++/** ++ * \warning These values *MUST* match drm.h ++ */ ++typedef enum { ++ /** \name Flags for DMA buffer dispatch */ ++ /*@{*/ ++ DRM_DMA_BLOCK = 0x01, /**< ++ * Block until buffer dispatched. ++ * ++ * \note the buffer may not yet have been ++ * processed by the hardware -- getting a ++ * hardware lock with the hardware quiescent ++ * will ensure that the buffer has been ++ * processed. ++ */ ++ DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ ++ DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ ++ /*@}*/ ++ ++ /** \name Flags for DMA buffer request */ ++ /*@{*/ ++ DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ ++ DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ ++ DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ ++ /*@}*/ ++} drmDMAFlags; ++ ++typedef enum { ++ DRM_PAGE_ALIGN = 0x01, ++ DRM_AGP_BUFFER = 0x02, ++ DRM_SG_BUFFER = 0x04, ++ DRM_FB_BUFFER = 0x08, ++ DRM_PCI_BUFFER_RO = 0x10 ++} drmBufDescFlags; ++ ++typedef enum { ++ DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ ++ DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ ++ DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ ++ DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ ++ /* These *HALT* flags aren't supported yet ++ -- they will be used to support the ++ full-screen DGA-like mode. */ ++ DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ ++ DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ ++} drmLockFlags; ++ ++typedef enum { ++ DRM_CONTEXT_PRESERVED = 0x01, /**< This context is preserved and ++ never swapped. */ ++ DRM_CONTEXT_2DONLY = 0x02 /**< This context is for 2D rendering only. */ ++} drm_context_tFlags, *drm_context_tFlagsPtr; ++ ++typedef struct _drmBufDesc { ++ int count; /**< Number of buffers of this size */ ++ int size; /**< Size in bytes */ ++ int low_mark; /**< Low water mark */ ++ int high_mark; /**< High water mark */ ++} drmBufDesc, *drmBufDescPtr; ++ ++typedef struct _drmBufInfo { ++ int count; /**< Number of buffers described in list */ ++ drmBufDescPtr list; /**< List of buffer descriptions */ ++} drmBufInfo, *drmBufInfoPtr; ++ ++typedef struct _drmBuf { ++ int idx; /**< Index into the master buffer list */ ++ int total; /**< Buffer size */ ++ int used; /**< Amount of buffer in use (for DMA) */ ++ drmAddress address; /**< Address */ ++} drmBuf, *drmBufPtr; ++ ++/** ++ * Buffer mapping information. ++ * ++ * Used by drmMapBufs() and drmUnmapBufs() to store information about the ++ * mapped buffers. ++ */ ++typedef struct _drmBufMap { ++ int count; /**< Number of buffers mapped */ ++ drmBufPtr list; /**< Buffers */ ++} drmBufMap, *drmBufMapPtr; ++ ++typedef struct _drmLock { ++ volatile unsigned int lock; ++ char padding[60]; ++ /* This is big enough for most current (and future?) architectures: ++ DEC Alpha: 32 bytes ++ Intel Merced: ? ++ Intel P5/PPro/PII/PIII: 32 bytes ++ Intel StrongARM: 32 bytes ++ Intel i386/i486: 16 bytes ++ MIPS: 32 bytes (?) ++ Motorola 68k: 16 bytes ++ Motorola PowerPC: 32 bytes ++ Sun SPARC: 32 bytes ++ */ ++} drmLock, *drmLockPtr; ++ ++/** ++ * Indices here refer to the offset into ++ * list in drmBufInfo ++ */ ++typedef struct _drmDMAReq { ++ drm_context_t context; /**< Context handle */ ++ int send_count; /**< Number of buffers to send */ ++ int *send_list; /**< List of handles to buffers */ ++ int *send_sizes; /**< Lengths of data to send, in bytes */ ++ drmDMAFlags flags; /**< Flags */ ++ int request_count; /**< Number of buffers requested */ ++ int request_size; /**< Desired size of buffers requested */ ++ int *request_list; /**< Buffer information */ ++ int *request_sizes; /**< Minimum acceptable sizes */ ++ int granted_count; /**< Number of buffers granted at this size */ ++} drmDMAReq, *drmDMAReqPtr; ++ ++typedef struct _drmRegion { ++ drm_handle_t handle; ++ unsigned int offset; ++ drmSize size; ++ drmAddress map; ++} drmRegion, *drmRegionPtr; ++ ++typedef struct _drmTextureRegion { ++ unsigned char next; ++ unsigned char prev; ++ unsigned char in_use; ++ unsigned char padding; /**< Explicitly pad this out */ ++ unsigned int age; ++} drmTextureRegion, *drmTextureRegionPtr; ++ ++ ++typedef enum { ++ DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ ++ DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ ++ /* bits 1-6 are reserved for high crtcs */ ++ DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, ++ DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ ++ DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ ++ DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ ++ DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ ++ DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ ++} drmVBlankSeqType; ++#define DRM_VBLANK_HIGH_CRTC_SHIFT 1 ++ ++typedef struct _drmVBlankReq { ++ drmVBlankSeqType type; ++ unsigned int sequence; ++ unsigned long signal; ++} drmVBlankReq, *drmVBlankReqPtr; ++ ++typedef struct _drmVBlankReply { ++ drmVBlankSeqType type; ++ unsigned int sequence; ++ long tval_sec; ++ long tval_usec; ++} drmVBlankReply, *drmVBlankReplyPtr; ++ ++typedef union _drmVBlank { ++ drmVBlankReq request; ++ drmVBlankReply reply; ++} drmVBlank, *drmVBlankPtr; ++ ++typedef struct _drmSetVersion { ++ int drm_di_major; ++ int drm_di_minor; ++ int drm_dd_major; ++ int drm_dd_minor; ++} drmSetVersion, *drmSetVersionPtr; ++ ++#define __drm_dummy_lock(lock) (*(__volatile__ unsigned int *)lock) ++ ++#define DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ ++#define DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ ++ ++#if defined(__GNUC__) && (__GNUC__ >= 2) ++# if defined(__i386) || defined(__AMD64__) || defined(__x86_64__) || defined(__amd64__) ++ /* Reflect changes here to drmP.h */ ++#define DRM_CAS(lock,old,new,__ret) \ ++ do { \ ++ int __dummy; /* Can't mark eax as clobbered */ \ ++ __asm__ __volatile__( \ ++ "lock ; cmpxchg %4,%1\n\t" \ ++ "setnz %0" \ ++ : "=d" (__ret), \ ++ "=m" (__drm_dummy_lock(lock)), \ ++ "=a" (__dummy) \ ++ : "2" (old), \ ++ "r" (new)); \ ++ } while (0) ++ ++#elif defined(__alpha__) ++ ++#define DRM_CAS(lock, old, new, ret) \ ++ do { \ ++ int tmp, old32; \ ++ __asm__ __volatile__( \ ++ " addl $31, %5, %3\n" \ ++ "1: ldl_l %0, %2\n" \ ++ " cmpeq %0, %3, %1\n" \ ++ " beq %1, 2f\n" \ ++ " mov %4, %0\n" \ ++ " stl_c %0, %2\n" \ ++ " beq %0, 3f\n" \ ++ " mb\n" \ ++ "2: cmpeq %1, 0, %1\n" \ ++ ".subsection 2\n" \ ++ "3: br 1b\n" \ ++ ".previous" \ ++ : "=&r"(tmp), "=&r"(ret), \ ++ "=m"(__drm_dummy_lock(lock)), \ ++ "=&r"(old32) \ ++ : "r"(new), "r"(old) \ ++ : "memory"); \ ++ } while (0) ++ ++#elif defined(__sparc__) ++ ++#define DRM_CAS(lock,old,new,__ret) \ ++do { register unsigned int __old __asm("o0"); \ ++ register unsigned int __new __asm("o1"); \ ++ register volatile unsigned int *__lock __asm("o2"); \ ++ __old = old; \ ++ __new = new; \ ++ __lock = (volatile unsigned int *)lock; \ ++ __asm__ __volatile__( \ ++ /*"cas [%2], %3, %0"*/ \ ++ ".word 0xd3e29008\n\t" \ ++ /*"membar #StoreStore | #StoreLoad"*/ \ ++ ".word 0x8143e00a" \ ++ : "=&r" (__new) \ ++ : "0" (__new), \ ++ "r" (__lock), \ ++ "r" (__old) \ ++ : "memory"); \ ++ __ret = (__new != __old); \ ++} while(0) ++ ++#elif defined(__ia64__) ++ ++#ifdef __INTEL_COMPILER ++/* this currently generates bad code (missing stop bits)... */ ++#include ++ ++#define DRM_CAS(lock,old,new,__ret) \ ++ do { \ ++ unsigned long __result, __old = (old) & 0xffffffff; \ ++ __mf(); \ ++ __result = _InterlockedCompareExchange_acq(&__drm_dummy_lock(lock), (new), __old);\ ++ __ret = (__result) != (__old); \ ++/* __ret = (__sync_val_compare_and_swap(&__drm_dummy_lock(lock), \ ++ (old), (new)) \ ++ != (old)); */\ ++ } while (0) ++ ++#else ++#define DRM_CAS(lock,old,new,__ret) \ ++ do { \ ++ unsigned int __result, __old = (old); \ ++ __asm__ __volatile__( \ ++ "mf\n" \ ++ "mov ar.ccv=%2\n" \ ++ ";;\n" \ ++ "cmpxchg4.acq %0=%1,%3,ar.ccv" \ ++ : "=r" (__result), "=m" (__drm_dummy_lock(lock)) \ ++ : "r" ((unsigned long)__old), "r" (new) \ ++ : "memory"); \ ++ __ret = (__result) != (__old); \ ++ } while (0) ++ ++#endif ++ ++#elif defined(__powerpc__) ++ ++#define DRM_CAS(lock,old,new,__ret) \ ++ do { \ ++ __asm__ __volatile__( \ ++ "sync;" \ ++ "0: lwarx %0,0,%1;" \ ++ " xor. %0,%3,%0;" \ ++ " bne 1f;" \ ++ " stwcx. %2,0,%1;" \ ++ " bne- 0b;" \ ++ "1: " \ ++ "sync;" \ ++ : "=&r"(__ret) \ ++ : "r"(lock), "r"(new), "r"(old) \ ++ : "cr0", "memory"); \ ++ } while (0) ++ ++# elif defined (__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ ++ || defined (__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ ++ || defined (__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) \ ++ || defined (__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ ++ || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ ++ || defined(__ARM_ARCH_7EM__) ++ /* excluding ARMv4/ARMv5 and lower (lacking ldrex/strex support) */ ++ #undef DRM_DEV_MODE ++ #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) ++ ++ #define DRM_CAS(lock,old,new,__ret) \ ++ do { \ ++ __asm__ __volatile__ ( \ ++ "1: ldrex %0, [%1]\n" \ ++ " teq %0, %2\n" \ ++ " ite eq\n" \ ++ " strexeq %0, %3, [%1]\n" \ ++ " movne %0, #1\n" \ ++ : "=&r" (__ret) \ ++ : "r" (lock), "r" (old), "r" (new) \ ++ : "cc","memory"); \ ++ } while (0) ++ ++#endif /* architecture */ ++#endif /* __GNUC__ >= 2 */ ++ ++#ifndef DRM_CAS ++#define DRM_CAS(lock,old,new,ret) do { ret=1; } while (0) /* FAST LOCK FAILS */ ++#endif ++ ++#if defined(__alpha__) ++#define DRM_CAS_RESULT(_result) long _result ++#elif defined(__powerpc__) ++#define DRM_CAS_RESULT(_result) int _result ++#else ++#define DRM_CAS_RESULT(_result) char _result ++#endif ++ ++#define DRM_LIGHT_LOCK(fd,lock,context) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ ++ if (__ret) drmGetLock(fd,context,0); \ ++ } while(0) ++ ++ /* This one counts fast locks -- for ++ benchmarking only. */ ++#define DRM_LIGHT_LOCK_COUNT(fd,lock,context,count) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ ++ if (__ret) drmGetLock(fd,context,0); \ ++ else ++count; \ ++ } while(0) ++ ++#define DRM_LOCK(fd,lock,context,flags) \ ++ do { \ ++ if (flags) drmGetLock(fd,context,flags); \ ++ else DRM_LIGHT_LOCK(fd,lock,context); \ ++ } while(0) ++ ++#define DRM_UNLOCK(fd,lock,context) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ DRM_CAS(lock,DRM_LOCK_HELD|context,context,__ret); \ ++ if (__ret) drmUnlock(fd,context); \ ++ } while(0) ++ ++ /* Simple spin locks */ ++#define DRM_SPINLOCK(spin,val) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ do { \ ++ DRM_CAS(spin,0,val,__ret); \ ++ if (__ret) while ((spin)->lock); \ ++ } while (__ret); \ ++ } while(0) ++ ++#define DRM_SPINLOCK_TAKE(spin,val) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ int cur; \ ++ do { \ ++ cur = (*spin).lock; \ ++ DRM_CAS(spin,cur,val,__ret); \ ++ } while (__ret); \ ++ } while(0) ++ ++#define DRM_SPINLOCK_COUNT(spin,val,count,__ret) \ ++ do { \ ++ int __i; \ ++ __ret = 1; \ ++ for (__i = 0; __ret && __i < count; __i++) { \ ++ DRM_CAS(spin,0,val,__ret); \ ++ if (__ret) for (;__i < count && (spin)->lock; __i++); \ ++ } \ ++ } while(0) ++ ++#define DRM_SPINUNLOCK(spin,val) \ ++ do { \ ++ DRM_CAS_RESULT(__ret); \ ++ if ((*spin).lock == val) { /* else server stole lock */ \ ++ do { \ ++ DRM_CAS(spin,val,0,__ret); \ ++ } while (__ret); \ ++ } \ ++ } while(0) ++ ++ ++ ++/* General user-level programmer's API: unprivileged */ ++extern int drmAvailable(void); ++extern int drmOpen(const char *name, const char *busid); ++ ++#define DRM_NODE_PRIMARY 0 ++#define DRM_NODE_CONTROL 1 ++#define DRM_NODE_RENDER 2 ++#define DRM_NODE_MAX 3 ++ ++extern int drmOpenWithType(const char *name, const char *busid, ++ int type); ++ ++extern int drmOpenControl(int minor); ++extern int drmOpenRender(int minor); ++extern int drmClose(int fd); ++extern drmVersionPtr drmGetVersion(int fd); ++extern drmVersionPtr drmGetLibVersion(int fd); ++extern int drmGetCap(int fd, uint64_t capability, uint64_t *value); ++extern void drmFreeVersion(drmVersionPtr); ++extern int drmGetMagic(int fd, drm_magic_t * magic); ++extern char *drmGetBusid(int fd); ++extern int drmGetInterruptFromBusID(int fd, int busnum, int devnum, ++ int funcnum); ++extern int drmGetMap(int fd, int idx, drm_handle_t *offset, ++ drmSize *size, drmMapType *type, ++ drmMapFlags *flags, drm_handle_t *handle, ++ int *mtrr); ++extern int drmGetClient(int fd, int idx, int *auth, int *pid, ++ int *uid, unsigned long *magic, ++ unsigned long *iocs); ++extern int drmGetStats(int fd, drmStatsT *stats); ++extern int drmSetInterfaceVersion(int fd, drmSetVersion *version); ++extern int drmCommandNone(int fd, unsigned long drmCommandIndex); ++extern int drmCommandRead(int fd, unsigned long drmCommandIndex, ++ void *data, unsigned long size); ++extern int drmCommandWrite(int fd, unsigned long drmCommandIndex, ++ void *data, unsigned long size); ++extern int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, ++ void *data, unsigned long size); ++ ++/* General user-level programmer's API: X server (root) only */ ++extern void drmFreeBusid(const char *busid); ++extern int drmSetBusid(int fd, const char *busid); ++extern int drmAuthMagic(int fd, drm_magic_t magic); ++extern int drmAddMap(int fd, ++ drm_handle_t offset, ++ drmSize size, ++ drmMapType type, ++ drmMapFlags flags, ++ drm_handle_t * handle); ++extern int drmRmMap(int fd, drm_handle_t handle); ++extern int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id, ++ drm_handle_t handle); ++ ++extern int drmAddBufs(int fd, int count, int size, ++ drmBufDescFlags flags, ++ int agp_offset); ++extern int drmMarkBufs(int fd, double low, double high); ++extern int drmCreateContext(int fd, drm_context_t * handle); ++extern int drmSetContextFlags(int fd, drm_context_t context, ++ drm_context_tFlags flags); ++extern int drmGetContextFlags(int fd, drm_context_t context, ++ drm_context_tFlagsPtr flags); ++extern int drmAddContextTag(int fd, drm_context_t context, void *tag); ++extern int drmDelContextTag(int fd, drm_context_t context); ++extern void *drmGetContextTag(int fd, drm_context_t context); ++extern drm_context_t * drmGetReservedContextList(int fd, int *count); ++extern void drmFreeReservedContextList(drm_context_t *); ++extern int drmSwitchToContext(int fd, drm_context_t context); ++extern int drmDestroyContext(int fd, drm_context_t handle); ++extern int drmCreateDrawable(int fd, drm_drawable_t * handle); ++extern int drmDestroyDrawable(int fd, drm_drawable_t handle); ++extern int drmUpdateDrawableInfo(int fd, drm_drawable_t handle, ++ drm_drawable_info_type_t type, ++ unsigned int num, void *data); ++extern int drmCtlInstHandler(int fd, int irq); ++extern int drmCtlUninstHandler(int fd); ++extern int drmSetClientCap(int fd, uint64_t capability, ++ uint64_t value); ++ ++extern int drmCrtcGetSequence(int fd, uint32_t crtcId, ++ uint64_t *sequence, uint64_t *ns); ++extern int drmCrtcQueueSequence(int fd, uint32_t crtcId, ++ uint32_t flags, uint64_t sequence, ++ uint64_t *sequence_queued, ++ uint64_t user_data); ++/* General user-level programmer's API: authenticated client and/or X */ ++extern int drmMap(int fd, ++ drm_handle_t handle, ++ drmSize size, ++ drmAddressPtr address); ++extern int drmUnmap(drmAddress address, drmSize size); ++extern drmBufInfoPtr drmGetBufInfo(int fd); ++extern drmBufMapPtr drmMapBufs(int fd); ++extern int drmUnmapBufs(drmBufMapPtr bufs); ++extern int drmDMA(int fd, drmDMAReqPtr request); ++extern int drmFreeBufs(int fd, int count, int *list); ++extern int drmGetLock(int fd, ++ drm_context_t context, ++ drmLockFlags flags); ++extern int drmUnlock(int fd, drm_context_t context); ++extern int drmFinish(int fd, int context, drmLockFlags flags); ++extern int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id, ++ drm_handle_t * handle); ++ ++/* AGP/GART support: X server (root) only */ ++extern int drmAgpAcquire(int fd); ++extern int drmAgpRelease(int fd); ++extern int drmAgpEnable(int fd, unsigned long mode); ++extern int drmAgpAlloc(int fd, unsigned long size, ++ unsigned long type, unsigned long *address, ++ drm_handle_t *handle); ++extern int drmAgpFree(int fd, drm_handle_t handle); ++extern int drmAgpBind(int fd, drm_handle_t handle, ++ unsigned long offset); ++extern int drmAgpUnbind(int fd, drm_handle_t handle); ++ ++/* AGP/GART info: authenticated client and/or X */ ++extern int drmAgpVersionMajor(int fd); ++extern int drmAgpVersionMinor(int fd); ++extern unsigned long drmAgpGetMode(int fd); ++extern unsigned long drmAgpBase(int fd); /* Physical location */ ++extern unsigned long drmAgpSize(int fd); /* Bytes */ ++extern unsigned long drmAgpMemoryUsed(int fd); ++extern unsigned long drmAgpMemoryAvail(int fd); ++extern unsigned int drmAgpVendorId(int fd); ++extern unsigned int drmAgpDeviceId(int fd); ++ ++/* PCI scatter/gather support: X server (root) only */ ++extern int drmScatterGatherAlloc(int fd, unsigned long size, ++ drm_handle_t *handle); ++extern int drmScatterGatherFree(int fd, drm_handle_t handle); ++ ++extern int drmWaitVBlank(int fd, drmVBlankPtr vbl); ++ ++/* Support routines */ ++extern void drmSetServerInfo(drmServerInfoPtr info); ++extern int drmError(int err, const char *label); ++extern void *drmMalloc(int size); ++extern void drmFree(void *pt); ++ ++/* Hash table routines */ ++extern void *drmHashCreate(void); ++extern int drmHashDestroy(void *t); ++extern int drmHashLookup(void *t, unsigned long key, void **value); ++extern int drmHashInsert(void *t, unsigned long key, void *value); ++extern int drmHashDelete(void *t, unsigned long key); ++extern int drmHashFirst(void *t, unsigned long *key, void **value); ++extern int drmHashNext(void *t, unsigned long *key, void **value); ++ ++/* PRNG routines */ ++extern void *drmRandomCreate(unsigned long seed); ++extern int drmRandomDestroy(void *state); ++extern unsigned long drmRandom(void *state); ++extern double drmRandomDouble(void *state); ++ ++/* Skip list routines */ ++ ++extern void *drmSLCreate(void); ++extern int drmSLDestroy(void *l); ++extern int drmSLLookup(void *l, unsigned long key, void **value); ++extern int drmSLInsert(void *l, unsigned long key, void *value); ++extern int drmSLDelete(void *l, unsigned long key); ++extern int drmSLNext(void *l, unsigned long *key, void **value); ++extern int drmSLFirst(void *l, unsigned long *key, void **value); ++extern void drmSLDump(void *l); ++extern int drmSLLookupNeighbors(void *l, unsigned long key, ++ unsigned long *prev_key, void **prev_value, ++ unsigned long *next_key, void **next_value); ++ ++extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); ++extern int drmOpenOnceWithType(const char *BusID, int *newlyopened, int type); ++extern void drmCloseOnce(int fd); ++extern void drmMsg(const char *format, ...) DRM_PRINTFLIKE(1, 2); ++ ++extern int drmSetMaster(int fd); ++extern int drmDropMaster(int fd); ++extern int drmIsMaster(int fd); ++ ++#define DRM_EVENT_CONTEXT_VERSION 4 ++ ++typedef struct _drmEventContext { ++ ++ /* This struct is versioned so we can add more pointers if we ++ * add more events. */ ++ int version; ++ ++ void (*vblank_handler)(int fd, ++ unsigned int sequence, ++ unsigned int tv_sec, ++ unsigned int tv_usec, ++ void *user_data); ++ ++ void (*page_flip_handler)(int fd, ++ unsigned int sequence, ++ unsigned int tv_sec, ++ unsigned int tv_usec, ++ void *user_data); ++ ++ void (*page_flip_handler2)(int fd, ++ unsigned int sequence, ++ unsigned int tv_sec, ++ unsigned int tv_usec, ++ unsigned int crtc_id, ++ void *user_data); ++ ++ void (*sequence_handler)(int fd, ++ uint64_t sequence, ++ uint64_t ns, ++ uint64_t user_data); ++} drmEventContext, *drmEventContextPtr; ++ ++extern int drmHandleEvent(int fd, drmEventContextPtr evctx); ++ ++extern char *drmGetDeviceNameFromFd(int fd); ++ ++/* Improved version of drmGetDeviceNameFromFd which attributes for any type of ++ * device/node - card, control or renderD. ++ */ ++extern char *drmGetDeviceNameFromFd2(int fd); ++extern int drmGetNodeTypeFromFd(int fd); ++ ++/* Convert between GEM handles and DMA-BUF file descriptors. ++ * ++ * Warning: since GEM handles are not reference-counted and are unique per ++ * DRM file description, the caller is expected to perform its own reference ++ * counting. drmPrimeFDToHandle is guaranteed to return the same handle for ++ * different FDs if they reference the same underlying buffer object. This ++ * could even be a buffer object originally created on the same DRM FD. ++ * ++ * When sharing a DRM FD with an API such as EGL or GBM, the caller must not ++ * use drmPrimeHandleToFD nor drmPrimeFDToHandle. A single user-space ++ * reference-counting implementation is necessary to avoid double-closing GEM ++ * handles. ++ * ++ * Two processes can't share the same DRM FD and both use it to create or ++ * import GEM handles, even when using a single user-space reference-counting ++ * implementation like GBM, because GBM doesn't share its state between ++ * processes. ++ */ ++extern int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd); ++extern int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle); ++ ++extern int drmCloseBufferHandle(int fd, uint32_t handle); ++ ++extern char *drmGetPrimaryDeviceNameFromFd(int fd); ++extern char *drmGetRenderDeviceNameFromFd(int fd); ++ ++#define DRM_BUS_PCI 0 ++#define DRM_BUS_USB 1 ++#define DRM_BUS_PLATFORM 2 ++#define DRM_BUS_HOST1X 3 ++ ++typedef struct _drmPciBusInfo { ++ uint16_t domain; ++ uint8_t bus; ++ uint8_t dev; ++ uint8_t func; ++} drmPciBusInfo, *drmPciBusInfoPtr; ++ ++typedef struct _drmPciDeviceInfo { ++ uint16_t vendor_id; ++ uint16_t device_id; ++ uint16_t subvendor_id; ++ uint16_t subdevice_id; ++ uint8_t revision_id; ++} drmPciDeviceInfo, *drmPciDeviceInfoPtr; ++ ++typedef struct _drmUsbBusInfo { ++ uint8_t bus; ++ uint8_t dev; ++} drmUsbBusInfo, *drmUsbBusInfoPtr; ++ ++typedef struct _drmUsbDeviceInfo { ++ uint16_t vendor; ++ uint16_t product; ++} drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; ++ ++#define DRM_PLATFORM_DEVICE_NAME_LEN 512 ++ ++typedef struct _drmPlatformBusInfo { ++ char fullname[DRM_PLATFORM_DEVICE_NAME_LEN]; ++} drmPlatformBusInfo, *drmPlatformBusInfoPtr; ++ ++typedef struct _drmPlatformDeviceInfo { ++ char **compatible; /* NULL terminated list of compatible strings */ ++} drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr; ++ ++#define DRM_HOST1X_DEVICE_NAME_LEN 512 ++ ++typedef struct _drmHost1xBusInfo { ++ char fullname[DRM_HOST1X_DEVICE_NAME_LEN]; ++} drmHost1xBusInfo, *drmHost1xBusInfoPtr; ++ ++typedef struct _drmHost1xDeviceInfo { ++ char **compatible; /* NULL terminated list of compatible strings */ ++} drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr; ++ ++typedef struct _drmDevice { ++ char **nodes; /* DRM_NODE_MAX sized array */ ++ int available_nodes; /* DRM_NODE_* bitmask */ ++ int bustype; ++ union { ++ drmPciBusInfoPtr pci; ++ drmUsbBusInfoPtr usb; ++ drmPlatformBusInfoPtr platform; ++ drmHost1xBusInfoPtr host1x; ++ } businfo; ++ union { ++ drmPciDeviceInfoPtr pci; ++ drmUsbDeviceInfoPtr usb; ++ drmPlatformDeviceInfoPtr platform; ++ drmHost1xDeviceInfoPtr host1x; ++ } deviceinfo; ++} drmDevice, *drmDevicePtr; ++ ++extern int drmGetDevice(int fd, drmDevicePtr *device); ++extern void drmFreeDevice(drmDevicePtr *device); ++ ++extern int drmGetDevices(drmDevicePtr devices[], int max_devices); ++extern void drmFreeDevices(drmDevicePtr devices[], int count); ++ ++#define DRM_DEVICE_GET_PCI_REVISION (1 << 0) ++extern int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device); ++extern int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); ++ ++extern int drmGetDeviceFromDevId(dev_t dev_id, uint32_t flags, drmDevicePtr *device); ++ ++extern int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b); ++ ++extern int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle); ++extern int drmSyncobjDestroy(int fd, uint32_t handle); ++extern int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd); ++extern int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle); ++ ++extern int drmSyncobjImportSyncFile(int fd, uint32_t handle, int sync_file_fd); ++extern int drmSyncobjExportSyncFile(int fd, uint32_t handle, int *sync_file_fd); ++extern int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles, ++ int64_t timeout_nsec, unsigned flags, ++ uint32_t *first_signaled); ++extern int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t handle_count); ++extern int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t handle_count); ++extern int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, ++ uint64_t *points, uint32_t handle_count); ++extern int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, ++ unsigned num_handles, ++ int64_t timeout_nsec, unsigned flags, ++ uint32_t *first_signaled); ++extern int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points, ++ uint32_t handle_count); ++extern int drmSyncobjQuery2(int fd, uint32_t *handles, uint64_t *points, ++ uint32_t handle_count, uint32_t flags); ++extern int drmSyncobjTransfer(int fd, ++ uint32_t dst_handle, uint64_t dst_point, ++ uint32_t src_handle, uint64_t src_point, ++ uint32_t flags); ++ ++extern char * ++drmGetFormatModifierVendor(uint64_t modifier); ++ ++extern char * ++drmGetFormatModifierName(uint64_t modifier); ++ ++#ifndef fourcc_mod_get_vendor ++#define fourcc_mod_get_vendor(modifier) \ ++ (((modifier) >> 56) & 0xff) ++#endif ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif +diff --git a/third_party/drm/libdrm/moz.build b/third_party/drm/libdrm/moz.build +new file mode 100644 +index 0000000..3b37b91 +--- /dev/null ++++ b/third_party/drm/libdrm/moz.build +@@ -0,0 +1,16 @@ ++# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- ++# vim: set filetype=python: ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++SOURCES += [ ++ 'mozdrm.cpp', ++] ++ ++if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": ++ CXXFLAGS += CONFIG['MOZ_GTK3_CFLAGS'] ++ ++LOCAL_INCLUDES += ['/third_party/drm'] ++ ++FINAL_LIBRARY = 'xul' +diff --git a/third_party/drm/libdrm/mozdrm.cpp b/third_party/drm/libdrm/mozdrm.cpp +new file mode 100644 +index 0000000..b2fb59b +--- /dev/null ++++ b/third_party/drm/libdrm/mozdrm.cpp +@@ -0,0 +1,66 @@ ++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ ++/* vim:expandtab:shiftwidth=4:tabstop=4: ++ */ ++/* This Source Code Form is subject to the terms of the Mozilla Public ++ * License, v. 2.0. If a copy of the MPL was not distributed with this ++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ ++ ++#include "mozilla/Types.h" ++#include "prlink.h" ++ ++#include ++ ++#define GET_FUNC(func, lib) \ ++ func##_fn = \ ++ (decltype(func##_fn))PR_FindFunctionSymbol(lib, #func) \ ++ ++#define IS_FUNC_LOADED(func) \ ++ (func != nullptr) \ ++ ++static int (*drmGetDevices2_fn)(uint32_t flags, drmDevicePtr devices[], int max_devices); ++static void (*drmFreeDevices_fn)(drmDevicePtr devices[], int count); ++ ++bool IsDRMLibraryLoaded() { ++ static bool isLoaded = ++ (IS_FUNC_LOADED(drmGetDevices2_fn) && ++ IS_FUNC_LOADED(drmFreeDevices_fn)); ++ ++ return isLoaded; ++} ++ ++bool LoadDRMLibrary() { ++ static PRLibrary* drmLib = nullptr; ++ static bool drmInitialized = false; ++ ++ //TODO Thread safe ++ if (!drmInitialized) { ++ drmInitialized = true; ++ drmLib = PR_LoadLibrary("libdrm.so.2"); ++ if (!drmLib) { ++ return false; ++ } ++ ++ GET_FUNC(drmGetDevices2, drmLib); ++ GET_FUNC(drmFreeDevices, drmLib); ++ } ++ ++ return IsDRMLibraryLoaded(); ++} ++ ++int ++drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices) ++{ ++ if (!LoadDRMLibrary()) { ++ return 0; ++ } ++ return drmGetDevices2_fn(flags, devices, max_devices); ++} ++ ++void ++drmFreeDevices(drmDevicePtr devices[], int count) ++{ ++ if (!LoadDRMLibrary()) { ++ return; ++ } ++ return drmFreeDevices_fn(devices, count); ++} +diff --git a/third_party/gbm/README b/third_party/gbm/README +new file mode 100644 +index 0000000..4b6e2e8 +--- /dev/null ++++ b/third_party/gbm/README +@@ -0,0 +1,4 @@ ++Libgbm is a gbm library wrapper needed to build and run Firefox with ++Pipewire support on Linux (https://gitlab.freedesktop.org/mesa/gbm). ++ ++libgbm directory stores headers of libgbm needed for build only. +diff --git a/third_party/gbm/gbm/gbm.h b/third_party/gbm/gbm/gbm.h +new file mode 100644 +index 0000000..a963ed7 +--- /dev/null ++++ b/third_party/gbm/gbm/gbm.h +@@ -0,0 +1,452 @@ ++/* ++ * Copyright © 2011 Intel Corporation ++ * ++ * 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. ++ * ++ * Authors: ++ * Benjamin Franzke ++ */ ++ ++#ifndef _GBM_H_ ++#define _GBM_H_ ++ ++#define __GBM__ 1 ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/** ++ * \file gbm.h ++ * \brief Generic Buffer Manager ++ */ ++ ++struct gbm_device; ++struct gbm_bo; ++struct gbm_surface; ++ ++/** ++ * \mainpage The Generic Buffer Manager ++ * ++ * This module provides an abstraction that the caller can use to request a ++ * buffer from the underlying memory management system for the platform. ++ * ++ * This allows the creation of portable code whilst still allowing access to ++ * the underlying memory manager. ++ */ ++ ++/** ++ * Abstraction representing the handle to a buffer allocated by the ++ * manager ++ */ ++union gbm_bo_handle { ++ void *ptr; ++ int32_t s32; ++ uint32_t u32; ++ int64_t s64; ++ uint64_t u64; ++}; ++ ++/** Format of the allocated buffer */ ++enum gbm_bo_format { ++ /** RGB with 8 bits per channel in a 32 bit value */ ++ GBM_BO_FORMAT_XRGB8888, ++ /** ARGB with 8 bits per channel in a 32 bit value */ ++ GBM_BO_FORMAT_ARGB8888 ++}; ++ ++ ++/** ++ * The FourCC format codes are taken from the drm_fourcc.h definition, and ++ * re-namespaced. New GBM formats must not be added, unless they are ++ * identical ports from drm_fourcc. ++ */ ++#define __gbm_fourcc_code(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ ++ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) ++ ++#define GBM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ ++ ++/* color index */ ++#define GBM_FORMAT_C8 __gbm_fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ ++ ++/* 8 bpp Red */ ++#define GBM_FORMAT_R8 __gbm_fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ ++ ++/* 16 bpp Red */ ++#define GBM_FORMAT_R16 __gbm_fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ ++ ++/* 16 bpp RG */ ++#define GBM_FORMAT_GR88 __gbm_fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ ++ ++/* 8 bpp RGB */ ++#define GBM_FORMAT_RGB332 __gbm_fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ ++#define GBM_FORMAT_BGR233 __gbm_fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ ++ ++/* 16 bpp RGB */ ++#define GBM_FORMAT_XRGB4444 __gbm_fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ ++#define GBM_FORMAT_XBGR4444 __gbm_fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ ++#define GBM_FORMAT_RGBX4444 __gbm_fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ ++#define GBM_FORMAT_BGRX4444 __gbm_fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ ++ ++#define GBM_FORMAT_ARGB4444 __gbm_fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ ++#define GBM_FORMAT_ABGR4444 __gbm_fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ ++#define GBM_FORMAT_RGBA4444 __gbm_fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ ++#define GBM_FORMAT_BGRA4444 __gbm_fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ ++ ++#define GBM_FORMAT_XRGB1555 __gbm_fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ ++#define GBM_FORMAT_XBGR1555 __gbm_fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ ++#define GBM_FORMAT_RGBX5551 __gbm_fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ ++#define GBM_FORMAT_BGRX5551 __gbm_fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ ++ ++#define GBM_FORMAT_ARGB1555 __gbm_fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ ++#define GBM_FORMAT_ABGR1555 __gbm_fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ ++#define GBM_FORMAT_RGBA5551 __gbm_fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ ++#define GBM_FORMAT_BGRA5551 __gbm_fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ ++ ++#define GBM_FORMAT_RGB565 __gbm_fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ ++#define GBM_FORMAT_BGR565 __gbm_fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ ++ ++/* 24 bpp RGB */ ++#define GBM_FORMAT_RGB888 __gbm_fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ ++#define GBM_FORMAT_BGR888 __gbm_fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ ++ ++/* 32 bpp RGB */ ++#define GBM_FORMAT_XRGB8888 __gbm_fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ ++#define GBM_FORMAT_XBGR8888 __gbm_fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ ++#define GBM_FORMAT_RGBX8888 __gbm_fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ ++#define GBM_FORMAT_BGRX8888 __gbm_fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ ++ ++#define GBM_FORMAT_ARGB8888 __gbm_fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ ++#define GBM_FORMAT_ABGR8888 __gbm_fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ ++#define GBM_FORMAT_RGBA8888 __gbm_fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ ++#define GBM_FORMAT_BGRA8888 __gbm_fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ ++ ++#define GBM_FORMAT_XRGB2101010 __gbm_fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ ++#define GBM_FORMAT_XBGR2101010 __gbm_fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ ++#define GBM_FORMAT_RGBX1010102 __gbm_fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ ++#define GBM_FORMAT_BGRX1010102 __gbm_fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ ++ ++#define GBM_FORMAT_ARGB2101010 __gbm_fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ ++#define GBM_FORMAT_ABGR2101010 __gbm_fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ ++#define GBM_FORMAT_RGBA1010102 __gbm_fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ ++#define GBM_FORMAT_BGRA1010102 __gbm_fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ ++ ++/* ++ * Floating point 64bpp RGB ++ * IEEE 754-2008 binary16 half-precision float ++ * [15:0] sign:exponent:mantissa 1:5:10 ++ */ ++#define GBM_FORMAT_XBGR16161616F __gbm_fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */ ++ ++#define GBM_FORMAT_ABGR16161616F __gbm_fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ ++ ++/* packed YCbCr */ ++#define GBM_FORMAT_YUYV __gbm_fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ ++#define GBM_FORMAT_YVYU __gbm_fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ ++#define GBM_FORMAT_UYVY __gbm_fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ ++#define GBM_FORMAT_VYUY __gbm_fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ ++ ++#define GBM_FORMAT_AYUV __gbm_fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ ++ ++/* ++ * 2 plane YCbCr ++ * index 0 = Y plane, [7:0] Y ++ * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian ++ * or ++ * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian ++ */ ++#define GBM_FORMAT_NV12 __gbm_fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ ++#define GBM_FORMAT_NV21 __gbm_fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ ++#define GBM_FORMAT_NV16 __gbm_fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ ++#define GBM_FORMAT_NV61 __gbm_fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ ++ ++/* ++ * 3 plane YCbCr ++ * index 0: Y plane, [7:0] Y ++ * index 1: Cb plane, [7:0] Cb ++ * index 2: Cr plane, [7:0] Cr ++ * or ++ * index 1: Cr plane, [7:0] Cr ++ * index 2: Cb plane, [7:0] Cb ++ */ ++#define GBM_FORMAT_YUV410 __gbm_fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ ++#define GBM_FORMAT_YVU410 __gbm_fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ ++#define GBM_FORMAT_YUV411 __gbm_fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ ++#define GBM_FORMAT_YVU411 __gbm_fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ ++#define GBM_FORMAT_YUV420 __gbm_fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ ++#define GBM_FORMAT_YVU420 __gbm_fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ ++#define GBM_FORMAT_YUV422 __gbm_fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ ++#define GBM_FORMAT_YVU422 __gbm_fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ ++#define GBM_FORMAT_YUV444 __gbm_fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ ++#define GBM_FORMAT_YVU444 __gbm_fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ ++ ++struct gbm_format_name_desc { ++ char name[5]; ++}; ++ ++/** ++ * Flags to indicate the intended use for the buffer - these are passed into ++ * gbm_bo_create(). The caller must set the union of all the flags that are ++ * appropriate ++ * ++ * \sa Use gbm_device_is_format_supported() to check if the combination of format ++ * and use flags are supported ++ */ ++enum gbm_bo_flags { ++ /** ++ * Buffer is going to be presented to the screen using an API such as KMS ++ */ ++ GBM_BO_USE_SCANOUT = (1 << 0), ++ /** ++ * Buffer is going to be used as cursor ++ */ ++ GBM_BO_USE_CURSOR = (1 << 1), ++ /** ++ * Deprecated ++ */ ++ GBM_BO_USE_CURSOR_64X64 = GBM_BO_USE_CURSOR, ++ /** ++ * Buffer is to be used for rendering - for example it is going to be used ++ * as the storage for a color buffer ++ */ ++ GBM_BO_USE_RENDERING = (1 << 2), ++ /** ++ * Buffer can be used for gbm_bo_write. This is guaranteed to work ++ * with GBM_BO_USE_CURSOR, but may not work for other combinations. ++ */ ++ GBM_BO_USE_WRITE = (1 << 3), ++ /** ++ * Buffer is linear, i.e. not tiled. ++ */ ++ GBM_BO_USE_LINEAR = (1 << 4), ++ /** ++ * Buffer is protected, i.e. encrypted and not readable by CPU or any ++ * other non-secure / non-trusted components nor by non-trusted OpenGL, ++ * OpenCL, and Vulkan applications. ++ */ ++ GBM_BO_USE_PROTECTED = (1 << 5), ++}; ++ ++int ++gbm_device_get_fd(struct gbm_device *gbm); ++ ++const char * ++gbm_device_get_backend_name(struct gbm_device *gbm); ++ ++int ++gbm_device_is_format_supported(struct gbm_device *gbm, ++ uint32_t format, uint32_t flags); ++ ++int ++gbm_device_get_format_modifier_plane_count(struct gbm_device *gbm, ++ uint32_t format, ++ uint64_t modifier); ++ ++void ++gbm_device_destroy(struct gbm_device *gbm); ++ ++struct gbm_device * ++gbm_create_device(int fd); ++ ++struct gbm_bo * ++gbm_bo_create(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, uint32_t flags); ++ ++struct gbm_bo * ++gbm_bo_create_with_modifiers(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, ++ const uint64_t *modifiers, ++ const unsigned int count); ++ ++struct gbm_bo * ++gbm_bo_create_with_modifiers2(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, ++ const uint64_t *modifiers, ++ const unsigned int count, ++ uint32_t flags); ++ ++#define GBM_BO_IMPORT_WL_BUFFER 0x5501 ++#define GBM_BO_IMPORT_EGL_IMAGE 0x5502 ++#define GBM_BO_IMPORT_FD 0x5503 ++#define GBM_BO_IMPORT_FD_MODIFIER 0x5504 ++ ++struct gbm_import_fd_data { ++ int fd; ++ uint32_t width; ++ uint32_t height; ++ uint32_t stride; ++ uint32_t format; ++}; ++ ++#define GBM_MAX_PLANES 4 ++ ++struct gbm_import_fd_modifier_data { ++ uint32_t width; ++ uint32_t height; ++ uint32_t format; ++ uint32_t num_fds; ++ int fds[GBM_MAX_PLANES]; ++ int strides[GBM_MAX_PLANES]; ++ int offsets[GBM_MAX_PLANES]; ++ uint64_t modifier; ++}; ++ ++struct gbm_bo * ++gbm_bo_import(struct gbm_device *gbm, uint32_t type, ++ void *buffer, uint32_t flags); ++ ++/** ++ * Flags to indicate the type of mapping for the buffer - these are ++ * passed into gbm_bo_map(). The caller must set the union of all the ++ * flags that are appropriate. ++ * ++ * These flags are independent of the GBM_BO_USE_* creation flags. However, ++ * mapping the buffer may require copying to/from a staging buffer. ++ * ++ * See also: pipe_map_flags ++ */ ++enum gbm_bo_transfer_flags { ++ /** ++ * Buffer contents read back (or accessed directly) at transfer ++ * create time. ++ */ ++ GBM_BO_TRANSFER_READ = (1 << 0), ++ /** ++ * Buffer contents will be written back at unmap time ++ * (or modified as a result of being accessed directly). ++ */ ++ GBM_BO_TRANSFER_WRITE = (1 << 1), ++ /** ++ * Read/modify/write ++ */ ++ GBM_BO_TRANSFER_READ_WRITE = (GBM_BO_TRANSFER_READ | GBM_BO_TRANSFER_WRITE), ++}; ++ ++void * ++gbm_bo_map(struct gbm_bo *bo, ++ uint32_t x, uint32_t y, uint32_t width, uint32_t height, ++ uint32_t flags, uint32_t *stride, void **map_data); ++ ++void ++gbm_bo_unmap(struct gbm_bo *bo, void *map_data); ++ ++uint32_t ++gbm_bo_get_width(struct gbm_bo *bo); ++ ++uint32_t ++gbm_bo_get_height(struct gbm_bo *bo); ++ ++uint32_t ++gbm_bo_get_stride(struct gbm_bo *bo); ++ ++uint32_t ++gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane); ++ ++uint32_t ++gbm_bo_get_format(struct gbm_bo *bo); ++ ++uint32_t ++gbm_bo_get_bpp(struct gbm_bo *bo); ++ ++uint32_t ++gbm_bo_get_offset(struct gbm_bo *bo, int plane); ++ ++struct gbm_device * ++gbm_bo_get_device(struct gbm_bo *bo); ++ ++union gbm_bo_handle ++gbm_bo_get_handle(struct gbm_bo *bo); ++ ++int ++gbm_bo_get_fd(struct gbm_bo *bo); ++ ++uint64_t ++gbm_bo_get_modifier(struct gbm_bo *bo); ++ ++int ++gbm_bo_get_plane_count(struct gbm_bo *bo); ++ ++union gbm_bo_handle ++gbm_bo_get_handle_for_plane(struct gbm_bo *bo, int plane); ++ ++int ++gbm_bo_get_fd_for_plane(struct gbm_bo *bo, int plane); ++ ++int ++gbm_bo_write(struct gbm_bo *bo, const void *buf, size_t count); ++ ++void ++gbm_bo_set_user_data(struct gbm_bo *bo, void *data, ++ void (*destroy_user_data)(struct gbm_bo *, void *)); ++ ++void * ++gbm_bo_get_user_data(struct gbm_bo *bo); ++ ++void ++gbm_bo_destroy(struct gbm_bo *bo); ++ ++struct gbm_surface * ++gbm_surface_create(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, uint32_t flags); ++ ++struct gbm_surface * ++gbm_surface_create_with_modifiers(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, ++ const uint64_t *modifiers, ++ const unsigned int count); ++ ++struct gbm_surface * ++gbm_surface_create_with_modifiers2(struct gbm_device *gbm, ++ uint32_t width, uint32_t height, ++ uint32_t format, ++ const uint64_t *modifiers, ++ const unsigned int count, ++ uint32_t flags); ++ ++struct gbm_bo * ++gbm_surface_lock_front_buffer(struct gbm_surface *surface); ++ ++void ++gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo); ++ ++int ++gbm_surface_has_free_buffers(struct gbm_surface *surface); ++ ++void ++gbm_surface_destroy(struct gbm_surface *surface); ++ ++char * ++gbm_format_get_name(uint32_t gbm_format, struct gbm_format_name_desc *desc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/third_party/gbm/libgbm/moz.build b/third_party/gbm/libgbm/moz.build +new file mode 100644 +index 0000000..0953d2f +--- /dev/null ++++ b/third_party/gbm/libgbm/moz.build +@@ -0,0 +1,16 @@ ++# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- ++# vim: set filetype=python: ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++SOURCES += [ ++ 'mozgbm.cpp', ++] ++ ++if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": ++ CXXFLAGS += CONFIG['MOZ_GTK3_CFLAGS'] ++ ++LOCAL_INCLUDES += ['/third_party/gbm'] ++ ++FINAL_LIBRARY = 'xul' +diff --git a/third_party/gbm/libgbm/mozgbm.cpp b/third_party/gbm/libgbm/mozgbm.cpp +new file mode 100644 +index 0000000..bc024a1 +--- /dev/null ++++ b/third_party/gbm/libgbm/mozgbm.cpp +@@ -0,0 +1,66 @@ ++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ ++/* vim:expandtab:shiftwidth=4:tabstop=4: ++ */ ++/* This Source Code Form is subject to the terms of the Mozilla Public ++ * License, v. 2.0. If a copy of the MPL was not distributed with this ++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ ++ ++#include "mozilla/Types.h" ++#include "prlink.h" ++ ++#include ++ ++#define GET_FUNC(func, lib) \ ++ func##_fn = \ ++ (decltype(func##_fn))PR_FindFunctionSymbol(lib, #func) \ ++ ++#define IS_FUNC_LOADED(func) \ ++ (func != nullptr) \ ++ ++static struct gbm_device * (*gbm_create_device_fn)(int fd); ++static void (*gbm_device_destroy_fn)(struct gbm_device* gbm); ++ ++bool IsGBMLibraryLoaded() { ++ static bool isLoaded = ++ (IS_FUNC_LOADED(gbm_create_device_fn) && ++ IS_FUNC_LOADED(gbm_device_destroy_fn)); ++ ++ return isLoaded; ++} ++ ++bool LoadGBMLibrary() { ++ static PRLibrary* gbmLib = nullptr; ++ static bool gbmInitialized = false; ++ ++ //TODO Thread safe ++ if (!gbmInitialized) { ++ gbmInitialized = true; ++ gbmLib = PR_LoadLibrary("libgbm.so.1"); ++ if (!gbmLib) { ++ return false; ++ } ++ ++ GET_FUNC(gbm_create_device, gbmLib); ++ GET_FUNC(gbm_device_destroy, gbmLib); ++ } ++ ++ return IsGBMLibraryLoaded(); ++} ++ ++struct gbm_device * ++gbm_create_device(int fd) ++{ ++ if (!LoadGBMLibrary()) { ++ return nullptr; ++ } ++ return gbm_create_device_fn(fd); ++} ++ ++void ++gbm_device_destroy(struct gbm_device* gbm) ++{ ++ if (!LoadGBMLibrary()) { ++ return; ++ } ++ return gbm_device_destroy_fn(gbm); ++} +diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build +index a5cf923..99cabbf 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build ++++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build +@@ -77,6 +77,8 @@ if CONFIG["OS_TARGET"] == "Darwin": + LOCAL_INCLUDES += [ + "/media/libyuv/libyuv/include/", + "/media/libyuv/libyuv/include/", ++ "/third_party/drm/", ++ "/third_party/gbm/", + "/third_party/pipewire/" + ] + +@@ -108,7 +110,8 @@ if CONFIG["OS_TARGET"] == "Linux": + LOCAL_INCLUDES += [ + "/media/libyuv/libyuv/include/", + "/media/libyuv/libyuv/include/", +- "/third_party/pipewire/", ++ "/third_party/drm/", ++ "/third_party/gbm/", + "/third_party/pipewire/" + ] + +@@ -126,15 +129,18 @@ if CONFIG["OS_TARGET"] == "Linux": + ] + + SOURCES += [ +- "/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc" ++ "/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.cc" + ] + + UNIFIED_SOURCES += [ + "/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc", +- "/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/shared_x_display.cc", +- "/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_finder_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_list_utils.cc", +@@ -162,6 +168,8 @@ if CONFIG["OS_TARGET"] == "OpenBSD": + + LOCAL_INCLUDES += [ + "/media/libyuv/libyuv/include/", ++ "/third_party/drm/", ++ "/third_party/gbm/", + "/third_party/pipewire/" + ] + +@@ -177,15 +185,18 @@ if CONFIG["OS_TARGET"] == "OpenBSD": + ] + + SOURCES += [ +- "/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc" ++ "/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.cc", ++ "/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.cc" + ] + + UNIFIED_SOURCES += [ + "/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc", +- "/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/shared_x_display.cc", +- "/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_finder_x11.cc", + "/third_party/libwebrtc/modules/desktop_capture/linux/window_list_utils.cc", +@@ -227,6 +238,7 @@ if CONFIG["OS_TARGET"] == "WINNT": + LOCAL_INCLUDES += [ + "/media/libyuv/libyuv/include/", + "/media/libyuv/libyuv/include/", ++ "/third_party/drm/" + "/third_party/pipewire/" + ] + +diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc +index c89896d..c8ef822 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc ++++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc +@@ -14,6 +14,9 @@ + #elif defined(WEBRTC_WIN) + #include "modules/desktop_capture/win/full_screen_win_application_handler.h" + #endif ++#if defined(WEBRTC_USE_PIPEWIRE) ++#include "modules/desktop_capture/linux/shared_screencast_stream.h" ++#endif + + namespace webrtc { + +@@ -35,6 +38,9 @@ DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() { + #if defined(WEBRTC_USE_X11) + result.set_x_display(SharedXDisplay::CreateDefault()); + #endif ++#if defined(WEBRTC_USE_PIPEWIRE) ++ result.set_screencast_stream(SharedScreenCastStream::CreateDefault()); ++#endif + #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + result.set_configuration_monitor(new DesktopConfigurationMonitor()); + result.set_full_screen_window_detector( +diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h +index ee0dd3a..ac56c8c 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h ++++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h +@@ -17,6 +17,10 @@ + #include "modules/desktop_capture/linux/shared_x_display.h" + #endif + ++#if defined(WEBRTC_USE_PIPEWIRE) ++#include "modules/desktop_capture/linux/shared_screencast_stream.h" ++#endif ++ + #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + #include "modules/desktop_capture/mac/desktop_configuration_monitor.h" + #endif +@@ -149,13 +153,26 @@ class RTC_EXPORT DesktopCaptureOptions { + #if defined(WEBRTC_USE_PIPEWIRE) + bool allow_pipewire() const { return allow_pipewire_; } + void set_allow_pipewire(bool allow) { allow_pipewire_ = allow; } ++ ++ const rtc::scoped_refptr& screencast_stream() const { ++ return screencast_stream_; ++ } ++ void set_screencast_stream( ++ rtc::scoped_refptr stream) { ++ screencast_stream_ = stream; ++ } + #endif + + private: + #if defined(WEBRTC_USE_X11) + rtc::scoped_refptr x_display_; + #endif +- ++#if defined(WEBRTC_USE_PIPEWIRE) ++ // An instance of shared PipeWire ScreenCast stream we share between ++ // BaseCapturerPipeWire and MouseCursorMonitorPipeWire as cursor information ++ // is sent together with screen content. ++ rtc::scoped_refptr screencast_stream_; ++#endif + #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + rtc::scoped_refptr configuration_monitor_; + bool allow_iosurface_ = false; +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc +index 2fd3b1a..e4685fc 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc +@@ -10,937 +10,67 @@ + + #include "modules/desktop_capture/linux/base_capturer_pipewire.h" + +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include "absl/memory/memory.h" + #include "modules/desktop_capture/desktop_capture_options.h" + #include "modules/desktop_capture/desktop_capturer.h" + #include "rtc_base/checks.h" + #include "rtc_base/logging.h" + +-#if defined(WEBRTC_DLOPEN_PIPEWIRE) +-#include "modules/desktop_capture/linux/pipewire_stubs.h" +- +-using modules_desktop_capture_linux::InitializeStubs; +-using modules_desktop_capture_linux::kModulePipewire; +-using modules_desktop_capture_linux::StubPathMap; +-#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) +- + namespace webrtc { + +-const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; +-const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; +-const char kDesktopRequestObjectPath[] = +- "/org/freedesktop/portal/desktop/request"; +-const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; +-const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; +-const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; +- +-const int kBytesPerPixel = 4; +- +-#if defined(WEBRTC_DLOPEN_PIPEWIRE) +-const char kPipeWireLib[] = "libpipewire-0.2.so.1"; +-#endif +- +-// static +-struct dma_buf_sync { +- uint64_t flags; +-}; +-#define DMA_BUF_SYNC_READ (1 << 0) +-#define DMA_BUF_SYNC_START (0 << 2) +-#define DMA_BUF_SYNC_END (1 << 2) +-#define DMA_BUF_BASE 'b' +-#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) +- +-static void SyncDmaBuf(int fd, uint64_t start_or_end) { +- struct dma_buf_sync sync = { 0 }; +- +- sync.flags = start_or_end | DMA_BUF_SYNC_READ; +- +- while(true) { +- int ret; +- ret = ioctl (fd, DMA_BUF_IOCTL_SYNC, &sync); +- if (ret == -1 && errno == EINTR) { +- continue; +- } else if (ret == -1) { +- RTC_LOG(LS_ERROR) << "Failed to synchronize DMA buffer: " << g_strerror(errno); +- break; +- } else { +- break; +- } +- } +-} +- +-// static +-void BaseCapturerPipeWire::OnCoreError(void *data, +- uint32_t id, +- int seq, +- int res, +- const char *message) { +- RTC_LOG(LS_ERROR) << "core error: " << message; +-} +- +-// static +-void BaseCapturerPipeWire::OnStreamStateChanged(void* data, +- pw_stream_state old_state, +- pw_stream_state state, +- const char* error_message) { +- BaseCapturerPipeWire* that = static_cast(data); +- RTC_DCHECK(that); +- +- switch (state) { +- case PW_STREAM_STATE_ERROR: +- RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; +- break; +- case PW_STREAM_STATE_PAUSED: +- case PW_STREAM_STATE_STREAMING: +- case PW_STREAM_STATE_UNCONNECTED: +- case PW_STREAM_STATE_CONNECTING: +- break; +- } ++BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options) ++ : options_(options) { ++ screencast_portal_ = std::make_unique( ++ ScreenCastPortal::CaptureSourceType::kAnyScreenContent, this); + } + +-// static +-void BaseCapturerPipeWire::OnStreamParamChanged(void *data, uint32_t id, +- const struct spa_pod *format) { +- BaseCapturerPipeWire* that = static_cast(data); +- RTC_DCHECK(that); +- +- RTC_LOG(LS_INFO) << "PipeWire stream param changed."; +- +- if (!format || id != SPA_PARAM_Format) { +- return; +- } +- +- spa_format_video_raw_parse(format, &that->spa_video_format_); +- +- auto width = that->spa_video_format_.size.width; +- auto height = that->spa_video_format_.size.height; +- // In order to be able to build in the non unified environment kBytesPerPixel +- // must be fully qualified, see Bug 1725145 +- auto stride = SPA_ROUND_UP_N(width * BasicDesktopFrame::kBytesPerPixel, 4); +- auto size = height * stride; +- +- that->desktop_size_ = DesktopSize(width, height); +- +- uint8_t buffer[1024] = {}; +- auto builder = spa_pod_builder{buffer, sizeof(buffer)}; +- +- // Setup buffers and meta header for new format. +- const struct spa_pod* params[3]; +- params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, +- SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, +- SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<(spa_pod_builder_add_object(&builder, +- 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] = reinterpret_cast(spa_pod_builder_add_object(&builder, +- SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, +- SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), +- SPA_PARAM_META_size, SPA_POD_Int (sizeof(struct spa_meta_region)))); +- pw_stream_update_params(that->pw_stream_, params, 3); +-} +- +-// static +-void BaseCapturerPipeWire::OnStreamProcess(void* data) { +- BaseCapturerPipeWire* that = static_cast(data); +- RTC_DCHECK(that); +- +- struct pw_buffer *next_buffer; +- struct pw_buffer *buffer = nullptr; ++BaseCapturerPipeWire::~BaseCapturerPipeWire() {} + +- next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); +- while (next_buffer) { +- buffer = next_buffer; +- next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); +- +- if (next_buffer) { +- pw_stream_queue_buffer (that->pw_stream_, buffer); +- } +- } +- +- if (!buffer) { +- return; ++void BaseCapturerPipeWire::OnScreenCastRequestResult( ++ ScreenCastPortal::RequestResponse result, ++ uint32_t stream_node_id, ++ int fd) { ++ if (result != ScreenCastPortal::RequestResponse::kSuccess || ++ !options_.screencast_stream()->StartScreenCastStream(stream_node_id, ++ fd)) { ++ capturer_failed_ = true; ++ RTC_LOG(LS_ERROR) << "ScreenCastPortal failed: " ++ << static_cast(result); + } +- +- that->HandleBuffer(buffer); +- +- pw_stream_queue_buffer(that->pw_stream_, buffer); + } + +-BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type) +- : capture_source_type_(source_type) {} +- +-BaseCapturerPipeWire::~BaseCapturerPipeWire() { +- if (pw_main_loop_) { +- pw_thread_loop_stop(pw_main_loop_); +- } +- +- if (pw_stream_) { +- pw_stream_destroy(pw_stream_); +- } +- +- if (pw_core_) { +- pw_core_disconnect(pw_core_); +- } +- +- if (pw_context_) { +- pw_context_destroy(pw_context_); +- } +- +- if (pw_main_loop_) { +- pw_thread_loop_destroy(pw_main_loop_); ++void BaseCapturerPipeWire::OnScreenCastSessionClosed() { ++ if (!capturer_failed_) { ++ options_.screencast_stream()->StopScreenCastStream(); + } +- +- if (start_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); +- } +- if (sources_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(connection_, +- sources_request_signal_id_); +- } +- if (session_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(connection_, +- session_request_signal_id_); +- } +- +- if (session_handle_) { +- GDBusMessage* message = g_dbus_message_new_method_call( +- kDesktopBusName, session_handle_, kSessionInterfaceName, "Close"); +- if (message) { +- GError* error = nullptr; +- g_dbus_connection_send_message(connection_, message, +- G_DBUS_SEND_MESSAGE_FLAGS_NONE, +- /*out_serial=*/nullptr, &error); +- if (error) { +- RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; +- g_error_free(error); +- } +- g_object_unref(message); +- } +- } +- +- g_free(start_handle_); +- g_free(sources_handle_); +- g_free(session_handle_); +- g_free(portal_handle_); +- +- if (cancellable_) { +- g_cancellable_cancel(cancellable_); +- g_object_unref(cancellable_); +- cancellable_ = nullptr; +- } +- +- if (proxy_) { +- g_object_unref(proxy_); +- proxy_ = nullptr; +- } +- +- if (pw_fd_ != -1) { +- close(pw_fd_); +- } +-} +- +-void BaseCapturerPipeWire::InitPortal() { +- cancellable_ = g_cancellable_new(); +- g_dbus_proxy_new_for_bus( +- G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, +- kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, +- cancellable_, +- reinterpret_cast(OnProxyRequested), this); +-} +- +-void BaseCapturerPipeWire::InitPipeWire() { +-#if defined(WEBRTC_DLOPEN_PIPEWIRE) +- StubPathMap paths; +- +- // Check if the PipeWire library is available. +- paths[kModulePipewire].push_back(kPipeWireLib); +- if (!InitializeStubs(paths)) { +- RTC_LOG(LS_ERROR) << "Failed to load the PipeWire library and symbols."; +- portal_init_failed_ = true; +- return; +- } +-#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) +- +- pw_init(/*argc=*/nullptr, /*argc=*/nullptr); +- +- pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr); +- pw_context_ = pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); +- if (!pw_context_) { +- RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; +- return; +- } +- +- pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); +- if (!pw_core_) { +- RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; +- return; +- } +- +- // Initialize event handlers, remote end and stream-related. +- pw_core_events_.version = PW_VERSION_CORE_EVENTS; +- pw_core_events_.error = &OnCoreError; +- +- pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; +- pw_stream_events_.state_changed = &OnStreamStateChanged; +- pw_stream_events_.param_changed = &OnStreamParamChanged; +- pw_stream_events_.process = &OnStreamProcess; +- +- pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); +- +- pw_stream_ = CreateReceivingStream(); +- if (!pw_stream_) { +- RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; +- return; +- } +- +- if (pw_thread_loop_start(pw_main_loop_) < 0) { +- RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; +- portal_init_failed_ = true; +- } +- +- RTC_LOG(LS_INFO) << "PipeWire remote opened."; +-} +- +-pw_stream* BaseCapturerPipeWire::CreateReceivingStream() { +- spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1}; +- spa_rectangle pwMaxScreenBounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; +- +- auto stream = pw_stream_new(pw_core_, "webrtc-pipewire-stream", nullptr); +- +- if (!stream) { +- RTC_LOG(LS_ERROR) << "Could not create receiving stream."; +- return nullptr; +- } +- +- uint8_t buffer[1024] = {}; +- const spa_pod* params[2]; +- spa_pod_builder builder = SPA_POD_BUILDER_INIT(buffer, sizeof (buffer)); +- +- params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, +- SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, +- SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), +- SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), +- SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, +- SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA), +- SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, +- &pwMinScreenBounds, +- &pwMaxScreenBounds), +- 0)); +- pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_, this); +- +- if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_, +- PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) { +- RTC_LOG(LS_ERROR) << "Could not connect receiving stream."; +- portal_init_failed_ = true; +- } +- +- return stream; +-} +- +-static void SpaBufferUnmap(unsigned char *map, int map_size, bool IsDMABuf, int fd) { +- if (map) { +- if (IsDMABuf) { +- SyncDmaBuf(fd, DMA_BUF_SYNC_END); +- } +- munmap(map, map_size); +- } +-} +- +-void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) { +- spa_buffer* spaBuffer = buffer->buffer; +- uint8_t *map = nullptr; +- uint8_t* src = nullptr; +- +- if (spaBuffer->datas[0].chunk->size == 0) { +- RTC_LOG(LS_ERROR) << "Failed to get video stream: Zero size."; +- return; +- } +- +- switch (spaBuffer->datas[0].type) { +- case SPA_DATA_MemFd: +- case SPA_DATA_DmaBuf: +- map = static_cast(mmap( +- nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, +- PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)); +- if (map == MAP_FAILED) { +- RTC_LOG(LS_ERROR) << "Failed to mmap memory: " << std::strerror(errno); +- return; +- } +- if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) { +- SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START); +- } +- src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t); +- break; +- case SPA_DATA_MemPtr: +- map = nullptr; +- src = static_cast(spaBuffer->datas[0].data); +- break; +- default: +- return; +- } +- +- if (!src) { +- RTC_LOG(LS_ERROR) << "Failed to get video stream: Wrong data after mmap()"; +- SpaBufferUnmap(map, +- spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, +- spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd); +- return; +- } +- +- struct spa_meta_region* video_metadata = +- static_cast( +- spa_buffer_find_meta_data(spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata))); +- +- // Video size from metada is bigger than an actual video stream size. +- // The metadata are wrong or we should up-scale te video...in both cases +- // just quit now. +- if (video_metadata && +- (video_metadata->region.size.width > (uint32_t)desktop_size_.width() || +- video_metadata->region.size.height > (uint32_t)desktop_size_.height())) { +- RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!"; +- SpaBufferUnmap(map, +- spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, +- spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd); +- return; +- } +- +- // Use video metada when video size from metadata is set and smaller than +- // video stream size, so we need to adjust it. +- video_metadata_use_ = (video_metadata && +- video_metadata->region.size.width != 0 && +- video_metadata->region.size.height != 0 && +- (video_metadata->region.size.width < (uint32_t)desktop_size_.width() || +- video_metadata->region.size.height < (uint32_t)desktop_size_.height())); +- +- DesktopSize video_size_prev = video_size_; +- if (video_metadata_use_) { +- video_size_ = DesktopSize(video_metadata->region.size.width, +- video_metadata->region.size.height); +- } else { +- video_size_ = desktop_size_; +- } +- +- webrtc::MutexLock lock(¤t_frame_lock_); +- if (!current_frame_ || !video_size_.equals(video_size_prev)) { +- current_frame_ = +- std::make_unique +- (video_size_.width() * video_size_.height() * BasicDesktopFrame::kBytesPerPixel); +- } +- +- const int32_t dstStride = video_size_.width() * BasicDesktopFrame::kBytesPerPixel; +- const int32_t srcStride = spaBuffer->datas[0].chunk->stride; +- +- // Adjust source content based on metadata video position +- if (video_metadata_use_ && +- (video_metadata->region.position.y + video_size_.height() <= desktop_size_.height())) { +- src += srcStride * video_metadata->region.position.y; +- } +- const int xOffset = +- video_metadata_use_ && +- (video_metadata->region.position.x + video_size_.width() <= desktop_size_.width()) +- ? video_metadata->region.position.x * BasicDesktopFrame::kBytesPerPixel +- : 0; +- +- uint8_t* dst = current_frame_.get(); +- for (int i = 0; i < video_size_.height(); ++i) { +- // Adjust source content based on crop video position if needed +- src += xOffset; +- std::memcpy(dst, src, dstStride); +- // If both sides decided to go with the RGBx format we need to convert it to +- // BGRx to match color format expected by WebRTC. +- if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx || +- spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { +- ConvertRGBxToBGRx(dst, dstStride); +- } +- src += srcStride - xOffset; +- dst += dstStride; +- } +- +- SpaBufferUnmap(map, +- spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, +- spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd); +-} +- +-void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) { +- // Change color format for KDE KWin which uses RGBx and not BGRx +- for (uint32_t i = 0; i < size; i += 4) { +- uint8_t tempR = frame[i]; +- uint8_t tempB = frame[i + 2]; +- frame[i] = tempB; +- frame[i + 2] = tempR; +- } +-} +- +-guint BaseCapturerPipeWire::SetupRequestResponseSignal( +- const gchar* object_path, +- GDBusSignalCallback callback) { +- return g_dbus_connection_signal_subscribe( +- connection_, kDesktopBusName, kRequestInterfaceName, "Response", +- object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, +- callback, this, /*user_data_free_func=*/nullptr); +-} +- +-// static +-void BaseCapturerPipeWire::OnProxyRequested(GObject* /*object*/, +- GAsyncResult* result, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- GError* error = nullptr; +- GDBusProxy *proxy = g_dbus_proxy_new_finish(result, &error); +- if (!proxy) { +- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- RTC_LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " +- << error->message; +- g_error_free(error); +- that->portal_init_failed_ = true; +- return; +- } +- that->proxy_ = proxy; +- that->connection_ = g_dbus_proxy_get_connection(that->proxy_); +- +- RTC_LOG(LS_INFO) << "Created proxy for the screen cast portal."; +- that->SessionRequest(); +-} +- +-// static +-gchar* BaseCapturerPipeWire::PrepareSignalHandle(GDBusConnection* connection, +- const gchar* token) { +- gchar* sender = g_strdup(g_dbus_connection_get_unique_name(connection) + 1); +- for (int i = 0; sender[i]; i++) { +- if (sender[i] == '.') { +- sender[i] = '_'; +- } +- } +- +- gchar* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender, "/", +- token, /*end of varargs*/ nullptr); +- g_free(sender); +- +- return handle; +-} +- +-void BaseCapturerPipeWire::SessionRequest() { +- GVariantBuilder builder; +- gchar* variant_string; +- +- g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); +- variant_string = +- g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); +- g_variant_builder_add(&builder, "{sv}", "session_handle_token", +- g_variant_new_string(variant_string)); +- g_free(variant_string); +- variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); +- g_variant_builder_add(&builder, "{sv}", "handle_token", +- g_variant_new_string(variant_string)); +- +- portal_handle_ = PrepareSignalHandle(connection_, variant_string); +- session_request_signal_id_ = SetupRequestResponseSignal( +- portal_handle_, OnSessionRequestResponseSignal); +- g_free(variant_string); +- +- RTC_LOG(LS_INFO) << "Screen cast session requested."; +- g_dbus_proxy_call( +- proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), +- G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, +- reinterpret_cast(OnSessionRequested), this); +-} +- +-// static +-void BaseCapturerPipeWire::OnSessionRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- GError* error = nullptr; +- GVariant* variant = g_dbus_proxy_call_finish(proxy, result, &error); +- if (!variant) { +- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: " +- << error->message; +- g_error_free(error); +- that->portal_init_failed_ = true; +- return; +- } +- RTC_LOG(LS_INFO) << "Initializing the screen cast session."; +- +- gchar* handle = nullptr; +- g_variant_get_child(variant, 0, "o", &handle); +- g_variant_unref(variant); +- if (!handle) { +- RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; +- if (that->session_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(that->connection_, +- that->session_request_signal_id_); +- that->session_request_signal_id_ = 0; +- } +- that->portal_init_failed_ = true; +- return; +- } +- +- g_free(handle); +- +- RTC_LOG(LS_INFO) << "Subscribing to the screen cast session."; +-} +- +-// static +-void BaseCapturerPipeWire::OnSessionRequestResponseSignal( +- GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- RTC_LOG(LS_INFO) +- << "Received response for the screen cast session subscription."; +- +- guint32 portal_response; +- GVariant* response_data; +- g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); +- +- GVariant* session_handle = +- g_variant_lookup_value(response_data, "session_handle", NULL); +- that->session_handle_ = g_variant_dup_string(session_handle, NULL); +- +- g_variant_unref(session_handle); +- g_variant_unref(response_data); +- +- if (!that->session_handle_ || portal_response) { +- RTC_LOG(LS_ERROR) +- << "Failed to request the screen cast session subscription."; +- that->portal_init_failed_ = true; +- return; +- } +- +- that->SourcesRequest(); +-} +- +-void BaseCapturerPipeWire::SourcesRequest() { +- GVariantBuilder builder; +- gchar* variant_string; +- +- g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); +- // We want to record monitor content. +- g_variant_builder_add(&builder, "{sv}", "types", +- g_variant_new_uint32(capture_source_type_)); +- // We don't want to allow selection of multiple sources. +- g_variant_builder_add(&builder, "{sv}", "multiple", +- g_variant_new_boolean(false)); +- variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); +- g_variant_builder_add(&builder, "{sv}", "handle_token", +- g_variant_new_string(variant_string)); +- +- sources_handle_ = PrepareSignalHandle(connection_, variant_string); +- sources_request_signal_id_ = SetupRequestResponseSignal( +- sources_handle_, OnSourcesRequestResponseSignal); +- g_free(variant_string); +- +- RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session."; +- g_dbus_proxy_call( +- proxy_, "SelectSources", +- g_variant_new("(oa{sv})", session_handle_, &builder), +- G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, +- reinterpret_cast(OnSourcesRequested), this); +-} +- +-// static +-void BaseCapturerPipeWire::OnSourcesRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- GError* error = nullptr; +- GVariant* variant = g_dbus_proxy_call_finish(proxy, result, &error); +- if (!variant) { +- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message; +- g_error_free(error); +- that->portal_init_failed_ = true; +- return; +- } +- +- RTC_LOG(LS_INFO) << "Sources requested from the screen cast session."; +- +- gchar* handle = nullptr; +- g_variant_get_child(variant, 0, "o", &handle); +- g_variant_unref(variant); +- if (!handle) { +- RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; +- if (that->sources_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(that->connection_, +- that->sources_request_signal_id_); +- that->sources_request_signal_id_ = 0; +- } +- that->portal_init_failed_ = true; +- return; +- } +- +- g_free(handle); +- +- RTC_LOG(LS_INFO) << "Subscribed to sources signal."; +-} +- +-// static +-void BaseCapturerPipeWire::OnSourcesRequestResponseSignal( +- GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- RTC_LOG(LS_INFO) << "Received sources signal from session."; +- +- guint32 portal_response; +- g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); +- if (portal_response) { +- RTC_LOG(LS_ERROR) +- << "Failed to select sources for the screen cast session."; +- that->portal_init_failed_ = true; +- return; +- } +- +- that->StartRequest(); +-} +- +-void BaseCapturerPipeWire::StartRequest() { +- GVariantBuilder builder; +- gchar* variant_string; +- +- g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); +- variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); +- g_variant_builder_add(&builder, "{sv}", "handle_token", +- g_variant_new_string(variant_string)); +- +- start_handle_ = PrepareSignalHandle(connection_, variant_string); +- start_request_signal_id_ = +- SetupRequestResponseSignal(start_handle_, OnStartRequestResponseSignal); +- g_free(variant_string); +- +- // "Identifier for the application window", this is Wayland, so not "x11:...". +- const gchar parent_window[] = ""; +- +- RTC_LOG(LS_INFO) << "Starting the screen cast session."; +- g_dbus_proxy_call( +- proxy_, "Start", +- g_variant_new("(osa{sv})", session_handle_, parent_window, &builder), +- G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, +- reinterpret_cast(OnStartRequested), this); +-} +- +-// static +-void BaseCapturerPipeWire::OnStartRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- GError* error = nullptr; +- GVariant* variant = g_dbus_proxy_call_finish(proxy, result, &error); +- if (!variant) { +- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: " +- << error->message; +- g_error_free(error); +- that->portal_init_failed_ = true; +- return; +- } +- +- RTC_LOG(LS_INFO) << "Initializing the start of the screen cast session."; +- +- gchar* handle = nullptr; +- g_variant_get_child(variant, 0, "o", &handle); +- g_variant_unref(variant); +- if (!handle) { +- RTC_LOG(LS_ERROR) +- << "Failed to initialize the start of the screen cast session."; +- if (that->start_request_signal_id_) { +- g_dbus_connection_signal_unsubscribe(that->connection_, +- that->start_request_signal_id_); +- that->start_request_signal_id_ = 0; +- } +- that->portal_init_failed_ = true; +- return; +- } +- +- g_free(handle); +- +- RTC_LOG(LS_INFO) << "Subscribed to the start signal."; +-} +- +-// static +-void BaseCapturerPipeWire::OnStartRequestResponseSignal( +- GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- RTC_LOG(LS_INFO) << "Start signal received."; +- guint32 portal_response; +- GVariant* response_data; +- GVariantIter* iter = nullptr; +- g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); +- if (portal_response || !response_data) { +- RTC_LOG(LS_ERROR) << "Failed to start the screen cast session."; +- that->portal_init_failed_ = true; +- return; +- } +- +- // Array of PipeWire streams. See +- // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml +- // documentation for . +- if (g_variant_lookup(response_data, "streams", "a(ua{sv})", &iter)) { +- GVariant* variant; +- +- while (g_variant_iter_next(iter, "@(ua{sv})", &variant)) { +- guint32 stream_id; +- GVariant* options; +- +- g_variant_get(variant, "(u@a{sv})", &stream_id, &options); +- RTC_DCHECK(options != nullptr); +- +- that->pw_stream_node_id_ = stream_id; +- g_variant_unref(options); +- g_variant_unref(variant); +- } +- } +- g_variant_iter_free(iter); +- g_variant_unref(response_data); +- +- that->OpenPipeWireRemote(); +-} +- +-void BaseCapturerPipeWire::OpenPipeWireRemote() { +- GVariantBuilder builder; +- g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); +- +- RTC_LOG(LS_INFO) << "Opening the PipeWire remote."; +- +- g_dbus_proxy_call_with_unix_fd_list( +- proxy_, "OpenPipeWireRemote", +- g_variant_new("(oa{sv})", session_handle_, &builder), +- G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, +- cancellable_, +- reinterpret_cast(OnOpenPipeWireRemoteRequested), +- this); +-} +- +-// static +-void BaseCapturerPipeWire::OnOpenPipeWireRemoteRequested( +- GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data) { +- BaseCapturerPipeWire* that = static_cast(user_data); +- RTC_DCHECK(that); +- +- GError* error = nullptr; +- GUnixFDList* outlist = nullptr; +- GVariant* variant = g_dbus_proxy_call_with_unix_fd_list_finish( +- proxy, &outlist, result, &error); +- if (!variant) { +- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: " +- << error->message; +- g_error_free(error); +- that->portal_init_failed_ = true; +- return; +- } +- +- gint32 index; +- g_variant_get(variant, "(h)", &index); +- +- if ((that->pw_fd_ = g_unix_fd_list_get(outlist, index, &error)) == -1) { +- RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " +- << error->message; +- g_error_free(error); +- g_variant_unref(variant); +- that->portal_init_failed_ = true; +- return; +- } +- +- g_variant_unref(variant); +- g_object_unref(outlist); +- +- that->InitPipeWire(); + } + + void BaseCapturerPipeWire::Start(Callback* callback) { + RTC_DCHECK(!callback_); + RTC_DCHECK(callback); + +- InitPortal(); +- + callback_ = callback; ++ ++ screencast_portal_->Start(); + } + + void BaseCapturerPipeWire::CaptureFrame() { +- if (portal_init_failed_) { ++ if (capturer_failed_) { + callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); + return; + } + +- webrtc::MutexLock lock(¤t_frame_lock_); +- if (!current_frame_) { +- callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); +- return; +- } +- +- DesktopSize frame_size = desktop_size_; +- if (video_metadata_use_) { +- frame_size = video_size_; +- } ++ std::unique_ptr frame = ++ options_.screencast_stream()->CaptureFrame(); + +- std::unique_ptr result(new BasicDesktopFrame(frame_size)); +- result->CopyPixelsFrom( +- current_frame_.get(), (frame_size.width() * BasicDesktopFrame::kBytesPerPixel), +- DesktopRect::MakeWH(frame_size.width(), frame_size.height())); +- if (!result) { ++ if (!frame || !frame->data()) { + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + +- // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on the +- // frame, see ScreenCapturerX11::CaptureFrame. ++ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on ++ // the frame, see ScreenCapturerX11::CaptureFrame. + +- callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); ++ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); + } + + // Keep in sync with defines at browser/actors/WebRTCParent.jsm +@@ -953,31 +83,13 @@ void BaseCapturerPipeWire::CaptureFrame() { + #define PIPEWIRE_NAME "####_PIPEWIRE_PORTAL_####" + + bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) { ++ RTC_DCHECK(sources->size() == 0); + sources->push_back({PIPEWIRE_ID, 0, PIPEWIRE_NAME}); + return true; + } + + bool BaseCapturerPipeWire::SelectSource(SourceId id) { +- // Screen selection is handled by the xdg-desktop-portal. + return id == PIPEWIRE_ID; + } + +-// static +-std::unique_ptr +-BaseCapturerPipeWire::CreateRawScreenCapturer( +- const DesktopCaptureOptions& options) { +- std::unique_ptr capturer = +- std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); +- return std::move(capturer);} +- +-// static +-std::unique_ptr +-BaseCapturerPipeWire::CreateRawWindowCapturer( +- const DesktopCaptureOptions& options) { +- +- std::unique_ptr capturer = +- std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); +- return std::move(capturer); +-} +- + } // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.h +index af8e20c..5db09e0 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.h ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/base_capturer_pipewire.h +@@ -11,160 +11,39 @@ + #ifndef MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_ + #define MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_ + +-#include +-#define typeof __typeof__ +-#include +-#include +- + #include "modules/desktop_capture/desktop_capture_options.h" + #include "modules/desktop_capture/desktop_capturer.h" +-#include "rtc_base/synchronization/mutex.h" ++#include "modules/desktop_capture/linux/screencast_portal.h" ++#include "modules/desktop_capture/linux/shared_screencast_stream.h" + + namespace webrtc { + +-class BaseCapturerPipeWire : public DesktopCapturer { ++class BaseCapturerPipeWire : public DesktopCapturer, ++ public ScreenCastPortal::PortalNotifier { + public: +- enum CaptureSourceType : uint32_t { +- kScreen = 0b01, +- kWindow = 0b10, +- kAny = 0b11 +- }; +- +- explicit BaseCapturerPipeWire(CaptureSourceType source_type); ++ BaseCapturerPipeWire(const DesktopCaptureOptions& options); + ~BaseCapturerPipeWire() override; + ++ BaseCapturerPipeWire(const BaseCapturerPipeWire&) = delete; ++ BaseCapturerPipeWire& operator=(const BaseCapturerPipeWire&) = delete; ++ + // DesktopCapturer interface. + void Start(Callback* delegate) override; + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + +- static std::unique_ptr CreateRawScreenCapturer( +- const DesktopCaptureOptions& options); +- +- static std::unique_ptr CreateRawWindowCapturer( +- const DesktopCaptureOptions& options); ++ // ScreenCastPortal::PortalNotifier interface. ++ void OnScreenCastRequestResult(ScreenCastPortal::RequestResponse result, ++ uint32_t stream_node_id, ++ int fd) override; ++ void OnScreenCastSessionClosed() override; + + private: +- // PipeWire types --> +- pw_context* pw_context_ = nullptr; +- pw_core* pw_core_ = nullptr; +- pw_stream* pw_stream_ = nullptr; +- pw_thread_loop* pw_main_loop_ = nullptr; +- +- spa_hook spa_core_listener_ = {}; +- spa_hook spa_stream_listener_ = {}; +- +- pw_core_events pw_core_events_ = {}; +- pw_stream_events pw_stream_events_ = {}; +- +- struct spa_video_info_raw spa_video_format_; +- +- guint32 pw_stream_node_id_ = 0; +- gint32 pw_fd_ = -1; +- +- CaptureSourceType capture_source_type_ = +- BaseCapturerPipeWire::CaptureSourceType::kAny; +- +- // <-- end of PipeWire types +- +- GDBusConnection* connection_ = nullptr; +- GDBusProxy* proxy_ = nullptr; +- GCancellable *cancellable_ = nullptr; +- gchar* portal_handle_ = nullptr; +- gchar* session_handle_ = nullptr; +- gchar* sources_handle_ = nullptr; +- gchar* start_handle_ = nullptr; +- guint session_request_signal_id_ = 0; +- guint sources_request_signal_id_ = 0; +- guint start_request_signal_id_ = 0; +- +- bool video_metadata_use_ = false; +- DesktopSize video_size_; +- DesktopSize desktop_size_ = {}; + DesktopCaptureOptions options_ = {}; +- +- webrtc::Mutex current_frame_lock_; +- std::unique_ptr current_frame_; + Callback* callback_ = nullptr; +- +- bool portal_init_failed_ = false; +- +- void InitPortal(); +- void InitPipeWire(); +- +- pw_stream* CreateReceivingStream(); +- void HandleBuffer(pw_buffer* buffer); +- +- void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); +- +- static void OnCoreError(void *data, +- uint32_t id, +- int seq, +- int res, +- const char *message); +- static void OnStreamParamChanged(void *data, +- uint32_t id, +- const struct spa_pod *format); +- static void OnStreamStateChanged(void* data, +- pw_stream_state old_state, +- pw_stream_state state, +- const char* error_message); +- static void OnStreamProcess(void* data); +- static void OnNewBuffer(void* data, uint32_t id); +- +- guint SetupRequestResponseSignal(const gchar* object_path, +- GDBusSignalCallback callback); +- +- static void OnProxyRequested(GObject* object, +- GAsyncResult* result, +- gpointer user_data); +- +- static gchar* PrepareSignalHandle(GDBusConnection* connection, +- const gchar* token); +- +- void SessionRequest(); +- static void OnSessionRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data); +- static void OnSessionRequestResponseSignal(GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data); +- +- void SourcesRequest(); +- static void OnSourcesRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data); +- static void OnSourcesRequestResponseSignal(GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data); +- +- void StartRequest(); +- static void OnStartRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data); +- static void OnStartRequestResponseSignal(GDBusConnection* connection, +- const gchar* sender_name, +- const gchar* object_path, +- const gchar* interface_name, +- const gchar* signal_name, +- GVariant* parameters, +- gpointer user_data); +- +- void OpenPipeWireRemote(); +- static void OnOpenPipeWireRemoteRequested(GDBusProxy *proxy, +- GAsyncResult* result, +- gpointer user_data); +- +- RTC_DISALLOW_COPY_AND_ASSIGN(BaseCapturerPipeWire); ++ bool capturer_failed_ = false; ++ std::unique_ptr screencast_portal_; + }; + + } // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/drm.sigs b/third_party/libwebrtc/modules/desktop_capture/linux/drm.sigs +new file mode 100644 +index 0000000..226979f +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/drm.sigs +@@ -0,0 +1,11 @@ ++// Copyright 2021 The WebRTC project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++//------------------------------------------------ ++// Functions from DRM used in capturer code. ++//-------- ++ ++// xf86drm.h ++int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); ++void drmFreeDevices(drmDevicePtr devices[], int count); +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.cc b/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.cc +new file mode 100644 +index 0000000..de63c2a +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.cc +@@ -0,0 +1,695 @@ ++/* ++ * Copyright 2021 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "modules/desktop_capture/linux/egl_dmabuf.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "absl/memory/memory.h" ++#include "absl/types/optional.h" ++#include "rtc_base/checks.h" ++#include "rtc_base/logging.h" ++#include "rtc_base/sanitizer.h" ++#include "rtc_base/string_encode.h" ++ ++namespace webrtc { ++ ++// EGL ++typedef EGLBoolean (*eglBindAPI_func)(EGLenum api); ++typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy, ++ EGLConfig config, ++ EGLContext share_context, ++ const EGLint* attrib_list); ++typedef EGLBoolean (*eglDestroyContext_func)(EGLDisplay display, ++ EGLContext context); ++typedef EGLBoolean (*eglTerminate_func)(EGLDisplay display); ++typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy, ++ EGLContext ctx, ++ EGLenum target, ++ EGLClientBuffer buffer, ++ const EGLint* attrib_list); ++typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy, ++ EGLImageKHR image); ++typedef EGLint (*eglGetError_func)(void); ++typedef void* (*eglGetProcAddress_func)(const char*); ++typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform, ++ void* native_display, ++ const EGLint* attrib_list); ++typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform, ++ void* native_display, ++ const EGLAttrib* attrib_list); ++ ++typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy, ++ EGLint* major, ++ EGLint* minor); ++typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy, ++ EGLSurface draw, ++ EGLSurface read, ++ EGLContext ctx); ++typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, ++ EGLint max_formats, ++ EGLint* formats, ++ EGLint* num_formats); ++typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, ++ EGLint format, ++ EGLint max_modifiers, ++ EGLuint64KHR* modifiers, ++ EGLBoolean* external_only, ++ EGLint* num_modifiers); ++typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name); ++typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target, ++ GLeglImageOES image); ++ ++// This doesn't follow naming conventions in WebRTC, where the naming ++// should look like e.g. egl_bind_api instead of EglBindAPI, however ++// we named them according to the exported functions they map to for ++// consistency. ++eglBindAPI_func EglBindAPI = nullptr; ++eglCreateContext_func EglCreateContext = nullptr; ++eglDestroyContext_func EglDestroyContext = nullptr; ++eglTerminate_func EglTerminate = nullptr; ++eglCreateImageKHR_func EglCreateImageKHR = nullptr; ++eglDestroyImageKHR_func EglDestroyImageKHR = nullptr; ++eglGetError_func EglGetError = nullptr; ++eglGetProcAddress_func EglGetProcAddress = nullptr; ++eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr; ++eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr; ++eglInitialize_func EglInitialize = nullptr; ++eglMakeCurrent_func EglMakeCurrent = nullptr; ++eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr; ++eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr; ++eglQueryString_func EglQueryString = nullptr; ++glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr; ++ ++// GL ++typedef void (*glBindTexture_func)(GLenum target, GLuint texture); ++typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures); ++typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures); ++typedef GLenum (*glGetError_func)(void); ++typedef const GLubyte* (*glGetString_func)(GLenum name); ++typedef void (*glGetTexImage_func)(GLenum target, ++ GLint level, ++ GLenum format, ++ GLenum type, ++ void* pixels); ++typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param); ++typedef void* (*glXGetProcAddressARB_func)(const char*); ++ ++// This doesn't follow naming conventions in WebRTC, where the naming ++// should look like e.g. egl_bind_api instead of EglBindAPI, however ++// we named them according to the exported functions they map to for ++// consistency. ++glBindTexture_func GlBindTexture = nullptr; ++glDeleteTextures_func GlDeleteTextures = nullptr; ++glGenTextures_func GlGenTextures = nullptr; ++glGetError_func GlGetError = nullptr; ++glGetString_func GlGetString = nullptr; ++glGetTexImage_func GlGetTexImage = nullptr; ++glTexParameteri_func GlTexParameteri = nullptr; ++glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr; ++ ++static const std::string FormatGLError(GLenum err) { ++ switch (err) { ++ case GL_NO_ERROR: ++ return "GL_NO_ERROR"; ++ case GL_INVALID_ENUM: ++ return "GL_INVALID_ENUM"; ++ case GL_INVALID_VALUE: ++ return "GL_INVALID_VALUE"; ++ case GL_INVALID_OPERATION: ++ return "GL_INVALID_OPERATION"; ++ case GL_STACK_OVERFLOW: ++ return "GL_STACK_OVERFLOW"; ++ case GL_STACK_UNDERFLOW: ++ return "GL_STACK_UNDERFLOW"; ++ case GL_OUT_OF_MEMORY: ++ return "GL_OUT_OF_MEMORY"; ++ default: ++ return "GL error code: " + std::to_string(err); ++ } ++} ++ ++static const std::string FormatEGLError(EGLint err) { ++ switch (err) { ++ case EGL_NOT_INITIALIZED: ++ return "EGL_NOT_INITIALIZED"; ++ case EGL_BAD_ACCESS: ++ return "EGL_BAD_ACCESS"; ++ case EGL_BAD_ALLOC: ++ return "EGL_BAD_ALLOC"; ++ case EGL_BAD_ATTRIBUTE: ++ return "EGL_BAD_ATTRIBUTE"; ++ case EGL_BAD_CONTEXT: ++ return "EGL_BAD_CONTEXT"; ++ case EGL_BAD_CONFIG: ++ return "EGL_BAD_CONFIG"; ++ case EGL_BAD_CURRENT_SURFACE: ++ return "EGL_BAD_CURRENT_SURFACE"; ++ case EGL_BAD_DISPLAY: ++ return "EGL_BAD_DISPLAY"; ++ case EGL_BAD_SURFACE: ++ return "EGL_BAD_SURFACE"; ++ case EGL_BAD_MATCH: ++ return "EGL_BAD_MATCH"; ++ case EGL_BAD_PARAMETER: ++ return "EGL_BAD_PARAMETER"; ++ case EGL_BAD_NATIVE_PIXMAP: ++ return "EGL_BAD_NATIVE_PIXMAP"; ++ case EGL_BAD_NATIVE_WINDOW: ++ return "EGL_BAD_NATIVE_WINDOW"; ++ case EGL_CONTEXT_LOST: ++ return "EGL_CONTEXT_LOST"; ++ default: ++ return "EGL error code: " + std::to_string(err); ++ } ++} ++ ++static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) { ++ switch (spa_format) { ++ case SPA_VIDEO_FORMAT_RGBA: ++ return DRM_FORMAT_ABGR8888; ++ case SPA_VIDEO_FORMAT_RGBx: ++ return DRM_FORMAT_XBGR8888; ++ case SPA_VIDEO_FORMAT_BGRA: ++ return DRM_FORMAT_ARGB8888; ++ case SPA_VIDEO_FORMAT_BGRx: ++ return DRM_FORMAT_XRGB8888; ++ default: ++ return DRM_FORMAT_INVALID; ++ } ++} ++ ++static void CloseLibrary(void* library) { ++ if (library) { ++ dlclose(library); ++ library = nullptr; ++ } ++} ++ ++static void* g_lib_egl = nullptr; ++ ++static bool OpenEGL() { ++ g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL); ++ if (g_lib_egl) { ++ EglGetProcAddress = ++ (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress"); ++ return EglGetProcAddress; ++ } ++ ++ return false; ++} ++ ++static bool LoadEGL() { ++ if (OpenEGL()) { ++ EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI"); ++ EglCreateContext = ++ (eglCreateContext_func)EglGetProcAddress("eglCreateContext"); ++ EglDestroyContext = ++ (eglDestroyContext_func)EglGetProcAddress("eglDestroyContext"); ++ EglTerminate = (eglTerminate_func)EglGetProcAddress("eglTerminate"); ++ EglCreateImageKHR = ++ (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR"); ++ EglDestroyImageKHR = ++ (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR"); ++ EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError"); ++ EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress( ++ "eglGetPlatformDisplayEXT"); ++ EglGetPlatformDisplay = ++ (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay"); ++ EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize"); ++ EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent"); ++ EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString"); ++ GlEGLImageTargetTexture2DOES = ++ (glEGLImageTargetTexture2DOES_func)EglGetProcAddress( ++ "glEGLImageTargetTexture2DOES"); ++ ++ return EglBindAPI && EglCreateContext && EglCreateImageKHR && ++ EglTerminate && EglDestroyContext && EglDestroyImageKHR && ++ EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay && ++ EglInitialize && EglMakeCurrent && EglQueryString && ++ GlEGLImageTargetTexture2DOES; ++ } ++ ++ return false; ++} ++ ++static void* g_lib_gl = nullptr; ++ ++static bool OpenGL() { ++ std::vector names = {"libGL.so.1", "libGL.so"}; ++ for (const std::string& name : names) { ++ g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); ++ if (g_lib_gl) { ++ GlXGetProcAddressARB = ++ (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB"); ++ return GlXGetProcAddressARB; ++ } ++ } ++ ++ return false; ++} ++ ++static bool LoadGL() { ++ if (OpenGL()) { ++ GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString"); ++ if (!GlGetString) { ++ return false; ++ } ++ ++ GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture"); ++ GlDeleteTextures = ++ (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures"); ++ GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures"); ++ GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError"); ++ GlGetTexImage = (glGetTexImage_func)GlXGetProcAddressARB("glGetTexImage"); ++ GlTexParameteri = ++ (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri"); ++ ++ return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError && ++ GlGetTexImage && GlTexParameteri; ++ } ++ ++ return false; ++} ++ ++EglDmaBuf::EglDmaBuf() { ++ if (!LoadEGL()) { ++ RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions."; ++ CloseLibrary(g_lib_egl); ++ return; ++ } ++ ++ if (!LoadGL()) { ++ RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions."; ++ CloseLibrary(g_lib_gl); ++ return; ++ } ++ ++ if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) { ++ return; ++ } ++ ++ bool has_platform_base_ext = false; ++ bool has_platform_gbm_ext = false; ++ bool has_khr_platform_gbm_ext = false; ++ ++ for (const auto& extension : egl_.extensions) { ++ if (extension == "EGL_EXT_platform_base") { ++ has_platform_base_ext = true; ++ continue; ++ } else if (extension == "EGL_MESA_platform_gbm") { ++ has_platform_gbm_ext = true; ++ continue; ++ } else if (extension == "EGL_KHR_platform_gbm") { ++ has_khr_platform_gbm_ext = true; ++ continue; ++ } ++ } ++ ++ if (!has_platform_base_ext || !has_platform_gbm_ext || ++ !has_khr_platform_gbm_ext) { ++ RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing"; ++ return; ++ } ++ ++ egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, ++ (void*)EGL_DEFAULT_DISPLAY, nullptr); ++ ++ if (egl_.display == EGL_NO_DISPLAY) { ++ RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: " ++ << FormatEGLError(EglGetError()) << "\n" ++ << "Defaulting to using first available render node"; ++ absl::optional render_node = GetRenderNode(); ++ if (!render_node) { ++ return; ++ } ++ ++ drm_fd_ = open(render_node->c_str(), O_RDWR); ++ ++ if (drm_fd_ < 0) { ++ RTC_LOG(LS_ERROR) << "Failed to open drm render node: " ++ << strerror(errno); ++ return; ++ } ++ ++ gbm_device_ = gbm_create_device(drm_fd_); ++ ++ if (!gbm_device_) { ++ RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno); ++ close(drm_fd_); ++ return; ++ } ++ ++ // Use eglGetPlatformDisplayEXT() to get the display pointer ++ // if the implementation supports it. ++ egl_.display = ++ EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr); ++ } ++ ++ if (egl_.display == EGL_NO_DISPLAY) { ++ RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: " ++ << FormatEGLError(EglGetError()); ++ return; ++ } ++ ++ EGLint major, minor; ++ if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) { ++ RTC_LOG(LS_ERROR) << "Error during eglInitialize: " ++ << FormatEGLError(EglGetError()); ++ return; ++ } ++ ++ if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { ++ RTC_LOG(LS_ERROR) << "bind OpenGL API failed"; ++ return; ++ } ++ ++ egl_.context = ++ EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr); ++ ++ if (egl_.context == EGL_NO_CONTEXT) { ++ RTC_LOG(LS_ERROR) << "Couldn't create EGL context: " ++ << FormatGLError(EglGetError()); ++ return; ++ } ++ ++ if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) { ++ return; ++ } ++ ++ bool has_image_dma_buf_import_modifiers_ext = false; ++ ++ for (const auto& extension : egl_.extensions) { ++ if (extension == "EGL_EXT_image_dma_buf_import") { ++ has_image_dma_buf_import_ext_ = true; ++ continue; ++ } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") { ++ has_image_dma_buf_import_modifiers_ext = true; ++ continue; ++ } ++ } ++ ++ if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) { ++ EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress( ++ "eglQueryDmaBufFormatsEXT"); ++ EglQueryDmaBufModifiersEXT = ++ (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress( ++ "eglQueryDmaBufModifiersEXT"); ++ } ++ ++ RTC_LOG(LS_INFO) << "Egl initialization succeeded"; ++ egl_initialized_ = true; ++} ++ ++EglDmaBuf::~EglDmaBuf() { ++ if (gbm_device_) { ++ gbm_device_destroy(gbm_device_); ++ close(drm_fd_); ++ } ++ ++ if (egl_.context != EGL_NO_CONTEXT) { ++ EglDestroyContext(egl_.display, egl_.context); ++ } ++ ++ if (egl_.display != EGL_NO_DISPLAY) { ++ EglTerminate(egl_.display); ++ } ++ ++ // BUG: crbug.com/1290566 ++ // Closing libEGL.so.1 when using NVidia drivers causes a crash ++ // when EglGetPlatformDisplayEXT() is used, at least this one is enough ++ // to be called to make it crash. ++ // It also looks that libepoxy and glad don't dlclose it either ++ // CloseLibrary(g_lib_egl); ++ // CloseLibrary(g_lib_gl); ++} ++ ++bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) { ++ // Get the list of client extensions ++ const char* client_extensions_cstring = EglQueryString(dpy, name); ++ if (!client_extensions_cstring) { ++ // If eglQueryString() returned NULL, the implementation doesn't support ++ // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. ++ RTC_LOG(LS_ERROR) << "No client extensions defined! " ++ << FormatEGLError(EglGetError()); ++ return false; ++ } ++ ++ std::vector client_extensions; ++ rtc::split(client_extensions_cstring, ' ', ++ &client_extensions); ++ for (const auto& extension : client_extensions) { ++ egl_.extensions.push_back(extension); ++ } ++ ++ return true; ++} ++ ++std::unique_ptr EglDmaBuf::ImageFromDmaBuf( ++ const DesktopSize& size, ++ uint32_t format, ++ const std::vector& plane_datas, ++ uint64_t modifier) { ++ std::unique_ptr src; ++ ++ if (!egl_initialized_) { ++ return src; ++ } ++ ++ if (plane_datas.size() <= 0) { ++ RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes"; ++ return src; ++ } ++ ++ EGLint attribs[47]; ++ int atti = 0; ++ ++ attribs[atti++] = EGL_WIDTH; ++ attribs[atti++] = static_cast(size.width()); ++ attribs[atti++] = EGL_HEIGHT; ++ attribs[atti++] = static_cast(size.height()); ++ attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; ++ attribs[atti++] = SpaPixelFormatToDrmFormat(format); ++ ++ if (plane_datas.size() > 0) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; ++ attribs[atti++] = plane_datas[0].fd; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; ++ attribs[atti++] = plane_datas[0].offset; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; ++ attribs[atti++] = plane_datas[0].stride; ++ ++ if (modifier != DRM_FORMAT_MOD_INVALID) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; ++ attribs[atti++] = modifier & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; ++ attribs[atti++] = modifier >> 32; ++ } ++ } ++ ++ if (plane_datas.size() > 1) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; ++ attribs[atti++] = plane_datas[1].fd; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; ++ attribs[atti++] = plane_datas[1].offset; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; ++ attribs[atti++] = plane_datas[1].stride; ++ ++ if (modifier != DRM_FORMAT_MOD_INVALID) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; ++ attribs[atti++] = modifier & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; ++ attribs[atti++] = modifier >> 32; ++ } ++ } ++ ++ if (plane_datas.size() > 2) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; ++ attribs[atti++] = plane_datas[2].fd; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; ++ attribs[atti++] = plane_datas[2].offset; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; ++ attribs[atti++] = plane_datas[2].stride; ++ ++ if (modifier != DRM_FORMAT_MOD_INVALID) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; ++ attribs[atti++] = modifier & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; ++ attribs[atti++] = modifier >> 32; ++ } ++ } ++ ++ if (plane_datas.size() > 3) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT; ++ attribs[atti++] = plane_datas[3].fd; ++ attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; ++ attribs[atti++] = plane_datas[3].offset; ++ attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; ++ attribs[atti++] = plane_datas[3].stride; ++ ++ if (modifier != DRM_FORMAT_MOD_INVALID) { ++ attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; ++ attribs[atti++] = modifier & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; ++ attribs[atti++] = modifier >> 32; ++ } ++ } ++ ++ attribs[atti++] = EGL_NONE; ++ ++ // bind context to render thread ++ EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context); ++ ++ // create EGL image from attribute list ++ EGLImageKHR image = EglCreateImageKHR( ++ egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs); ++ ++ if (image == EGL_NO_IMAGE) { ++ RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - " ++ << FormatEGLError(EglGetError()); ++ return src; ++ } ++ ++ // create GL 2D texture for framebuffer ++ GLuint texture; ++ GlGenTextures(1, &texture); ++ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ++ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ++ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ++ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ++ GlBindTexture(GL_TEXTURE_2D, texture); ++ GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); ++ ++ src = std::make_unique(plane_datas[0].stride * size.height()); ++ ++ GLenum gl_format = GL_BGRA; ++ switch (format) { ++ case SPA_VIDEO_FORMAT_RGBx: ++ gl_format = GL_RGBA; ++ break; ++ case SPA_VIDEO_FORMAT_RGBA: ++ gl_format = GL_RGBA; ++ break; ++ case SPA_VIDEO_FORMAT_BGRx: ++ gl_format = GL_BGRA; ++ break; ++ default: ++ gl_format = GL_BGRA; ++ break; ++ } ++ GlGetTexImage(GL_TEXTURE_2D, 0, gl_format, GL_UNSIGNED_BYTE, src.get()); ++ ++ if (GlGetError()) { ++ RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer."; ++ return src; ++ } ++ ++ GlDeleteTextures(1, &texture); ++ EglDestroyImageKHR(egl_.display, image); ++ ++ return src; ++} ++ ++std::vector EglDmaBuf::QueryDmaBufModifiers(uint32_t format) { ++ if (!egl_initialized_) { ++ return {}; ++ } ++ ++ // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we ++ // can still use modifier-less DMA-BUFs if we have required extension ++ if (EglQueryDmaBufFormatsEXT == nullptr || ++ EglQueryDmaBufModifiersEXT == nullptr) { ++ return has_image_dma_buf_import_ext_ ++ ? std::vector{DRM_FORMAT_MOD_INVALID} ++ : std::vector{}; ++ } ++ ++ uint32_t drm_format = SpaPixelFormatToDrmFormat(format); ++ // Should never happen as it's us who controls the list of supported formats ++ RTC_DCHECK(drm_format != DRM_FORMAT_INVALID); ++ ++ EGLint count = 0; ++ EGLBoolean success = ++ EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count); ++ ++ if (!success || !count) { ++ RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats."; ++ return {DRM_FORMAT_MOD_INVALID}; ++ } ++ ++ std::vector formats(count); ++ if (!EglQueryDmaBufFormatsEXT(egl_.display, count, ++ reinterpret_cast(formats.data()), ++ &count)) { ++ RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats."; ++ return {DRM_FORMAT_MOD_INVALID}; ++ } ++ ++ if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) { ++ RTC_LOG(LS_ERROR) << "Format " << drm_format ++ << " not supported for modifiers."; ++ return {DRM_FORMAT_MOD_INVALID}; ++ } ++ ++ success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr, ++ nullptr, &count); ++ ++ if (!success || !count) { ++ RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers."; ++ return {DRM_FORMAT_MOD_INVALID}; ++ } ++ ++ std::vector modifiers(count); ++ if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count, ++ modifiers.data(), nullptr, &count)) { ++ RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers."; ++ } ++ ++ // Support modifier-less buffers ++ modifiers.push_back(DRM_FORMAT_MOD_INVALID); ++ return modifiers; ++} ++ ++absl::optional EglDmaBuf::GetRenderNode() { ++ int max_devices = drmGetDevices2(0, nullptr, 0); ++ if (max_devices <= 0) { ++ RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno=" ++ << -max_devices << ")"; ++ return absl::nullopt; ++ } ++ ++ std::vector devices(max_devices); ++ int ret = drmGetDevices2(0, devices.data(), max_devices); ++ if (ret < 0) { ++ RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret; ++ return absl::nullopt; ++ } ++ ++ std::string render_node; ++ ++ for (const drmDevicePtr& device : devices) { ++ if (device->available_nodes & (1 << DRM_NODE_RENDER)) { ++ render_node = device->nodes[DRM_NODE_RENDER]; ++ break; ++ } ++ } ++ ++ drmFreeDevices(devices.data(), ret); ++ return render_node; ++} ++ ++} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.h b/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.h +new file mode 100644 +index 0000000..b755d8b +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/egl_dmabuf.h +@@ -0,0 +1,68 @@ ++/* ++ * Copyright 2021 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_ ++#define MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "absl/types/optional.h" ++#include "modules/desktop_capture/desktop_geometry.h" ++ ++namespace webrtc { ++ ++class EglDmaBuf { ++ public: ++ struct EGLStruct { ++ std::vector extensions; ++ EGLDisplay display = EGL_NO_DISPLAY; ++ EGLContext context = EGL_NO_CONTEXT; ++ }; ++ ++ struct PlaneData { ++ int32_t fd; ++ uint32_t stride; ++ uint32_t offset; ++ }; ++ ++ EglDmaBuf(); ++ ~EglDmaBuf(); ++ ++ std::unique_ptr ImageFromDmaBuf( ++ const DesktopSize& size, ++ uint32_t format, ++ const std::vector& plane_datas, ++ uint64_t modifiers); ++ std::vector QueryDmaBufModifiers(uint32_t format); ++ ++ bool IsEglInitialized() const { return egl_initialized_; } ++ ++ private: ++ bool GetClientExtensions(EGLDisplay dpy, EGLint name); ++ ++ bool egl_initialized_ = false; ++ bool has_image_dma_buf_import_ext_ = false; ++ int32_t drm_fd_ = -1; // for GBM buffer mmap ++ gbm_device* gbm_device_ = nullptr; // for passed GBM buffer retrieval ++ ++ EGLStruct egl_; ++ ++ absl::optional GetRenderNode(); ++}; ++ ++} // namespace webrtc ++ ++#endif // MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.cc +new file mode 100644 +index 0000000..09dea24 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.cc +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.h" ++ ++#include ++ ++#include "modules/desktop_capture/desktop_capture_options.h" ++#include "modules/desktop_capture/desktop_capturer.h" ++#include "rtc_base/checks.h" ++#include "rtc_base/logging.h" ++ ++namespace webrtc { ++ ++MouseCursorMonitorPipeWire::MouseCursorMonitorPipeWire( ++ const DesktopCaptureOptions& options) ++ : options_(options) { ++} ++ ++MouseCursorMonitorPipeWire::~MouseCursorMonitorPipeWire() {} ++ ++void MouseCursorMonitorPipeWire::Init(Callback* callback, Mode mode) { ++ RTC_DCHECK(!callback_); ++ RTC_DCHECK(callback); ++ ++ callback_ = callback; ++ mode_ = mode; ++} ++ ++void MouseCursorMonitorPipeWire::Capture() { ++ RTC_DCHECK(callback_); ++ ++ std::unique_ptr mouse_cursor = ++ options_.screencast_stream()->CaptureCursor(); ++ ++ if (mouse_cursor && mouse_cursor->image()->data()) { ++ callback_->OnMouseCursor(mouse_cursor.release()); ++ } ++ ++ if (mode_ == SHAPE_AND_POSITION) { ++ absl::optional mouse_cursor_position = ++ options_.screencast_stream()->CaptureCursorPosition(); ++ if (mouse_cursor_position) { ++ callback_->OnMouseCursorPosition(mouse_cursor_position.value()); ++ } ++ } ++} ++ ++} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.h +new file mode 100644 +index 0000000..9b9ccf7 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ ++#define MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ ++ ++#include ++ ++#include "api/scoped_refptr.h" ++#include "modules/desktop_capture/desktop_capture_options.h" ++#include "modules/desktop_capture/desktop_capture_types.h" ++#include "modules/desktop_capture/linux/shared_screencast_stream.h" ++#include "modules/desktop_capture/mouse_cursor.h" ++#include "modules/desktop_capture/mouse_cursor_monitor.h" ++ ++namespace webrtc { ++ ++class MouseCursorMonitorPipeWire : public MouseCursorMonitor { ++ public: ++ explicit MouseCursorMonitorPipeWire(const DesktopCaptureOptions& options); ++ ~MouseCursorMonitorPipeWire() override; ++ ++ // MouseCursorMonitor: ++ void Init(Callback* callback, Mode mode) override; ++ void Capture() override; ++ ++ DesktopCaptureOptions options_; ++ Callback* callback_ = nullptr; ++ Mode mode_ = SHAPE_AND_POSITION; ++}; ++ ++} // namespace webrtc ++ ++#endif // MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/pipewire.sigs b/third_party/libwebrtc/modules/desktop_capture/linux/pipewire.sigs +index 3e21e9d..06a97b8 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/pipewire.sigs ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/pipewire.sigs +@@ -7,38 +7,44 @@ + //------------------------------------------------ + + // core.h +-void pw_core_destroy(pw_core *core); +-pw_type *pw_core_get_type(pw_core *core); +-pw_core * pw_core_new(pw_loop *main_loop, pw_properties *props); ++int pw_core_disconnect(pw_core *core); + + // loop.h + void pw_loop_destroy(pw_loop *loop); +-pw_loop * pw_loop_new(pw_properties *properties); ++pw_loop * pw_loop_new(const spa_dict *props); ++ + + // pipewire.h + void pw_init(int *argc, char **argv[]); ++const char* pw_get_library_version(); + + // properties.h + pw_properties * pw_properties_new_string(const char *args); + +-// remote.h +-void pw_remote_add_listener(pw_remote *remote, spa_hook *listener, const pw_remote_events *events, void *data); +-int pw_remote_connect_fd(pw_remote *remote, int fd); +-void pw_remote_destroy(pw_remote *remote); +-pw_remote * pw_remote_new(pw_core *core, pw_properties *properties, size_t user_data_size); +- + // stream.h + void pw_stream_add_listener(pw_stream *stream, spa_hook *listener, const pw_stream_events *events, void *data); +-int pw_stream_connect(pw_stream *stream, enum pw_direction direction, const char *port_path, enum pw_stream_flags flags, const spa_pod **params, uint32_t n_params); ++int pw_stream_connect(pw_stream *stream, enum pw_direction direction, uint32_t target_id, enum pw_stream_flags flags, const spa_pod **params, uint32_t n_params); ++int pw_stream_disconnect(pw_stream *stream); + pw_buffer *pw_stream_dequeue_buffer(pw_stream *stream); + void pw_stream_destroy(pw_stream *stream); +-void pw_stream_finish_format(pw_stream *stream, int res, const spa_pod **params, uint32_t n_params); +-pw_stream * pw_stream_new(pw_remote *remote, const char *name, pw_properties *props); ++pw_stream * pw_stream_new(pw_core *core, const char *name, pw_properties *props); + int pw_stream_queue_buffer(pw_stream *stream, pw_buffer *buffer); + int pw_stream_set_active(pw_stream *stream, bool active); ++int pw_stream_update_params(pw_stream *stream, const spa_pod **params, uint32_t n_params); + + // thread-loop.h + void pw_thread_loop_destroy(pw_thread_loop *loop); +-pw_thread_loop * pw_thread_loop_new(pw_loop *loop, const char *name); ++pw_thread_loop * pw_thread_loop_new(const char *name, const spa_dict *props); + int pw_thread_loop_start(pw_thread_loop *loop); + void pw_thread_loop_stop(pw_thread_loop *loop); ++void pw_thread_loop_lock(pw_thread_loop *loop); ++void pw_thread_loop_unlock(pw_thread_loop *loop); ++pw_loop * pw_thread_loop_get_loop(pw_thread_loop *loop); ++void pw_thread_loop_signal(pw_thread_loop *loop, bool wait_for_accept); ++void pw_thread_loop_wait(pw_thread_loop *loop); ++ ++// context.h ++void pw_context_destroy(pw_context *context); ++pw_context *pw_context_new(pw_loop *main_loop, pw_properties *props, size_t user_data_size); ++pw_core * pw_context_connect(pw_context *context, pw_properties *properties, size_t user_data_size); ++pw_core * pw_context_connect_fd(pw_context *context, int fd, pw_properties *properties, size_t user_data_size); +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/pipewire_stub_header.fragment b/third_party/libwebrtc/modules/desktop_capture/linux/pipewire_stub_header.fragment +index 9d7dbd2..06ae18d 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/pipewire_stub_header.fragment ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/pipewire_stub_header.fragment +@@ -5,4 +5,5 @@ extern "C" { + + #include + ++#include + } +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.cc b/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.cc +new file mode 100644 +index 0000000..51ca57a +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.cc +@@ -0,0 +1,57 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "modules/desktop_capture/linux/scoped_glib.h" ++ ++namespace webrtc { ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_error_free(ptr_); ++ } ++} ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_free(ptr_); ++ } ++} ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_variant_unref(ptr_); ++ } ++} ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_variant_iter_free(ptr_); ++ } ++} ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_object_unref(ptr_); ++ } ++} ++ ++template <> ++Scoped::~Scoped() { ++ if (ptr_) { ++ g_object_unref(ptr_); ++ } ++} ++ ++} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.h b/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.h +new file mode 100644 +index 0000000..bf77855 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/scoped_glib.h +@@ -0,0 +1,65 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SCOPED_GLIB_H_ ++#define MODULES_DESKTOP_CAPTURE_LINUX_SCOPED_GLIB_H_ ++ ++#include ++ ++#include "rtc_base/checks.h" ++ ++namespace webrtc { ++ ++template ++class Scoped { ++ public: ++ Scoped() {} ++ explicit Scoped(T* val) { ptr_ = val; } ++ ~Scoped() { RTC_DCHECK_NOTREACHED(); } ++ ++ T* operator->() const { return ptr_; } ++ ++ explicit operator bool() const { return ptr_ != nullptr; } ++ ++ bool operator!() const { return ptr_ == nullptr; } ++ ++ T* get() const { return ptr_; } ++ ++ T** receive() { ++ RTC_CHECK(!ptr_); ++ return &ptr_; ++ } ++ ++ Scoped& operator=(T* val) { ++ RTC_DCHECK(val); ++ ptr_ = val; ++ return *this; ++ } ++ ++ protected: ++ T* ptr_ = nullptr; ++}; ++ ++template <> ++Scoped::~Scoped(); ++template <> ++Scoped::~Scoped(); ++template <> ++Scoped::~Scoped(); ++template <> ++Scoped::~Scoped(); ++template <> ++Scoped::~Scoped(); ++template <> ++Scoped::~Scoped(); ++ ++} // namespace webrtc ++ ++#endif // MODULES_DESKTOP_CAPTURE_LINUX_SCOPED_GLIB_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc +deleted file mode 100644 +index 3813d69..0000000 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc ++++ /dev/null +@@ -1,28 +0,0 @@ +-/* +- * Copyright 2018 The WebRTC project authors. All Rights Reserved. +- * +- * Use of this source code is governed by a BSD-style license +- * that can be found in the LICENSE file in the root of the source +- * tree. An additional intellectual property rights grant can be found +- * in the file PATENTS. All contributing project authors may +- * be found in the AUTHORS file in the root of the source tree. +- */ +- +-#include "modules/desktop_capture/linux/screen_capturer_pipewire.h" +- +-#include +- +-namespace webrtc { +- +-ScreenCapturerPipeWire::ScreenCapturerPipeWire() +- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kScreen) {} +-ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {} +- +-// static +-std::unique_ptr +-ScreenCapturerPipeWire::CreateRawScreenCapturer( +- const DesktopCaptureOptions& options) { +- return std::make_unique(); +-} +- +-} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.h +deleted file mode 100644 +index 66dcd68..0000000 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/screen_capturer_pipewire.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* +- * Copyright 2018 The WebRTC project authors. All Rights Reserved. +- * +- * Use of this source code is governed by a BSD-style license +- * that can be found in the LICENSE file in the root of the source +- * tree. An additional intellectual property rights grant can be found +- * in the file PATENTS. All contributing project authors may +- * be found in the AUTHORS file in the root of the source tree. +- */ +- +-#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_ +-#define MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_ +- +-#include +- +-#include "modules/desktop_capture/linux/base_capturer_pipewire.h" +- +-namespace webrtc { +- +-class ScreenCapturerPipeWire : public BaseCapturerPipeWire { +- public: +- ScreenCapturerPipeWire(); +- ~ScreenCapturerPipeWire() override; +- +- static std::unique_ptr CreateRawScreenCapturer( +- const DesktopCaptureOptions& options); +- +- RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerPipeWire); +-}; +- +-} // namespace webrtc +- +-#endif // MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.cc b/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.cc +new file mode 100644 +index 0000000..306e984 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.cc +@@ -0,0 +1,532 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "modules/desktop_capture/linux/screencast_portal.h" ++ ++#include ++#include ++ ++#include "modules/desktop_capture/linux/scoped_glib.h" ++#include "rtc_base/checks.h" ++#include "rtc_base/logging.h" ++ ++namespace webrtc { ++ ++const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; ++const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; ++const char kDesktopRequestObjectPath[] = ++ "/org/freedesktop/portal/desktop/request"; ++const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; ++const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; ++const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; ++ ++ScreenCastPortal::ScreenCastPortal(CaptureSourceType source_type, ++ PortalNotifier* notifier) ++ : notifier_(notifier), capture_source_type_(source_type) {} ++ ++ScreenCastPortal::~ScreenCastPortal() { ++ if (start_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); ++ } ++ if (sources_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, ++ sources_request_signal_id_); ++ } ++ if (session_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, ++ session_request_signal_id_); ++ } ++ ++ if (!session_handle_.empty()) { ++ Scoped message( ++ g_dbus_message_new_method_call(kDesktopBusName, session_handle_.c_str(), ++ kSessionInterfaceName, "Close")); ++ if (message.get()) { ++ Scoped error; ++ g_dbus_connection_send_message(connection_, message.get(), ++ G_DBUS_SEND_MESSAGE_FLAGS_NONE, ++ /*out_serial=*/nullptr, error.receive()); ++ if (error.get()) { ++ RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; ++ } ++ } ++ } ++ ++ if (cancellable_) { ++ g_cancellable_cancel(cancellable_); ++ g_object_unref(cancellable_); ++ cancellable_ = nullptr; ++ } ++ ++ if (proxy_) { ++ g_object_unref(proxy_); ++ proxy_ = nullptr; ++ } ++ ++ if (pw_fd_ != -1) { ++ close(pw_fd_); ++ } ++} ++ ++void ScreenCastPortal::Start() { ++ cancellable_ = g_cancellable_new(); ++ g_dbus_proxy_new_for_bus( ++ G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, ++ kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, ++ cancellable_, reinterpret_cast(OnProxyRequested), ++ this); ++} ++ ++void ScreenCastPortal::PortalFailed(RequestResponse result) { ++ notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_); ++} ++ ++uint32_t ScreenCastPortal::SetupRequestResponseSignal( ++ const char* object_path, ++ GDBusSignalCallback callback) { ++ return g_dbus_connection_signal_subscribe( ++ connection_, kDesktopBusName, kRequestInterfaceName, "Response", ++ object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, ++ callback, this, /*user_data_free_func=*/nullptr); ++} ++ ++// static ++void ScreenCastPortal::OnProxyRequested(GObject* /*object*/, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ Scoped error; ++ GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); ++ if (!proxy) { ++ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ RTC_LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " ++ << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ that->proxy_ = proxy; ++ that->connection_ = g_dbus_proxy_get_connection(that->proxy_); ++ ++ RTC_LOG(LS_INFO) << "Created proxy for the screen cast portal."; ++ ++ that->SessionRequest(); ++} ++ ++// static ++std::string ScreenCastPortal::PrepareSignalHandle(GDBusConnection* connection, ++ const char* token) { ++ Scoped sender( ++ g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); ++ for (int i = 0; sender.get()[i]; ++i) { ++ if (sender.get()[i] == '.') { ++ sender.get()[i] = '_'; ++ } ++ } ++ ++ const char* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), ++ "/", token, /*end of varargs*/ nullptr); ++ ++ return handle; ++} ++ ++void ScreenCastPortal::SessionRequest() { ++ GVariantBuilder builder; ++ Scoped variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ variant_string = ++ g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "session_handle_token", ++ g_variant_new_string(variant_string.get())); ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string.get())); ++ ++ portal_handle_ = PrepareSignalHandle(connection_, variant_string.get()); ++ session_request_signal_id_ = SetupRequestResponseSignal( ++ portal_handle_.c_str(), OnSessionRequestResponseSignal); ++ ++ RTC_LOG(LS_INFO) << "Screen cast session requested."; ++ g_dbus_proxy_call(proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, ++ reinterpret_cast(OnSessionRequested), ++ this); ++} ++ ++// static ++void ScreenCastPortal::OnSessionRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ Scoped error; ++ Scoped variant( ++ g_dbus_proxy_call_finish(proxy, result, error.receive())); ++ if (!variant) { ++ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: " ++ << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ RTC_LOG(LS_INFO) << "Initializing the screen cast session."; ++ ++ Scoped handle; ++ g_variant_get_child(variant.get(), 0, "o", &handle); ++ if (!handle) { ++ RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; ++ if (that->session_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(that->connection_, ++ that->session_request_signal_id_); ++ that->session_request_signal_id_ = 0; ++ } ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ RTC_LOG(LS_INFO) << "Subscribing to the screen cast session."; ++} ++ ++// static ++void ScreenCastPortal::OnSessionRequestResponseSignal( ++ GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_INFO) ++ << "Received response for the screen cast session subscription."; ++ ++ uint32_t portal_response; ++ Scoped response_data; ++ g_variant_get(parameters, "(u@a{sv})", &portal_response, ++ response_data.receive()); ++ Scoped session_handle( ++ g_variant_lookup_value(response_data.get(), "session_handle", nullptr)); ++ that->session_handle_ = g_variant_dup_string(session_handle.get(), nullptr); ++ ++ if (that->session_handle_.empty() || portal_response) { ++ RTC_LOG(LS_ERROR) ++ << "Failed to request the screen cast session subscription."; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ that->session_closed_signal_id_ = g_dbus_connection_signal_subscribe( ++ that->connection_, kDesktopBusName, kSessionInterfaceName, "Closed", ++ that->session_handle_.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE, ++ OnSessionClosedSignal, that, /*user_data_free_func=*/nullptr); ++ ++ that->SourcesRequest(); ++} ++ ++// static ++void ScreenCastPortal::OnSessionClosedSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_INFO) << "Received closed signal from session."; ++ ++ that->notifier_->OnScreenCastSessionClosed(); ++ ++ // Unsubscribe from the signal and free the session handle to avoid calling ++ // Session::Close from the destructor since it's already closed ++ g_dbus_connection_signal_unsubscribe(that->connection_, ++ that->session_closed_signal_id_); ++} ++ ++void ScreenCastPortal::SourcesRequest() { ++ GVariantBuilder builder; ++ Scoped variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ // We want to record monitor content. ++ g_variant_builder_add( ++ &builder, "{sv}", "types", ++ g_variant_new_uint32(static_cast(capture_source_type_))); ++ // We don't want to allow selection of multiple sources. ++ g_variant_builder_add(&builder, "{sv}", "multiple", ++ g_variant_new_boolean(false)); ++ ++ Scoped variant( ++ g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes")); ++ if (variant.get()) { ++ uint32_t modes = 0; ++ g_variant_get(variant.get(), "u", &modes); ++ // Make request only if this mode is advertised by the portal ++ // implementation. ++ if (modes & static_cast(cursor_mode_)) { ++ g_variant_builder_add( ++ &builder, "{sv}", "cursor_mode", ++ g_variant_new_uint32(static_cast(cursor_mode_))); ++ } ++ } ++ ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string.get())); ++ ++ sources_handle_ = PrepareSignalHandle(connection_, variant_string.get()); ++ sources_request_signal_id_ = SetupRequestResponseSignal( ++ sources_handle_.c_str(), OnSourcesRequestResponseSignal); ++ ++ RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session."; ++ g_dbus_proxy_call( ++ proxy_, "SelectSources", ++ g_variant_new("(oa{sv})", session_handle_.c_str(), &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, ++ reinterpret_cast(OnSourcesRequested), this); ++} ++ ++// static ++void ScreenCastPortal::OnSourcesRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ Scoped error; ++ Scoped variant( ++ g_dbus_proxy_call_finish(proxy, result, error.receive())); ++ if (!variant) { ++ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ RTC_LOG(LS_INFO) << "Sources requested from the screen cast session."; ++ ++ Scoped handle; ++ g_variant_get_child(variant.get(), 0, "o", handle.receive()); ++ if (!handle) { ++ RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; ++ if (that->sources_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(that->connection_, ++ that->sources_request_signal_id_); ++ that->sources_request_signal_id_ = 0; ++ } ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ RTC_LOG(LS_INFO) << "Subscribed to sources signal."; ++} ++ ++// static ++void ScreenCastPortal::OnSourcesRequestResponseSignal( ++ GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_INFO) << "Received sources signal from session."; ++ ++ uint32_t portal_response; ++ g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); ++ if (portal_response) { ++ RTC_LOG(LS_ERROR) ++ << "Failed to select sources for the screen cast session."; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ that->StartRequest(); ++} ++ ++void ScreenCastPortal::StartRequest() { ++ GVariantBuilder builder; ++ Scoped variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string.get())); ++ ++ start_handle_ = PrepareSignalHandle(connection_, variant_string.get()); ++ start_request_signal_id_ = SetupRequestResponseSignal( ++ start_handle_.c_str(), OnStartRequestResponseSignal); ++ ++ // "Identifier for the application window", this is Wayland, so not "x11:...". ++ const char parent_window[] = ""; ++ ++ RTC_LOG(LS_INFO) << "Starting the screen cast session."; ++ g_dbus_proxy_call(proxy_, "Start", ++ g_variant_new("(osa{sv})", session_handle_.c_str(), ++ parent_window, &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, ++ reinterpret_cast(OnStartRequested), ++ this); ++} ++ ++// static ++void ScreenCastPortal::OnStartRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ Scoped error; ++ Scoped variant( ++ g_dbus_proxy_call_finish(proxy, result, error.receive())); ++ if (!variant) { ++ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: " ++ << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ RTC_LOG(LS_INFO) << "Initializing the start of the screen cast session."; ++ ++ Scoped handle; ++ g_variant_get_child(variant.get(), 0, "o", handle.receive()); ++ if (!handle) { ++ RTC_LOG(LS_ERROR) ++ << "Failed to initialize the start of the screen cast session."; ++ if (that->start_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(that->connection_, ++ that->start_request_signal_id_); ++ that->start_request_signal_id_ = 0; ++ } ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ RTC_LOG(LS_INFO) << "Subscribed to the start signal."; ++} ++ ++// static ++void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_INFO) << "Start signal received."; ++ uint32_t portal_response; ++ Scoped response_data; ++ Scoped iter; ++ g_variant_get(parameters, "(u@a{sv})", &portal_response, ++ response_data.receive()); ++ if (portal_response || !response_data) { ++ RTC_LOG(LS_ERROR) << "Failed to start the screen cast session."; ++ that->PortalFailed(static_cast(portal_response)); ++ return; ++ } ++ ++ // Array of PipeWire streams. See ++ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml ++ // documentation for . ++ if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})", ++ iter.receive())) { ++ Scoped variant; ++ ++ while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) { ++ uint32_t stream_id; ++ uint32_t type; ++ Scoped options; ++ ++ g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive()); ++ RTC_DCHECK(options.get()); ++ ++ if (g_variant_lookup(options.get(), "source_type", "u", &type)) { ++ that->capture_source_type_ = ++ static_cast(type); ++ } ++ ++ that->pw_stream_node_id_ = stream_id; ++ ++ break; ++ } ++ } ++ ++ that->OpenPipeWireRemote(); ++} ++ ++void ScreenCastPortal::OpenPipeWireRemote() { ++ GVariantBuilder builder; ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ ++ RTC_LOG(LS_INFO) << "Opening the PipeWire remote."; ++ ++ g_dbus_proxy_call_with_unix_fd_list( ++ proxy_, "OpenPipeWireRemote", ++ g_variant_new("(oa{sv})", session_handle_.c_str(), &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, cancellable_, ++ reinterpret_cast(OnOpenPipeWireRemoteRequested), ++ this); ++} ++ ++// static ++void ScreenCastPortal::OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCastPortal* that = static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ Scoped error; ++ Scoped outlist; ++ Scoped variant(g_dbus_proxy_call_with_unix_fd_list_finish( ++ proxy, outlist.receive(), result, error.receive())); ++ if (!variant) { ++ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: " ++ << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ int32_t index; ++ g_variant_get(variant.get(), "(h)", &index); ++ ++ that->pw_fd_ = g_unix_fd_list_get(outlist.get(), index, error.receive()); ++ ++ if (that->pw_fd_ == -1) { ++ RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " ++ << error->message; ++ that->PortalFailed(RequestResponse::kError); ++ return; ++ } ++ ++ that->notifier_->OnScreenCastRequestResult( ++ ScreenCastPortal::RequestResponse::kSuccess, that->pw_stream_node_id_, ++ that->pw_fd_); ++} ++ ++} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.h b/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.h +new file mode 100644 +index 0000000..7da218e +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/screencast_portal.h +@@ -0,0 +1,169 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SCREENCAST_PORTAL_H_ ++#define MODULES_DESKTOP_CAPTURE_LINUX_SCREENCAST_PORTAL_H_ ++ ++#include ++ ++#include ++ ++#include "absl/types/optional.h" ++ ++namespace webrtc { ++ ++class ScreenCastPortal { ++ public: ++ // Values are set based on source type property in ++ // xdg-desktop-portal/screencast ++ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml ++ enum class CaptureSourceType : uint32_t { ++ kScreen = 0b01, ++ kWindow = 0b10, ++ kAnyScreenContent = kScreen | kWindow ++ }; ++ ++ // Values are set based on cursor mode property in ++ // xdg-desktop-portal/screencast ++ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml ++ enum class CursorMode : uint32_t { ++ // Mouse cursor will not be included in any form ++ kHidden = 0b01, ++ // Mouse cursor will be part of the screen content ++ kEmbedded = 0b10, ++ // Mouse cursor information will be send separately in form of metadata ++ kMetadata = 0b100 ++ }; ++ ++ // Interface that must be implemented by the ScreenCastPortal consumers. ++ enum class RequestResponse { ++ // Success, the request is carried out. ++ kSuccess, ++ // The user cancelled the interaction. ++ kUserCancelled, ++ // The user interaction was ended in some other way. ++ kError, ++ ++ kMaxValue = kError ++ }; ++ ++ class PortalNotifier { ++ public: ++ virtual void OnScreenCastRequestResult(RequestResponse result, ++ uint32_t stream_node_id, ++ int fd) = 0; ++ virtual void OnScreenCastSessionClosed() = 0; ++ ++ protected: ++ PortalNotifier() = default; ++ virtual ~PortalNotifier() = default; ++ }; ++ ++ explicit ScreenCastPortal(CaptureSourceType source_type, ++ PortalNotifier* notifier); ++ ~ScreenCastPortal(); ++ ++ // Initialize ScreenCastPortal with series of DBus calls where we try to ++ // obtain all the required information, like PipeWire file descriptor and ++ // PipeWire stream node ID. ++ // ++ // The observer will return whether the communication with xdg-desktop-portal ++ // was successful and only then you will be able to get all the required ++ // information in order to continue working with PipeWire. ++ void Start(); ++ ++ private: ++ PortalNotifier* notifier_; ++ ++ // A PipeWire stream ID of stream we will be connecting to ++ uint32_t pw_stream_node_id_ = 0; ++ // A file descriptor of PipeWire socket ++ int pw_fd_ = -1; ++ ++ CaptureSourceType capture_source_type_ = ++ ScreenCastPortal::CaptureSourceType::kScreen; ++ ++ CursorMode cursor_mode_ = ScreenCastPortal::CursorMode::kMetadata; ++ ++ GDBusConnection* connection_ = nullptr; ++ GDBusProxy* proxy_ = nullptr; ++ GCancellable* cancellable_ = nullptr; ++ std::string portal_handle_; ++ std::string session_handle_; ++ std::string sources_handle_; ++ std::string start_handle_; ++ guint session_request_signal_id_ = 0; ++ guint sources_request_signal_id_ = 0; ++ guint start_request_signal_id_ = 0; ++ guint session_closed_signal_id_ = 0; ++ ++ void PortalFailed(RequestResponse result); ++ ++ uint32_t SetupRequestResponseSignal(const char* object_path, ++ GDBusSignalCallback callback); ++ ++ static void OnProxyRequested(GObject* object, ++ GAsyncResult* result, ++ gpointer user_data); ++ ++ static std::string PrepareSignalHandle(GDBusConnection* connection, ++ const char* token); ++ ++ void SessionRequest(); ++ static void OnSessionRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnSessionRequestResponseSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ static void OnSessionClosedSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ void SourcesRequest(); ++ static void OnSourcesRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnSourcesRequestResponseSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ ++ void StartRequest(); ++ static void OnStartRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnStartRequestResponseSignal(GDBusConnection* connection, ++ const char* sender_name, ++ const char* object_path, ++ const char* interface_name, ++ const char* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ ++ void OpenPipeWireRemote(); ++ static void OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, ++ GAsyncResult* result, ++ gpointer user_data); ++}; ++ ++} // namespace webrtc ++ ++#endif // MODULES_DESKTOP_CAPTURE_LINUX_SCREENCAST_PORTAL_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.cc b/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.cc +new file mode 100644 +index 0000000..c6ba661 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.cc +@@ -0,0 +1,872 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "modules/desktop_capture/linux/shared_screencast_stream.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "absl/memory/memory.h" ++#include "modules/desktop_capture/linux/egl_dmabuf.h" ++#include "modules/desktop_capture/screen_capture_frame_queue.h" ++#include "rtc_base/checks.h" ++#include "rtc_base/logging.h" ++#include "rtc_base/sanitizer.h" ++#include "rtc_base/string_encode.h" ++#include "rtc_base/string_to_number.h" ++#include "rtc_base/synchronization/mutex.h" ++ ++#if defined(WEBRTC_DLOPEN_PIPEWIRE) ++#include "modules/desktop_capture/linux/pipewire_stubs.h" ++using modules_desktop_capture_linux_wayland::InitializeStubs; ++using modules_desktop_capture_linux_wayland::kModuleDrm; ++using modules_desktop_capture_linux_wayland::kModulePipewire; ++using modules_desktop_capture_linux_wayland::StubPathMap; ++#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) ++ ++namespace webrtc { ++ ++const int kBytesPerPixel = 4; ++ ++#if defined(WEBRTC_DLOPEN_PIPEWIRE) ++const char kPipeWireLib[] = "libpipewire-0.3.so.0"; ++const char kDrmLib[] = "libdrm.so.2"; ++#endif ++ ++#if !PW_CHECK_VERSION(0, 3, 29) ++#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) ++#endif ++#if !PW_CHECK_VERSION(0, 3, 33) ++#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) ++#endif ++ ++constexpr int kCursorBpp = 4; ++constexpr int CursorMetaSize(int w, int h) { ++ return (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + ++ w * h * kCursorBpp); ++} ++ ++struct PipeWireVersion { ++ int major = 0; ++ int minor = 0; ++ int micro = 0; ++}; ++ ++constexpr PipeWireVersion kDmaBufMinVersion = {0, 3, 24}; ++constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33}; ++constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40}; ++ ++PipeWireVersion ParsePipeWireVersion(const char* version) { ++ std::vector parsed_version; ++ rtc::split(version, '.', &parsed_version); ++ ++ if (parsed_version.size() != 3) { ++ return {}; ++ } ++ ++ absl::optional major = rtc::StringToNumber(parsed_version.at(0)); ++ absl::optional minor = rtc::StringToNumber(parsed_version.at(1)); ++ absl::optional micro = rtc::StringToNumber(parsed_version.at(2)); ++ ++ // Return invalid version if we failed to parse it ++ if (!major || !minor || !micro) { ++ return {0, 0, 0}; ++ } ++ ++ return {major.value(), micro.value(), micro.value()}; ++} ++ ++spa_pod* BuildFormat(spa_pod_builder* builder, ++ uint32_t format, ++ const std::vector& modifiers) { ++ bool first = true; ++ spa_pod_frame frames[2]; ++ spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; ++ spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; ++ ++ spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, ++ SPA_PARAM_EnumFormat); ++ spa_pod_builder_add(builder, SPA_FORMAT_mediaType, ++ SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); ++ spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, ++ SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); ++ spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); ++ ++ if (modifiers.size()) { ++ if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { ++ spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, ++ SPA_POD_PROP_FLAG_MANDATORY); ++ spa_pod_builder_long(builder, modifiers[0]); ++ } else { ++ spa_pod_builder_prop( ++ builder, SPA_FORMAT_VIDEO_modifier, ++ SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); ++ spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); ++ ++ // modifiers from the array ++ for (int64_t val : modifiers) { ++ spa_pod_builder_long(builder, val); ++ // Add the first modifier twice as the very first value is the default ++ // option ++ if (first) { ++ spa_pod_builder_long(builder, val); ++ first = false; ++ } ++ } ++ spa_pod_builder_pop(builder, &frames[1]); ++ } ++ } ++ ++ spa_pod_builder_add( ++ builder, SPA_FORMAT_VIDEO_size, ++ SPA_POD_CHOICE_RANGE_Rectangle( ++ &pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), ++ 0); ++ ++ return static_cast(spa_pod_builder_pop(builder, &frames[0])); ++} ++ ++class PipeWireThreadLoopLock { ++ public: ++ explicit PipeWireThreadLoopLock(pw_thread_loop* loop) : loop_(loop) { ++ pw_thread_loop_lock(loop_); ++ } ++ ~PipeWireThreadLoopLock() { pw_thread_loop_unlock(loop_); } ++ ++ private: ++ pw_thread_loop* const loop_; ++}; ++ ++class ScopedBuf { ++ public: ++ ScopedBuf() {} ++ ScopedBuf(uint8_t* map, int map_size, int fd) ++ : map_(map), map_size_(map_size), fd_(fd) {} ++ ~ScopedBuf() { ++ if (map_ != MAP_FAILED) { ++ munmap(map_, map_size_); ++ } ++ } ++ ++ explicit operator bool() { return map_ != MAP_FAILED; } ++ ++ void initialize(uint8_t* map, int map_size, int fd) { ++ map_ = map; ++ map_size_ = map_size; ++ fd_ = fd; ++ } ++ ++ uint8_t* get() { return map_; } ++ ++ protected: ++ uint8_t* map_ = static_cast(MAP_FAILED); ++ int map_size_; ++ int fd_; ++}; ++ ++class SharedScreenCastStreamPrivate { ++ public: ++ SharedScreenCastStreamPrivate(); ++ ~SharedScreenCastStreamPrivate(); ++ ++ bool StartScreenCastStream(uint32_t stream_node_id, int fd); ++ void StopScreenCastStream(); ++ std::unique_ptr CaptureFrame(); ++ std::unique_ptr CaptureCursor(); ++ DesktopVector CaptureCursorPosition(); ++ ++ private: ++ uint32_t pw_stream_node_id_ = 0; ++ int pw_fd_ = -1; ++ ++ DesktopSize desktop_size_ = {}; ++ DesktopSize video_size_; ++ ++ webrtc::Mutex queue_lock_; ++ ScreenCaptureFrameQueue queue_ ++ RTC_GUARDED_BY(&queue_lock_); ++ std::unique_ptr mouse_cursor_; ++ DesktopVector mouse_cursor_position_ = DesktopVector(-1, -1); ++ ++ int64_t modifier_; ++ std::unique_ptr egl_dmabuf_; ++ // List of modifiers we query as supported by the graphics card/driver ++ std::vector modifiers_; ++ ++ // PipeWire types ++ struct pw_context* pw_context_ = nullptr; ++ struct pw_core* pw_core_ = nullptr; ++ struct pw_stream* pw_stream_ = nullptr; ++ struct pw_thread_loop* pw_main_loop_ = nullptr; ++ struct spa_source* renegotiate_ = nullptr; ++ ++ spa_hook spa_core_listener_; ++ spa_hook spa_stream_listener_; ++ ++ // A number used to verify all previous methods and the resulting ++ // events have been handled. ++ int server_version_sync_ = 0; ++ // Version of the running PipeWire server we communicate with ++ PipeWireVersion pw_server_version_; ++ // Version of the library used to run our code ++ PipeWireVersion pw_client_version_; ++ ++ // event handlers ++ pw_core_events pw_core_events_ = {}; ++ pw_stream_events pw_stream_events_ = {}; ++ ++ struct spa_video_info_raw spa_video_format_; ++ ++ void ProcessBuffer(pw_buffer* buffer); ++ void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); ++ ++ // PipeWire callbacks ++ static void OnCoreError(void* data, ++ uint32_t id, ++ int seq, ++ int res, ++ const char* message); ++ static void OnCoreDone(void* user_data, uint32_t id, int seq); ++ static void OnCoreInfo(void* user_data, const pw_core_info* info); ++ static void OnStreamParamChanged(void* data, ++ uint32_t id, ++ const struct spa_pod* format); ++ static void OnStreamStateChanged(void* data, ++ pw_stream_state old_state, ++ pw_stream_state state, ++ const char* error_message); ++ static void OnStreamProcess(void* data); ++ // This will be invoked in case we fail to process DMA-BUF PW buffer using ++ // negotiated stream parameters (modifier). We will drop the modifier we ++ // failed to use and try to use a different one or fallback to shared memory ++ // buffers. ++ static void OnRenegotiateFormat(void* data, uint64_t); ++}; ++ ++bool operator>=(const PipeWireVersion& current_pw_version, ++ const PipeWireVersion& required_pw_version) { ++ if (!current_pw_version.major && !current_pw_version.minor && ++ !current_pw_version.micro) { ++ return false; ++ } ++ ++ return std::tie(current_pw_version.major, current_pw_version.minor, ++ current_pw_version.micro) >= ++ std::tie(required_pw_version.major, required_pw_version.minor, ++ required_pw_version.micro); ++} ++ ++bool operator<=(const PipeWireVersion& current_pw_version, ++ const PipeWireVersion& required_pw_version) { ++ if (!current_pw_version.major && !current_pw_version.minor && ++ !current_pw_version.micro) { ++ return false; ++ } ++ ++ return std::tie(current_pw_version.major, current_pw_version.minor, ++ current_pw_version.micro) <= ++ std::tie(required_pw_version.major, required_pw_version.minor, ++ required_pw_version.micro); ++} ++ ++void SharedScreenCastStreamPrivate::OnCoreError(void* data, ++ uint32_t id, ++ int seq, ++ int res, ++ const char* message) { ++ SharedScreenCastStreamPrivate* that = ++ static_cast(data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message; ++} ++ ++void SharedScreenCastStreamPrivate::OnCoreInfo(void* data, ++ const pw_core_info* info) { ++ SharedScreenCastStreamPrivate* stream = ++ static_cast(data); ++ RTC_DCHECK(stream); ++ ++ stream->pw_server_version_ = ParsePipeWireVersion(info->version); ++} ++ ++void SharedScreenCastStreamPrivate::OnCoreDone(void* data, ++ uint32_t id, ++ int seq) { ++ const SharedScreenCastStreamPrivate* stream = ++ static_cast(data); ++ RTC_DCHECK(stream); ++ ++ if (id == PW_ID_CORE && stream->server_version_sync_ == seq) { ++ pw_thread_loop_signal(stream->pw_main_loop_, false); ++ } ++} ++ ++// static ++void SharedScreenCastStreamPrivate::OnStreamStateChanged( ++ void* data, ++ pw_stream_state old_state, ++ pw_stream_state state, ++ const char* error_message) { ++ SharedScreenCastStreamPrivate* that = ++ static_cast(data); ++ RTC_DCHECK(that); ++ ++ switch (state) { ++ case PW_STREAM_STATE_ERROR: ++ RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; ++ break; ++ case PW_STREAM_STATE_PAUSED: ++ case PW_STREAM_STATE_STREAMING: ++ case PW_STREAM_STATE_UNCONNECTED: ++ case PW_STREAM_STATE_CONNECTING: ++ break; ++ } ++} ++ ++// static ++void SharedScreenCastStreamPrivate::OnStreamParamChanged( ++ void* data, ++ uint32_t id, ++ const struct spa_pod* format) { ++ SharedScreenCastStreamPrivate* that = ++ static_cast(data); ++ RTC_DCHECK(that); ++ ++ RTC_LOG(LS_INFO) << "PipeWire stream format changed."; ++ if (!format || id != SPA_PARAM_Format) { ++ return; ++ } ++ ++ spa_format_video_raw_parse(format, &that->spa_video_format_); ++ ++ auto width = that->spa_video_format_.size.width; ++ auto height = that->spa_video_format_.size.height; ++ auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4); ++ auto size = height * stride; ++ ++ that->desktop_size_ = DesktopSize(width, height); ++ ++ uint8_t buffer[1024] = {}; ++ auto builder = spa_pod_builder{buffer, sizeof(buffer)}; ++ ++ // Setup buffers and meta header for new format. ++ ++ // When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as ++ // the server announces support for it. ++ // See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox ++ const bool has_modifier = ++ spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); ++ that->modifier_ = ++ has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID; ++ std::vector params; ++ const int buffer_types = ++ has_modifier || (that->pw_server_version_ >= kDmaBufMinVersion) ++ ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | ++ (1 << SPA_DATA_MemPtr) ++ : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); ++ ++ params.push_back(reinterpret_cast(spa_pod_builder_add_object( ++ &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, ++ SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, ++ SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers, ++ SPA_POD_CHOICE_RANGE_Int(8, 1, 32), SPA_PARAM_BUFFERS_dataType, ++ SPA_POD_CHOICE_FLAGS_Int(buffer_types)))); ++ params.push_back(reinterpret_cast(spa_pod_builder_add_object( ++ &builder, 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.push_back(reinterpret_cast(spa_pod_builder_add_object( ++ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, ++ SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size, ++ SPA_POD_Int(sizeof(struct spa_meta_region))))); ++ params.push_back(reinterpret_cast(spa_pod_builder_add_object( ++ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, ++ SPA_POD_Id(SPA_META_Cursor), SPA_PARAM_META_size, ++ SPA_POD_CHOICE_RANGE_Int(CursorMetaSize(64, 64), CursorMetaSize(1, 1), ++ CursorMetaSize(384, 384))))); ++ params.push_back(reinterpret_cast(spa_pod_builder_add_object( ++ &builder, 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) * 16, ++ sizeof(struct spa_meta_region) * 1, ++ sizeof(struct spa_meta_region) * 16)))); ++ ++ pw_stream_update_params(that->pw_stream_, params.data(), params.size()); ++} ++ ++// static ++void SharedScreenCastStreamPrivate::OnStreamProcess(void* data) { ++ SharedScreenCastStreamPrivate* that = ++ static_cast(data); ++ RTC_DCHECK(that); ++ ++ struct pw_buffer* next_buffer; ++ struct pw_buffer* buffer = nullptr; ++ ++ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); ++ while (next_buffer) { ++ buffer = next_buffer; ++ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); ++ ++ if (next_buffer) { ++ pw_stream_queue_buffer(that->pw_stream_, buffer); ++ } ++ } ++ ++ if (!buffer) { ++ return; ++ } ++ ++ that->ProcessBuffer(buffer); ++ ++ pw_stream_queue_buffer(that->pw_stream_, buffer); ++} ++ ++void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { ++ SharedScreenCastStreamPrivate* that = ++ static_cast(data); ++ RTC_DCHECK(that); ++ ++ { ++ PipeWireThreadLoopLock thread_loop_lock(that->pw_main_loop_); ++ ++ uint8_t buffer[2048] = {}; ++ ++ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; ++ ++ std::vector params; ++ ++ for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, ++ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { ++ if (!that->modifiers_.empty()) { ++ params.push_back(BuildFormat(&builder, format, that->modifiers_)); ++ } ++ params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); ++ } ++ ++ pw_stream_update_params(that->pw_stream_, params.data(), params.size()); ++ } ++} ++ ++SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {} ++ ++SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() { ++ if (pw_main_loop_) { ++ pw_thread_loop_stop(pw_main_loop_); ++ } ++ ++ if (pw_stream_) { ++ pw_stream_destroy(pw_stream_); ++ } ++ ++ if (pw_core_) { ++ pw_core_disconnect(pw_core_); ++ } ++ ++ if (pw_context_) { ++ pw_context_destroy(pw_context_); ++ } ++ ++ if (pw_main_loop_) { ++ pw_thread_loop_destroy(pw_main_loop_); ++ } ++} ++ ++bool SharedScreenCastStreamPrivate::StartScreenCastStream( ++ uint32_t stream_node_id, ++ int fd) { ++#if defined(WEBRTC_DLOPEN_PIPEWIRE) ++ StubPathMap paths; ++ ++ // Check if the PipeWire and DRM libraries are available. ++ paths[kModulePipewire].push_back(kPipeWireLib); ++ paths[kModuleDrm].push_back(kDrmLib); ++ ++ if (!InitializeStubs(paths)) { ++ RTC_LOG(LS_ERROR) ++ << "One of following libraries is missing on your system:\n" ++ << " - PipeWire (" << kPipeWireLib << ")\n" ++ << " - drm (" << kDrmLib << ")"; ++ return false; ++ } ++#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) ++ egl_dmabuf_ = std::make_unique(); ++ ++ pw_stream_node_id_ = stream_node_id; ++ pw_fd_ = fd; ++ ++ pw_init(/*argc=*/nullptr, /*argc=*/nullptr); ++ ++ pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr); ++ ++ pw_context_ = ++ pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); ++ if (!pw_context_) { ++ RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; ++ return false; ++ } ++ ++ if (pw_thread_loop_start(pw_main_loop_) < 0) { ++ RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; ++ return false; ++ } ++ ++ pw_client_version_ = ParsePipeWireVersion(pw_get_library_version()); ++ ++ // Initialize event handlers, remote end and stream-related. ++ pw_core_events_.version = PW_VERSION_CORE_EVENTS; ++ pw_core_events_.info = &OnCoreInfo; ++ pw_core_events_.done = &OnCoreDone; ++ pw_core_events_.error = &OnCoreError; ++ ++ pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; ++ pw_stream_events_.state_changed = &OnStreamStateChanged; ++ pw_stream_events_.param_changed = &OnStreamParamChanged; ++ pw_stream_events_.process = &OnStreamProcess; ++ ++ { ++ PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); ++ ++ pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); ++ if (!pw_core_) { ++ RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; ++ return false; ++ } ++ ++ pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); ++ ++ // Add an event that can be later invoked by pw_loop_signal_event() ++ renegotiate_ = pw_loop_add_event(pw_thread_loop_get_loop(pw_main_loop_), ++ OnRenegotiateFormat, this); ++ ++ server_version_sync_ = ++ pw_core_sync(pw_core_, PW_ID_CORE, server_version_sync_); ++ ++ pw_thread_loop_wait(pw_main_loop_); ++ ++ pw_properties* reuseProps = ++ pw_properties_new_string("pipewire.client.reuse=1"); ++ pw_stream_ = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps); ++ ++ if (!pw_stream_) { ++ RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; ++ return false; ++ } ++ ++ pw_stream_add_listener(pw_stream_, &spa_stream_listener_, ++ &pw_stream_events_, this); ++ uint8_t buffer[2048] = {}; ++ ++ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; ++ ++ std::vector params; ++ const bool has_required_pw_client_version = ++ pw_client_version_ >= kDmaBufModifierMinVersion; ++ const bool has_required_pw_server_version = ++ pw_server_version_ >= kDmaBufModifierMinVersion; ++ for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, ++ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { ++ // Modifiers can be used with PipeWire >= 0.3.33 ++ if (has_required_pw_client_version && has_required_pw_server_version) { ++ modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format); ++ ++ if (!modifiers_.empty()) { ++ params.push_back(BuildFormat(&builder, format, modifiers_)); ++ } ++ } ++ ++ params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); ++ } ++ ++ if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_, ++ PW_STREAM_FLAG_AUTOCONNECT, params.data(), ++ params.size()) != 0) { ++ RTC_LOG(LS_ERROR) << "Could not connect receiving stream."; ++ return false; ++ } ++ ++ RTC_LOG(LS_INFO) << "PipeWire remote opened."; ++ } ++ return true; ++} ++ ++void SharedScreenCastStreamPrivate::StopScreenCastStream() { ++ if (pw_stream_) { ++ pw_stream_disconnect(pw_stream_); ++ } ++} ++ ++std::unique_ptr SharedScreenCastStreamPrivate::CaptureFrame() { ++ webrtc::MutexLock lock(&queue_lock_); ++ ++ if (!queue_.current_frame()) { ++ return std::unique_ptr{}; ++ } ++ ++ std::unique_ptr frame = queue_.current_frame()->Share(); ++ return std::move(frame); ++} ++ ++std::unique_ptr SharedScreenCastStreamPrivate::CaptureCursor() { ++ if (!mouse_cursor_) { ++ return nullptr; ++ } ++ ++ return std::move(mouse_cursor_); ++} ++ ++DesktopVector SharedScreenCastStreamPrivate::CaptureCursorPosition() { ++ return mouse_cursor_position_; ++} ++ ++void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { ++ spa_buffer* spa_buffer = buffer->buffer; ++ ScopedBuf map; ++ std::unique_ptr src_unique_ptr; ++ uint8_t* src = nullptr; ++ ++ // Try to update the mouse cursor first, because it can be the only ++ // information carried by the buffer ++ { ++ const struct spa_meta_cursor* cursor = ++ static_cast(spa_buffer_find_meta_data( ++ spa_buffer, SPA_META_Cursor, sizeof(*cursor))); ++ if (spa_meta_cursor_is_valid(cursor)) { ++ struct spa_meta_bitmap* bitmap = nullptr; ++ ++ if (cursor->bitmap_offset) ++ bitmap = ++ SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap); ++ ++ if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) { ++ const uint8_t* bitmap_data = ++ SPA_MEMBER(bitmap, bitmap->offset, uint8_t); ++ BasicDesktopFrame* mouse_frame = new BasicDesktopFrame( ++ DesktopSize(bitmap->size.width, bitmap->size.height)); ++ mouse_frame->CopyPixelsFrom( ++ bitmap_data, bitmap->stride, ++ DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height)); ++ mouse_cursor_ = std::make_unique( ++ mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y)); ++ } ++ mouse_cursor_position_.set(cursor->position.x, cursor->position.y); ++ } ++ } ++ ++ if (spa_buffer->datas[0].chunk->size == 0) { ++ return; ++ } ++ ++ if (spa_buffer->datas[0].type == SPA_DATA_MemFd) { ++ map.initialize( ++ static_cast( ++ mmap(nullptr, ++ spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, ++ PROT_READ, MAP_PRIVATE, spa_buffer->datas[0].fd, 0)), ++ spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, ++ spa_buffer->datas[0].fd); ++ ++ if (!map) { ++ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: " ++ << std::strerror(errno); ++ return; ++ } ++ ++ src = SPA_MEMBER(map.get(), spa_buffer->datas[0].mapoffset, uint8_t); ++ } else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) { ++ const uint n_planes = spa_buffer->n_datas; ++ ++ if (!n_planes) { ++ return; ++ } ++ ++ std::vector plane_datas; ++ for (uint32_t i = 0; i < n_planes; ++i) { ++ EglDmaBuf::PlaneData data = { ++ static_cast(spa_buffer->datas[i].fd), ++ static_cast(spa_buffer->datas[i].chunk->stride), ++ static_cast(spa_buffer->datas[i].chunk->offset)}; ++ plane_datas.push_back(data); ++ } ++ ++ src_unique_ptr = egl_dmabuf_->ImageFromDmaBuf( ++ desktop_size_, spa_video_format_.format, plane_datas, modifier_); ++ if (src_unique_ptr) { ++ src = src_unique_ptr.get(); ++ } else { ++ RTC_LOG(LS_ERROR) << "Dropping DMA-BUF modifier: " << modifier_ ++ << " and trying to renegotiate stream parameters"; ++ ++ if (pw_client_version_ >= kDropSingleModifierMinVersion) { ++ modifiers_.erase( ++ std::remove(modifiers_.begin(), modifiers_.end(), modifier_), ++ modifiers_.end()); ++ } else { ++ modifiers_.clear(); ++ } ++ ++ pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_), ++ renegotiate_); ++ return; ++ } ++ } else if (spa_buffer->datas[0].type == SPA_DATA_MemPtr) { ++ src = static_cast(spa_buffer->datas[0].data); ++ } ++ ++ if (!src) { ++ return; ++ } ++ struct spa_meta_region* video_metadata = ++ static_cast(spa_buffer_find_meta_data( ++ spa_buffer, SPA_META_VideoCrop, sizeof(*video_metadata))); ++ ++ // Video size from metadata is bigger than an actual video stream size. ++ // The metadata are wrong or we should up-scale the video...in both cases ++ // just quit now. ++ if (video_metadata && (video_metadata->region.size.width > ++ static_cast(desktop_size_.width()) || ++ video_metadata->region.size.height > ++ static_cast(desktop_size_.height()))) { ++ RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!"; ++ return; ++ } ++ ++ // Use video metadata when video size from metadata is set and smaller than ++ // video stream size, so we need to adjust it. ++ bool video_metadata_use = false; ++ const struct spa_rectangle* video_metadata_size = ++ video_metadata ? &video_metadata->region.size : nullptr; ++ ++ if (video_metadata_size && video_metadata_size->width != 0 && ++ video_metadata_size->height != 0 && ++ (static_cast(video_metadata_size->width) < desktop_size_.width() || ++ static_cast(video_metadata_size->height) < ++ desktop_size_.height())) { ++ video_metadata_use = true; ++ } ++ ++ if (video_metadata_use) { ++ video_size_ = ++ DesktopSize(video_metadata_size->width, video_metadata_size->height); ++ } else { ++ video_size_ = desktop_size_; ++ } ++ ++ uint32_t y_offset = video_metadata_use && (video_metadata->region.position.y + ++ video_size_.height() <= ++ desktop_size_.height()) ++ ? video_metadata->region.position.y ++ : 0; ++ uint32_t x_offset = video_metadata_use && (video_metadata->region.position.x + ++ video_size_.width() <= ++ desktop_size_.width()) ++ ? video_metadata->region.position.x ++ : 0; ++ ++ uint8_t* updated_src = src + (spa_buffer->datas[0].chunk->stride * y_offset) + ++ (kBytesPerPixel * x_offset); ++ ++ webrtc::MutexLock lock(&queue_lock_); ++ ++ // Move to the next frame if the current one is being used and shared ++ if (queue_.current_frame() && queue_.current_frame()->IsShared()) { ++ queue_.MoveToNextFrame(); ++ if (queue_.current_frame() && queue_.current_frame()->IsShared()) { ++ RTC_LOG(LS_WARNING) ++ << "Failed to process PipeWire buffer: no available frame"; ++ return; ++ } ++ } ++ ++ if (!queue_.current_frame() || ++ !queue_.current_frame()->size().equals(video_size_)) { ++ std::unique_ptr frame(new BasicDesktopFrame( ++ DesktopSize(video_size_.width(), video_size_.height()))); ++ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame))); ++ } ++ ++ queue_.current_frame()->CopyPixelsFrom( ++ updated_src, ++ (spa_buffer->datas[0].chunk->stride - (kBytesPerPixel * x_offset)), ++ DesktopRect::MakeWH(video_size_.width(), video_size_.height())); ++ ++ if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx || ++ spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { ++ uint8_t* tmp_src = queue_.current_frame()->data(); ++ for (int i = 0; i < video_size_.height(); ++i) { ++ // If both sides decided to go with the RGBx format we need to convert ++ // it to BGRx to match color format expected by WebRTC. ++ ConvertRGBxToBGRx(tmp_src, queue_.current_frame()->stride()); ++ tmp_src += queue_.current_frame()->stride(); ++ } ++ } ++} ++ ++void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame, ++ uint32_t size) { ++ for (uint32_t i = 0; i < size; i += 4) { ++ uint8_t tempR = frame[i]; ++ uint8_t tempB = frame[i + 2]; ++ frame[i] = tempB; ++ frame[i + 2] = tempR; ++ } ++} ++ ++SharedScreenCastStream::SharedScreenCastStream() ++ : private_(std::make_unique()) {} ++ ++SharedScreenCastStream::~SharedScreenCastStream() {} ++ ++rtc::scoped_refptr ++SharedScreenCastStream::CreateDefault() { ++ // Explicit new, to access non-public constructor. ++ return rtc::scoped_refptr(new SharedScreenCastStream()); ++} ++ ++bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id, ++ int fd) { ++ return private_->StartScreenCastStream(stream_node_id, fd); ++} ++ ++void SharedScreenCastStream::StopScreenCastStream() { ++ private_->StopScreenCastStream(); ++} ++ ++std::unique_ptr SharedScreenCastStream::CaptureFrame() { ++ return private_->CaptureFrame(); ++} ++ ++std::unique_ptr SharedScreenCastStream::CaptureCursor() { ++ return private_->CaptureCursor(); ++} ++ ++absl::optional SharedScreenCastStream::CaptureCursorPosition() { ++ DesktopVector position = private_->CaptureCursorPosition(); ++ ++ // Consider only (x >= 0 and y >= 0) a valid position ++ if (position.x() < 0 || position.y() < 0) { ++ return absl::nullopt; ++ } ++ ++ return position; ++} ++ ++} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.h b/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.h +new file mode 100644 +index 0000000..72411e5 +--- /dev/null ++++ b/third_party/libwebrtc/modules/desktop_capture/linux/shared_screencast_stream.h +@@ -0,0 +1,71 @@ ++/* ++ * Copyright 2022 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SHARED_SCREENCAST_STREAM_H_ ++#define MODULES_DESKTOP_CAPTURE_LINUX_SHARED_SCREENCAST_STREAM_H_ ++ ++#include ++ ++#include "absl/types/optional.h" ++#include "api/ref_counted_base.h" ++#include "api/scoped_refptr.h" ++#include "modules/desktop_capture/desktop_frame.h" ++#include "modules/desktop_capture/mouse_cursor.h" ++#include "rtc_base/system/rtc_export.h" ++ ++namespace webrtc { ++ ++class SharedScreenCastStreamPrivate; ++ ++class RTC_EXPORT SharedScreenCastStream ++ : public rtc::RefCountedBase { ++ public: ++ static rtc::scoped_refptr CreateDefault(); ++ ++ bool StartScreenCastStream(uint32_t stream_node_id, int fd); ++ void StopScreenCastStream(); ++ ++ // Below functions return the most recent information we get from a ++ // PipeWire buffer on each Process() callback. This assumes that we ++ // managed to successfuly connect to a PipeWire stream provided by the ++ // compositor (based on stream parameters). The cursor data are obtained ++ // from spa_meta_cursor stream metadata and therefore the cursor is not ++ // part of actual screen/window frame. ++ ++ // Returns the most recent screen/window frame we obtained from PipeWire ++ // buffer. Will return an empty frame in case we didn't manage to get a frame ++ // from PipeWire buffer. ++ std::unique_ptr CaptureFrame(); ++ ++ // Returns the most recent mouse cursor image. Will return an nullptr cursor ++ // in case we didn't manage to get a cursor from PipeWire buffer. NOTE: the ++ // cursor image might not be updated on every cursor location change, but ++ // actually only when its shape changes. ++ std::unique_ptr CaptureCursor(); ++ ++ // Returns the most recent mouse cursor position. Will not return a value in ++ // case we didn't manage to get it from PipeWire buffer. ++ absl::optional CaptureCursorPosition(); ++ ++ ~SharedScreenCastStream(); ++ ++ protected: ++ SharedScreenCastStream(); ++ ++ private: ++ SharedScreenCastStream(const SharedScreenCastStream&) = delete; ++ SharedScreenCastStream& operator=(const SharedScreenCastStream&) = delete; ++ ++ std::unique_ptr private_; ++}; ++ ++} // namespace webrtc ++ ++#endif // MODULES_DESKTOP_CAPTURE_LINUX_SHARED_SCREENCAST_STREAM_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc +deleted file mode 100644 +index c43a1f1..0000000 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc ++++ /dev/null +@@ -1,28 +0,0 @@ +-/* +- * Copyright 2018 The WebRTC project authors. All Rights Reserved. +- * +- * Use of this source code is governed by a BSD-style license +- * that can be found in the LICENSE file in the root of the source +- * tree. An additional intellectual property rights grant can be found +- * in the file PATENTS. All contributing project authors may +- * be found in the AUTHORS file in the root of the source tree. +- */ +- +-#include "modules/desktop_capture/linux/window_capturer_pipewire.h" +- +-#include +- +-namespace webrtc { +- +-WindowCapturerPipeWire::WindowCapturerPipeWire() +- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kWindow) {} +-WindowCapturerPipeWire::~WindowCapturerPipeWire() {} +- +-// static +-std::unique_ptr +-WindowCapturerPipeWire::CreateRawWindowCapturer( +- const DesktopCaptureOptions& options) { +- return std::make_unique(); +-} +- +-} // namespace webrtc +diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.h +deleted file mode 100644 +index 7f184ef..0000000 +--- a/third_party/libwebrtc/modules/desktop_capture/linux/window_capturer_pipewire.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* +- * Copyright 2018 The WebRTC project authors. All Rights Reserved. +- * +- * Use of this source code is governed by a BSD-style license +- * that can be found in the LICENSE file in the root of the source +- * tree. An additional intellectual property rights grant can be found +- * in the file PATENTS. All contributing project authors may +- * be found in the AUTHORS file in the root of the source tree. +- */ +- +-#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_ +-#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_ +- +-#include +- +-#include "modules/desktop_capture/linux/base_capturer_pipewire.h" +- +-namespace webrtc { +- +-class WindowCapturerPipeWire : public BaseCapturerPipeWire { +- public: +- WindowCapturerPipeWire(); +- ~WindowCapturerPipeWire() override; +- +- static std::unique_ptr CreateRawWindowCapturer( +- const DesktopCaptureOptions& options); +- +- RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerPipeWire); +-}; +- +-} // namespace webrtc +- +-#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_ +diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc +index e569f6e..3bb51e8 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc ++++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc +@@ -17,6 +17,10 @@ + #include "modules/desktop_capture/linux/mouse_cursor_monitor_x11.h" + #endif // defined(WEBRTC_USE_X11) + ++#if defined(WEBRTC_USE_PIPEWIRE) ++#include "modules/desktop_capture/linux/mouse_cursor_monitor_pipewire.h" ++#endif // defined(WEBRTC_USE_PIPEWIRE) ++ + namespace webrtc { + + // static +@@ -44,6 +48,13 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( + // static + std::unique_ptr MouseCursorMonitor::Create( + const DesktopCaptureOptions& options) { ++#if defined(WEBRTC_USE_PIPEWIRE) ++ if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland() && ++ options.screencast_stream()) { ++ return std::make_unique(options); ++ } ++#endif // defined(WEBRTC_USE_PIPEWIRE) ++ + #if defined(WEBRTC_USE_X11) + return MouseCursorMonitorX11::Create(options); + #else +diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc +index 57a2002..b44ae35 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc ++++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc +@@ -14,7 +14,7 @@ + #include "modules/desktop_capture/desktop_capturer.h" + + #if defined(WEBRTC_USE_PIPEWIRE) +-#include "modules/desktop_capture/linux/screen_capturer_pipewire.h" ++#include "modules/desktop_capture/linux/base_capturer_pipewire.h" + #endif // defined(WEBRTC_USE_PIPEWIRE) + + #if defined(WEBRTC_USE_X11) +@@ -28,7 +28,7 @@ std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( + const DesktopCaptureOptions& options) { + #if defined(WEBRTC_USE_PIPEWIRE) + if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { +- return BaseCapturerPipeWire::CreateRawScreenCapturer(options); ++ return std::make_unique(options); + } + #endif // defined(WEBRTC_USE_PIPEWIRE) + +diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc b/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc +index ed03ba0..3bc6577 100644 +--- a/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc ++++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc +@@ -14,7 +14,7 @@ + #include "modules/desktop_capture/desktop_capturer.h" + + #if defined(WEBRTC_USE_PIPEWIRE) +-#include "modules/desktop_capture/linux/window_capturer_pipewire.h" ++#include "modules/desktop_capture/linux/base_capturer_pipewire.h" + #endif // defined(WEBRTC_USE_PIPEWIRE) + + #if defined(WEBRTC_USE_X11) +@@ -28,7 +28,7 @@ std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + #if defined(WEBRTC_USE_PIPEWIRE) + if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { +- return BaseCapturerPipeWire::CreateRawWindowCapturer(options); ++ return std::make_unique(options); + } + #endif // defined(WEBRTC_USE_PIPEWIRE) + +diff --git a/third_party/moz.build b/third_party/moz.build +index 1941c11..f804531 100644 +--- a/third_party/moz.build ++++ b/third_party/moz.build +@@ -49,6 +49,12 @@ with Files("libwebrtc/**"): + with Files("pipewire/**"): + BUG_COMPONENT = ("Core", "WebRTC") + ++with Files("drm/**"): ++ BUG_COMPONENT = ("Core", "WebRTC") ++ ++with Files("gbm/**"): ++ BUG_COMPONENT = ("Core", "WebRTC") ++ + with Files('rlbox_wasm2c_sandbox/**'): + BUG_COMPONENT = ('Firefox Build System', 'General') + +diff --git a/third_party/pipewire/libpipewire/mozpipewire.cpp b/third_party/pipewire/libpipewire/mozpipewire.cpp +index 1ecfc31..fbeeb8e 100644 +--- a/third_party/pipewire/libpipewire/mozpipewire.cpp ++++ b/third_party/pipewire/libpipewire/mozpipewire.cpp +@@ -69,11 +69,13 @@ static int (*pw_stream_connect_fn)(struct pw_stream *stream, + enum pw_stream_flags flags, + const struct spa_pod **params, + uint32_t n_params); ++static int (*pw_stream_disconnect_fn)(struct pw_stream *stream); + static struct pw_buffer* (*pw_stream_dequeue_buffer_fn)(struct pw_stream *stream); + static void (*pw_stream_destroy_fn)(struct pw_stream *stream); + static struct pw_stream* (*pw_stream_new_fn)(struct pw_core *core, + const char *name, + struct pw_properties *props); ++ + static int (*pw_stream_queue_buffer_fn)(struct pw_stream *stream, + struct pw_buffer *buffer); + static int (*pw_stream_update_params_fn)(struct pw_stream *stream, +@@ -87,7 +89,10 @@ static int (*pw_thread_loop_start_fn)(struct pw_thread_loop *loop); + static void (*pw_thread_loop_stop_fn)(struct pw_thread_loop *loop); + static void (*pw_thread_loop_lock_fn)(struct pw_thread_loop *loop); + static void (*pw_thread_loop_unlock_fn)(struct pw_thread_loop *loop); ++static void (*pw_thread_loop_wait_fn)(struct pw_thread_loop *loop); ++static void (*pw_thread_loop_signal_fn)(struct pw_thread_loop *loop, bool wait_for_accept); + static struct pw_properties* (*pw_properties_new_string_fn)(const char *str); ++static const char* (*pw_get_library_version_fn)(); + + bool IsPwLibraryLoaded() { + static bool isLoaded = +@@ -99,6 +104,7 @@ bool IsPwLibraryLoaded() { + IS_FUNC_LOADED(pw_init_fn) && + IS_FUNC_LOADED(pw_stream_add_listener_fn) && + IS_FUNC_LOADED(pw_stream_connect_fn) && ++ IS_FUNC_LOADED(pw_stream_disconnect_fn) && + IS_FUNC_LOADED(pw_stream_dequeue_buffer_fn) && + IS_FUNC_LOADED(pw_stream_destroy_fn) && + IS_FUNC_LOADED(pw_stream_new_fn) && +@@ -111,7 +117,10 @@ bool IsPwLibraryLoaded() { + IS_FUNC_LOADED(pw_thread_loop_stop_fn) && + IS_FUNC_LOADED(pw_thread_loop_lock_fn) && + IS_FUNC_LOADED(pw_thread_loop_unlock_fn) && +- IS_FUNC_LOADED(pw_properties_new_string_fn)); ++ IS_FUNC_LOADED(pw_thread_loop_signal_fn) && ++ IS_FUNC_LOADED(pw_thread_loop_wait_fn) && ++ IS_FUNC_LOADED(pw_properties_new_string_fn) && ++ IS_FUNC_LOADED(pw_get_library_version_fn)); + + return isLoaded; + } +@@ -136,6 +145,7 @@ bool LoadPWLibrary() { + GET_FUNC(pw_init, pwLib); + GET_FUNC(pw_stream_add_listener, pwLib); + GET_FUNC(pw_stream_connect, pwLib); ++ GET_FUNC(pw_stream_disconnect, pwLib); + GET_FUNC(pw_stream_dequeue_buffer, pwLib); + GET_FUNC(pw_stream_destroy, pwLib); + GET_FUNC(pw_stream_new, pwLib); +@@ -148,7 +158,10 @@ bool LoadPWLibrary() { + GET_FUNC(pw_thread_loop_stop, pwLib); + GET_FUNC(pw_thread_loop_lock, pwLib); + GET_FUNC(pw_thread_loop_unlock, pwLib); ++ GET_FUNC(pw_thread_loop_signal, pwLib); ++ GET_FUNC(pw_thread_loop_wait, pwLib); + GET_FUNC(pw_properties_new_string, pwLib); ++ GET_FUNC(pw_get_library_version, pwLib); + } + + return IsPwLibraryLoaded(); +@@ -242,6 +255,15 @@ pw_stream_connect(struct pw_stream *stream, + params, n_params); + } + ++int ++pw_stream_disconnect(struct pw_stream *stream) ++{ ++ if (!LoadPWLibrary()) { ++ return 0; ++ } ++ return pw_stream_disconnect_fn(stream); ++} ++ + struct pw_buffer * + pw_stream_dequeue_buffer(struct pw_stream *stream) + { +@@ -356,6 +378,23 @@ pw_thread_loop_unlock(struct pw_thread_loop *loop) + return pw_thread_loop_unlock_fn(loop); + } + ++void ++pw_thread_loop_signal(struct pw_thread_loop *loop, bool wait_for_accept) ++{ ++ if (!LoadPWLibrary()) { ++ return; ++ } ++ return pw_thread_loop_signal_fn(loop, wait_for_accept); ++} ++ ++void ++pw_thread_loop_wait(struct pw_thread_loop *loop) ++{ ++ if (!LoadPWLibrary()) { ++ return; ++ } ++ return pw_thread_loop_wait_fn(loop); ++} + + struct pw_properties * + pw_properties_new_string(const char *str) +@@ -366,3 +405,12 @@ pw_properties_new_string(const char *str) + return pw_properties_new_string_fn(str); + } + ++const char* ++pw_get_library_version() ++{ ++ if (!LoadPWLibrary()) { ++ return nullptr; ++ } ++ return pw_get_library_version_fn(); ++} ++ -- cgit