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(); +} +