summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorpants <niklasjo2004@gmail.com>2026-09-21 14:16:59 -0700
committerpants <niklasjo2004@gmail.com>2026-09-21 14:16:59 -0700
commita091c00c00c1d5ae5e3da7ef31b98293c76d479f (patch)
tree62c4b3bc114faac9922873ae48672c46cc2cf7d4 /include
Diffstat (limited to 'include')
-rw-r--r--include/args_parser.h118
-rw-r--r--include/capture/capture.h64
-rw-r--r--include/capture/kms.h23
-rw-r--r--include/capture/nvfbc.h20
-rw-r--r--include/capture/portal.h17
-rw-r--r--include/capture/v4l2.h30
-rw-r--r--include/capture/xcomposite.h19
-rw-r--r--include/capture/ximage.h20
-rw-r--r--include/codec_query/codec_query.h25
-rw-r--r--include/codec_query/nvenc.h8
-rw-r--r--include/codec_query/vaapi.h8
-rw-r--r--include/codec_query/vulkan.h8
-rw-r--r--include/color_conversion.h78
-rw-r--r--include/cuda.h108
-rw-r--r--include/cursor.h28
-rw-r--r--include/damage.h74
-rw-r--r--include/dbus.h49
-rw-r--r--include/defs.h112
-rw-r--r--include/egl.h321
-rw-r--r--include/encoder/encoder.h43
-rw-r--r--include/encoder/video/nvenc.h16
-rw-r--r--include/encoder/video/software.h15
-rw-r--r--include/encoder/video/vaapi.h15
-rw-r--r--include/encoder/video/video.h30
-rw-r--r--include/encoder/video/vulkan.h15
-rw-r--r--include/image_writer.h26
-rw-r--r--include/library_loader.h17
-rw-r--r--include/overclock.h17
-rw-r--r--include/pipewire_audio.h163
-rw-r--r--include/pipewire_video.h132
-rw-r--r--include/plugins.h38
-rw-r--r--include/replay_buffer/replay_buffer.h54
-rw-r--r--include/replay_buffer/replay_buffer_disk.h44
-rw-r--r--include/replay_buffer/replay_buffer_ram.h22
-rw-r--r--include/shader.h23
-rw-r--r--include/sound.hpp82
-rw-r--r--include/utils.h65
-rw-r--r--include/vec2.h16
-rw-r--r--include/window/wayland.h8
-rw-r--r--include/window/window.h37
-rw-r--r--include/window/x11.h10
-rw-r--r--include/window_texture.h30
-rw-r--r--include/xnvctrl.h45
43 files changed, 2093 insertions, 0 deletions
diff --git a/include/args_parser.h b/include/args_parser.h
new file mode 100644
index 0000000..316b5e1
--- /dev/null
+++ b/include/args_parser.h
@@ -0,0 +1,118 @@
+#ifndef GSR_ARGS_PARSER_H
+#define GSR_ARGS_PARSER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "defs.h"
+#include "vec2.h"
+
+typedef struct gsr_egl gsr_egl;
+
+#define NUM_ARGS 32
+
+typedef enum {
+ GSR_CAPTURE_SOURCE_TYPE_WINDOW,
+ GSR_CAPTURE_SOURCE_TYPE_FOCUSED_WINDOW,
+ GSR_CAPTURE_SOURCE_TYPE_MONITOR,
+ GSR_CAPTURE_SOURCE_TYPE_REGION,
+ GSR_CAPTURE_SOURCE_TYPE_PORTAL,
+ GSR_CAPTURE_SOURCE_TYPE_V4L2
+} CaptureSourceType;
+
+typedef enum {
+ ARG_TYPE_STRING,
+ ARG_TYPE_BOOLEAN,
+ ARG_TYPE_ENUM,
+ ARG_TYPE_I64,
+ ARG_TYPE_DOUBLE,
+} ArgType;
+
+typedef struct {
+ const char *name;
+ int value;
+} ArgEnum;
+
+typedef struct {
+ ArgType type;
+ const char **values;
+ int capacity_num_values;
+ int num_values;
+
+ const char *key;
+ bool optional;
+ bool list;
+
+ const ArgEnum *enum_values;
+ int num_enum_values;
+
+ int64_t integer_value_min;
+ int64_t integer_value_max;
+
+ union {
+ bool boolean;
+ int enum_value;
+ int64_t i64_value;
+ double d_value;
+ } typed_value;
+} Arg;
+
+typedef struct {
+ void (*version)(void *userdata);
+ void (*info)(void *userdata);
+ void (*list_audio_devices)(void *userdata);
+ void (*list_application_audio)(void *userdata);
+ void (*list_v4l2_devices)(void *userdata);
+ void (*list_capture_options)(const char *card_path, void *userdata);
+} args_handlers;
+
+typedef struct {
+ Arg args[NUM_ARGS];
+
+ gsr_video_encoder_hardware video_encoder;
+ gsr_pixel_format pixel_format;
+ gsr_framerate_mode framerate_mode;
+ gsr_color_range color_range;
+ gsr_tune tune;
+ gsr_video_codec video_codec;
+ gsr_audio_codec audio_codec;
+ gsr_bitrate_mode bitrate_mode;
+ gsr_video_quality video_quality;
+ gsr_replay_storage replay_storage;
+
+ const char *capture_source;
+ const char *container_format;
+ const char *filename;
+ const char *replay_recording_directory;
+ const char *portal_session_token_filepath;
+ const char *recording_saved_script;
+ bool verbose;
+ bool gl_debug;
+ bool fallback_cpu_encoding;
+ bool record_cursor;
+ bool date_folders;
+ bool restore_portal_session;
+ bool restart_replay_on_save;
+ bool overclock;
+ bool is_livestream;
+ bool is_output_piped;
+ bool low_latency_recording;
+ bool very_old_gpu;
+ int64_t video_bitrate;
+ int64_t audio_bitrate;
+ int64_t fps;
+ int64_t replay_buffer_size_secs;
+ double keyint;
+ vec2i output_resolution;
+ vec2i region_size;
+ vec2i region_position;
+} args_parser;
+
+/* |argv| is stored as a reference */
+bool args_parser_parse(args_parser *self, int argc, char **argv, const args_handlers *args_handlers, void *userdata);
+void args_parser_deinit(args_parser *self);
+
+bool args_parser_validate_with_gl_info(args_parser *self, gsr_egl *egl);
+void args_parser_print_usage(void);
+Arg* args_parser_get_arg(args_parser *self, const char *arg_name);
+
+#endif /* GSR_ARGS_PARSER_H */
diff --git a/include/capture/capture.h b/include/capture/capture.h
new file mode 100644
index 0000000..56cf909
--- /dev/null
+++ b/include/capture/capture.h
@@ -0,0 +1,64 @@
+#ifndef GSR_CAPTURE_CAPTURE_H
+#define GSR_CAPTURE_CAPTURE_H
+
+#include "../color_conversion.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct AVCodecContext AVCodecContext;
+typedef struct AVStream AVStream;
+typedef struct AVFrame AVFrame;
+typedef struct AVMasteringDisplayMetadata AVMasteringDisplayMetadata;
+typedef struct AVContentLightMetadata AVContentLightMetadata;
+typedef struct gsr_capture gsr_capture;
+typedef struct gsr_capture_metadata gsr_capture_metadata;
+
+typedef enum {
+ GSR_CAPTURE_ALIGN_START,
+ GSR_CAPTURE_ALIGN_CENTER,
+ GSR_CAPTURE_ALIGN_END
+} gsr_capture_alignment;
+
+struct gsr_capture_metadata {
+ // Size of the video
+ vec2i video_size;
+ // The captured output gets scaled to this size. By default this will be the same size as the captured target
+ vec2i recording_size;
+ vec2i position;
+ int fps;
+ gsr_capture_alignment halign;
+ gsr_capture_alignment valign;
+ gsr_flip flip;
+};
+
+struct gsr_capture {
+ /* These methods should not be called manually. Call gsr_capture_* instead. |capture_metadata->video_size| should be set by this function */
+ int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
+ void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
+ void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */
+ bool (*should_stop)(gsr_capture *cap, bool *err); /* can be NULL. If NULL, return false */
+ bool (*capture_has_synchronous_task)(gsr_capture *cap); /* can be NULL. If this returns true then the time spent in |capture| is ignored for video/audio (capture is paused while the synchronous task happens) */
+ void (*pre_capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* can be NULL */
+ int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* Return 0 if the frame was captured */
+ bool (*uses_external_image)(gsr_capture *cap); /* can be NULL. If NULL, return false */
+ bool (*set_hdr_metadata)(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata); /* can be NULL. If NULL, return false */
+ uint64_t (*get_window_id)(gsr_capture *cap); /* can be NULL. Returns 0 if unknown */
+ bool (*is_damaged)(gsr_capture *cap); /* can be NULL */
+ void (*clear_damage)(gsr_capture *cap); /* can be NULL */
+ void (*destroy)(gsr_capture *cap);
+
+ void *priv; /* can be NULL */
+ bool started;
+};
+
+int gsr_capture_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
+void gsr_capture_on_event(gsr_capture *cap, gsr_egl *egl);
+void gsr_capture_tick(gsr_capture *cap);
+bool gsr_capture_should_stop(gsr_capture *cap, bool *err);
+int gsr_capture_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion);
+bool gsr_capture_uses_external_image(gsr_capture *cap);
+bool gsr_capture_set_hdr_metadata(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata);
+void gsr_capture_destroy(gsr_capture *cap);
+
+#endif /* GSR_CAPTURE_CAPTURE_H */
diff --git a/include/capture/kms.h b/include/capture/kms.h
new file mode 100644
index 0000000..deb12a0
--- /dev/null
+++ b/include/capture/kms.h
@@ -0,0 +1,23 @@
+#ifndef GSR_CAPTURE_KMS_H
+#define GSR_CAPTURE_KMS_H
+
+#include "capture.h"
+#include "../cursor.h"
+#include "../../kms/kms_shared.h"
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_cursor *x11_cursor;
+ gsr_kms_response *kms_response;
+ const char *display_to_capture; /* A copy is made of this */
+ bool hdr;
+ bool record_cursor;
+ int fps;
+ vec2i output_resolution;
+ vec2i region_size;
+ vec2i region_position;
+} gsr_capture_kms_params;
+
+gsr_capture* gsr_capture_kms_create(const gsr_capture_kms_params *params);
+
+#endif /* GSR_CAPTURE_KMS_H */
diff --git a/include/capture/nvfbc.h b/include/capture/nvfbc.h
new file mode 100644
index 0000000..7e30d01
--- /dev/null
+++ b/include/capture/nvfbc.h
@@ -0,0 +1,20 @@
+#ifndef GSR_CAPTURE_NVFBC_H
+#define GSR_CAPTURE_NVFBC_H
+
+#include "capture.h"
+#include "../vec2.h"
+
+typedef struct {
+ gsr_egl *egl;
+ const char *display_to_capture; /* if this is "screen", then the entire x11 screen is captured (all displays). A copy is made of this */
+ int fps;
+ bool direct_capture;
+ bool record_cursor;
+ vec2i output_resolution;
+ vec2i region_size;
+ vec2i region_position;
+} gsr_capture_nvfbc_params;
+
+gsr_capture* gsr_capture_nvfbc_create(const gsr_capture_nvfbc_params *params);
+
+#endif /* GSR_CAPTURE_NVFBC_H */
diff --git a/include/capture/portal.h b/include/capture/portal.h
new file mode 100644
index 0000000..74cdba9
--- /dev/null
+++ b/include/capture/portal.h
@@ -0,0 +1,17 @@
+#ifndef GSR_CAPTURE_PORTAL_H
+#define GSR_CAPTURE_PORTAL_H
+
+#include "capture.h"
+
+typedef struct {
+ gsr_egl *egl;
+ bool record_cursor;
+ bool restore_portal_session;
+ /* If this is set to NULL then this defaults to $XDG_CONFIG_HOME/gpu-screen-recorder/restore_token ($XDG_CONFIG_HOME defaults to $HOME/.config) */
+ const char *portal_session_token_filepath;
+ vec2i output_resolution;
+} gsr_capture_portal_params;
+
+gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params);
+
+#endif /* GSR_CAPTURE_PORTAL_H */
diff --git a/include/capture/v4l2.h b/include/capture/v4l2.h
new file mode 100644
index 0000000..b7665aa
--- /dev/null
+++ b/include/capture/v4l2.h
@@ -0,0 +1,30 @@
+#ifndef GSR_CAPTURE_V4L2_H
+#define GSR_CAPTURE_V4L2_H
+
+#include "capture.h"
+
+typedef enum {
+ GSR_CAPTURE_V4L2_PIXFMT_AUTO,
+ GSR_CAPTURE_V4L2_PIXFMT_YUYV,
+ GSR_CAPTURE_V4L2_PIXFMT_MJPEG
+} gsr_capture_v4l2_pixfmt;
+
+typedef struct {
+ bool yuyv;
+ bool mjpeg;
+} gsr_capture_v4l2_supported_pixfmts;
+
+typedef struct {
+ gsr_egl *egl;
+ vec2i output_resolution;
+ const char *device_path;
+ gsr_capture_v4l2_pixfmt pixfmt;
+ int fps;
+} gsr_capture_v4l2_params;
+
+gsr_capture* gsr_capture_v4l2_create(const gsr_capture_v4l2_params *params);
+
+typedef void (*v4l2_devices_query_callback)(const char *path, gsr_capture_v4l2_supported_pixfmts supported_pixfmts, vec2i size, void *userdata);
+void gsr_capture_v4l2_list_devices(v4l2_devices_query_callback callback, void *userdata);
+
+#endif /* GSR_CAPTURE_V4L2_H */
diff --git a/include/capture/xcomposite.h b/include/capture/xcomposite.h
new file mode 100644
index 0000000..15ab2a8
--- /dev/null
+++ b/include/capture/xcomposite.h
@@ -0,0 +1,19 @@
+#ifndef GSR_CAPTURE_XCOMPOSITE_H
+#define GSR_CAPTURE_XCOMPOSITE_H
+
+#include "capture.h"
+#include "../vec2.h"
+#include "../cursor.h"
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_cursor *cursor;
+ unsigned long window;
+ bool follow_focused; /* If this is set then |window| is ignored */
+ bool record_cursor;
+ vec2i output_resolution;
+} gsr_capture_xcomposite_params;
+
+gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params *params);
+
+#endif /* GSR_CAPTURE_XCOMPOSITE_H */
diff --git a/include/capture/ximage.h b/include/capture/ximage.h
new file mode 100644
index 0000000..8b86618
--- /dev/null
+++ b/include/capture/ximage.h
@@ -0,0 +1,20 @@
+#ifndef GSR_CAPTURE_XIMAGE_H
+#define GSR_CAPTURE_XIMAGE_H
+
+#include "capture.h"
+#include "../vec2.h"
+#include "../cursor.h"
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_cursor *cursor;
+ const char *display_to_capture; /* A copy is made of this */
+ bool record_cursor;
+ vec2i output_resolution;
+ vec2i region_size;
+ vec2i region_position;
+} gsr_capture_ximage_params;
+
+gsr_capture* gsr_capture_ximage_create(const gsr_capture_ximage_params *params);
+
+#endif /* GSR_CAPTURE_XIMAGE_H */
diff --git a/include/codec_query/codec_query.h b/include/codec_query/codec_query.h
new file mode 100644
index 0000000..4993ced
--- /dev/null
+++ b/include/codec_query/codec_query.h
@@ -0,0 +1,25 @@
+#ifndef GSR_CODEC_QUERY_H
+#define GSR_CODEC_QUERY_H
+
+#include <stdbool.h>
+#include "../vec2.h"
+
+typedef struct {
+ bool supported;
+ bool low_power;
+ vec2i max_resolution;
+} gsr_supported_video_codec;
+
+typedef struct {
+ gsr_supported_video_codec h264;
+ gsr_supported_video_codec hevc;
+ gsr_supported_video_codec hevc_hdr;
+ gsr_supported_video_codec hevc_10bit;
+ gsr_supported_video_codec av1;
+ gsr_supported_video_codec av1_hdr;
+ gsr_supported_video_codec av1_10bit;
+ gsr_supported_video_codec vp8;
+ gsr_supported_video_codec vp9;
+} gsr_supported_video_codecs;
+
+#endif /* GSR_CODEC_QUERY_H */
diff --git a/include/codec_query/nvenc.h b/include/codec_query/nvenc.h
new file mode 100644
index 0000000..c01acf6
--- /dev/null
+++ b/include/codec_query/nvenc.h
@@ -0,0 +1,8 @@
+#ifndef GSR_CODEC_QUERY_NVENC_H
+#define GSR_CODEC_QUERY_NVENC_H
+
+#include "codec_query.h"
+
+bool gsr_get_supported_video_codecs_nvenc(gsr_supported_video_codecs *video_codecs, bool cleanup);
+
+#endif /* GSR_CODEC_QUERY_NVENC_H */
diff --git a/include/codec_query/vaapi.h b/include/codec_query/vaapi.h
new file mode 100644
index 0000000..60bdeca
--- /dev/null
+++ b/include/codec_query/vaapi.h
@@ -0,0 +1,8 @@
+#ifndef GSR_CODEC_QUERY_VAAPI_H
+#define GSR_CODEC_QUERY_VAAPI_H
+
+#include "codec_query.h"
+
+bool gsr_get_supported_video_codecs_vaapi(gsr_supported_video_codecs *video_codecs, const char *card_path, bool cleanup);
+
+#endif /* GSR_CODEC_QUERY_VAAPI_H */
diff --git a/include/codec_query/vulkan.h b/include/codec_query/vulkan.h
new file mode 100644
index 0000000..bb06c6b
--- /dev/null
+++ b/include/codec_query/vulkan.h
@@ -0,0 +1,8 @@
+#ifndef GSR_CODEC_QUERY_VULKAN_H
+#define GSR_CODEC_QUERY_VULKAN_H
+
+#include "codec_query.h"
+
+bool gsr_get_supported_video_codecs_vulkan(gsr_supported_video_codecs *video_codecs, const char *card_path, bool cleanup);
+
+#endif /* GSR_CODEC_QUERY_VULKAN_H */
diff --git a/include/color_conversion.h b/include/color_conversion.h
new file mode 100644
index 0000000..9157712
--- /dev/null
+++ b/include/color_conversion.h
@@ -0,0 +1,78 @@
+#ifndef GSR_COLOR_CONVERSION_H
+#define GSR_COLOR_CONVERSION_H
+
+#include "shader.h"
+#include "defs.h"
+#include "vec2.h"
+#include <stdbool.h>
+
+#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 12
+#define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2
+
+typedef enum {
+ GSR_SOURCE_COLOR_RGB,
+ GSR_SOURCE_COLOR_BGR,
+ GSR_SOURCE_COLOR_YUYV
+} gsr_source_color;
+
+typedef enum {
+ GSR_DESTINATION_COLOR_NV12, /* YUV420, BT709, 8-bit */
+ GSR_DESTINATION_COLOR_P010, /* YUV420, BT2020, 10-bit */
+ GSR_DESTINATION_COLOR_RGB8
+} gsr_destination_color;
+
+typedef enum {
+ GSR_ROT_0,
+ GSR_ROT_90,
+ GSR_ROT_180,
+ GSR_ROT_270
+} gsr_rotation;
+
+typedef enum {
+ GSR_FLIP_NONE = 0,
+ GSR_FLIP_HORIZONTAL = (1 << 0),
+ GSR_FLIP_VERTICAL = (1 << 1)
+} gsr_flip;
+
+typedef struct {
+ int rotation_matrix;
+ int offset;
+} gsr_color_graphics_uniforms;
+
+typedef struct {
+ gsr_egl *egl;
+
+ gsr_destination_color destination_color;
+
+ unsigned int destination_textures[2];
+ vec2i destination_textures_size[2];
+ int num_destination_textures;
+
+ gsr_color_range color_range;
+ bool load_external_image_shader;
+} gsr_color_conversion_params;
+
+typedef struct {
+ gsr_color_conversion_params params;
+
+ gsr_color_graphics_uniforms graphics_uniforms[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
+ gsr_shader graphics_shaders[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
+
+ unsigned int framebuffers[GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS];
+
+ unsigned int vertex_array_object_id;
+ unsigned int vertex_buffer_object_id;
+
+ bool schedule_clear;
+} gsr_color_conversion;
+
+int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);
+void gsr_color_conversion_deinit(gsr_color_conversion *self);
+
+void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_flip flip, gsr_source_color source_color, bool external_texture);
+void gsr_color_conversion_clear(gsr_color_conversion *self);
+void gsr_color_conversion_read_destination_texture(gsr_color_conversion *self, int destination_texture_index, int x, int y, int width, int height, unsigned int color_format, unsigned int data_format, void *pixels);
+
+gsr_rotation gsr_monitor_rotation_to_rotation(gsr_monitor_rotation monitor_rotation);
+
+#endif /* GSR_COLOR_CONVERSION_H */
diff --git a/include/cuda.h b/include/cuda.h
new file mode 100644
index 0000000..fd1f9f9
--- /dev/null
+++ b/include/cuda.h
@@ -0,0 +1,108 @@
+#ifndef GSR_CUDA_H
+#define GSR_CUDA_H
+
+#include "overclock.h"
+#include <stddef.h>
+#include <stdbool.h>
+
+// To prevent hwcontext_cuda.h from including cuda.h
+#define CUDA_VERSION 11070
+
+#define CU_CTX_SCHED_AUTO 0
+
+#if defined(_WIN64) || defined(__LP64__)
+typedef unsigned long long CUdeviceptr_v2;
+#else
+typedef unsigned int CUdeviceptr_v2;
+#endif
+typedef CUdeviceptr_v2 CUdeviceptr;
+
+typedef int CUresult;
+typedef int CUdevice_v1;
+typedef CUdevice_v1 CUdevice;
+typedef struct CUctx_st *CUcontext;
+typedef struct CUstream_st *CUstream;
+typedef struct CUarray_st *CUarray;
+
+#define CUDA_SUCCESS 0
+
+typedef enum CUgraphicsMapResourceFlags_enum {
+ CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE = 0x00,
+ CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01,
+ CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02
+} CUgraphicsMapResourceFlags;
+
+typedef enum CUgraphicsRegisterFlags_enum {
+ CU_GRAPHICS_REGISTER_FLAGS_NONE = 0x00,
+ CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 0x01,
+ CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 0x02,
+ CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 0x04,
+ CU_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 0x08
+} CUgraphicsRegisterFlags;
+
+typedef enum CUmemorytype_enum {
+ CU_MEMORYTYPE_HOST = 0x01, /**< Host memory */
+ CU_MEMORYTYPE_DEVICE = 0x02, /**< Device memory */
+ CU_MEMORYTYPE_ARRAY = 0x03, /**< Array memory */
+ CU_MEMORYTYPE_UNIFIED = 0x04 /**< Unified device or host memory */
+} CUmemorytype;
+
+typedef struct CUDA_MEMCPY2D_st {
+ size_t srcXInBytes; /**< Source X in bytes */
+ size_t srcY; /**< Source Y */
+
+ CUmemorytype srcMemoryType; /**< Source memory type (host, device, array) */
+ const void *srcHost; /**< Source host pointer */
+ CUdeviceptr srcDevice; /**< Source device pointer */
+ CUarray srcArray; /**< Source array reference */
+ size_t srcPitch; /**< Source pitch (ignored when src is array) */
+
+ size_t dstXInBytes; /**< Destination X in bytes */
+ size_t dstY; /**< Destination Y */
+
+ CUmemorytype dstMemoryType; /**< Destination memory type (host, device, array) */
+ void *dstHost; /**< Destination host pointer */
+ CUdeviceptr dstDevice; /**< Destination device pointer */
+ CUarray dstArray; /**< Destination array reference */
+ size_t dstPitch; /**< Destination pitch (ignored when dst is array) */
+
+ size_t WidthInBytes; /**< Width of 2D memory copy in bytes */
+ size_t Height; /**< Height of 2D memory copy */
+} CUDA_MEMCPY2D_v2;
+typedef CUDA_MEMCPY2D_v2 CUDA_MEMCPY2D;
+
+typedef struct CUgraphicsResource_st *CUgraphicsResource;
+
+typedef struct gsr_cuda gsr_cuda;
+struct gsr_cuda {
+ gsr_overclock overclock;
+ bool do_overclock;
+
+ void *library;
+ CUcontext cu_ctx;
+
+ CUresult (*cuInit)(unsigned int Flags);
+ CUresult (*cuDeviceGetCount)(int *count);
+ CUresult (*cuDeviceGet)(CUdevice *device, int ordinal);
+ CUresult (*cuCtxCreate_v2)(CUcontext *pctx, unsigned int flags, CUdevice dev);
+ CUresult (*cuCtxDestroy_v2)(CUcontext ctx);
+ CUresult (*cuCtxPushCurrent_v2)(CUcontext ctx);
+ CUresult (*cuCtxPopCurrent_v2)(CUcontext *pctx);
+ CUresult (*cuGetErrorString)(CUresult error, const char **pStr);
+ CUresult (*cuMemcpy2D_v2)(const CUDA_MEMCPY2D *pCopy);
+ CUresult (*cuMemcpy2DAsync_v2)(const CUDA_MEMCPY2D *pcopy, CUstream hStream);
+ CUresult (*cuStreamSynchronize)(CUstream hStream);
+
+ CUresult (*cuGraphicsGLRegisterImage)(CUgraphicsResource *pCudaResource, unsigned int image, unsigned int target, unsigned int flags);
+ CUresult (*cuGraphicsEGLRegisterImage)(CUgraphicsResource *pCudaResource, void *image, unsigned int flags);
+ CUresult (*cuGraphicsResourceSetMapFlags)(CUgraphicsResource resource, unsigned int flags);
+ CUresult (*cuGraphicsMapResources)(unsigned int count, CUgraphicsResource *resources, CUstream hStream);
+ CUresult (*cuGraphicsUnmapResources)(unsigned int count, CUgraphicsResource *resources, CUstream hStream);
+ CUresult (*cuGraphicsUnregisterResource)(CUgraphicsResource resource);
+ CUresult (*cuGraphicsSubResourceGetMappedArray)(CUarray *pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel);
+};
+
+bool gsr_cuda_load(gsr_cuda *self, Display *display, bool overclock);
+void gsr_cuda_unload(gsr_cuda *self);
+
+#endif /* GSR_CUDA_H */
diff --git a/include/cursor.h b/include/cursor.h
new file mode 100644
index 0000000..1564714
--- /dev/null
+++ b/include/cursor.h
@@ -0,0 +1,28 @@
+#ifndef GSR_CURSOR_H
+#define GSR_CURSOR_H
+
+#include "egl.h"
+#include "vec2.h"
+
+typedef struct {
+ gsr_egl *egl;
+ Display *display;
+ int x_fixes_event_base;
+
+ unsigned int texture_id;
+ vec2i size;
+ vec2i hotspot;
+ vec2i position;
+
+ bool cursor_image_set;
+ bool visible;
+} gsr_cursor;
+
+int gsr_cursor_init(gsr_cursor *self, gsr_egl *egl, Display *display);
+void gsr_cursor_deinit(gsr_cursor *self);
+
+/* Returns true if the cursor image has updated or if the cursor has moved */
+bool gsr_cursor_on_event(gsr_cursor *self, XEvent *xev);
+void gsr_cursor_tick(gsr_cursor *self, Window relative_to);
+
+#endif /* GSR_CURSOR_H */
diff --git a/include/damage.h b/include/damage.h
new file mode 100644
index 0000000..9f8bc64
--- /dev/null
+++ b/include/damage.h
@@ -0,0 +1,74 @@
+#ifndef GSR_DAMAGE_H
+#define GSR_DAMAGE_H
+
+#include "cursor.h"
+#include "utils.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+
+#define GSR_DAMAGE_MAX_MONITORS 32
+#define GSR_DAMAGE_MAX_TRACKED_TARGETS 12
+
+typedef struct {
+ int64_t window_id;
+ vec2i window_pos;
+ vec2i window_size;
+ uint64_t damage;
+ int refcount;
+} gsr_damage_window;
+
+typedef struct {
+ char *monitor_name;
+ gsr_monitor *monitor;
+ int refcount;
+} gsr_damage_monitor;
+
+typedef struct {
+ gsr_egl *egl;
+ Display *display;
+ bool track_cursor;
+
+ int damage_event;
+ int damage_error;
+ bool damaged;
+
+ uint64_t monitor_damage;
+
+ int randr_event;
+ int randr_error;
+
+ gsr_cursor *cursor;
+ gsr_monitor monitors[GSR_DAMAGE_MAX_MONITORS];
+ int num_monitors;
+
+ gsr_damage_window windows_tracked[GSR_DAMAGE_MAX_TRACKED_TARGETS];
+ int num_windows_tracked;
+
+ gsr_damage_monitor monitors_tracked[GSR_DAMAGE_MAX_TRACKED_TARGETS];
+ int num_monitors_tracked;
+
+ int all_monitors_tracked_refcount;
+ vec2i cursor_pos;
+} gsr_damage;
+
+bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, gsr_cursor *cursor, bool track_cursor);
+void gsr_damage_deinit(gsr_damage *self);
+
+/* This is reference counted */
+bool gsr_damage_start_tracking_window(gsr_damage *self, int64_t window);
+void gsr_damage_stop_tracking_window(gsr_damage *self, int64_t window);
+
+/* This is reference counted. If |monitor_name| is NULL then all monitors are tracked */
+bool gsr_damage_start_tracking_monitor(gsr_damage *self, const char *monitor_name);
+void gsr_damage_stop_tracking_monitor(gsr_damage *self, const char *monitor_name);
+
+void gsr_damage_on_event(gsr_damage *self, XEvent *xev);
+void gsr_damage_tick(gsr_damage *self);
+/* Also returns true if damage tracking is not available */
+bool gsr_damage_is_damaged(gsr_damage *self);
+void gsr_damage_clear(gsr_damage *self);
+
+#endif /* GSR_DAMAGE_H */
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644
index 0000000..229f7ea
--- /dev/null
+++ b/include/dbus.h
@@ -0,0 +1,49 @@
+#ifndef GSR_DBUS_H
+#define GSR_DBUS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <dbus/dbus.h>
+
+#define DBUS_RANDOM_STR_SIZE 16
+
+typedef enum {
+ GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
+ GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
+ GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
+ GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
+} gsr_portal_capture_type;
+
+typedef enum {
+ GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
+ GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
+ GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
+} gsr_portal_cursor_mode;
+
+typedef struct {
+ DBusConnection *con;
+ DBusError err;
+ char random_str[DBUS_RANDOM_STR_SIZE + 1];
+ unsigned int handle_counter;
+ bool desktop_portal_rule_added;
+ uint32_t screencast_version;
+ char *screencast_restore_token;
+} gsr_dbus;
+
+/* Blocking. TODO: Make non-blocking */
+bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token);
+void gsr_dbus_deinit(gsr_dbus *self);
+
+/* The follow functions should be called in order to setup ScreenCast properly */
+/* These functions that return an int return the response status code */
+int gsr_dbus_screencast_create_session(gsr_dbus *self, char **session_handle);
+/*
+ |capture_type| is a bitmask of gsr_portal_capture_type values. gsr_portal_capture_type values that are not supported by the desktop portal will be ignored.
+ |gsr_portal_cursor_mode| is a bitmask of gsr_portal_cursor_mode values. gsr_portal_cursor_mode values that are not supported will be ignored.
+*/
+int gsr_dbus_screencast_select_sources(gsr_dbus *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode);
+int gsr_dbus_screencast_start(gsr_dbus *self, const char *session_handle, uint32_t *pipewire_node);
+bool gsr_dbus_screencast_open_pipewire_remote(gsr_dbus *self, const char *session_handle, int *pipewire_fd);
+const char* gsr_dbus_screencast_get_restore_token(gsr_dbus *self);
+
+#endif /* GSR_DBUS_H */
diff --git a/include/defs.h b/include/defs.h
new file mode 100644
index 0000000..d780005
--- /dev/null
+++ b/include/defs.h
@@ -0,0 +1,112 @@
+#ifndef GSR_DEFS_H
+#define GSR_DEFS_H
+
+#include <stdbool.h>
+
+#define GSR_VIDEO_CODEC_AUTO -1
+#define GSR_BITRATE_MODE_AUTO -1
+
+typedef enum {
+ GSR_GPU_VENDOR_AMD,
+ GSR_GPU_VENDOR_INTEL,
+ GSR_GPU_VENDOR_NVIDIA,
+ GSR_GPU_VENDOR_BROADCOM,
+} gsr_gpu_vendor;
+
+typedef struct {
+ gsr_gpu_vendor vendor;
+ int gpu_version; /* 0 if unknown */
+ bool is_steam_deck;
+} gsr_gpu_info;
+
+typedef enum {
+ GSR_MONITOR_ROT_0,
+ GSR_MONITOR_ROT_90,
+ GSR_MONITOR_ROT_180,
+ GSR_MONITOR_ROT_270,
+} gsr_monitor_rotation;
+
+typedef enum {
+ GSR_CONNECTION_X11,
+ GSR_CONNECTION_WAYLAND,
+ GSR_CONNECTION_DRM,
+} gsr_connection_type;
+
+typedef enum {
+ GSR_VIDEO_QUALITY_MEDIUM,
+ GSR_VIDEO_QUALITY_HIGH,
+ GSR_VIDEO_QUALITY_VERY_HIGH,
+ GSR_VIDEO_QUALITY_ULTRA,
+} gsr_video_quality;
+
+typedef enum {
+ GSR_VIDEO_CODEC_H264,
+ GSR_VIDEO_CODEC_HEVC,
+ GSR_VIDEO_CODEC_HEVC_HDR,
+ GSR_VIDEO_CODEC_HEVC_10BIT,
+ GSR_VIDEO_CODEC_AV1,
+ GSR_VIDEO_CODEC_AV1_HDR,
+ GSR_VIDEO_CODEC_AV1_10BIT,
+ GSR_VIDEO_CODEC_VP8,
+ GSR_VIDEO_CODEC_VP9,
+ GSR_VIDEO_CODEC_H264_VULKAN,
+ GSR_VIDEO_CODEC_HEVC_VULKAN,
+} gsr_video_codec;
+
+typedef enum {
+ GSR_AUDIO_CODEC_AAC,
+ GSR_AUDIO_CODEC_OPUS,
+ GSR_AUDIO_CODEC_FLAC,
+} gsr_audio_codec;
+
+typedef enum {
+ GSR_PIXEL_FORMAT_YUV420,
+ GSR_PIXEL_FORMAT_YUV444,
+} gsr_pixel_format;
+
+typedef enum {
+ GSR_FRAMERATE_MODE_CONSTANT,
+ GSR_FRAMERATE_MODE_VARIABLE,
+ GSR_FRAMERATE_MODE_CONTENT,
+} gsr_framerate_mode;
+
+typedef enum {
+ GSR_BITRATE_MODE_QP,
+ GSR_BITRATE_MODE_VBR,
+ GSR_BITRATE_MODE_CBR,
+} gsr_bitrate_mode;
+
+typedef enum {
+ GSR_TUNE_PERFORMANCE,
+ GSR_TUNE_QUALITY,
+} gsr_tune;
+
+typedef enum {
+ GSR_VIDEO_ENCODER_HW_GPU,
+ GSR_VIDEO_ENCODER_HW_CPU,
+} gsr_video_encoder_hardware;
+
+typedef enum {
+ GSR_COLOR_RANGE_LIMITED,
+ GSR_COLOR_RANGE_FULL,
+} gsr_color_range;
+
+typedef enum {
+ GSR_COLOR_DEPTH_8_BITS,
+ GSR_COLOR_DEPTH_10_BITS,
+} gsr_color_depth;
+
+typedef enum {
+ GSR_REPLAY_STORAGE_RAM,
+ GSR_REPLAY_STORAGE_DISK,
+} gsr_replay_storage;
+
+bool video_codec_is_hdr(gsr_video_codec video_codec);
+gsr_video_codec hdr_video_codec_to_sdr_video_codec(gsr_video_codec video_codec);
+gsr_color_depth video_codec_to_bit_depth(gsr_video_codec video_codec);
+const char* video_codec_to_string(gsr_video_codec video_codec);
+bool video_codec_is_av1(gsr_video_codec video_codec);
+bool video_codec_is_vulkan(gsr_video_codec video_codec);
+const char* audio_codec_get_name(gsr_audio_codec audio_codec);
+
+#endif /* GSR_DEFS_H */
diff --git a/include/egl.h b/include/egl.h
new file mode 100644
index 0000000..b10fa06
--- /dev/null
+++ b/include/egl.h
@@ -0,0 +1,321 @@
+#ifndef GSR_EGL_H
+#define GSR_EGL_H
+
+/* OpenGL EGL library with a hidden window context (to allow using the opengl functions) */
+
+#include <X11/X.h>
+#include <X11/Xutil.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "vec2.h"
+#include "defs.h"
+
+typedef struct gsr_window gsr_window;
+
+#ifdef _WIN64
+typedef signed long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed long int khronos_intptr_t;
+typedef unsigned long int khronos_uintptr_t;
+typedef signed long int khronos_ssize_t;
+typedef unsigned long int khronos_usize_t;
+#endif
+
+typedef void* EGLDisplay;
+typedef void* EGLNativeDisplayType;
+typedef uintptr_t EGLNativeWindowType;
+typedef uintptr_t EGLNativePixmapType;
+typedef void* EGLConfig;
+typedef void* EGLSurface;
+typedef void* EGLContext;
+typedef void* EGLClientBuffer;
+typedef void* EGLImage;
+typedef void* EGLImageKHR;
+typedef void *GLeglImageOES;
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+typedef struct __GLXFBConfigRec *GLXFBConfig;
+typedef struct __GLXcontextRec *GLXContext;
+typedef XID GLXDrawable;
+typedef void(*__GLXextFuncPtr)(void);
+
+#define EGL_SUCCESS 0x3000
+#define EGL_BUFFER_SIZE 0x3020
+#define EGL_RENDERABLE_TYPE 0x3040
+#define EGL_OPENGL_API 0x30A2
+#define EGL_OPENGL_ES_API 0x30A0
+#define EGL_OPENGL_BIT 0x0008
+#define EGL_OPENGL_ES_BIT 0x0001
+#define EGL_OPENGL_ES2_BIT 0x0004
+#define EGL_OPENGL_ES3_BIT 0x00000040
+#define EGL_NONE 0x3038
+#define EGL_CONTEXT_CLIENT_VERSION 0x3098
+#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0
+#define EGL_BACK_BUFFER 0x3084
+#define EGL_GL_TEXTURE_2D 0x30B1
+#define EGL_TRUE 1
+#define EGL_IMAGE_PRESERVED_KHR 0x30D2
+#define EGL_NATIVE_PIXMAP_KHR 0x30B0
+#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
+#define EGL_WIDTH 0x3057
+#define EGL_HEIGHT 0x3056
+#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
+#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
+#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
+#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
+#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
+#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
+#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
+#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
+#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
+#define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440
+#define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441
+#define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442
+#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443
+#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444
+#define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445
+#define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446
+#define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447
+#define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448
+#define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449
+#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A
+#define EGL_LINUX_DMA_BUF_EXT 0x3270
+#define EGL_RED_SIZE 0x3024
+#define EGL_ALPHA_SIZE 0x3021
+#define EGL_BLUE_SIZE 0x3022
+#define EGL_GREEN_SIZE 0x3023
+#define EGL_DEVICE_EXT 0x322C
+#define EGL_DRM_DEVICE_FILE_EXT 0x3233
+
+#define GL_FLOAT 0x1406
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_TRIANGLES 0x0004
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#define GL_RED 0x1903
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
+#define GL_ALPHA 0x1906
+#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#define GL_RG 0x8227
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_RGB8 0x8051
+#define GL_RGBA8 0x8058
+#define GL_RGBA16 0x805B
+#define GL_R8 0x8229
+#define GL_RG8 0x822B
+#define GL_R16 0x822A
+#define GL_RG16 0x822C
+#define GL_RGB16 0x8054
+#define GL_RGBA32F 0x8814
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_NEAREST 0x2600
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_LINEAR 0x2601
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_BLEND 0x0BE2
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_MAP_WRITE_BIT 0x0002
+
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+
+typedef unsigned int (*FUNC_eglExportDMABUFImageQueryMESA)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, uint64_t *modifiers);
+typedef unsigned int (*FUNC_eglExportDMABUFImageMESA)(EGLDisplay dpy, EGLImageKHR image, int *fds, int32_t *strides, int32_t *offsets);
+typedef void (*FUNC_glEGLImageTargetTexture2DOES)(unsigned int target, GLeglImageOES image);
+typedef GLXContext (*FUNC_glXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+typedef void (*FUNC_glXSwapIntervalEXT)(Display * dpy, GLXDrawable drawable, int interval);
+typedef int (*FUNC_glXSwapIntervalMESA)(unsigned int interval);
+typedef int (*FUNC_glXSwapIntervalSGI)(int interval);
+typedef void (*GLDEBUGPROC)(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char *message, const void *userParam);
+typedef int (*FUNC_eglQueryDisplayAttribEXT)(EGLDisplay dpy, int32_t attribute, intptr_t *value);
+typedef const char* (*FUNC_eglQueryDeviceStringEXT)(void *device, int32_t name);
+typedef int (*FUNC_eglQueryDmaBufModifiersEXT)(EGLDisplay dpy, int32_t format, int32_t max_modifiers, uint64_t *modifiers, int *external_only, int32_t *num_modifiers);
+typedef void (*FUNC_glCreateMemoryObjectsEXT)(int n, unsigned int *memoryObjects);
+typedef void (*FUNC_glImportMemoryFdEXT)(unsigned int memory, uint64_t size, unsigned int handleType, int fd);
+typedef unsigned char (*FUNC_glIsMemoryObjectEXT)(unsigned int memoryObject);
+typedef void (*FUNC_glTexStorageMem2DEXT)(unsigned int target, int levels, unsigned int internalFormat, int width, int height, unsigned int memory, uint64_t offset);
+typedef void (*FUNC_glBufferStorageMemEXT)(unsigned int target, ssize_t size, unsigned int memory, uint64_t offset);
+typedef void (*FUNC_glNamedBufferStorageMemEXT)(unsigned int buffer, ssize_t size, unsigned int memory, uint64_t offset);
+typedef void (*FUNC_glMemoryObjectParameterivEXT)(unsigned int memoryObject, unsigned int pname, const int *params);
+
+typedef enum {
+ GSR_GL_CONTEXT_TYPE_EGL,
+ GSR_GL_CONTEXT_TYPE_GLX
+} gsr_gl_context_type;
+
+typedef struct gsr_egl gsr_egl;
+struct gsr_egl {
+ void *egl_library;
+ void *glx_library;
+ void *gl_library;
+
+ gsr_gl_context_type context_type;
+ gsr_window *window;
+
+ EGLDisplay egl_display;
+ EGLSurface egl_surface;
+ EGLContext egl_context;
+ const char *dri_card_path;
+
+ void *glx_context;
+ void *glx_fb_config;
+
+ gsr_gpu_info gpu_info;
+
+ char card_path[128];
+
+ int32_t (*eglGetError)(void);
+ EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id);
+ unsigned int (*eglInitialize)(EGLDisplay dpy, int32_t *major, int32_t *minor);
+ unsigned int (*eglTerminate)(EGLDisplay dpy);
+ unsigned int (*eglChooseConfig)(EGLDisplay dpy, const int32_t *attrib_list, EGLConfig *configs, int32_t config_size, int32_t *num_config);
+ EGLSurface (*eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const int32_t *attrib_list);
+ EGLContext (*eglCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const int32_t *attrib_list);
+ unsigned int (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+ EGLImage (*eglCreateImage)(EGLDisplay dpy, EGLContext ctx, unsigned int target, EGLClientBuffer buffer, const intptr_t *attrib_list);
+ unsigned int (*eglDestroyContext)(EGLDisplay dpy, EGLContext ctx);
+ unsigned int (*eglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
+ unsigned int (*eglDestroyImage)(EGLDisplay dpy, EGLImage image);
+ unsigned int (*eglSwapInterval)(EGLDisplay dpy, int32_t interval);
+ unsigned int (*eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
+ unsigned int (*eglBindAPI)(unsigned int api);
+ __eglMustCastToProperFunctionPointerType (*eglGetProcAddress)(const char *procname);
+
+ FUNC_eglExportDMABUFImageQueryMESA eglExportDMABUFImageQueryMESA;
+ FUNC_eglExportDMABUFImageMESA eglExportDMABUFImageMESA;
+ FUNC_glEGLImageTargetTexture2DOES glEGLImageTargetTexture2DOES;
+ FUNC_eglQueryDisplayAttribEXT eglQueryDisplayAttribEXT;
+ FUNC_eglQueryDeviceStringEXT eglQueryDeviceStringEXT;
+ FUNC_eglQueryDmaBufModifiersEXT eglQueryDmaBufModifiersEXT;
+ FUNC_glCreateMemoryObjectsEXT glCreateMemoryObjectsEXT;
+ FUNC_glImportMemoryFdEXT glImportMemoryFdEXT;
+ FUNC_glIsMemoryObjectEXT glIsMemoryObjectEXT;
+ FUNC_glTexStorageMem2DEXT glTexStorageMem2DEXT;
+ FUNC_glBufferStorageMemEXT glBufferStorageMemEXT;
+ FUNC_glNamedBufferStorageMemEXT glNamedBufferStorageMemEXT;
+ FUNC_glMemoryObjectParameterivEXT glMemoryObjectParameterivEXT;
+
+ __GLXextFuncPtr (*glXGetProcAddress)(const unsigned char *procName);
+ GLXFBConfig* (*glXChooseFBConfig)(Display *dpy, int screen, const int *attribList, int *nitems);
+ Bool (*glXMakeContextCurrent)(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+ // TODO: Remove
+ GLXContext (*glXCreateNewContext)(Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct);
+ void (*glXDestroyContext)(Display *dpy, GLXContext ctx);
+ void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable);
+ FUNC_glXCreateContextAttribsARB glXCreateContextAttribsARB;
+
+ /* Optional */
+ FUNC_glXSwapIntervalEXT glXSwapIntervalEXT;
+ FUNC_glXSwapIntervalMESA glXSwapIntervalMESA;
+ FUNC_glXSwapIntervalSGI glXSwapIntervalSGI;
+
+ unsigned int (*glGetError)(void);
+ const unsigned char* (*glGetString)(unsigned int name);
+ void (*glFlush)(void);
+ void (*glFinish)(void);
+ void (*glClear)(unsigned int mask);
+ void (*glClearColor)(float red, float green, float blue, float alpha);
+ void (*glGenTextures)(int n, unsigned int *textures);
+ void (*glDeleteTextures)(int n, const unsigned int *texture);
+ void (*glActiveTexture)(unsigned int texture);
+ void (*glBindTexture)(unsigned int target, unsigned int texture);
+ void (*glBindImageTexture)(unsigned int unit, unsigned int texture, int level, unsigned char layered, int layer, unsigned int access, unsigned int format);
+ void (*glTexParameteri)(unsigned int target, unsigned int pname, int param);
+ void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
+ void (*glTexParameterfv)(unsigned int target, unsigned int pname, const float *params);
+ void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels);
+ void (*glTexSubImage2D)(unsigned int target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, const void *pixels);
+ void (*glTexStorage2D)(unsigned int target, int levels, unsigned int internalformat, int width, int height);
+ void (*glGetTexImage)(unsigned int target, int level, unsigned int format, unsigned int type, void *pixels);
+ void (*glGenFramebuffers)(int n, unsigned int *framebuffers);
+ void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer);
+ void (*glDeleteFramebuffers)(int n, const unsigned int *framebuffers);
+ /* TODO: Note: this is only available since OpenGL ES 3.1 */
+ void (*glMemoryBarrier)(unsigned int barriers);
+ void (*glViewport)(int x, int y, int width, int height);
+ void (*glFramebufferTexture2D)(unsigned int target, unsigned int attachment, unsigned int textarget, unsigned int texture, int level);
+ void (*glDrawBuffers)(int n, const unsigned int *bufs);
+ unsigned int (*glCheckFramebufferStatus)(unsigned int target);
+ void (*glBindBuffer)(unsigned int target, unsigned int buffer);
+ void (*glGenBuffers)(int n, unsigned int *buffers);
+ void (*glBufferData)(unsigned int target, khronos_ssize_t size, const void *data, unsigned int usage);
+ void (*glBufferSubData)(unsigned int target, khronos_intptr_t offset, khronos_ssize_t size, const void *data);
+ void (*glDeleteBuffers)(int n, const unsigned int *buffers);
+ void (*glGenVertexArrays)(int n, unsigned int *arrays);
+ void (*glBindVertexArray)(unsigned int array);
+ void (*glDeleteVertexArrays)(int n, const unsigned int *arrays);
+ unsigned int (*glCreateProgram)(void);
+ unsigned int (*glCreateShader)(unsigned int type);
+ void (*glAttachShader)(unsigned int program, unsigned int shader);
+ void (*glBindAttribLocation)(unsigned int program, unsigned int index, const char *name);
+ void (*glCompileShader)(unsigned int shader);
+ void (*glLinkProgram)(unsigned int program);
+ void (*glShaderSource)(unsigned int shader, int count, const char *const*string, const int *length);
+ void (*glUseProgram)(unsigned int program);
+ void (*glGetProgramInfoLog)(unsigned int program, int bufSize, int *length, char *infoLog);
+ void (*glGetShaderiv)(unsigned int shader, unsigned int pname, int *params);
+ void (*glGetShaderInfoLog)(unsigned int shader, int bufSize, int *length, char *infoLog);
+ void (*glDeleteProgram)(unsigned int program);
+ void (*glDeleteShader)(unsigned int shader);
+ void (*glGetProgramiv)(unsigned int program, unsigned int pname, int *params);
+ void (*glVertexAttribPointer)(unsigned int index, int size, unsigned int type, unsigned char normalized, int stride, const void *pointer);
+ void (*glEnableVertexAttribArray)(unsigned int index);
+ void (*glDrawArrays)(unsigned int mode, int first, int count);
+ void (*glEnable)(unsigned int cap);
+ void (*glDisable)(unsigned int cap);
+ void (*glBlendFunc)(unsigned int sfactor, unsigned int dfactor);
+ void (*glPixelStorei)(unsigned int pname, int param);
+ int (*glGetUniformLocation)(unsigned int program, const char *name);
+ void (*glUniform1f)(int location, float v0);
+ void (*glUniform2f)(int location, float v0, float v1);
+ void (*glUniform1i)(int location, int v0);
+ void (*glUniform2i)(int location, int v0, int v1);
+ void (*glUniformMatrix2fv)(int location, int count, unsigned char transpose, const float *value);
+ void (*glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam);
+ void (*glScissor)(int x, int y, int width, int height);
+ void (*glCreateBuffers)(int n, unsigned int *buffers);
+ void (*glReadPixels)(int x, int y, int width, int height, unsigned int format, unsigned int type, void *pixels);
+ void* (*glMapBufferRange)(unsigned int target, intptr_t offset, ssize_t length, unsigned int access);
+ unsigned char (*glUnmapBuffer)(unsigned int target);
+ void (*glGetIntegerv)(unsigned int pname, int *params);
+};
+
+bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug);
+void gsr_egl_unload(gsr_egl *self);
+
+/* Does opengl swap with egl or glx, depending on which one is active */
+void gsr_egl_swap_buffers(gsr_egl *self);
+
+#endif /* GSR_EGL_H */
diff --git a/include/encoder/encoder.h b/include/encoder/encoder.h
new file mode 100644
index 0000000..7e550f6
--- /dev/null
+++ b/include/encoder/encoder.h
@@ -0,0 +1,43 @@
+#ifndef GSR_ENCODER_H
+#define GSR_ENCODER_H
+
+#include "../replay_buffer/replay_buffer.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <pthread.h>
+
+#define GSR_MAX_RECORDING_DESTINATIONS 128
+
+typedef struct AVCodecContext AVCodecContext;
+typedef struct AVFormatContext AVFormatContext;
+typedef struct AVStream AVStream;
+
+typedef struct {
+ size_t id;
+ AVCodecContext *codec_context;
+ AVFormatContext *format_context;
+ AVStream *stream;
+ int64_t start_pts;
+ bool has_received_keyframe;
+} gsr_encoder_recording_destination;
+
+typedef struct {
+ gsr_replay_buffer *replay_buffer;
+ pthread_mutex_t file_write_mutex;
+ bool mutex_created;
+
+ gsr_encoder_recording_destination recording_destinations[GSR_MAX_RECORDING_DESTINATIONS];
+ size_t num_recording_destinations;
+ size_t recording_destination_id_counter;
+} gsr_encoder;
+
+bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size_t replay_buffer_num_packets, double replay_buffer_time, const char *replay_directory);
+void gsr_encoder_deinit(gsr_encoder *self);
+
+void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_context, int64_t pts, int stream_index);
+/* Returns the id to the recording destination, or -1 on error */
+size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts);
+bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id);
+
+#endif /* GSR_ENCODER_H */
diff --git a/include/encoder/video/nvenc.h b/include/encoder/video/nvenc.h
new file mode 100644
index 0000000..d4a906b
--- /dev/null
+++ b/include/encoder/video/nvenc.h
@@ -0,0 +1,16 @@
+#ifndef GSR_ENCODER_VIDEO_NVENC_H
+#define GSR_ENCODER_VIDEO_NVENC_H
+
+#include "video.h"
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ gsr_egl *egl;
+ bool overclock;
+ gsr_color_depth color_depth;
+} gsr_video_encoder_nvenc_params;
+
+gsr_video_encoder* gsr_video_encoder_nvenc_create(const gsr_video_encoder_nvenc_params *params);
+
+#endif /* GSR_ENCODER_VIDEO_NVENC_H */
diff --git a/include/encoder/video/software.h b/include/encoder/video/software.h
new file mode 100644
index 0000000..fd2dc6b
--- /dev/null
+++ b/include/encoder/video/software.h
@@ -0,0 +1,15 @@
+#ifndef GSR_ENCODER_VIDEO_SOFTWARE_H
+#define GSR_ENCODER_VIDEO_SOFTWARE_H
+
+#include "video.h"
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_color_depth color_depth;
+} gsr_video_encoder_software_params;
+
+gsr_video_encoder* gsr_video_encoder_software_create(const gsr_video_encoder_software_params *params);
+
+#endif /* GSR_ENCODER_VIDEO_SOFTWARE_H */
diff --git a/include/encoder/video/vaapi.h b/include/encoder/video/vaapi.h
new file mode 100644
index 0000000..b509f17
--- /dev/null
+++ b/include/encoder/video/vaapi.h
@@ -0,0 +1,15 @@
+#ifndef GSR_ENCODER_VIDEO_VAAPI_H
+#define GSR_ENCODER_VIDEO_VAAPI_H
+
+#include "video.h"
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_color_depth color_depth;
+} gsr_video_encoder_vaapi_params;
+
+gsr_video_encoder* gsr_video_encoder_vaapi_create(const gsr_video_encoder_vaapi_params *params);
+
+#endif /* GSR_ENCODER_VIDEO_VAAPI_H */
diff --git a/include/encoder/video/video.h b/include/encoder/video/video.h
new file mode 100644
index 0000000..a77a1aa
--- /dev/null
+++ b/include/encoder/video/video.h
@@ -0,0 +1,30 @@
+#ifndef GSR_ENCODER_VIDEO_H
+#define GSR_ENCODER_VIDEO_H
+
+#include "../../color_conversion.h"
+#include <stdbool.h>
+
+#define GSR_MAX_RECORDING_DESTINATIONS 128
+
+typedef struct gsr_video_encoder gsr_video_encoder;
+typedef struct AVCodecContext AVCodecContext;
+typedef struct AVFrame AVFrame;
+
+struct gsr_video_encoder {
+ bool (*start)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
+ void (*destroy)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
+ void (*copy_textures_to_frame)(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion); /* Can be NULL */
+ /* |textures| should be able to fit 2 elements */
+ void (*get_textures)(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
+
+ void *priv;
+ bool started;
+};
+
+/* Set |replay_buffer_time_seconds| and |fps| to 0 to disable replay buffer */
+bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
+void gsr_video_encoder_destroy(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
+void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion);
+void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
+
+#endif /* GSR_ENCODER_VIDEO_H */
diff --git a/include/encoder/video/vulkan.h b/include/encoder/video/vulkan.h
new file mode 100644
index 0000000..383fc4f
--- /dev/null
+++ b/include/encoder/video/vulkan.h
@@ -0,0 +1,15 @@
+#ifndef GSR_ENCODER_VIDEO_VULKAN_H
+#define GSR_ENCODER_VIDEO_VULKAN_H
+
+#include "video.h"
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ gsr_egl *egl;
+ gsr_color_depth color_depth;
+} gsr_video_encoder_vulkan_params;
+
+gsr_video_encoder* gsr_video_encoder_vulkan_create(const gsr_video_encoder_vulkan_params *params);
+
+#endif /* GSR_ENCODER_VIDEO_VULKAN_H */
diff --git a/include/image_writer.h b/include/image_writer.h
new file mode 100644
index 0000000..01101b2
--- /dev/null
+++ b/include/image_writer.h
@@ -0,0 +1,26 @@
+#ifndef GSR_IMAGE_WRITER_H
+#define GSR_IMAGE_WRITER_H
+
+#include <stdbool.h>
+
+typedef struct gsr_egl gsr_egl;
+
+typedef enum {
+ GSR_IMAGE_FORMAT_JPEG,
+ GSR_IMAGE_FORMAT_PNG
+} gsr_image_format;
+
+typedef struct {
+ gsr_egl *egl;
+ int width;
+ int height;
+ unsigned int texture;
+} gsr_image_writer;
+
+bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height);
+void gsr_image_writer_deinit(gsr_image_writer *self);
+
+/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */
+bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality);
+
+#endif /* GSR_IMAGE_WRITER_H */
diff --git a/include/library_loader.h b/include/library_loader.h
new file mode 100644
index 0000000..47bc9f0
--- /dev/null
+++ b/include/library_loader.h
@@ -0,0 +1,17 @@
+#ifndef GSR_LIBRARY_LOADER_H
+#define GSR_LIBRARY_LOADER_H
+
+#include <stdbool.h>
+
+typedef struct {
+ void **func;
+ const char *name;
+} dlsym_assign;
+
+void* dlsym_print_fail(void *handle, const char *name, bool required);
+/* |dlsyms| should be null terminated */
+bool dlsym_load_list(void *handle, const dlsym_assign *dlsyms);
+/* |dlsyms| should be null terminated */
+void dlsym_load_list_optional(void *handle, const dlsym_assign *dlsyms);
+
+#endif /* GSR_LIBRARY_LOADER_H */
diff --git a/include/overclock.h b/include/overclock.h
new file mode 100644
index 0000000..d6ff901
--- /dev/null
+++ b/include/overclock.h
@@ -0,0 +1,17 @@
+#ifndef GSR_OVERCLOCK_H
+#define GSR_OVERCLOCK_H
+
+#include "xnvctrl.h"
+
+typedef struct {
+ gsr_xnvctrl xnvctrl;
+ int num_performance_levels;
+} gsr_overclock;
+
+bool gsr_overclock_load(gsr_overclock *self, Display *display);
+void gsr_overclock_unload(gsr_overclock *self);
+
+bool gsr_overclock_start(gsr_overclock *self);
+void gsr_overclock_stop(gsr_overclock *self);
+
+#endif /* GSR_OVERCLOCK_H */
diff --git a/include/pipewire_audio.h b/include/pipewire_audio.h
new file mode 100644
index 0000000..c5a3b92
--- /dev/null
+++ b/include/pipewire_audio.h
@@ -0,0 +1,163 @@
+#ifndef GSR_PIPEWIRE_AUDIO_H
+#define GSR_PIPEWIRE_AUDIO_H
+
+#include <pipewire/thread-loop.h>
+#include <pipewire/context.h>
+#include <pipewire/core.h>
+#include <spa/utils/hook.h>
+
+#include <stdbool.h>
+
+typedef enum {
+ GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, /* Application audio */
+ GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT, /* Audio recording input */
+ GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE /* Audio output or input device or combined (virtual) sink */
+} gsr_pipewire_audio_node_type;
+
+typedef struct {
+ uint32_t id;
+ char *name;
+ gsr_pipewire_audio_node_type type;
+} gsr_pipewire_audio_node;
+
+typedef enum {
+ GSR_PIPEWIRE_AUDIO_PORT_DIRECTION_INPUT,
+ GSR_PIPEWIRE_AUDIO_PORT_DIRECTION_OUTPUT
+} gsr_pipewire_audio_port_direction;
+
+typedef struct {
+ uint32_t id;
+ uint32_t node_id;
+ gsr_pipewire_audio_port_direction direction;
+ char *name;
+} gsr_pipewire_audio_port;
+
+typedef struct {
+ uint32_t id;
+ uint32_t output_node_id;
+ uint32_t input_node_id;
+} gsr_pipewire_audio_link;
+
+typedef enum {
+ GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_STREAM, /* Application */
+ GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_SINK /* Combined (virtual) sink */
+} gsr_pipewire_audio_link_input_type;
+
+typedef enum {
+ GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_STANDARD,
+ GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_OUTPUT,
+ GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_INPUT
+} gsr_pipewire_audio_requested_type;
+
+typedef struct {
+ char *name;
+ gsr_pipewire_audio_requested_type type;
+} gsr_pipewire_audio_requested_output;
+
+typedef struct {
+ gsr_pipewire_audio_requested_output *outputs;
+ int num_outputs;
+ char *input_name;
+ bool inverted;
+ gsr_pipewire_audio_node_type output_type;
+ gsr_pipewire_audio_link_input_type input_type;
+} gsr_pipewire_audio_requested_link;
+
+typedef struct {
+ struct pw_thread_loop *thread_loop;
+ struct pw_context *context;
+ struct pw_core *core;
+ struct spa_hook core_listener;
+ struct pw_registry *registry;
+ struct spa_hook registry_listener;
+ int server_version_sync;
+
+ struct pw_proxy *metadata_proxy;
+ struct spa_hook metadata_listener;
+ struct spa_hook metadata_proxy_listener;
+ char default_output_device_name[128];
+ char default_input_device_name[128];
+
+ gsr_pipewire_audio_node *stream_nodes;
+ size_t num_stream_nodes;
+ size_t stream_nodes_capacity_items;
+
+ gsr_pipewire_audio_port *ports;
+ size_t num_ports;
+ size_t ports_capacity_items;
+
+ gsr_pipewire_audio_link *links;
+ size_t num_links;
+ size_t links_capacity_items;
+
+ gsr_pipewire_audio_requested_link *requested_links;
+ size_t num_requested_links;
+ size_t requested_links_capacity_items;
+
+ bool running;
+} gsr_pipewire_audio;
+
+bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
+void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self);
+
+/*
+ This function links audio source outputs from applications that match the name |app_names| to the input
+ that matches the name |stream_name_input|.
+ If an application or a new application starts outputting audio after this function is called and the app name matches
+ then it will automatically link the audio sources.
+ |app_names| and |stream_name_input| are case-insensitive matches.
+*/
+bool gsr_pipewire_audio_add_link_from_apps_to_stream(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *stream_name_input);
+/*
+ This function links audio source outputs from all applications except the ones that match the name |app_names| to the input
+ that matches the name |stream_name_input|.
+ If an application or a new application starts outputting audio after this function is called and the app name doesn't match
+ then it will automatically link the audio sources.
+ |app_names| and |stream_name_input| are case-insensitive matches.
+*/
+bool gsr_pipewire_audio_add_link_from_apps_to_stream_inverted(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *stream_name_input);
+
+/*
+ This function links audio source outputs from applications that match the name |app_names| to the input
+ that matches the name |sink_name_input|.
+ If an application or a new application starts outputting audio after this function is called and the app name matches
+ then it will automatically link the audio sources.
+ |app_names| and |sink_name_input| are case-insensitive matches.
+*/
+bool gsr_pipewire_audio_add_link_from_apps_to_sink(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *sink_name_input);
+/*
+ This function links audio source outputs from all applications except the ones that match the name |app_names| to the input
+ that matches the name |sink_name_input|.
+ If an application or a new application starts outputting audio after this function is called and the app name doesn't match
+ then it will automatically link the audio sources.
+ |app_names| and |sink_name_input| are case-insensitive matches.
+*/
+bool gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *sink_name_input);
+
+/*
+ This function links audio source outputs from devices that match the name |source_names| to the input
+ that matches the name |stream_name_input|.
+ If a device or a new device starts outputting audio after this function is called and the device name matches
+ then it will automatically link the audio sources.
+ |source_names| and |stream_name_input| are case-insensitive matches.
+ |source_names| can include "default_output" or "default_input" to use the default output/input
+ and it will automatically switch when the default output/input is changed in system audio settings.
+*/
+bool gsr_pipewire_audio_add_link_from_sources_to_stream(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *stream_name_input);
+
+/*
+ This function links audio source outputs from devices that match the name |source_names| to the input
+ that matches the name |sink_name_input|.
+ If a device or a new device starts outputting audio after this function is called and the device name matches
+ then it will automatically link the audio sources.
+ |source_names| and |sink_name_input| are case-insensitive matches.
+ |source_names| can include "default_output" or "default_input" to use the default output/input
+ and it will automatically switch when the default output/input is changed in system audio settings.
+*/
+bool gsr_pipewire_audio_add_link_from_sources_to_sink(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *sink_name_input);
+
+/* Return true to continue */
+typedef bool (*gsr_pipewire_audio_app_query_callback)(const char *app_name, void *userdata);
+void gsr_pipewire_audio_for_each_app(gsr_pipewire_audio *self, gsr_pipewire_audio_app_query_callback callback, void *userdata);
+
+#endif /* GSR_PIPEWIRE_AUDIO_H */
diff --git a/include/pipewire_video.h b/include/pipewire_video.h
new file mode 100644
index 0000000..8722d21
--- /dev/null
+++ b/include/pipewire_video.h
@@ -0,0 +1,132 @@
+#ifndef GSR_PIPEWIRE_VIDEO_H
+#define GSR_PIPEWIRE_VIDEO_H
+
+#include "defs.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#include <spa/utils/hook.h>
+#include <spa/param/video/format.h>
+
+#define GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS 1024
+#define GSR_PIPEWIRE_VIDEO_MAX_VIDEO_FORMATS 10
+#define GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES 4
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ int major;
+ int minor;
+ int micro;
+} gsr_pipewire_video_data_version;
+
+typedef struct {
+ uint32_t fps_num;
+ uint32_t fps_den;
+} gsr_pipewire_video_video_info;
+
+typedef struct {
+ int fd;
+ uint32_t offset;
+ int32_t stride;
+} gsr_pipewire_video_dmabuf_data;
+
+typedef struct {
+ int x, y;
+ int width, height;
+} gsr_pipewire_video_region;
+
+typedef struct {
+ enum spa_video_format format;
+ size_t modifiers_index;
+ size_t modifiers_size;
+} gsr_video_format;
+
+typedef struct {
+ unsigned int texture_id;
+ unsigned int external_texture_id;
+ unsigned int cursor_texture_id;
+} gsr_texture_map;
+
+typedef struct {
+ gsr_pipewire_video_region region;
+ gsr_pipewire_video_region cursor_region;
+ gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
+ int num_dmabuf_data;
+ uint32_t fourcc;
+ uint64_t modifiers;
+ bool using_external_image;
+ gsr_monitor_rotation rotation;
+ int texture_width;
+ int texture_height;
+} gsr_map_texture_output;
+
+typedef struct {
+ gsr_egl *egl;
+ int fd;
+ uint32_t node;
+ pthread_mutex_t mutex;
+ bool mutex_initialized;
+
+ struct pw_thread_loop *thread_loop;
+ struct pw_context *context;
+ struct pw_core *core;
+ struct spa_hook core_listener;
+ struct pw_stream *stream;
+ struct spa_hook stream_listener;
+ struct spa_source *reneg;
+ struct spa_video_info format;
+ int server_version_sync;
+ bool negotiated;
+ bool damaged;
+ bool has_modifier;
+
+ struct {
+ bool visible;
+ bool valid;
+ uint8_t *data;
+ int x, y;
+ int hotspot_x, hotspot_y;
+ int width, height;
+ } cursor;
+
+ struct {
+ bool valid;
+ int x, y;
+ uint32_t width, height;
+ } crop;
+
+ gsr_video_format supported_video_formats[GSR_PIPEWIRE_VIDEO_MAX_VIDEO_FORMATS];
+
+ gsr_pipewire_video_data_version server_version;
+ gsr_pipewire_video_video_info video_info;
+ gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
+ size_t dmabuf_num_planes;
+
+ bool no_modifiers_fallback;
+ bool external_texture_fallback;
+
+ uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
+ size_t num_modifiers;
+
+ bool paused;
+ double paused_start_secs;
+
+ gsr_monitor_rotation rotation;
+} gsr_pipewire_video;
+
+/*
+ |capture_cursor| only applies to when capturing a window or region.
+ In other cases |pipewire_node|'s setup will determine if the cursor is included.
+ Note that the cursor is not guaranteed to be shown even if set to true, it depends on the wayland compositor.
+*/
+bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl);
+void gsr_pipewire_video_deinit(gsr_pipewire_video *self);
+
+bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_map_texture_output *output);
+bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self);
+void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self);
+bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self);
+
+#endif /* GSR_PIPEWIRE_VIDEO_H */
diff --git a/include/plugins.h b/include/plugins.h
new file mode 100644
index 0000000..7588b61
--- /dev/null
+++ b/include/plugins.h
@@ -0,0 +1,38 @@
+#ifndef GSR_PLUGINS_H
+#define GSR_PLUGINS_H
+
+#include "../plugin/plugin.h"
+#include <stdbool.h>
+#include "color_conversion.h"
+
+#define GSR_MAX_PLUGINS 128
+
+typedef bool (*gsr_plugin_init_func)(const gsr_plugin_init_params *params, gsr_plugin_init_return *ret);
+typedef void (*gsr_plugin_deinit_func)(void *userdata);
+
+typedef struct {
+ gsr_plugin_init_return data;
+ void *lib;
+ gsr_plugin_init_func gsr_plugin_init;
+ gsr_plugin_deinit_func gsr_plugin_deinit;
+} gsr_plugin;
+
+typedef struct {
+ gsr_plugin plugins[GSR_MAX_PLUGINS];
+ int num_plugins;
+
+ gsr_plugin_init_params init_params;
+ gsr_egl *egl;
+
+ unsigned int texture;
+ gsr_color_conversion color_conversion;
+} gsr_plugins;
+
+bool gsr_plugins_init(gsr_plugins *self, gsr_plugin_init_params init_params, gsr_egl *egl);
+/* Plugins are unloaded in reverse order */
+void gsr_plugins_deinit(gsr_plugins *self);
+
+bool gsr_plugins_load_plugin(gsr_plugins *self, const char *plugin_filepath);
+void gsr_plugins_draw(gsr_plugins *self);
+
+#endif /* GSR_PLUGINS_H */
diff --git a/include/replay_buffer/replay_buffer.h b/include/replay_buffer/replay_buffer.h
new file mode 100644
index 0000000..a04a3be
--- /dev/null
+++ b/include/replay_buffer/replay_buffer.h
@@ -0,0 +1,54 @@
+#ifndef GSR_REPLAY_BUFFER_H
+#define GSR_REPLAY_BUFFER_H
+
+#include "../defs.h"
+#include <pthread.h>
+#include <stdbool.h>
+#include <libavcodec/packet.h>
+
+typedef struct gsr_replay_buffer gsr_replay_buffer;
+
+typedef struct {
+ size_t packet_index;
+ size_t file_index;
+} gsr_replay_buffer_iterator;
+
+struct gsr_replay_buffer {
+ void (*destroy)(gsr_replay_buffer *self);
+ bool (*append)(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp);
+ void (*clear)(gsr_replay_buffer *self);
+ AVPacket* (*iterator_get_packet)(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
+ /* The returned data should be free'd with free */
+ uint8_t* (*iterator_get_packet_data)(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
+ /* The clone has to be destroyed before the replay buffer it clones is destroyed */
+ gsr_replay_buffer* (*clone)(gsr_replay_buffer *self);
+ /* Returns {0, 0} if replay buffer is empty */
+ gsr_replay_buffer_iterator (*find_packet_index_by_time_passed)(gsr_replay_buffer *self, int seconds);
+ /* Returns {-1, 0} if not found */
+ gsr_replay_buffer_iterator (*find_keyframe)(gsr_replay_buffer *self, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index);
+ bool (*iterator_next)(gsr_replay_buffer *self, gsr_replay_buffer_iterator *iterator);
+
+ pthread_mutex_t mutex;
+ bool mutex_initialized;
+ gsr_replay_buffer *original_replay_buffer;
+};
+
+gsr_replay_buffer* gsr_replay_buffer_create(gsr_replay_storage replay_storage, const char *replay_directory, double replay_buffer_time, size_t replay_buffer_num_packets);
+void gsr_replay_buffer_destroy(gsr_replay_buffer *self);
+
+void gsr_replay_buffer_lock(gsr_replay_buffer *self);
+void gsr_replay_buffer_unlock(gsr_replay_buffer *self);
+bool gsr_replay_buffer_append(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp);
+void gsr_replay_buffer_clear(gsr_replay_buffer *self);
+AVPacket* gsr_replay_buffer_iterator_get_packet(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
+/* The returned data should be free'd with free */
+uint8_t* gsr_replay_buffer_iterator_get_packet_data(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
+/* The clone has to be destroyed before the replay buffer it clones is destroyed */
+gsr_replay_buffer* gsr_replay_buffer_clone(gsr_replay_buffer *self);
+/* Returns {0, 0} if replay buffer is empty */
+gsr_replay_buffer_iterator gsr_replay_buffer_find_packet_index_by_time_passed(gsr_replay_buffer *self, int seconds);
+/* Returns {-1, 0} if not found */
+gsr_replay_buffer_iterator gsr_replay_buffer_find_keyframe(gsr_replay_buffer *self, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index);
+bool gsr_replay_buffer_iterator_next(gsr_replay_buffer *self, gsr_replay_buffer_iterator *iterator);
+
+#endif /* GSR_REPLAY_BUFFER_H */ \ No newline at end of file
diff --git a/include/replay_buffer/replay_buffer_disk.h b/include/replay_buffer/replay_buffer_disk.h
new file mode 100644
index 0000000..6873bb0
--- /dev/null
+++ b/include/replay_buffer/replay_buffer_disk.h
@@ -0,0 +1,44 @@
+#ifndef GSR_REPLAY_BUFFER_DISK_H
+#define GSR_REPLAY_BUFFER_DISK_H
+
+#include "replay_buffer.h"
+#include <limits.h>
+
+#define GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES 1024
+
+typedef struct {
+ AVPacket packet;
+ size_t data_index;
+ double timestamp;
+} gsr_av_packet_disk;
+
+typedef struct {
+ size_t id;
+ double start_timestamp;
+ double end_timestamp;
+ int ref_counter;
+ int fd;
+
+ gsr_av_packet_disk *packets;
+ size_t capacity_num_packets;
+ size_t num_packets;
+} gsr_replay_buffer_file;
+
+typedef struct {
+ gsr_replay_buffer replay_buffer;
+ double replay_buffer_time;
+
+ size_t storage_counter;
+ size_t storage_num_bytes_written;
+ int storage_fd;
+ gsr_replay_buffer_file *files[GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES]; // GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES * REPLAY_BUFFER_FILE_SIZE_BYTES = 256gb, should be enough for everybody
+ size_t num_files;
+
+ char replay_directory[PATH_MAX];
+
+ bool owns_directory;
+} gsr_replay_buffer_disk;
+
+gsr_replay_buffer* gsr_replay_buffer_disk_create(const char *replay_directory, double replay_buffer_time);
+
+#endif /* GSR_REPLAY_BUFFER_DISK_H */ \ No newline at end of file
diff --git a/include/replay_buffer/replay_buffer_ram.h b/include/replay_buffer/replay_buffer_ram.h
new file mode 100644
index 0000000..a43d1b9
--- /dev/null
+++ b/include/replay_buffer/replay_buffer_ram.h
@@ -0,0 +1,22 @@
+#ifndef GSR_REPLAY_BUFFER_RAM_H
+#define GSR_REPLAY_BUFFER_RAM_H
+
+#include "replay_buffer.h"
+
+typedef struct {
+ AVPacket packet;
+ int ref_counter;
+ double timestamp;
+} gsr_av_packet_ram;
+
+typedef struct {
+ gsr_replay_buffer replay_buffer;
+ gsr_av_packet_ram **packets;
+ size_t capacity_num_packets;
+ size_t num_packets;
+ size_t index;
+} gsr_replay_buffer_ram;
+
+gsr_replay_buffer* gsr_replay_buffer_ram_create(size_t replay_buffer_num_packets);
+
+#endif /* GSR_REPLAY_BUFFER_RAM_H */ \ No newline at end of file
diff --git a/include/shader.h b/include/shader.h
new file mode 100644
index 0000000..2fd2603
--- /dev/null
+++ b/include/shader.h
@@ -0,0 +1,23 @@
+#ifndef GSR_SHADER_H
+#define GSR_SHADER_H
+
+#include <stdbool.h>
+
+typedef struct gsr_egl gsr_egl;
+
+typedef struct {
+ gsr_egl *egl;
+ unsigned int program_id;
+} gsr_shader;
+
+/* |vertex_shader| or |fragment_shader| may be NULL */
+int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader);
+void gsr_shader_deinit(gsr_shader *self);
+
+int gsr_shader_bind_attribute_location(gsr_shader *self, const char *attribute, int location);
+void gsr_shader_use(gsr_shader *self);
+void gsr_shader_use_none(gsr_shader *self);
+
+void gsr_shader_enable_debug_output(bool enable);
+
+#endif /* GSR_SHADER_H */
diff --git a/include/sound.hpp b/include/sound.hpp
new file mode 100644
index 0000000..c7164f8
--- /dev/null
+++ b/include/sound.hpp
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2020 dec05eba
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef GPU_SCREEN_RECORDER_H
+#define GPU_SCREEN_RECORDER_H
+
+#include <vector>
+#include <string>
+
+typedef struct {
+ void *handle;
+ unsigned int frames;
+} SoundDevice;
+
+struct AudioDevice {
+ std::string name;
+ std::string description;
+};
+
+struct AudioDevices {
+ std::string default_output;
+ std::string default_input;
+ std::vector<AudioDevice> audio_inputs;
+};
+
+enum class AudioInputType {
+ DEVICE,
+ APPLICATION
+};
+
+struct AudioInput {
+ std::string name;
+ AudioInputType type = AudioInputType::DEVICE;
+ bool inverted = false;
+};
+
+struct MergedAudioInputs {
+ std::string track_name;
+ std::vector<AudioInput> audio_inputs;
+};
+
+typedef enum {
+ S16,
+ S32,
+ F32
+} AudioFormat;
+
+/*
+ Get a sound device by name, returning the device into the |device| parameter.
+ |device_name| can be a device name or "default_output", "default_input" or "" to not connect to any device (used for app audio for example).
+ If the device name is "default_output" or "default_input" then it will automatically switch which
+ device is records from when the default output/input is changed in the system audio settings.
+ Returns 0 on success, or a negative value on failure.
+*/
+int sound_device_get_by_name(SoundDevice *device, const char *node_name, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format);
+
+void sound_device_close(SoundDevice *device);
+
+/*
+ Returns the next chunk of audio into @buffer.
+ Returns the number of frames read, or a negative value on failure.
+*/
+int sound_device_read_next_chunk(SoundDevice *device, void **buffer, double timeout_sec, double *latency_seconds);
+
+AudioDevices get_pulseaudio_inputs();
+bool pulseaudio_server_is_pipewire();
+
+#endif /* GPU_SCREEN_RECORDER_H */
diff --git a/include/utils.h b/include/utils.h
new file mode 100644
index 0000000..ff8cb90
--- /dev/null
+++ b/include/utils.h
@@ -0,0 +1,65 @@
+#ifndef GSR_UTILS_H
+#define GSR_UTILS_H
+
+#include "vec2.h"
+#include "../include/egl.h"
+#include "../include/defs.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct AVCodecContext AVCodecContext;
+typedef struct AVFrame AVFrame;
+typedef struct gsr_capture_metadata gsr_capture_metadata;
+
+typedef struct {
+ const char *name;
+ int name_len;
+ vec2i pos; /* This is 0, 0 on wayland. Use |drm_monitor_get_display_server_data| to get the position */
+ vec2i size;
+ uint32_t connector_id; /* Only on x11 and drm */
+ gsr_monitor_rotation rotation; /* Only on x11 and wayland */
+ uint32_t monitor_identifier; /* On x11 this is the crtc id */
+} gsr_monitor;
+
+typedef struct {
+ const char *name;
+ int name_len;
+ gsr_monitor *monitor;
+ bool found_monitor;
+} get_monitor_by_name_userdata;
+
+double clock_get_monotonic_seconds(void);
+bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size);
+bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size);
+
+typedef void (*active_monitor_callback)(const gsr_monitor *monitor, void *userdata);
+void for_each_active_monitor_output_x11_not_cached(Display *display, active_monitor_callback callback, void *userdata);
+void for_each_active_monitor_output(const gsr_window *window, const char *card_path, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata);
+bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type, const char *name, gsr_monitor *monitor);
+bool drm_monitor_get_display_server_data(const gsr_window *window, const gsr_monitor *monitor, gsr_monitor_rotation *monitor_rotation, vec2i *monitor_position);
+
+int get_connector_type_by_name(const char *name);
+int get_connector_type_id_by_name(const char *name);
+uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
+
+bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
+
+bool try_card_has_valid_plane(const char *card_path);
+/* |output| should be at least 128 bytes in size */
+bool gsr_get_valid_card_path(gsr_egl *egl, char *output, bool is_monitor_capture);
+/* |render_path| should be at least 128 bytes in size */
+bool gsr_card_path_get_render_path(const char *card_path, char *render_path);
+
+int create_directory_recursive(char *path);
+
+/* |img_attr| needs to be at least 44 in size */
+void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, uint32_t height, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, int num_planes, bool use_modifier);
+
+vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);
+vec2i gsr_capture_get_target_position(vec2i output_size, gsr_capture_metadata *capture_metadata);
+
+unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter);
+
+bool get_nvidia_driver_version(int *major, int *minor);
+
+#endif /* GSR_UTILS_H */
diff --git a/include/vec2.h b/include/vec2.h
new file mode 100644
index 0000000..8fd3858
--- /dev/null
+++ b/include/vec2.h
@@ -0,0 +1,16 @@
+#ifndef VEC2_H
+#define VEC2_H
+
+typedef struct {
+ int x, y;
+} vec2i;
+
+typedef struct {
+ float x, y;
+} vec2f;
+
+typedef struct {
+ double x, y;
+} vec2d;
+
+#endif /* VEC2_H */
diff --git a/include/window/wayland.h b/include/window/wayland.h
new file mode 100644
index 0000000..3535b0f
--- /dev/null
+++ b/include/window/wayland.h
@@ -0,0 +1,8 @@
+#ifndef GSR_WINDOW_WAYLAND_H
+#define GSR_WINDOW_WAYLAND_H
+
+#include "window.h"
+
+gsr_window* gsr_window_wayland_create(void);
+
+#endif /* GSR_WINDOW_WAYLAND_H */
diff --git a/include/window/window.h b/include/window/window.h
new file mode 100644
index 0000000..7839f6a
--- /dev/null
+++ b/include/window/window.h
@@ -0,0 +1,37 @@
+#ifndef GSR_WINDOW_H
+#define GSR_WINDOW_H
+
+#include "../utils.h"
+#include <stdbool.h>
+
+typedef union _XEvent XEvent;
+typedef struct gsr_window gsr_window;
+
+typedef enum {
+ GSR_DISPLAY_SERVER_X11,
+ GSR_DISPLAY_SERVER_WAYLAND
+} gsr_display_server;
+
+struct gsr_window {
+ void (*destroy)(gsr_window *self);
+ /* Returns true if an event is available */
+ bool (*process_event)(gsr_window *self);
+ XEvent* (*get_event_data)(gsr_window *self); /* can be NULL */
+ gsr_display_server (*get_display_server)(void);
+ void* (*get_display)(gsr_window *self);
+ void* (*get_window)(gsr_window *self);
+ void (*for_each_active_monitor_output_cached)(const gsr_window *self, active_monitor_callback callback, void *userdata);
+ void *priv;
+};
+
+void gsr_window_destroy(gsr_window *self);
+
+/* Returns true if an event is available */
+bool gsr_window_process_event(gsr_window *self);
+XEvent* gsr_window_get_event_data(gsr_window *self);
+gsr_display_server gsr_window_get_display_server(const gsr_window *self);
+void* gsr_window_get_display(gsr_window *self);
+void* gsr_window_get_window(gsr_window *self);
+void gsr_window_for_each_active_monitor_output_cached(const gsr_window *self, active_monitor_callback callback, void *userdata);
+
+#endif /* GSR_WINDOW_H */
diff --git a/include/window/x11.h b/include/window/x11.h
new file mode 100644
index 0000000..e0c2948
--- /dev/null
+++ b/include/window/x11.h
@@ -0,0 +1,10 @@
+#ifndef GSR_WINDOW_X11_H
+#define GSR_WINDOW_X11_H
+
+#include "window.h"
+
+typedef struct _XDisplay Display;
+
+gsr_window* gsr_window_x11_create(Display *display);
+
+#endif /* GSR_WINDOW_X11_H */
diff --git a/include/window_texture.h b/include/window_texture.h
new file mode 100644
index 0000000..bdada33
--- /dev/null
+++ b/include/window_texture.h
@@ -0,0 +1,30 @@
+#ifndef WINDOW_TEXTURE_H
+#define WINDOW_TEXTURE_H
+
+#include "egl.h"
+
+typedef struct {
+ Display *display;
+ Window window;
+ Pixmap pixmap;
+ EGLImage image;
+ unsigned int texture_id;
+ int redirected;
+ gsr_egl *egl;
+ unsigned int window_width;
+ unsigned int window_height;
+} WindowTexture;
+
+/* Returns 0 on success */
+int window_texture_init(WindowTexture *window_texture, Display *display, Window window, gsr_egl *egl);
+void window_texture_deinit(WindowTexture *self);
+
+/*
+ This should ONLY be called when the target window is resized.
+ Returns 0 on success.
+*/
+int window_texture_on_resize(WindowTexture *self);
+
+unsigned int window_texture_get_opengl_texture_id(WindowTexture *self);
+
+#endif /* WINDOW_TEXTURE_H */
diff --git a/include/xnvctrl.h b/include/xnvctrl.h
new file mode 100644
index 0000000..33fc442
--- /dev/null
+++ b/include/xnvctrl.h
@@ -0,0 +1,45 @@
+#ifndef GSR_XNVCTRL_H
+#define GSR_XNVCTRL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define NV_CTRL_GPU_NVCLOCK_OFFSET 409
+#define NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET 410
+#define NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS 424
+#define NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS 425
+
+#define NV_CTRL_TARGET_TYPE_GPU 1
+
+#define NV_CTRL_STRING_PERFORMANCE_MODES 29
+
+typedef struct _XDisplay Display;
+
+typedef struct {
+ int type;
+ union {
+ struct {
+ int64_t min;
+ int64_t max;
+ } range;
+ struct {
+ unsigned int ints;
+ } bits;
+ } u;
+ unsigned int permissions;
+} NVCTRLAttributeValidValuesRec;
+
+typedef struct {
+ Display *display;
+ void *library;
+
+ int (*XNVCTRLQueryExtension)(Display *dpy, int *event_basep, int *error_basep);
+ int (*XNVCTRLSetTargetAttributeAndGetStatus)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, int value);
+ int (*XNVCTRLQueryValidTargetAttributeValues)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, NVCTRLAttributeValidValuesRec *values);
+ int (*XNVCTRLQueryTargetStringAttribute)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, char **ptr);
+} gsr_xnvctrl;
+
+bool gsr_xnvctrl_load(gsr_xnvctrl *self, Display *display);
+void gsr_xnvctrl_unload(gsr_xnvctrl *self);
+
+#endif /* GSR_XNVCTRL_H */