summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Damhet2020-08-09 13:22:33 +0200
committerAntoine Damhet2020-08-09 13:22:33 +0200
commitfd61839bc1525cf1ba9ef99d701db342db38f6d7 (patch)
tree33197189d0967f8b0005fe94ce483d4ca065da0d
downloadaur-fd61839bc1525cf1ba9ef99d701db342db38f6d7.tar.gz
Initial commit
Signed-off-by: Antoine Damhet <antoine.damhet@lse.epita.fr>
-rw-r--r--.SRCINFO71
-rw-r--r--0001-deps-glad-Add-EGL.patch545
-rw-r--r--0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch35
-rw-r--r--0003-libobs-opengl-Factor-out-GLX-winsys.patch403
-rw-r--r--0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch745
-rw-r--r--0005-deps-glad-Make-X11-required-as-well.patch26
-rw-r--r--0006-ci-Install-qtbase5-private-dev-on-Linux.patch24
-rw-r--r--0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch2683
-rw-r--r--0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch258
-rw-r--r--0009-UI-Set-the-Unix-platform-on-startup.patch67
-rw-r--r--0010-linux-capture-Fail-to-load-when-running-on-EGL.patch38
-rw-r--r--0011-libobs-Add-a-Wayland-platform.patch422
-rw-r--r--0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch50
-rw-r--r--0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch474
-rw-r--r--0014-UI-Retrieve-Wayland-surface-from-QWindow.patch107
-rw-r--r--PKGBUILD84
16 files changed, 6032 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..fa0df7ec6eeb
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,71 @@
+pkgbase = obs-studio-wayland
+ pkgdesc = Free, open source software for live streaming and recording (with wayland patches)
+ pkgver = 25.0.8
+ pkgrel = 1
+ url = https://obsproject.com
+ arch = x86_64
+ license = GPL2
+ makedepends = cmake
+ makedepends = libfdk-aac
+ makedepends = libxcomposite
+ makedepends = x264
+ makedepends = vlc
+ makedepends = swig
+ makedepends = python
+ makedepends = luajit
+ depends = ffmpeg
+ depends = jansson
+ depends = libxinerama
+ depends = libxkbcommon-x11
+ depends = mbedtls
+ depends = qt5-svg
+ depends = qt5-x11extras
+ depends = curl
+ depends = jack
+ depends = gtk-update-icon-cache
+ optdepends = libfdk-aac: FDK AAC codec support
+ optdepends = libxcomposite: XComposite capture support
+ optdepends = libva-intel-driver: hardware encoding
+ optdepends = libva-mesa-driver: hardware encoding
+ optdepends = luajit: scripting support
+ optdepends = python: scripting support
+ optdepends = vlc: VLC Media Source support
+ optdepends = obs-xdg-portal-git: screen capture with xdg-desktop-portal interface
+ optdepends = xdg-desktop-portal-wlr: screen capture on wlroots compositors
+ provides = obs-studio
+ conflicts = obs-studio
+ source = obs-studio-25.0.8.tar.gz::https://github.com/jp9000/obs-studio/archive/25.0.8.tar.gz
+ source = https://github.com/obsproject/obs-studio/commit/8a1429e29ebd6bf31ad6ae63c6992e2c03893767.patch
+ source = 0001-deps-glad-Add-EGL.patch
+ source = 0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch
+ source = 0003-libobs-opengl-Factor-out-GLX-winsys.patch
+ source = 0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch
+ source = 0005-deps-glad-Make-X11-required-as-well.patch
+ source = 0006-ci-Install-qtbase5-private-dev-on-Linux.patch
+ source = 0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch
+ source = 0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch
+ source = 0009-UI-Set-the-Unix-platform-on-startup.patch
+ source = 0010-linux-capture-Fail-to-load-when-running-on-EGL.patch
+ source = 0011-libobs-Add-a-Wayland-platform.patch
+ source = 0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch
+ source = 0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch
+ source = 0014-UI-Retrieve-Wayland-surface-from-QWindow.patch
+ sha512sums = a97c03dc218a4e03e48f6a7dc82b4a59ebeee2039f17be66bb847681ce9ff3d25e6e015be4af78fe44739f6fad5089b6e683d7657c2e4fde8e547df9a2594a08
+ sha512sums = 1ff0e088eed61554268009f3d8c5a23c0888bfbe860d6cb288ddf348108446c152fd87e2cb8f54613a88378d8474550632c90f924005d5e0343bf1a801339ccc
+ sha512sums = bfe2b0e6da69ffdca95229eb4015515148fdda909355add1d2dec71cf97e9fdabdfc832c74f455a890846708f28d5bcbec64589e853904d539a438b2dcbd7a18
+ sha512sums = 5221b6a7a46f99c58cde1c5406f83d50def2d5b3a2e97be7db759d94d74a5be46da092209e6a4122a6de4b704632c3f013535f80b570349b029ea4124151c4f6
+ sha512sums = c9a0660c95bd18a02620fb0b870032563669544e7a721e4d91dafb8aebb96d1735414a9e37ed56355fc5afeb8f437a434b4fd5f147c9658cc6974e8e8bab4463
+ sha512sums = 0b404ff252f94bcdd957d43db26c54c6b47de5a8f810f4febdb0aa5b873c48f23ef2817361e5ce9c09a189e770978cfca24767167604434ece771d759e7c4270
+ sha512sums = 47f5bffb469ece2b961000cd2d8656b82cba8ac0fa09fa7703c662e0cee2e48744d5b8aa93a4b4508436ea5edfe3038fa7aa88a3b43466f88c7504e6a8ba51ed
+ sha512sums = d15c21968a3024888ce4c8e884d861f147358e95a42c1de557251a4c2fccbdddf9cf5a285deedbf73cffbd25fdaad44dd972cb10bf9a5b23a0049b239e75961f
+ sha512sums = c1f94ccd836c51ff83735df614bf6d3e2c310c599685e700ae5726ace20434edd04ef0c9be0a8c0f4c458dd164ad1ac817fd32bcbeeefb0107a6ce4cbce9cb08
+ sha512sums = 6ce870404a6d2bfbb25935a6da59a07447307f8592dd1dc1aaebba2b9f959633565ba4cdc7d50ee3c4e5b4c169397298daa5804c3060fc780dba52099f687393
+ sha512sums = 6374229b662949e2989eb372a922fda872d2a08e817690b2262f99dc8a02261a75aeeacfc40da2b68a04228b38cda4aeaca4212068e8605b7532662dc459abb4
+ sha512sums = 16dfa319e9e18ef8e946b9723e27d1ea1f56e4de8656d8112571bc87aa7ade8dbda4293f064c2477cdaf92c60fca4484b2c7ac322835bf402657275933f6ab52
+ sha512sums = c81a421475293d3d5c64a744c10a925dc26975a6dfd46e1b3b2a92931da43c311d0a153548889b4e9831bee61ab8b0f5fc504ad3f0ed7f0628f93287e12ad3d3
+ sha512sums = ea36fee6228d582f5f3b886a3de61ad8b139691c3bf30e24a7b20f1eab2f9e43b0dfbf6f254dcef00e2bfbf6826f223a957d3e78524ebd864c64433529e40441
+ sha512sums = a93f186ed24ee979a4297aa063c435ae541f5f1958b86373f6534a2dd85e2178d6f151f115200c987b5e1d999ebd94d6ce0597ef1e7b3588bcb161c53dd4878e
+ sha512sums = c4e6a23edf080076c27599e02909a068b11094848f733297496e7ea0061df56be4becdb58449ec7a05ff2a659fa4c0f75f4006cb204578477308d24d764fba41
+
+pkgname = obs-studio-wayland
+
diff --git a/0001-deps-glad-Add-EGL.patch b/0001-deps-glad-Add-EGL.patch
new file mode 100644
index 000000000000..2a2c7bd524bf
--- /dev/null
+++ b/0001-deps-glad-Add-EGL.patch
@@ -0,0 +1,545 @@
+From b91db14ec87f4f1a2fba7d6cb89c4e6d07a0472f Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 13:10:16 -0300
+Subject: [PATCH 01/15] deps-glad: Add EGL
+
+The code is generated by https://glad.dav1d.de/
+---
+ cmake/Modules/FindEGL.cmake | 53 +++++
+ deps/glad/CMakeLists.txt | 11 +-
+ deps/glad/include/glad/glad_egl.h | 363 ++++++++++++++++++++++++++++++
+ deps/glad/src/glad_egl.c | 48 ++++
+ 4 files changed, 473 insertions(+), 2 deletions(-)
+ create mode 100644 cmake/Modules/FindEGL.cmake
+ create mode 100644 deps/glad/include/glad/glad_egl.h
+ create mode 100644 deps/glad/src/glad_egl.c
+
+diff --git a/cmake/Modules/FindEGL.cmake b/cmake/Modules/FindEGL.cmake
+new file mode 100644
+index 00000000..ee27cc90
+--- /dev/null
++++ b/cmake/Modules/FindEGL.cmake
+@@ -0,0 +1,53 @@
++# - Try to Find EGL
++# Once done, this will define
++#
++# EGL_FOUND - system has EGL installed.
++# EGL_INCLUDE_DIRS - directories which contain the EGL headers.
++# EGL_LIBRARIES - libraries required to link against EGL.
++# EGL_DEFINITIONS - Compiler switches required for using EGL.
++#
++# Copyright (C) 2012 Intel Corporation. All rights reserved.
++# 2020 Georges Basile Stavracas Neto
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions
++# are met:
++# 1. Redistributions of source code must retain the above copyright
++# notice, this list of conditions and the following disclaimer.
++# 2. Redistributions in binary form must reproduce the above copyright
++# notice, this list of conditions and the following disclaimer in the
++# documentation and/or other materials provided with the distribution.
++#
++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
++# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
++# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
++# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
++# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
++# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
++# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++
++find_package(PkgConfig)
++
++pkg_check_modules(PC_EGL egl)
++
++if (PC_EGL_FOUND)
++ set(EGL_DEFINITIONS ${PC_EGL_CFLAGS_OTHER})
++endif ()
++
++find_path(EGL_INCLUDE_DIRS NAMES EGL/egl.h
++ HINTS ${PC_EGL_INCLUDE_DIR} ${PC_EGL_INCLUDE_DIRS}
++)
++
++find_library(EGL_LIBRARIES NAMES egl EGL
++ HINTS ${PC_EGL_LIBRARY_DIRS}
++)
++
++include(FindPackageHandleStandardArgs)
++FIND_PACKAGE_HANDLE_STANDARD_ARGS(EGL DEFAULT_MSG EGL_INCLUDE_DIRS EGL_LIBRARIES)
++
++mark_as_advanced(EGL_INCLUDE_DIRS EGL_LIBRARIES)
+diff --git a/deps/glad/CMakeLists.txt b/deps/glad/CMakeLists.txt
+index 2c4cf13a..9cb7e8fd 100644
+--- a/deps/glad/CMakeLists.txt
++++ b/deps/glad/CMakeLists.txt
+@@ -4,6 +4,7 @@ find_package(OpenGL)
+
+ if(NOT WIN32 AND NOT APPLE)
+ find_package(X11)
++ find_package(EGL REQUIRED)
+ endif()
+
+ set(glad_SOURCES
+@@ -19,7 +20,9 @@ if(WIN32)
+ obsglad.rc)
+ elseif(NOT APPLE)
+ set(glad_PLATFORM_SOURCES
++ src/glad_egl.c
+ src/glad_glx.c
++ include/glad/glad_egl.h
+ include/glad/glad_glx.h)
+ endif()
+
+@@ -28,7 +31,9 @@ set(glad_include_dirs
+
+ if (UNIX AND NOT APPLE)
+ list (APPEND glad_include_dirs
+- PRIVATE ${X11_X11_INCLUDE_PATH})
++ PRIVATE
++ ${X11_X11_INCLUDE_PATH}
++ ${EGL_INCLUDE_DIRS})
+ endif()
+
+ add_library(glad SHARED
+@@ -54,7 +59,9 @@ endif()
+
+ if(NOT WIN32 AND NOT APPLE)
+ set(glad_PLATFORM_DEPS
+- ${X11_X11_LIB})
++ ${X11_X11_LIB}
++ ${EGL_LIBRARIES})
++
+ # only link to libdl on linux
+ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ set(glad_PLATFORM_DEPS
+diff --git a/deps/glad/include/glad/glad_egl.h b/deps/glad/include/glad/glad_egl.h
+new file mode 100644
+index 00000000..411729f0
+--- /dev/null
++++ b/deps/glad/include/glad/glad_egl.h
+@@ -0,0 +1,363 @@
++/*
++
++ EGL loader generated by glad 0.1.33 on Mon Mar 9 17:01:26 2020.
++
++ Language/Generator: C/C++
++ Specification: egl
++ APIs: egl=1.5
++ Profile: -
++ Extensions:
++ EGL_EXT_platform_wayland,
++ EGL_EXT_platform_x11,
++ EGL_KHR_platform_wayland,
++ EGL_KHR_platform_x11
++ Loader: True
++ Local files: False
++ Omit khrplatform: False
++ Reproducible: False
++
++ Commandline:
++ --api="egl=1.5" --generator="c" --spec="egl" --extensions="EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_KHR_platform_wayland,EGL_KHR_platform_x11"
++ Online:
++ https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.5&extensions=EGL_EXT_platform_wayland&extensions=EGL_EXT_platform_x11&extensions=EGL_KHR_platform_wayland&extensions=EGL_KHR_platform_x11
++*/
++
++
++#ifndef __glad_egl_h_
++
++#ifdef __egl_h_
++#error EGL header already included, remove this include, glad already provides it
++#endif
++
++#define __glad_egl_h_
++#define __egl_h_
++
++#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
++#define APIENTRY __stdcall
++#endif
++
++#ifndef APIENTRY
++#define APIENTRY
++#endif
++#ifndef APIENTRYP
++#define APIENTRYP APIENTRY *
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++typedef void* (* GLADloadproc)(const char *name);
++
++#define GLAD_GLAPI_EXPORT
++
++#ifndef GLAPI
++# if defined(GLAD_GLAPI_EXPORT)
++# if defined(WIN32) || defined(__CYGWIN__)
++# if defined(GLAD_GLAPI_EXPORT_BUILD)
++# if defined(__GNUC__)
++# define GLAPI __attribute__ ((dllexport)) extern
++# else
++# define GLAPI __declspec(dllexport) extern
++# endif
++# else
++# if defined(__GNUC__)
++# define GLAPI __attribute__ ((dllimport)) extern
++# else
++# define GLAPI __declspec(dllimport) extern
++# endif
++# endif
++# elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
++# define GLAPI __attribute__ ((visibility ("default"))) extern
++# else
++# define GLAPI extern
++# endif
++# else
++# define GLAPI extern
++# endif
++#endif
++
++GLAPI int gladLoadEGL(void);
++GLAPI int gladLoadEGLLoader(GLADloadproc);
++
++#include <KHR/khrplatform.h>
++#include <EGL/eglplatform.h>
++struct AHardwareBuffer;
++struct wl_buffer;
++struct wl_display;
++struct wl_resource;
++typedef unsigned int EGLBoolean;
++typedef unsigned int EGLenum;
++typedef intptr_t EGLAttribKHR;
++typedef intptr_t EGLAttrib;
++typedef void *EGLClientBuffer;
++typedef void *EGLConfig;
++typedef void *EGLContext;
++typedef void *EGLDeviceEXT;
++typedef void *EGLDisplay;
++typedef void *EGLImage;
++typedef void *EGLImageKHR;
++typedef void *EGLLabelKHR;
++typedef void *EGLObjectKHR;
++typedef void *EGLOutputLayerEXT;
++typedef void *EGLOutputPortEXT;
++typedef void *EGLStreamKHR;
++typedef void *EGLSurface;
++typedef void *EGLSync;
++typedef void *EGLSyncKHR;
++typedef void *EGLSyncNV;
++typedef void (*__eglMustCastToProperFunctionPointerType)(void);
++typedef khronos_utime_nanoseconds_t EGLTimeKHR;
++typedef khronos_utime_nanoseconds_t EGLTime;
++typedef khronos_utime_nanoseconds_t EGLTimeNV;
++typedef khronos_utime_nanoseconds_t EGLuint64NV;
++typedef khronos_uint64_t EGLuint64KHR;
++typedef khronos_stime_nanoseconds_t EGLnsecsANDROID;
++typedef int EGLNativeFileDescriptorKHR;
++typedef khronos_ssize_t EGLsizeiANDROID;
++typedef void (*EGLSetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize);
++typedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize);
++struct EGLClientPixmapHI {
++ void *pData;
++ EGLint iWidth;
++ EGLint iHeight;
++ EGLint iStride;
++};
++typedef void (APIENTRY *EGLDEBUGPROCKHR)(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message);
++#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC
++#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC
++#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC
++#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
++#define EGL_ALPHA_SIZE 0x3021
++#define EGL_BAD_ACCESS 0x3002
++#define EGL_BAD_ALLOC 0x3003
++#define EGL_BAD_ATTRIBUTE 0x3004
++#define EGL_BAD_CONFIG 0x3005
++#define EGL_BAD_CONTEXT 0x3006
++#define EGL_BAD_CURRENT_SURFACE 0x3007
++#define EGL_BAD_DISPLAY 0x3008
++#define EGL_BAD_MATCH 0x3009
++#define EGL_BAD_NATIVE_PIXMAP 0x300A
++#define EGL_BAD_NATIVE_WINDOW 0x300B
++#define EGL_BAD_PARAMETER 0x300C
++#define EGL_BAD_SURFACE 0x300D
++#define EGL_BLUE_SIZE 0x3022
++#define EGL_BUFFER_SIZE 0x3020
++#define EGL_CONFIG_CAVEAT 0x3027
++#define EGL_CONFIG_ID 0x3028
++#define EGL_CORE_NATIVE_ENGINE 0x305B
++#define EGL_DEPTH_SIZE 0x3025
++#define EGL_DONT_CARE EGL_CAST(EGLint,-1)
++#define EGL_DRAW 0x3059
++#define EGL_EXTENSIONS 0x3055
++#define EGL_FALSE 0
++#define EGL_GREEN_SIZE 0x3023
++#define EGL_HEIGHT 0x3056
++#define EGL_LARGEST_PBUFFER 0x3058
++#define EGL_LEVEL 0x3029
++#define EGL_MAX_PBUFFER_HEIGHT 0x302A
++#define EGL_MAX_PBUFFER_PIXELS 0x302B
++#define EGL_MAX_PBUFFER_WIDTH 0x302C
++#define EGL_NATIVE_RENDERABLE 0x302D
++#define EGL_NATIVE_VISUAL_ID 0x302E
++#define EGL_NATIVE_VISUAL_TYPE 0x302F
++#define EGL_NONE 0x3038
++#define EGL_NON_CONFORMANT_CONFIG 0x3051
++#define EGL_NOT_INITIALIZED 0x3001
++#define EGL_NO_CONTEXT EGL_CAST(EGLContext,0)
++#define EGL_NO_DISPLAY EGL_CAST(EGLDisplay,0)
++#define EGL_NO_SURFACE EGL_CAST(EGLSurface,0)
++#define EGL_PBUFFER_BIT 0x0001
++#define EGL_PIXMAP_BIT 0x0002
++#define EGL_READ 0x305A
++#define EGL_RED_SIZE 0x3024
++#define EGL_SAMPLES 0x3031
++#define EGL_SAMPLE_BUFFERS 0x3032
++#define EGL_SLOW_CONFIG 0x3050
++#define EGL_STENCIL_SIZE 0x3026
++#define EGL_SUCCESS 0x3000
++#define EGL_SURFACE_TYPE 0x3033
++#define EGL_TRANSPARENT_BLUE_VALUE 0x3035
++#define EGL_TRANSPARENT_GREEN_VALUE 0x3036
++#define EGL_TRANSPARENT_RED_VALUE 0x3037
++#define EGL_TRANSPARENT_RGB 0x3052
++#define EGL_TRANSPARENT_TYPE 0x3034
++#define EGL_TRUE 1
++#define EGL_VENDOR 0x3053
++#define EGL_VERSION 0x3054
++#define EGL_WIDTH 0x3057
++#define EGL_WINDOW_BIT 0x0004
++#define EGL_BACK_BUFFER 0x3084
++#define EGL_BIND_TO_TEXTURE_RGB 0x3039
++#define EGL_BIND_TO_TEXTURE_RGBA 0x303A
++#define EGL_CONTEXT_LOST 0x300E
++#define EGL_MIN_SWAP_INTERVAL 0x303B
++#define EGL_MAX_SWAP_INTERVAL 0x303C
++#define EGL_MIPMAP_TEXTURE 0x3082
++#define EGL_MIPMAP_LEVEL 0x3083
++#define EGL_NO_TEXTURE 0x305C
++#define EGL_TEXTURE_2D 0x305F
++#define EGL_TEXTURE_FORMAT 0x3080
++#define EGL_TEXTURE_RGB 0x305D
++#define EGL_TEXTURE_RGBA 0x305E
++#define EGL_TEXTURE_TARGET 0x3081
++#define EGL_ALPHA_FORMAT 0x3088
++#define EGL_ALPHA_FORMAT_NONPRE 0x308B
++#define EGL_ALPHA_FORMAT_PRE 0x308C
++#define EGL_ALPHA_MASK_SIZE 0x303E
++#define EGL_BUFFER_PRESERVED 0x3094
++#define EGL_BUFFER_DESTROYED 0x3095
++#define EGL_CLIENT_APIS 0x308D
++#define EGL_COLORSPACE 0x3087
++#define EGL_COLORSPACE_sRGB 0x3089
++#define EGL_COLORSPACE_LINEAR 0x308A
++#define EGL_COLOR_BUFFER_TYPE 0x303F
++#define EGL_CONTEXT_CLIENT_TYPE 0x3097
++#define EGL_DISPLAY_SCALING 10000
++#define EGL_HORIZONTAL_RESOLUTION 0x3090
++#define EGL_LUMINANCE_BUFFER 0x308F
++#define EGL_LUMINANCE_SIZE 0x303D
++#define EGL_OPENGL_ES_BIT 0x0001
++#define EGL_OPENVG_BIT 0x0002
++#define EGL_OPENGL_ES_API 0x30A0
++#define EGL_OPENVG_API 0x30A1
++#define EGL_OPENVG_IMAGE 0x3096
++#define EGL_PIXEL_ASPECT_RATIO 0x3092
++#define EGL_RENDERABLE_TYPE 0x3040
++#define EGL_RENDER_BUFFER 0x3086
++#define EGL_RGB_BUFFER 0x308E
++#define EGL_SINGLE_BUFFER 0x3085
++#define EGL_SWAP_BEHAVIOR 0x3093
++#define EGL_UNKNOWN EGL_CAST(EGLint,-1)
++#define EGL_VERTICAL_RESOLUTION 0x3091
++#define EGL_CONFORMANT 0x3042
++#define EGL_CONTEXT_CLIENT_VERSION 0x3098
++#define EGL_MATCH_NATIVE_PIXMAP 0x3041
++#define EGL_OPENGL_ES2_BIT 0x0004
++#define EGL_VG_ALPHA_FORMAT 0x3088
++#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B
++#define EGL_VG_ALPHA_FORMAT_PRE 0x308C
++#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040
++#define EGL_VG_COLORSPACE 0x3087
++#define EGL_VG_COLORSPACE_sRGB 0x3089
++#define EGL_VG_COLORSPACE_LINEAR 0x308A
++#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020
++#define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0)
++#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200
++#define EGL_MULTISAMPLE_RESOLVE 0x3099
++#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A
++#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B
++#define EGL_OPENGL_API 0x30A2
++#define EGL_OPENGL_BIT 0x0008
++#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
++#define EGL_CONTEXT_MAJOR_VERSION 0x3098
++#define EGL_CONTEXT_MINOR_VERSION 0x30FB
++#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
++#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
++#define EGL_NO_RESET_NOTIFICATION 0x31BE
++#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF
++#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
++#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
++#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0
++#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
++#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2
++#define EGL_OPENGL_ES3_BIT 0x00000040
++#define EGL_CL_EVENT_HANDLE 0x309C
++#define EGL_SYNC_CL_EVENT 0x30FE
++#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF
++#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0
++#define EGL_SYNC_TYPE 0x30F7
++#define EGL_SYNC_STATUS 0x30F1
++#define EGL_SYNC_CONDITION 0x30F8
++#define EGL_SIGNALED 0x30F2
++#define EGL_UNSIGNALED 0x30F3
++#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
++#define EGL_FOREVER 0xFFFFFFFFFFFFFFFF
++#define EGL_TIMEOUT_EXPIRED 0x30F5
++#define EGL_CONDITION_SATISFIED 0x30F6
++#define EGL_NO_SYNC EGL_CAST(EGLSync,0)
++#define EGL_SYNC_FENCE 0x30F9
++#define EGL_GL_COLORSPACE 0x309D
++#define EGL_GL_COLORSPACE_SRGB 0x3089
++#define EGL_GL_COLORSPACE_LINEAR 0x308A
++#define EGL_GL_RENDERBUFFER 0x30B9
++#define EGL_GL_TEXTURE_2D 0x30B1
++#define EGL_GL_TEXTURE_LEVEL 0x30BC
++#define EGL_GL_TEXTURE_3D 0x30B2
++#define EGL_GL_TEXTURE_ZOFFSET 0x30BD
++#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
++#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
++#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
++#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
++#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
++#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
++#define EGL_IMAGE_PRESERVED 0x30D2
++#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
++EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
++EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
++EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
++EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
++EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
++EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
++EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx);
++EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface);
++EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
++EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
++EGLDisplay eglGetCurrentDisplay(void);
++EGLSurface eglGetCurrentSurface(EGLint readdraw);
++EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
++EGLint eglGetError(void);
++__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname);
++EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
++EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
++EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
++const char *eglQueryString(EGLDisplay dpy, EGLint name);
++EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
++EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);
++EGLBoolean eglTerminate(EGLDisplay dpy);
++EGLBoolean eglWaitGL(void);
++EGLBoolean eglWaitNative(EGLint engine);
++EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
++EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
++EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
++EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval);
++EGLBoolean eglBindAPI(EGLenum api);
++EGLenum eglQueryAPI(void);
++EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
++EGLBoolean eglReleaseThread(void);
++EGLBoolean eglWaitClient(void);
++EGLContext eglGetCurrentContext(void);
++EGLSync eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
++EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSync sync);
++EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
++EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
++EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
++EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImage image);
++EGLDisplay eglGetPlatformDisplay(EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
++EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
++EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
++EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags);
++#define EGL_PLATFORM_WAYLAND_EXT 0x31D8
++#define EGL_PLATFORM_X11_EXT 0x31D5
++#define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6
++#define EGL_PLATFORM_WAYLAND_KHR 0x31D8
++#define EGL_PLATFORM_X11_KHR 0x31D5
++#define EGL_PLATFORM_X11_SCREEN_KHR 0x31D6
++#ifndef EGL_EXT_platform_wayland
++#define EGL_EXT_platform_wayland 1
++#endif
++#ifndef EGL_EXT_platform_x11
++#define EGL_EXT_platform_x11 1
++#endif
++#ifndef EGL_KHR_platform_wayland
++#define EGL_KHR_platform_wayland 1
++#endif
++#ifndef EGL_KHR_platform_x11
++#define EGL_KHR_platform_x11 1
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/deps/glad/src/glad_egl.c b/deps/glad/src/glad_egl.c
+new file mode 100644
+index 00000000..e3cd1fc2
+--- /dev/null
++++ b/deps/glad/src/glad_egl.c
+@@ -0,0 +1,48 @@
++/*
++
++ EGL loader generated by glad 0.1.33 on Mon Mar 9 17:01:26 2020.
++
++ Language/Generator: C/C++
++ Specification: egl
++ APIs: egl=1.5
++ Profile: -
++ Extensions:
++ EGL_EXT_platform_wayland,
++ EGL_EXT_platform_x11,
++ EGL_KHR_platform_wayland,
++ EGL_KHR_platform_x11
++ Loader: True
++ Local files: False
++ Omit khrplatform: False
++ Reproducible: False
++
++ Commandline:
++ --api="egl=1.5" --generator="c" --spec="egl" --extensions="EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_KHR_platform_wayland,EGL_KHR_platform_x11"
++ Online:
++ https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.5&extensions=EGL_EXT_platform_wayland&extensions=EGL_EXT_platform_x11&extensions=EGL_KHR_platform_wayland&extensions=EGL_KHR_platform_x11
++*/
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <glad/glad_egl.h>
++
++int gladLoadEGL(void) {
++ return gladLoadEGLLoader((GLADloadproc)eglGetProcAddress);
++}
++
++static int find_extensionsEGL(void) {
++ return 1;
++}
++
++static void find_coreEGL(void) {
++}
++
++int gladLoadEGLLoader(GLADloadproc load) {
++ (void) load;
++ find_coreEGL();
++
++ if (!find_extensionsEGL()) return 0;
++ return 1;
++}
++
+--
+2.28.0
+
diff --git a/0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch b/0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch
new file mode 100644
index 000000000000..f73cc33566fc
--- /dev/null
+++ b/0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch
@@ -0,0 +1,35 @@
+From 49e7a26b3aa5523662e68401554a1474fa8baf86 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 13:19:31 -0300
+Subject: [PATCH 02/15] libobs-opengl: Rename gl-x11.c to gl-x11-glx.c
+
+This is in preparation for the future abstraction layer (gl-x11-*)
+and also to match the actual name of the windowing system. When
+running under X11, we can glue OpenGL through GLX or EGL, so the
+new file name matches that now.
+---
+ libobs-opengl/CMakeLists.txt | 2 +-
+ libobs-opengl/{gl-x11.c => gl-x11-glx.c} | 0
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+ rename libobs-opengl/{gl-x11.c => gl-x11-glx.c} (100%)
+
+diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
+index 1d53d518..776f1330 100644
+--- a/libobs-opengl/CMakeLists.txt
++++ b/libobs-opengl/CMakeLists.txt
+@@ -49,7 +49,7 @@ else() #This needs to change to be more specific to get ready for Wayland
+ ${X11_XCB_LIBRARIES})
+
+ set(libobs-opengl_PLATFORM_SOURCES
+- gl-x11.c)
++ gl-x11-glx.c)
+ endif()
+
+ set(libobs-opengl_SOURCES
+diff --git a/libobs-opengl/gl-x11.c b/libobs-opengl/gl-x11-glx.c
+similarity index 100%
+rename from libobs-opengl/gl-x11.c
+rename to libobs-opengl/gl-x11-glx.c
+--
+2.28.0
+
diff --git a/0003-libobs-opengl-Factor-out-GLX-winsys.patch b/0003-libobs-opengl-Factor-out-GLX-winsys.patch
new file mode 100644
index 000000000000..ae99c34a6c16
--- /dev/null
+++ b/0003-libobs-opengl-Factor-out-GLX-winsys.patch
@@ -0,0 +1,403 @@
+From dc135a39d088ead4c603f95b1483c7076da4cea4 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 16:46:37 -0300
+Subject: [PATCH 03/15] libobs-opengl: Factor out GLX winsys
+
+Move the GLX-related code to gl-x11-glx, and introduce gl-nix as
+a winsys-agnostic abstraction layer. gl-nix serves as the runtime
+selector of which winsys going to be used. Only the X11/GLX winsys
+is available now, but later commits will introduce the X11/EGL
+winsys as well.
+
+The gl-nix code was originally written by Jason Francis <cycl0ps@tuta.io>
+---
+ libobs-opengl/CMakeLists.txt | 1 +
+ libobs-opengl/gl-nix.c | 107 +++++++++++++++++++++++++++++++++++
+ libobs-opengl/gl-nix.h | 53 +++++++++++++++++
+ libobs-opengl/gl-x11-glx.c | 59 +++++++++++++------
+ libobs-opengl/gl-x11-glx.h | 22 +++++++
+ 5 files changed, 224 insertions(+), 18 deletions(-)
+ create mode 100644 libobs-opengl/gl-nix.c
+ create mode 100644 libobs-opengl/gl-nix.h
+ create mode 100644 libobs-opengl/gl-x11-glx.h
+
+diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
+index 776f1330..50afb923 100644
+--- a/libobs-opengl/CMakeLists.txt
++++ b/libobs-opengl/CMakeLists.txt
+@@ -49,6 +49,7 @@ else() #This needs to change to be more specific to get ready for Wayland
+ ${X11_XCB_LIBRARIES})
+
+ set(libobs-opengl_PLATFORM_SOURCES
++ gl-nix.c
+ gl-x11-glx.c)
+ endif()
+
+diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
+new file mode 100644
+index 00000000..574d4f77
+--- /dev/null
++++ b/libobs-opengl/gl-nix.c
+@@ -0,0 +1,107 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "gl-nix.h"
++#include "gl-x11-glx.h"
++
++static const struct gl_winsys_vtable *gl_vtable = NULL;
++
++static void init_winsys(void)
++{
++ assert(gl_vtable == NULL);
++
++ gl_vtable = gl_x11_glx_get_winsys_vtable();
++
++ assert(gl_vtable != NULL);
++}
++
++extern struct gl_windowinfo *
++gl_windowinfo_create(const struct gs_init_data *info)
++{
++ return gl_vtable->windowinfo_create(info);
++}
++
++extern void gl_windowinfo_destroy(struct gl_windowinfo *info)
++{
++ gl_vtable->windowinfo_destroy(info);
++}
++
++extern struct gl_platform *gl_platform_create(gs_device_t *device,
++ uint32_t adapter)
++{
++ init_winsys();
++
++ return gl_vtable->platform_create(device, adapter);
++}
++
++extern void gl_platform_destroy(struct gl_platform *plat)
++{
++ gl_vtable->platform_destroy(plat);
++
++ gl_vtable = NULL;
++}
++
++extern bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
++{
++ return gl_vtable->platform_init_swapchain(swap);
++}
++
++extern void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
++{
++ gl_vtable->platform_cleanup_swapchain(swap);
++}
++
++extern void device_enter_context(gs_device_t *device)
++{
++ gl_vtable->device_enter_context(device);
++}
++
++extern void device_leave_context(gs_device_t *device)
++{
++ gl_vtable->device_leave_context(device);
++}
++
++extern void *device_get_device_obj(gs_device_t *device)
++{
++ return gl_vtable->device_get_device_obj(device);
++}
++
++extern void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width,
++ uint32_t *height)
++{
++ gl_vtable->getclientsize(swap, width, height);
++}
++
++extern void gl_clear_context(gs_device_t *device)
++{
++ gl_vtable->clear_context(device);
++}
++
++extern void gl_update(gs_device_t *device)
++{
++ gl_vtable->update(device);
++}
++
++extern void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap)
++{
++ gl_vtable->device_load_swapchain(device, swap);
++}
++
++extern void device_present(gs_device_t *device)
++{
++ gl_vtable->device_present(device);
++}
+diff --git a/libobs-opengl/gl-nix.h b/libobs-opengl/gl-nix.h
+new file mode 100644
+index 00000000..209cc308
+--- /dev/null
++++ b/libobs-opengl/gl-nix.h
+@@ -0,0 +1,53 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "gl-subsystem.h"
++
++struct gl_winsys_vtable {
++ struct gl_windowinfo *(*windowinfo_create)(
++ const struct gs_init_data *info);
++ void (*windowinfo_destroy)(struct gl_windowinfo *info);
++
++ struct gl_platform *(*platform_create)(gs_device_t *device,
++ uint32_t adapter);
++
++ void (*platform_destroy)(struct gl_platform *plat);
++
++ bool (*platform_init_swapchain)(struct gs_swap_chain *swap);
++
++ void (*platform_cleanup_swapchain)(struct gs_swap_chain *swap);
++
++ void (*device_enter_context)(gs_device_t *device);
++
++ void (*device_leave_context)(gs_device_t *device);
++
++ void *(*device_get_device_obj)(gs_device_t *device);
++
++ void (*getclientsize)(const struct gs_swap_chain *swap, uint32_t *width,
++ uint32_t *height);
++
++ void (*clear_context)(gs_device_t *device);
++
++ void (*update)(gs_device_t *device);
++
++ void (*device_load_swapchain)(gs_device_t *device,
++ gs_swapchain_t *swap);
++
++ void (*device_present)(gs_device_t *device);
++};
+diff --git a/libobs-opengl/gl-x11-glx.c b/libobs-opengl/gl-x11-glx.c
+index d04115b0..a562b564 100644
+--- a/libobs-opengl/gl-x11-glx.c
++++ b/libobs-opengl/gl-x11-glx.c
+@@ -36,7 +36,7 @@
+
+ #include <stdio.h>
+
+-#include "gl-subsystem.h"
++#include "gl-nix.h"
+
+ #include <glad/glad_glx.h>
+
+@@ -221,14 +221,14 @@ static void gl_context_destroy(struct gl_platform *plat)
+ bfree(plat);
+ }
+
+-extern struct gl_windowinfo *
+-gl_windowinfo_create(const struct gs_init_data *info)
++static struct gl_windowinfo *
++gl_x11_glx_windowinfo_create(const struct gs_init_data *info)
+ {
+ UNUSED_PARAMETER(info);
+ return bmalloc(sizeof(struct gl_windowinfo));
+ }
+
+-extern void gl_windowinfo_destroy(struct gl_windowinfo *info)
++static void gl_x11_glx_windowinfo_destroy(struct gl_windowinfo *info)
+ {
+ bfree(info);
+ }
+@@ -294,8 +294,8 @@ static int x_error_handler(Display *display, XErrorEvent *error)
+ return 0;
+ }
+
+-extern struct gl_platform *gl_platform_create(gs_device_t *device,
+- uint32_t adapter)
++static struct gl_platform *gl_x11_glx_platform_create(gs_device_t *device,
++ uint32_t adapter)
+ {
+ /* There's some trickery here... we're mixing libX11, xcb, and GLX
+ For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
+@@ -346,7 +346,7 @@ success:
+ return plat;
+ }
+
+-extern void gl_platform_destroy(struct gl_platform *plat)
++static void gl_x11_glx_platform_destroy(struct gl_platform *plat)
+ {
+ if (!plat) /* In what case would platform be invalid here? */
+ return;
+@@ -354,7 +354,7 @@ extern void gl_platform_destroy(struct gl_platform *plat)
+ gl_context_destroy(plat);
+ }
+
+-extern bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
++static bool gl_x11_glx_platform_init_swapchain(struct gs_swap_chain *swap)
+ {
+ Display *display = swap->device->plat->display;
+ xcb_connection_t *xcb_conn = XGetXCBConnection(display);
+@@ -429,13 +429,13 @@ success:
+ return status;
+ }
+
+-extern void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
++static void gl_x11_glx_platform_cleanup_swapchain(struct gs_swap_chain *swap)
+ {
+ UNUSED_PARAMETER(swap);
+ /* Really nothing to clean up? */
+ }
+
+-extern void device_enter_context(gs_device_t *device)
++static void gl_x11_glx_device_enter_context(gs_device_t *device)
+ {
+ GLXContext context = device->plat->context;
+ Display *display = device->plat->display;
+@@ -453,7 +453,7 @@ extern void device_enter_context(gs_device_t *device)
+ }
+ }
+
+-extern void device_leave_context(gs_device_t *device)
++static void gl_x11_glx_device_leave_context(gs_device_t *device)
+ {
+ Display *display = device->plat->display;
+
+@@ -462,13 +462,13 @@ extern void device_leave_context(gs_device_t *device)
+ }
+ }
+
+-void *device_get_device_obj(gs_device_t *device)
++static void *gl_x11_glx_device_get_device_obj(gs_device_t *device)
+ {
+ return device->plat->context;
+ }
+
+-extern void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width,
+- uint32_t *height)
++static void gl_x11_glx_getclientsize(const struct gs_swap_chain *swap,
++ uint32_t *width, uint32_t *height)
+ {
+ xcb_connection_t *xcb_conn =
+ XGetXCBConnection(swap->device->plat->display);
+@@ -484,7 +484,7 @@ extern void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width,
+ free(geometry);
+ }
+
+-extern void gl_clear_context(gs_device_t *device)
++static void gl_x11_glx_clear_context(gs_device_t *device)
+ {
+ Display *display = device->plat->display;
+
+@@ -493,7 +493,7 @@ extern void gl_clear_context(gs_device_t *device)
+ }
+ }
+
+-extern void gl_update(gs_device_t *device)
++static void gl_x11_glx_update(gs_device_t *device)
+ {
+ Display *display = device->plat->display;
+ xcb_window_t window = device->cur_swap->wi->window;
+@@ -506,7 +506,8 @@ extern void gl_update(gs_device_t *device)
+ values);
+ }
+
+-extern void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap)
++static void gl_x11_glx_device_load_swapchain(gs_device_t *device,
++ gs_swapchain_t *swap)
+ {
+ if (device->cur_swap == swap)
+ return;
+@@ -536,7 +537,7 @@ enum swap_type {
+ SWAP_TYPE_SGI,
+ };
+
+-extern void device_present(gs_device_t *device)
++static void gl_x11_glx_device_present(gs_device_t *device)
+ {
+ static bool initialized = false;
+ static enum swap_type swap_type = SWAP_TYPE_NORMAL;
+@@ -577,3 +578,25 @@ extern void device_present(gs_device_t *device)
+
+ glXSwapBuffers(display, window);
+ }
++
++static const struct gl_winsys_vtable glx_winsys_vtable = {
++ .windowinfo_create = gl_x11_glx_windowinfo_create,
++ .windowinfo_destroy = gl_x11_glx_windowinfo_destroy,
++ .platform_create = gl_x11_glx_platform_create,
++ .platform_destroy = gl_x11_glx_platform_destroy,
++ .platform_init_swapchain = gl_x11_glx_platform_init_swapchain,
++ .platform_cleanup_swapchain = gl_x11_glx_platform_cleanup_swapchain,
++ .device_enter_context = gl_x11_glx_device_enter_context,
++ .device_leave_context = gl_x11_glx_device_leave_context,
++ .device_get_device_obj = gl_x11_glx_device_get_device_obj,
++ .getclientsize = gl_x11_glx_getclientsize,
++ .clear_context = gl_x11_glx_clear_context,
++ .update = gl_x11_glx_update,
++ .device_load_swapchain = gl_x11_glx_device_load_swapchain,
++ .device_present = gl_x11_glx_device_present,
++};
++
++const struct gl_winsys_vtable *gl_x11_glx_get_winsys_vtable(void)
++{
++ return &glx_winsys_vtable;
++}
+diff --git a/libobs-opengl/gl-x11-glx.h b/libobs-opengl/gl-x11-glx.h
+new file mode 100644
+index 00000000..bdedf55c
+--- /dev/null
++++ b/libobs-opengl/gl-x11-glx.h
+@@ -0,0 +1,22 @@
++/******************************************************************************
++ Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "gl-nix.h"
++
++const struct gl_winsys_vtable *gl_x11_glx_get_winsys_vtable(void);
+--
+2.28.0
+
diff --git a/0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch b/0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch
new file mode 100644
index 000000000000..1dad1ee061a4
--- /dev/null
+++ b/0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch
@@ -0,0 +1,745 @@
+From 3016893fbe071ce95c20ea24844713a068862c29 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 17:35:49 -0300
+Subject: [PATCH 04/15] libobs-opengl: Introduce the X11/EGL winsys
+
+Introduce the EGL/X11 winsys, and use it when the OBS_USE_EGL environment
+variable is defined. This variable is only temporary, for future commits
+will add a proper concept of platform.
+
+All the EGL/X11 code is authored by Ivan Avdeev <me@w23.ru>.
+---
+ libobs-opengl/CMakeLists.txt | 1 +
+ libobs-opengl/gl-nix.c | 8 +-
+ libobs-opengl/gl-x11-egl.c | 651 +++++++++++++++++++++++++++++++++++
+ libobs-opengl/gl-x11-egl.h | 22 ++
+ 4 files changed, 681 insertions(+), 1 deletion(-)
+ create mode 100644 libobs-opengl/gl-x11-egl.c
+ create mode 100644 libobs-opengl/gl-x11-egl.h
+
+diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
+index 50afb923..9c645c3d 100644
+--- a/libobs-opengl/CMakeLists.txt
++++ b/libobs-opengl/CMakeLists.txt
+@@ -50,6 +50,7 @@ else() #This needs to change to be more specific to get ready for Wayland
+
+ set(libobs-opengl_PLATFORM_SOURCES
+ gl-nix.c
++ gl-x11-egl.c
+ gl-x11-glx.c)
+ endif()
+
+diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
+index 574d4f77..4b616ef1 100644
+--- a/libobs-opengl/gl-nix.c
++++ b/libobs-opengl/gl-nix.c
+@@ -17,6 +17,7 @@
+
+ #include "gl-nix.h"
+ #include "gl-x11-glx.h"
++#include "gl-x11-egl.h"
+
+ static const struct gl_winsys_vtable *gl_vtable = NULL;
+
+@@ -24,7 +25,12 @@ static void init_winsys(void)
+ {
+ assert(gl_vtable == NULL);
+
+- gl_vtable = gl_x11_glx_get_winsys_vtable();
++ if (getenv("OBS_USE_EGL")) {
++ gl_vtable = gl_x11_egl_get_winsys_vtable();
++ blog(LOG_INFO, "Using EGL/X11");
++ } else {
++ gl_vtable = gl_x11_glx_get_winsys_vtable();
++ }
+
+ assert(gl_vtable != NULL);
+ }
+diff --git a/libobs-opengl/gl-x11-egl.c b/libobs-opengl/gl-x11-egl.c
+new file mode 100644
+index 00000000..5b413995
+--- /dev/null
++++ b/libobs-opengl/gl-x11-egl.c
+@@ -0,0 +1,651 @@
++/******************************************************************************
++ Copyright (C) 2019 by Ivan Avdeev <me@w23.ru>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++/* GL context initialization using EGL instead of GLX
++ * Which is essential for improved and more performant screen grabbing and
++ * VA-API feeding techniques.
++ *
++ * Note: most of x11-related functionality was taken from gl-x11.c
++ */
++
++#include <X11/Xlib.h>
++#include <X11/Xlib-xcb.h>
++
++#include <xcb/xcb.h>
++
++#include <stdio.h>
++
++#include "gl-x11-egl.h"
++
++#include <glad/glad_egl.h>
++
++typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(
++ EGLenum platform, void *native_display, const EGLint *attrib_list);
++
++static const int ctx_attribs[] = {
++#ifdef _DEBUG
++ EGL_CONTEXT_OPENGL_DEBUG,
++ EGL_TRUE,
++#endif
++ EGL_CONTEXT_OPENGL_PROFILE_MASK,
++ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
++ EGL_CONTEXT_MAJOR_VERSION,
++ 3,
++ EGL_CONTEXT_MINOR_VERSION,
++ 3,
++ EGL_NONE,
++};
++
++static int ctx_pbuffer_attribs[] = {EGL_WIDTH, 2, EGL_HEIGHT, 2, EGL_NONE};
++
++static const EGLint ctx_config_attribs[] = {EGL_STENCIL_SIZE,
++ 0,
++ EGL_DEPTH_SIZE,
++ 0,
++ EGL_BUFFER_SIZE,
++ 32,
++ EGL_ALPHA_SIZE,
++ 8,
++ EGL_RENDERABLE_TYPE,
++ EGL_OPENGL_BIT,
++ EGL_SURFACE_TYPE,
++ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
++ EGL_NONE};
++
++struct gl_windowinfo {
++ EGLConfig config;
++
++ /* Windows in X11 are defined with integers (XID).
++ * xcb_window_t is a define for this... they are
++ * compatible with Xlib as well.
++ */
++ xcb_window_t window;
++ EGLSurface surface;
++
++ /* We can't fetch screen without a request so we cache it. */
++ int screen;
++};
++
++struct gl_platform {
++ Display *xdisplay;
++ EGLDisplay edisplay;
++ EGLConfig config;
++ EGLContext context;
++ EGLSurface pbuffer;
++};
++
++/* The following utility functions are copied verbatim from GLX code. */
++
++/*
++ * Since we cannot take advantage of the asynchronous nature of xcb,
++ * all of the helper functions are synchronous but thread-safe.
++ *
++ * They check for errors and will return 0 on problems
++ * with the exception of when 0 is a valid return value... in which case
++ * read the specific function comments.
++ */
++
++/* Returns -1 on invalid screen. */
++static int get_screen_num_from_xcb_screen(xcb_connection_t *xcb_conn,
++ xcb_screen_t *screen)
++{
++ xcb_screen_iterator_t iter =
++ xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
++ int screen_num = 0;
++
++ for (; iter.rem; xcb_screen_next(&iter), ++screen_num)
++ if (iter.data == screen)
++ return screen_num;
++
++ return -1;
++}
++
++static xcb_screen_t *get_screen_from_root(xcb_connection_t *xcb_conn,
++ xcb_window_t root)
++{
++ xcb_screen_iterator_t iter =
++ xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
++
++ while (iter.rem) {
++ if (iter.data->root == root)
++ return iter.data;
++
++ xcb_screen_next(&iter);
++ }
++
++ return 0;
++}
++
++static inline int get_screen_num_from_root(xcb_connection_t *xcb_conn,
++ xcb_window_t root)
++{
++ xcb_screen_t *screen = get_screen_from_root(xcb_conn, root);
++
++ if (!screen)
++ return -1;
++
++ return get_screen_num_from_xcb_screen(xcb_conn, screen);
++}
++
++static xcb_get_geometry_reply_t *get_window_geometry(xcb_connection_t *xcb_conn,
++ xcb_drawable_t drawable)
++{
++ xcb_get_geometry_cookie_t cookie;
++ xcb_generic_error_t *error;
++ xcb_get_geometry_reply_t *reply;
++
++ cookie = xcb_get_geometry(xcb_conn, drawable);
++ reply = xcb_get_geometry_reply(xcb_conn, cookie, &error);
++
++ if (error) {
++ blog(LOG_ERROR, "Failed to fetch parent window geometry!");
++ free(error);
++ free(reply);
++ return 0;
++ }
++
++ free(error);
++ return reply;
++}
++
++static const char *get_egl_error_string2(const EGLint error)
++{
++ switch (error) {
++#define OBS_EGL_CASE_ERROR(e) \
++ case e: \
++ return #e;
++ OBS_EGL_CASE_ERROR(EGL_SUCCESS)
++ OBS_EGL_CASE_ERROR(EGL_NOT_INITIALIZED)
++ OBS_EGL_CASE_ERROR(EGL_BAD_ACCESS)
++ OBS_EGL_CASE_ERROR(EGL_BAD_ALLOC)
++ OBS_EGL_CASE_ERROR(EGL_BAD_ATTRIBUTE)
++ OBS_EGL_CASE_ERROR(EGL_BAD_CONTEXT)
++ OBS_EGL_CASE_ERROR(EGL_BAD_CONFIG)
++ OBS_EGL_CASE_ERROR(EGL_BAD_CURRENT_SURFACE)
++ OBS_EGL_CASE_ERROR(EGL_BAD_DISPLAY)
++ OBS_EGL_CASE_ERROR(EGL_BAD_SURFACE)
++ OBS_EGL_CASE_ERROR(EGL_BAD_MATCH)
++ OBS_EGL_CASE_ERROR(EGL_BAD_PARAMETER)
++ OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_PIXMAP)
++ OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_WINDOW)
++ OBS_EGL_CASE_ERROR(EGL_CONTEXT_LOST)
++#undef OBS_EGL_CASE_ERROR
++ default:
++ return "Unknown";
++ }
++}
++static const char *get_egl_error_string()
++{
++ return get_egl_error_string2(eglGetError());
++}
++
++static EGLDisplay get_egl_display(struct gl_platform *plat)
++{
++ Display *display = plat->xdisplay;
++ EGLDisplay edisplay = EGL_NO_DISPLAY;
++ const char *egl_client_extensions = NULL;
++
++ egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
++
++ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
++ (PFNEGLGETPLATFORMDISPLAYEXTPROC)(
++ strstr(egl_client_extensions, "EGL_EXT_platform_base")
++ ? eglGetProcAddress("eglGetPlatformDisplayEXT")
++ : NULL);
++
++ if (eglGetPlatformDisplayEXT) {
++ edisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,
++ display, NULL);
++ if (EGL_NO_DISPLAY == edisplay)
++ blog(LOG_ERROR, "Failed to get EGL/X11 display");
++ }
++
++ if (EGL_NO_DISPLAY == edisplay)
++ edisplay = eglGetDisplay(display);
++
++ return edisplay;
++}
++
++static bool gl_context_create(struct gl_platform *plat)
++{
++ Display *display = plat->xdisplay;
++ int frame_buf_config_count = 0;
++ EGLDisplay edisplay = EGL_NO_DISPLAY;
++ EGLConfig config = NULL;
++ EGLContext context = EGL_NO_CONTEXT;
++ int egl_min = 0, egl_maj = 0;
++ bool success = false;
++
++ eglBindAPI(EGL_OPENGL_API);
++
++ edisplay = get_egl_display(plat);
++
++ if (EGL_NO_DISPLAY == edisplay) {
++ blog(LOG_ERROR,
++ "Failed to get EGL display using eglGetDisplay");
++ return false;
++ }
++
++ if (!eglInitialize(edisplay, &egl_maj, &egl_min)) {
++ blog(LOG_ERROR, "Failed to initialize EGL: %s",
++ get_egl_error_string());
++ return false;
++ }
++
++ if (!eglChooseConfig(edisplay, ctx_config_attribs, &config, 1,
++ &frame_buf_config_count)) {
++ blog(LOG_ERROR, "Unable to find suitable EGL config: %s",
++ get_egl_error_string());
++ goto error;
++ }
++
++ context =
++ eglCreateContext(edisplay, config, EGL_NO_CONTEXT, ctx_attribs);
++#ifdef _DEBUG
++ if (EGL_NO_CONTEXT == context) {
++ const EGLint error = eglGetError();
++ if (error == EGL_BAD_ATTRIBUTE) {
++ /* Sometimes creation fails because debug gl is not supported */
++ blog(LOG_ERROR,
++ "Unable to create EGL context with DEBUG attrib, trying without");
++ context = eglCreateContext(edisplay, config,
++ EGL_NO_CONTEXT,
++ ctx_attribs + 2);
++ } else {
++ blog(LOG_ERROR, "Unable to create EGL context: %s",
++ get_egl_error_string2(error));
++ goto error;
++ }
++ }
++#endif
++ if (EGL_NO_CONTEXT == context) {
++ blog(LOG_ERROR, "Unable to create EGL context: %s",
++ get_egl_error_string());
++ goto error;
++ }
++
++ plat->pbuffer =
++ eglCreatePbufferSurface(edisplay, config, ctx_pbuffer_attribs);
++ if (EGL_NO_SURFACE == plat->pbuffer) {
++ blog(LOG_ERROR, "Failed to create OpenGL pbuffer: %s",
++ get_egl_error_string());
++ goto error;
++ }
++
++ plat->edisplay = edisplay;
++ plat->config = config;
++ plat->context = context;
++
++ success = true;
++ blog(LOG_DEBUG, "Created EGLDisplay %p", plat->edisplay);
++
++error:
++ if (!success) {
++ if (EGL_NO_CONTEXT != context)
++ eglDestroyContext(edisplay, context);
++ eglTerminate(edisplay);
++ }
++
++ XSync(display, false);
++ return success;
++}
++
++static void gl_context_destroy(struct gl_platform *plat)
++{
++ eglMakeCurrent(plat->edisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
++ EGL_NO_CONTEXT);
++ eglDestroyContext(plat->edisplay, plat->context);
++}
++
++static struct gl_windowinfo *
++gl_x11_egl_windowinfo_create(const struct gs_init_data *info)
++{
++ UNUSED_PARAMETER(info);
++ return bmalloc(sizeof(struct gl_windowinfo));
++}
++
++static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
++{
++ UNUSED_PARAMETER(info);
++ bfree(info);
++}
++
++static Display *open_windowless_display(void)
++{
++ Display *display = XOpenDisplay(NULL);
++ xcb_connection_t *xcb_conn;
++ xcb_screen_iterator_t screen_iterator;
++ xcb_screen_t *screen;
++ int screen_num;
++
++ if (!display) {
++ blog(LOG_ERROR, "Unable to open new X connection!");
++ return NULL;
++ }
++
++ xcb_conn = XGetXCBConnection(display);
++ if (!xcb_conn) {
++ blog(LOG_ERROR, "Unable to get XCB connection to main display");
++ goto error;
++ }
++
++ screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
++ screen = screen_iterator.data;
++ if (!screen) {
++ blog(LOG_ERROR, "Unable to get screen root");
++ goto error;
++ }
++
++ screen_num = get_screen_num_from_root(xcb_conn, screen->root);
++ if (screen_num == -1) {
++ blog(LOG_ERROR, "Unable to get screen number from root");
++ goto error;
++ }
++
++ if (!gladLoadEGL()) {
++ blog(LOG_ERROR, "Unable to load EGL entry functions.");
++ goto error;
++ }
++
++ return display;
++
++error:
++ if (display)
++ XCloseDisplay(display);
++ return NULL;
++}
++
++static int x_error_handler(Display *display, XErrorEvent *error)
++{
++ char str1[512];
++ char str2[512];
++ char str3[512];
++ XGetErrorText(display, error->error_code, str1, sizeof(str1));
++ XGetErrorText(display, error->request_code, str2, sizeof(str2));
++ XGetErrorText(display, error->minor_code, str3, sizeof(str3));
++
++ blog(LOG_ERROR,
++ "X Error: %s, Major opcode: %s, "
++ "Minor opcode: %s, Serial: %lu",
++ str1, str2, str3, error->serial);
++ return 0;
++}
++
++static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
++ uint32_t adapter)
++{
++ /* There's some trickery here... we're mixing libX11, xcb, and EGL
++ For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
++ Essentially, EGL requires Xlib. Everything else we use xcb. */
++ struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
++ Display *display = open_windowless_display();
++
++ if (!display) {
++ goto fail_display_open;
++ }
++
++ XSetEventQueueOwner(display, XCBOwnsEventQueue);
++ XSetErrorHandler(x_error_handler);
++
++ /* We assume later that cur_swap is already set. */
++ device->plat = plat;
++
++ plat->xdisplay = display;
++
++ if (!gl_context_create(plat)) {
++ blog(LOG_ERROR, "Failed to create context!");
++ goto fail_context_create;
++ }
++
++ if (!eglMakeCurrent(plat->edisplay, plat->pbuffer, plat->pbuffer,
++ plat->context)) {
++ blog(LOG_ERROR, "Failed to make context current: %s",
++ get_egl_error_string());
++ goto fail_make_current;
++ }
++
++ if (!gladLoadGL()) {
++ blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
++ goto fail_load_gl;
++ }
++
++ goto success;
++
++fail_make_current:
++ gl_context_destroy(plat);
++fail_context_create:
++fail_load_gl:
++ XCloseDisplay(display);
++fail_display_open:
++ bfree(plat);
++ plat = NULL;
++success:
++ UNUSED_PARAMETER(adapter);
++ return plat;
++}
++
++static void gl_x11_egl_platform_destroy(struct gl_platform *plat)
++{
++ if (!plat)
++ return;
++
++ gl_context_destroy(plat);
++ eglTerminate(plat->edisplay);
++ bfree(plat);
++}
++
++static bool gl_x11_egl_platform_init_swapchain(struct gs_swap_chain *swap)
++{
++ const struct gl_platform *plat = swap->device->plat;
++ Display *display = plat->xdisplay;
++ xcb_connection_t *xcb_conn = XGetXCBConnection(display);
++ xcb_window_t wid = xcb_generate_id(xcb_conn);
++ xcb_window_t parent = swap->info.window.id;
++ xcb_get_geometry_reply_t *geometry =
++ get_window_geometry(xcb_conn, parent);
++ bool status = false;
++
++ int screen_num;
++ int visual;
++
++ if (!geometry)
++ goto fail_geometry_request;
++
++ screen_num = get_screen_num_from_root(xcb_conn, geometry->root);
++ if (screen_num == -1) {
++ goto fail_screen;
++ }
++
++ {
++ if (!eglGetConfigAttrib(plat->edisplay, plat->config,
++ EGL_NATIVE_VISUAL_ID,
++ (EGLint *)&visual)) {
++ blog(LOG_ERROR,
++ "Cannot get visual id for EGL context: %s",
++ get_egl_error_string());
++ goto fail_visual_id;
++ }
++ }
++
++ xcb_colormap_t colormap = xcb_generate_id(xcb_conn);
++ uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP;
++ uint32_t mask_values[] = {0, colormap, 0};
++
++ xcb_create_colormap(xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent,
++ visual);
++
++ xcb_create_window(xcb_conn, 24 /* Hardcoded? */, wid, parent, 0, 0,
++ geometry->width, geometry->height, 0, 0, visual, mask,
++ mask_values);
++
++ const EGLSurface surface =
++ eglCreateWindowSurface(plat->edisplay, plat->config, wid, 0);
++ if (EGL_NO_SURFACE == surface) {
++ blog(LOG_ERROR, "Cannot get window EGL surface: %s",
++ get_egl_error_string());
++ goto fail_window_surface;
++ }
++
++ swap->wi->config = plat->config;
++ swap->wi->window = wid;
++ swap->wi->surface = surface;
++ swap->wi->screen = screen_num;
++
++ xcb_map_window(xcb_conn, wid);
++
++ status = true;
++ goto success;
++
++fail_window_surface:
++fail_visual_id:
++fail_screen:
++fail_geometry_request:
++success:
++ free(geometry);
++ return status;
++}
++
++static void gl_x11_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
++{
++ UNUSED_PARAMETER(swap);
++ /* Really nothing to clean up? */
++}
++
++static void gl_x11_egl_device_enter_context(gs_device_t *device)
++{
++ const EGLContext context = device->plat->context;
++ const EGLDisplay display = device->plat->edisplay;
++ const EGLSurface surface = (device->cur_swap)
++ ? device->cur_swap->wi->surface
++ : device->plat->pbuffer;
++
++ if (!eglMakeCurrent(display, surface, surface, context))
++ blog(LOG_ERROR, "Failed to make context current: %s",
++ get_egl_error_string());
++}
++
++static void gl_x11_egl_device_leave_context(gs_device_t *device)
++{
++ const EGLDisplay display = device->plat->edisplay;
++
++ if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
++ EGL_NO_CONTEXT)) {
++ blog(LOG_ERROR, "Failed to reset current context: %s",
++ get_egl_error_string());
++ }
++}
++
++static void *gl_x11_egl_device_get_device_obj(gs_device_t *device)
++{
++ return device->plat->context;
++}
++
++static void gl_x11_egl_getclientsize(const struct gs_swap_chain *swap,
++ uint32_t *width, uint32_t *height)
++{
++ xcb_connection_t *xcb_conn =
++ XGetXCBConnection(swap->device->plat->xdisplay);
++ xcb_window_t window = swap->wi->window;
++
++ xcb_get_geometry_reply_t *geometry =
++ get_window_geometry(xcb_conn, window);
++ if (geometry) {
++ *width = geometry->width;
++ *height = geometry->height;
++ }
++
++ free(geometry);
++}
++
++static void gl_x11_egl_update(gs_device_t *device)
++{
++ Display *display = device->plat->xdisplay;
++ xcb_window_t window = device->cur_swap->wi->window;
++
++ uint32_t values[] = {device->cur_swap->info.cx,
++ device->cur_swap->info.cy};
++
++ xcb_configure_window(XGetXCBConnection(display), window,
++ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
++ values);
++}
++
++static void gl_x11_egl_clear_context(gs_device_t *device)
++{
++ Display *display = device->plat->edisplay;
++
++ if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
++ EGL_NO_CONTEXT)) {
++ blog(LOG_ERROR, "Failed to reset current context.");
++ }
++}
++
++static void gl_x11_egl_device_load_swapchain(gs_device_t *device,
++ gs_swapchain_t *swap)
++{
++ if (device->cur_swap == swap)
++ return;
++
++ device->cur_swap = swap;
++
++ device_enter_context(device);
++}
++
++enum swap_type {
++ SWAP_TYPE_NORMAL,
++ SWAP_TYPE_EXT,
++ SWAP_TYPE_MESA,
++ SWAP_TYPE_SGI,
++};
++
++static void gl_x11_egl_device_present(gs_device_t *device)
++{
++ Display *display = device->plat->xdisplay;
++
++ xcb_connection_t *xcb_conn = XGetXCBConnection(display);
++ xcb_generic_event_t *xcb_event;
++ while ((xcb_event = xcb_poll_for_event(xcb_conn))) {
++ free(xcb_event);
++ }
++
++ if (!eglSwapBuffers(device->plat->edisplay,
++ device->cur_swap->wi->surface))
++ blog(LOG_ERROR, "Cannot swap EGL buffers: %s",
++ get_egl_error_string());
++}
++
++static const struct gl_winsys_vtable egl_x11_winsys_vtable = {
++ .windowinfo_create = gl_x11_egl_windowinfo_create,
++ .windowinfo_destroy = gl_x11_egl_windowinfo_destroy,
++ .platform_create = gl_x11_egl_platform_create,
++ .platform_destroy = gl_x11_egl_platform_destroy,
++ .platform_init_swapchain = gl_x11_egl_platform_init_swapchain,
++ .platform_cleanup_swapchain = gl_x11_egl_platform_cleanup_swapchain,
++ .device_enter_context = gl_x11_egl_device_enter_context,
++ .device_leave_context = gl_x11_egl_device_leave_context,
++ .device_get_device_obj = gl_x11_egl_device_get_device_obj,
++ .getclientsize = gl_x11_egl_getclientsize,
++ .clear_context = gl_x11_egl_clear_context,
++ .update = gl_x11_egl_update,
++ .device_load_swapchain = gl_x11_egl_device_load_swapchain,
++ .device_present = gl_x11_egl_device_present,
++};
++
++const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void)
++{
++ return &egl_x11_winsys_vtable;
++}
+diff --git a/libobs-opengl/gl-x11-egl.h b/libobs-opengl/gl-x11-egl.h
+new file mode 100644
+index 00000000..44ab3111
+--- /dev/null
++++ b/libobs-opengl/gl-x11-egl.h
+@@ -0,0 +1,22 @@
++/******************************************************************************
++ Copyright (C) 2019 by Ivan Avdeev <me@w23.ru>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "gl-nix.h"
++
++const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void);
+--
+2.28.0
+
diff --git a/0005-deps-glad-Make-X11-required-as-well.patch b/0005-deps-glad-Make-X11-required-as-well.patch
new file mode 100644
index 000000000000..f971e8c97f09
--- /dev/null
+++ b/0005-deps-glad-Make-X11-required-as-well.patch
@@ -0,0 +1,26 @@
+From 6b71db73fb1aef30ad06e576d2b2377052ed9f56 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Sat, 11 Apr 2020 23:49:05 -0300
+Subject: [PATCH 05/15] deps/glad: Make X11 required as well
+
+To keep consistency with the EGL line
+---
+ deps/glad/CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/deps/glad/CMakeLists.txt b/deps/glad/CMakeLists.txt
+index 9cb7e8fd..a31a5b91 100644
+--- a/deps/glad/CMakeLists.txt
++++ b/deps/glad/CMakeLists.txt
+@@ -3,7 +3,7 @@ project(glad)
+ find_package(OpenGL)
+
+ if(NOT WIN32 AND NOT APPLE)
+- find_package(X11)
++ find_package(X11 REQUIRED)
+ find_package(EGL REQUIRED)
+ endif()
+
+--
+2.28.0
+
diff --git a/0006-ci-Install-qtbase5-private-dev-on-Linux.patch b/0006-ci-Install-qtbase5-private-dev-on-Linux.patch
new file mode 100644
index 000000000000..804e48baef7b
--- /dev/null
+++ b/0006-ci-Install-qtbase5-private-dev-on-Linux.patch
@@ -0,0 +1,24 @@
+From e3eeda73e23c9b6a22562583eb9b7bf5c8a481df Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Fri, 6 Mar 2020 16:22:01 -0300
+Subject: [PATCH 06/15] ci: Install qtbase5-private-dev on Linux
+
+---
+ CI/install-dependencies-linux.sh | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/CI/install-dependencies-linux.sh b/CI/install-dependencies-linux.sh
+index 0d92fbf9..478f953a 100755
+--- a/CI/install-dependencies-linux.sh
++++ b/CI/install-dependencies-linux.sh
+@@ -42,6 +42,7 @@ sudo apt-get install -y \
+ pkg-config \
+ python3-dev \
+ qtbase5-dev \
++ qtbase5-private-dev \
+ libqt5svg5-dev \
+ swig
+
+--
+2.28.0
+
diff --git a/0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch b/0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch
new file mode 100644
index 000000000000..47c412c2775f
--- /dev/null
+++ b/0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch
@@ -0,0 +1,2683 @@
+From 93ecb043ff029fe430c59d2654a9f3fc590747ec Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Fri, 6 Mar 2020 17:24:33 -0300
+Subject: [PATCH 07/15] libobs/nix: Move X11-specific code to obs-nix-x11.c
+
+Currently, obs-nix.c is highly tied to the X11 display
+server. It includes X11 headers directly, and make use
+of X11 functions. Most of the code inside obs-nix.c that
+is X11-specific is related to hotkeys handling.
+
+Introduce a new vtable for hotkeys callbacks, that will
+used by X11 and Wayland to expose their specific routines.
+In this commit, only the X11 hotkeys vtable is implemented.
+
+Move all the X11-specific code to obs-nix-x11.c, and add
+a new function to retrieve the X11 hotkeys vtable.
+---
+ libobs/CMakeLists.txt | 1 +
+ libobs/obs-nix-x11.c | 1270 +++++++++++++++++++++++++++++++++++++++++
+ libobs/obs-nix-x11.h | 22 +
+ libobs/obs-nix.c | 1224 +--------------------------------------
+ libobs/obs-nix.h | 42 ++
+ 5 files changed, 1348 insertions(+), 1211 deletions(-)
+ create mode 100644 libobs/obs-nix-x11.c
+ create mode 100644 libobs/obs-nix-x11.h
+ create mode 100644 libobs/obs-nix.h
+
+diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt
+index 0b592d7a..1625363f 100644
+--- a/libobs/CMakeLists.txt
++++ b/libobs/CMakeLists.txt
+@@ -180,6 +180,7 @@ elseif(APPLE)
+ elseif(UNIX)
+ set(libobs_PLATFORM_SOURCES
+ obs-nix.c
++ obs-nix-x11.c
+ util/threading-posix.c
+ util/pipe-posix.c
+ util/platform-nix.c)
+diff --git a/libobs/obs-nix-x11.c b/libobs/obs-nix-x11.c
+new file mode 100644
+index 00000000..29aa3c7f
+--- /dev/null
++++ b/libobs/obs-nix-x11.c
+@@ -0,0 +1,1270 @@
++/******************************************************************************
++ Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
++ Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "obs-internal.h"
++#include "obs-nix-x11.h"
++
++#include <xcb/xcb.h>
++#if USE_XINPUT
++#include <xcb/xinput.h>
++#endif
++#include <X11/Xlib.h>
++#include <X11/Xutil.h>
++#include <X11/Xlib-xcb.h>
++#include <X11/XF86keysym.h>
++#include <X11/Sunkeysym.h>
++
++void obs_nix_x11_log_info(void)
++{
++ Display *dpy = XOpenDisplay(NULL);
++ if (!dpy) {
++ blog(LOG_INFO, "Unable to open X display");
++ return;
++ }
++
++ int protocol_version = ProtocolVersion(dpy);
++ int protocol_revision = ProtocolRevision(dpy);
++ int vendor_release = VendorRelease(dpy);
++ const char *vendor_name = ServerVendor(dpy);
++
++ if (strstr(vendor_name, "X.Org")) {
++ blog(LOG_INFO,
++ "Window System: X%d.%d, Vendor: %s, Version: %d"
++ ".%d.%d",
++ protocol_version, protocol_revision, vendor_name,
++ vendor_release / 10000000, (vendor_release / 100000) % 100,
++ (vendor_release / 1000) % 100);
++ } else {
++ blog(LOG_INFO,
++ "Window System: X%d.%d - vendor string: %s - "
++ "vendor release: %d",
++ protocol_version, protocol_revision, vendor_name,
++ vendor_release);
++ }
++}
++
++/* So here's how linux works with key mapping:
++ *
++ * First, there's a global key symbol enum (xcb_keysym_t) which has unique
++ * values for all possible symbols keys can have (e.g., '1' and '!' are
++ * different values).
++ *
++ * Then there's a key code (xcb_keycode_t), which is basically an index to the
++ * actual key itself on the keyboard (e.g., '1' and '!' will share the same
++ * value).
++ *
++ * xcb_keysym_t values should be given to libobs, and libobs will translate it
++ * to an obs_key_t, and although xcb_keysym_t can differ ('!' vs '1'), it will
++ * get the obs_key_t value that represents the actual key pressed; in other
++ * words it will be based on the key code rather than the key symbol. The same
++ * applies to checking key press states.
++ */
++
++struct keycode_list {
++ DARRAY(xcb_keycode_t) list;
++};
++
++struct obs_hotkeys_platform {
++ Display *display;
++ xcb_keysym_t base_keysyms[OBS_KEY_LAST_VALUE];
++ struct keycode_list keycodes[OBS_KEY_LAST_VALUE];
++ xcb_keycode_t min_keycode;
++ xcb_keycode_t super_l_code;
++ xcb_keycode_t super_r_code;
++
++ /* stores a copy of the keysym map for keycodes */
++ xcb_keysym_t *keysyms;
++ int num_keysyms;
++ int syms_per_code;
++
++#if USE_XINPUT
++ bool pressed[XINPUT_MOUSE_LEN];
++ bool update[XINPUT_MOUSE_LEN];
++ bool button_pressed[XINPUT_MOUSE_LEN];
++#endif
++};
++
++#define MOUSE_1 (1 << 16)
++#define MOUSE_2 (2 << 16)
++#define MOUSE_3 (3 << 16)
++#define MOUSE_4 (4 << 16)
++#define MOUSE_5 (5 << 16)
++
++static int get_keysym(obs_key_t key)
++{
++ switch (key) {
++ case OBS_KEY_RETURN:
++ return XK_Return;
++ case OBS_KEY_ESCAPE:
++ return XK_Escape;
++ case OBS_KEY_TAB:
++ return XK_Tab;
++ case OBS_KEY_BACKSPACE:
++ return XK_BackSpace;
++ case OBS_KEY_INSERT:
++ return XK_Insert;
++ case OBS_KEY_DELETE:
++ return XK_Delete;
++ case OBS_KEY_PAUSE:
++ return XK_Pause;
++ case OBS_KEY_PRINT:
++ return XK_Print;
++ case OBS_KEY_HOME:
++ return XK_Home;
++ case OBS_KEY_END:
++ return XK_End;
++ case OBS_KEY_LEFT:
++ return XK_Left;
++ case OBS_KEY_UP:
++ return XK_Up;
++ case OBS_KEY_RIGHT:
++ return XK_Right;
++ case OBS_KEY_DOWN:
++ return XK_Down;
++ case OBS_KEY_PAGEUP:
++ return XK_Prior;
++ case OBS_KEY_PAGEDOWN:
++ return XK_Next;
++
++ case OBS_KEY_SHIFT:
++ return XK_Shift_L;
++ case OBS_KEY_CONTROL:
++ return XK_Control_L;
++ case OBS_KEY_ALT:
++ return XK_Alt_L;
++ case OBS_KEY_CAPSLOCK:
++ return XK_Caps_Lock;
++ case OBS_KEY_NUMLOCK:
++ return XK_Num_Lock;
++ case OBS_KEY_SCROLLLOCK:
++ return XK_Scroll_Lock;
++
++ case OBS_KEY_F1:
++ return XK_F1;
++ case OBS_KEY_F2:
++ return XK_F2;
++ case OBS_KEY_F3:
++ return XK_F3;
++ case OBS_KEY_F4:
++ return XK_F4;
++ case OBS_KEY_F5:
++ return XK_F5;
++ case OBS_KEY_F6:
++ return XK_F6;
++ case OBS_KEY_F7:
++ return XK_F7;
++ case OBS_KEY_F8:
++ return XK_F8;
++ case OBS_KEY_F9:
++ return XK_F9;
++ case OBS_KEY_F10:
++ return XK_F10;
++ case OBS_KEY_F11:
++ return XK_F11;
++ case OBS_KEY_F12:
++ return XK_F12;
++ case OBS_KEY_F13:
++ return XK_F13;
++ case OBS_KEY_F14:
++ return XK_F14;
++ case OBS_KEY_F15:
++ return XK_F15;
++ case OBS_KEY_F16:
++ return XK_F16;
++ case OBS_KEY_F17:
++ return XK_F17;
++ case OBS_KEY_F18:
++ return XK_F18;
++ case OBS_KEY_F19:
++ return XK_F19;
++ case OBS_KEY_F20:
++ return XK_F20;
++ case OBS_KEY_F21:
++ return XK_F21;
++ case OBS_KEY_F22:
++ return XK_F22;
++ case OBS_KEY_F23:
++ return XK_F23;
++ case OBS_KEY_F24:
++ return XK_F24;
++ case OBS_KEY_F25:
++ return XK_F25;
++ case OBS_KEY_F26:
++ return XK_F26;
++ case OBS_KEY_F27:
++ return XK_F27;
++ case OBS_KEY_F28:
++ return XK_F28;
++ case OBS_KEY_F29:
++ return XK_F29;
++ case OBS_KEY_F30:
++ return XK_F30;
++ case OBS_KEY_F31:
++ return XK_F31;
++ case OBS_KEY_F32:
++ return XK_F32;
++ case OBS_KEY_F33:
++ return XK_F33;
++ case OBS_KEY_F34:
++ return XK_F34;
++ case OBS_KEY_F35:
++ return XK_F35;
++
++ case OBS_KEY_MENU:
++ return XK_Menu;
++ case OBS_KEY_HYPER_L:
++ return XK_Hyper_L;
++ case OBS_KEY_HYPER_R:
++ return XK_Hyper_R;
++ case OBS_KEY_HELP:
++ return XK_Help;
++ case OBS_KEY_CANCEL:
++ return XK_Cancel;
++ case OBS_KEY_FIND:
++ return XK_Find;
++ case OBS_KEY_REDO:
++ return XK_Redo;
++ case OBS_KEY_UNDO:
++ return XK_Undo;
++ case OBS_KEY_SPACE:
++ return XK_space;
++
++ case OBS_KEY_COPY:
++ return XF86XK_Copy;
++ case OBS_KEY_CUT:
++ return XF86XK_Cut;
++ case OBS_KEY_OPEN:
++ return XF86XK_Open;
++ case OBS_KEY_PASTE:
++ return XF86XK_Paste;
++ case OBS_KEY_FRONT:
++ return SunXK_Front;
++ case OBS_KEY_PROPS:
++ return SunXK_Props;
++
++ case OBS_KEY_EXCLAM:
++ return XK_exclam;
++ case OBS_KEY_QUOTEDBL:
++ return XK_quotedbl;
++ case OBS_KEY_NUMBERSIGN:
++ return XK_numbersign;
++ case OBS_KEY_DOLLAR:
++ return XK_dollar;
++ case OBS_KEY_PERCENT:
++ return XK_percent;
++ case OBS_KEY_AMPERSAND:
++ return XK_ampersand;
++ case OBS_KEY_APOSTROPHE:
++ return XK_apostrophe;
++ case OBS_KEY_PARENLEFT:
++ return XK_parenleft;
++ case OBS_KEY_PARENRIGHT:
++ return XK_parenright;
++ case OBS_KEY_ASTERISK:
++ return XK_asterisk;
++ case OBS_KEY_PLUS:
++ return XK_plus;
++ case OBS_KEY_COMMA:
++ return XK_comma;
++ case OBS_KEY_MINUS:
++ return XK_minus;
++ case OBS_KEY_PERIOD:
++ return XK_period;
++ case OBS_KEY_SLASH:
++ return XK_slash;
++ case OBS_KEY_0:
++ return XK_0;
++ case OBS_KEY_1:
++ return XK_1;
++ case OBS_KEY_2:
++ return XK_2;
++ case OBS_KEY_3:
++ return XK_3;
++ case OBS_KEY_4:
++ return XK_4;
++ case OBS_KEY_5:
++ return XK_5;
++ case OBS_KEY_6:
++ return XK_6;
++ case OBS_KEY_7:
++ return XK_7;
++ case OBS_KEY_8:
++ return XK_8;
++ case OBS_KEY_9:
++ return XK_9;
++ case OBS_KEY_NUMEQUAL:
++ return XK_KP_Equal;
++ case OBS_KEY_NUMASTERISK:
++ return XK_KP_Multiply;
++ case OBS_KEY_NUMPLUS:
++ return XK_KP_Add;
++ case OBS_KEY_NUMCOMMA:
++ return XK_KP_Separator;
++ case OBS_KEY_NUMMINUS:
++ return XK_KP_Subtract;
++ case OBS_KEY_NUMPERIOD:
++ return XK_KP_Decimal;
++ case OBS_KEY_NUMSLASH:
++ return XK_KP_Divide;
++ case OBS_KEY_NUM0:
++ return XK_KP_0;
++ case OBS_KEY_NUM1:
++ return XK_KP_1;
++ case OBS_KEY_NUM2:
++ return XK_KP_2;
++ case OBS_KEY_NUM3:
++ return XK_KP_3;
++ case OBS_KEY_NUM4:
++ return XK_KP_4;
++ case OBS_KEY_NUM5:
++ return XK_KP_5;
++ case OBS_KEY_NUM6:
++ return XK_KP_6;
++ case OBS_KEY_NUM7:
++ return XK_KP_7;
++ case OBS_KEY_NUM8:
++ return XK_KP_8;
++ case OBS_KEY_NUM9:
++ return XK_KP_9;
++ case OBS_KEY_COLON:
++ return XK_colon;
++ case OBS_KEY_SEMICOLON:
++ return XK_semicolon;
++ case OBS_KEY_LESS:
++ return XK_less;
++ case OBS_KEY_EQUAL:
++ return XK_equal;
++ case OBS_KEY_GREATER:
++ return XK_greater;
++ case OBS_KEY_QUESTION:
++ return XK_question;
++ case OBS_KEY_AT:
++ return XK_at;
++ case OBS_KEY_A:
++ return XK_A;
++ case OBS_KEY_B:
++ return XK_B;
++ case OBS_KEY_C:
++ return XK_C;
++ case OBS_KEY_D:
++ return XK_D;
++ case OBS_KEY_E:
++ return XK_E;
++ case OBS_KEY_F:
++ return XK_F;
++ case OBS_KEY_G:
++ return XK_G;
++ case OBS_KEY_H:
++ return XK_H;
++ case OBS_KEY_I:
++ return XK_I;
++ case OBS_KEY_J:
++ return XK_J;
++ case OBS_KEY_K:
++ return XK_K;
++ case OBS_KEY_L:
++ return XK_L;
++ case OBS_KEY_M:
++ return XK_M;
++ case OBS_KEY_N:
++ return XK_N;
++ case OBS_KEY_O:
++ return XK_O;
++ case OBS_KEY_P:
++ return XK_P;
++ case OBS_KEY_Q:
++ return XK_Q;
++ case OBS_KEY_R:
++ return XK_R;
++ case OBS_KEY_S:
++ return XK_S;
++ case OBS_KEY_T:
++ return XK_T;
++ case OBS_KEY_U:
++ return XK_U;
++ case OBS_KEY_V:
++ return XK_V;
++ case OBS_KEY_W:
++ return XK_W;
++ case OBS_KEY_X:
++ return XK_X;
++ case OBS_KEY_Y:
++ return XK_Y;
++ case OBS_KEY_Z:
++ return XK_Z;
++ case OBS_KEY_BRACKETLEFT:
++ return XK_bracketleft;
++ case OBS_KEY_BACKSLASH:
++ return XK_backslash;
++ case OBS_KEY_BRACKETRIGHT:
++ return XK_bracketright;
++ case OBS_KEY_ASCIICIRCUM:
++ return XK_asciicircum;
++ case OBS_KEY_UNDERSCORE:
++ return XK_underscore;
++ case OBS_KEY_QUOTELEFT:
++ return XK_quoteleft;
++ case OBS_KEY_BRACELEFT:
++ return XK_braceleft;
++ case OBS_KEY_BAR:
++ return XK_bar;
++ case OBS_KEY_BRACERIGHT:
++ return XK_braceright;
++ case OBS_KEY_ASCIITILDE:
++ return XK_grave;
++ case OBS_KEY_NOBREAKSPACE:
++ return XK_nobreakspace;
++ case OBS_KEY_EXCLAMDOWN:
++ return XK_exclamdown;
++ case OBS_KEY_CENT:
++ return XK_cent;
++ case OBS_KEY_STERLING:
++ return XK_sterling;
++ case OBS_KEY_CURRENCY:
++ return XK_currency;
++ case OBS_KEY_YEN:
++ return XK_yen;
++ case OBS_KEY_BROKENBAR:
++ return XK_brokenbar;
++ case OBS_KEY_SECTION:
++ return XK_section;
++ case OBS_KEY_DIAERESIS:
++ return XK_diaeresis;
++ case OBS_KEY_COPYRIGHT:
++ return XK_copyright;
++ case OBS_KEY_ORDFEMININE:
++ return XK_ordfeminine;
++ case OBS_KEY_GUILLEMOTLEFT:
++ return XK_guillemotleft;
++ case OBS_KEY_NOTSIGN:
++ return XK_notsign;
++ case OBS_KEY_HYPHEN:
++ return XK_hyphen;
++ case OBS_KEY_REGISTERED:
++ return XK_registered;
++ case OBS_KEY_MACRON:
++ return XK_macron;
++ case OBS_KEY_DEGREE:
++ return XK_degree;
++ case OBS_KEY_PLUSMINUS:
++ return XK_plusminus;
++ case OBS_KEY_TWOSUPERIOR:
++ return XK_twosuperior;
++ case OBS_KEY_THREESUPERIOR:
++ return XK_threesuperior;
++ case OBS_KEY_ACUTE:
++ return XK_acute;
++ case OBS_KEY_MU:
++ return XK_mu;
++ case OBS_KEY_PARAGRAPH:
++ return XK_paragraph;
++ case OBS_KEY_PERIODCENTERED:
++ return XK_periodcentered;
++ case OBS_KEY_CEDILLA:
++ return XK_cedilla;
++ case OBS_KEY_ONESUPERIOR:
++ return XK_onesuperior;
++ case OBS_KEY_MASCULINE:
++ return XK_masculine;
++ case OBS_KEY_GUILLEMOTRIGHT:
++ return XK_guillemotright;
++ case OBS_KEY_ONEQUARTER:
++ return XK_onequarter;
++ case OBS_KEY_ONEHALF:
++ return XK_onehalf;
++ case OBS_KEY_THREEQUARTERS:
++ return XK_threequarters;
++ case OBS_KEY_QUESTIONDOWN:
++ return XK_questiondown;
++ case OBS_KEY_AGRAVE:
++ return XK_Agrave;
++ case OBS_KEY_AACUTE:
++ return XK_Aacute;
++ case OBS_KEY_ACIRCUMFLEX:
++ return XK_Acircumflex;
++ case OBS_KEY_ATILDE:
++ return XK_Atilde;
++ case OBS_KEY_ADIAERESIS:
++ return XK_Adiaeresis;
++ case OBS_KEY_ARING:
++ return XK_Aring;
++ case OBS_KEY_AE:
++ return XK_AE;
++ case OBS_KEY_CCEDILLA:
++ return XK_cedilla;
++ case OBS_KEY_EGRAVE:
++ return XK_Egrave;
++ case OBS_KEY_EACUTE:
++ return XK_Eacute;
++ case OBS_KEY_ECIRCUMFLEX:
++ return XK_Ecircumflex;
++ case OBS_KEY_EDIAERESIS:
++ return XK_Ediaeresis;
++ case OBS_KEY_IGRAVE:
++ return XK_Igrave;
++ case OBS_KEY_IACUTE:
++ return XK_Iacute;
++ case OBS_KEY_ICIRCUMFLEX:
++ return XK_Icircumflex;
++ case OBS_KEY_IDIAERESIS:
++ return XK_Idiaeresis;
++ case OBS_KEY_ETH:
++ return XK_ETH;
++ case OBS_KEY_NTILDE:
++ return XK_Ntilde;
++ case OBS_KEY_OGRAVE:
++ return XK_Ograve;
++ case OBS_KEY_OACUTE:
++ return XK_Oacute;
++ case OBS_KEY_OCIRCUMFLEX:
++ return XK_Ocircumflex;
++ case OBS_KEY_ODIAERESIS:
++ return XK_Odiaeresis;
++ case OBS_KEY_MULTIPLY:
++ return XK_multiply;
++ case OBS_KEY_OOBLIQUE:
++ return XK_Ooblique;
++ case OBS_KEY_UGRAVE:
++ return XK_Ugrave;
++ case OBS_KEY_UACUTE:
++ return XK_Uacute;
++ case OBS_KEY_UCIRCUMFLEX:
++ return XK_Ucircumflex;
++ case OBS_KEY_UDIAERESIS:
++ return XK_Udiaeresis;
++ case OBS_KEY_YACUTE:
++ return XK_Yacute;
++ case OBS_KEY_THORN:
++ return XK_Thorn;
++ case OBS_KEY_SSHARP:
++ return XK_ssharp;
++ case OBS_KEY_DIVISION:
++ return XK_division;
++ case OBS_KEY_YDIAERESIS:
++ return XK_Ydiaeresis;
++ case OBS_KEY_MULTI_KEY:
++ return XK_Multi_key;
++ case OBS_KEY_CODEINPUT:
++ return XK_Codeinput;
++ case OBS_KEY_SINGLECANDIDATE:
++ return XK_SingleCandidate;
++ case OBS_KEY_MULTIPLECANDIDATE:
++ return XK_MultipleCandidate;
++ case OBS_KEY_PREVIOUSCANDIDATE:
++ return XK_PreviousCandidate;
++ case OBS_KEY_MODE_SWITCH:
++ return XK_Mode_switch;
++ case OBS_KEY_KANJI:
++ return XK_Kanji;
++ case OBS_KEY_MUHENKAN:
++ return XK_Muhenkan;
++ case OBS_KEY_HENKAN:
++ return XK_Henkan;
++ case OBS_KEY_ROMAJI:
++ return XK_Romaji;
++ case OBS_KEY_HIRAGANA:
++ return XK_Hiragana;
++ case OBS_KEY_KATAKANA:
++ return XK_Katakana;
++ case OBS_KEY_HIRAGANA_KATAKANA:
++ return XK_Hiragana_Katakana;
++ case OBS_KEY_ZENKAKU:
++ return XK_Zenkaku;
++ case OBS_KEY_HANKAKU:
++ return XK_Hankaku;
++ case OBS_KEY_ZENKAKU_HANKAKU:
++ return XK_Zenkaku_Hankaku;
++ case OBS_KEY_TOUROKU:
++ return XK_Touroku;
++ case OBS_KEY_MASSYO:
++ return XK_Massyo;
++ case OBS_KEY_KANA_LOCK:
++ return XK_Kana_Lock;
++ case OBS_KEY_KANA_SHIFT:
++ return XK_Kana_Shift;
++ case OBS_KEY_EISU_SHIFT:
++ return XK_Eisu_Shift;
++ case OBS_KEY_EISU_TOGGLE:
++ return XK_Eisu_toggle;
++ case OBS_KEY_HANGUL:
++ return XK_Hangul;
++ case OBS_KEY_HANGUL_START:
++ return XK_Hangul_Start;
++ case OBS_KEY_HANGUL_END:
++ return XK_Hangul_End;
++ case OBS_KEY_HANGUL_HANJA:
++ return XK_Hangul_Hanja;
++ case OBS_KEY_HANGUL_JAMO:
++ return XK_Hangul_Jamo;
++ case OBS_KEY_HANGUL_ROMAJA:
++ return XK_Hangul_Romaja;
++ case OBS_KEY_HANGUL_BANJA:
++ return XK_Hangul_Banja;
++ case OBS_KEY_HANGUL_PREHANJA:
++ return XK_Hangul_PreHanja;
++ case OBS_KEY_HANGUL_POSTHANJA:
++ return XK_Hangul_PostHanja;
++ case OBS_KEY_HANGUL_SPECIAL:
++ return XK_Hangul_Special;
++ case OBS_KEY_DEAD_GRAVE:
++ return XK_dead_grave;
++ case OBS_KEY_DEAD_ACUTE:
++ return XK_dead_acute;
++ case OBS_KEY_DEAD_CIRCUMFLEX:
++ return XK_dead_circumflex;
++ case OBS_KEY_DEAD_TILDE:
++ return XK_dead_tilde;
++ case OBS_KEY_DEAD_MACRON:
++ return XK_dead_macron;
++ case OBS_KEY_DEAD_BREVE:
++ return XK_dead_breve;
++ case OBS_KEY_DEAD_ABOVEDOT:
++ return XK_dead_abovedot;
++ case OBS_KEY_DEAD_DIAERESIS:
++ return XK_dead_diaeresis;
++ case OBS_KEY_DEAD_ABOVERING:
++ return XK_dead_abovering;
++ case OBS_KEY_DEAD_DOUBLEACUTE:
++ return XK_dead_doubleacute;
++ case OBS_KEY_DEAD_CARON:
++ return XK_dead_caron;
++ case OBS_KEY_DEAD_CEDILLA:
++ return XK_dead_cedilla;
++ case OBS_KEY_DEAD_OGONEK:
++ return XK_dead_ogonek;
++ case OBS_KEY_DEAD_IOTA:
++ return XK_dead_iota;
++ case OBS_KEY_DEAD_VOICED_SOUND:
++ return XK_dead_voiced_sound;
++ case OBS_KEY_DEAD_SEMIVOICED_SOUND:
++ return XK_dead_semivoiced_sound;
++ case OBS_KEY_DEAD_BELOWDOT:
++ return XK_dead_belowdot;
++ case OBS_KEY_DEAD_HOOK:
++ return XK_dead_hook;
++ case OBS_KEY_DEAD_HORN:
++ return XK_dead_horn;
++
++ case OBS_KEY_MOUSE1:
++ return MOUSE_1;
++ case OBS_KEY_MOUSE2:
++ return MOUSE_2;
++ case OBS_KEY_MOUSE3:
++ return MOUSE_3;
++ case OBS_KEY_MOUSE4:
++ return MOUSE_4;
++ case OBS_KEY_MOUSE5:
++ return MOUSE_5;
++
++ /* TODO: Implement keys for non-US keyboards */
++ default:;
++ }
++ return 0;
++}
++
++static inline void fill_base_keysyms(struct obs_core_hotkeys *hotkeys)
++{
++ for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
++ hotkeys->platform_context->base_keysyms[i] = get_keysym(i);
++}
++
++static obs_key_t key_from_base_keysym(obs_hotkeys_platform_t *context,
++ xcb_keysym_t code)
++{
++ for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
++ if (context->base_keysyms[i] == (xcb_keysym_t)code) {
++ return (obs_key_t)i;
++ }
++ }
++
++ return OBS_KEY_NONE;
++}
++
++static inline void add_key(obs_hotkeys_platform_t *context, obs_key_t key,
++ int code)
++{
++ xcb_keycode_t kc = (xcb_keycode_t)code;
++ da_push_back(context->keycodes[key].list, &kc);
++
++ if (context->keycodes[key].list.num > 1) {
++ blog(LOG_DEBUG,
++ "found alternate keycode %d for %s "
++ "which already has keycode %d",
++ code, obs_key_to_name(key),
++ (int)context->keycodes[key].list.array[0]);
++ }
++}
++
++static inline bool fill_keycodes(struct obs_core_hotkeys *hotkeys)
++{
++ obs_hotkeys_platform_t *context = hotkeys->platform_context;
++ xcb_connection_t *connection = XGetXCBConnection(context->display);
++ const struct xcb_setup_t *setup = xcb_get_setup(connection);
++ xcb_get_keyboard_mapping_cookie_t cookie;
++ xcb_get_keyboard_mapping_reply_t *reply;
++ xcb_generic_error_t *error = NULL;
++ int code;
++
++ int mincode = setup->min_keycode;
++ int maxcode = setup->max_keycode;
++
++ context->min_keycode = setup->min_keycode;
++
++ cookie = xcb_get_keyboard_mapping(connection, mincode,
++ maxcode - mincode + 1);
++
++ reply = xcb_get_keyboard_mapping_reply(connection, cookie, &error);
++
++ if (error || !reply) {
++ blog(LOG_WARNING, "xcb_get_keyboard_mapping_reply failed");
++ goto error1;
++ }
++
++ const xcb_keysym_t *keysyms = xcb_get_keyboard_mapping_keysyms(reply);
++ int syms_per_code = (int)reply->keysyms_per_keycode;
++
++ context->num_keysyms = (maxcode - mincode + 1) * syms_per_code;
++ context->syms_per_code = syms_per_code;
++ context->keysyms =
++ bmemdup(keysyms, sizeof(xcb_keysym_t) * context->num_keysyms);
++
++ for (code = mincode; code <= maxcode; code++) {
++ const xcb_keysym_t *sym;
++ obs_key_t key;
++
++ sym = &keysyms[(code - mincode) * syms_per_code];
++
++ for (int i = 0; i < syms_per_code; i++) {
++ if (!sym[i])
++ break;
++
++ if (sym[i] == XK_Super_L) {
++ context->super_l_code = code;
++ break;
++ } else if (sym[i] == XK_Super_R) {
++ context->super_r_code = code;
++ break;
++ } else {
++ key = key_from_base_keysym(context, sym[i]);
++
++ if (key != OBS_KEY_NONE) {
++ add_key(context, key, code);
++ break;
++ }
++ }
++ }
++ }
++
++error1:
++ free(reply);
++ free(error);
++
++ return error != NULL || reply == NULL;
++}
++
++static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context,
++ xcb_connection_t *connection)
++{
++ int def_screen_idx = XDefaultScreen(context->display);
++ xcb_screen_iterator_t iter;
++
++ iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
++ while (iter.rem) {
++ if (def_screen_idx-- == 0)
++ return iter.data;
++
++ xcb_screen_next(&iter);
++ }
++
++ return NULL;
++}
++
++static inline xcb_window_t root_window(obs_hotkeys_platform_t *context,
++ xcb_connection_t *connection)
++{
++ xcb_screen_t *screen = default_screen(context, connection);
++ if (screen)
++ return screen->root;
++ return 0;
++}
++
++#if USE_XINPUT
++static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)
++{
++ obs_hotkeys_platform_t *context = hotkeys->platform_context;
++ xcb_connection_t *connection = XGetXCBConnection(context->display);
++ xcb_window_t window = root_window(context, connection);
++
++ struct {
++ xcb_input_event_mask_t head;
++ xcb_input_xi_event_mask_t mask;
++ } mask;
++ mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
++ mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
++ mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
++ XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE;
++
++ xcb_input_xi_select_events(connection, window, 1, &mask.head);
++ xcb_flush(connection);
++}
++#endif
++
++static bool obs_nix_x11_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
++{
++ Display *display = XOpenDisplay(NULL);
++ if (!display)
++ return false;
++
++ hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
++ hotkeys->platform_context->display = display;
++
++#if USE_XINPUT
++ registerMouseEvents(hotkeys);
++#endif
++ fill_base_keysyms(hotkeys);
++ fill_keycodes(hotkeys);
++ return true;
++}
++
++static void obs_nix_x11_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
++{
++ obs_hotkeys_platform_t *context = hotkeys->platform_context;
++
++ for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
++ da_free(context->keycodes[i].list);
++
++ bfree(context->keysyms);
++ bfree(context);
++
++ hotkeys->platform_context = NULL;
++}
++
++static bool mouse_button_pressed(xcb_connection_t *connection,
++ obs_hotkeys_platform_t *context, obs_key_t key)
++{
++ bool ret = false;
++
++#if USE_XINPUT
++ memset(context->pressed, 0, XINPUT_MOUSE_LEN);
++ memset(context->update, 0, XINPUT_MOUSE_LEN);
++
++ xcb_generic_event_t *ev;
++ while ((ev = xcb_poll_for_event(connection))) {
++ if ((ev->response_type & ~80) == XCB_GE_GENERIC) {
++ switch (((xcb_ge_event_t *)ev)->event_type) {
++ case XCB_INPUT_RAW_BUTTON_PRESS: {
++ xcb_input_raw_button_press_event_t *mot;
++ mot = (xcb_input_raw_button_press_event_t *)ev;
++ if (mot->detail < XINPUT_MOUSE_LEN) {
++ context->pressed[mot->detail - 1] =
++ true;
++ context->update[mot->detail - 1] = true;
++ } else {
++ blog(LOG_WARNING, "Unsupported button");
++ }
++ break;
++ }
++ case XCB_INPUT_RAW_BUTTON_RELEASE: {
++ xcb_input_raw_button_release_event_t *mot;
++ mot = (xcb_input_raw_button_release_event_t *)ev;
++ if (mot->detail < XINPUT_MOUSE_LEN)
++ context->update[mot->detail - 1] = true;
++ else
++ blog(LOG_WARNING, "Unsupported button");
++ break;
++ }
++ default:
++ break;
++ }
++ }
++ free(ev);
++ }
++
++ // Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click.
++ // Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored.
++ switch (key) {
++ case OBS_KEY_MOUSE1:
++ ret = context->pressed[0] || context->button_pressed[0];
++ break;
++ case OBS_KEY_MOUSE2:
++ ret = context->pressed[2] || context->button_pressed[2];
++ break;
++ case OBS_KEY_MOUSE3:
++ ret = context->pressed[1] || context->button_pressed[1];
++ break;
++ case OBS_KEY_MOUSE4:
++ ret = context->pressed[7] || context->button_pressed[7];
++ break;
++ case OBS_KEY_MOUSE5:
++ ret = context->pressed[8] || context->button_pressed[8];
++ break;
++ case OBS_KEY_MOUSE6:
++ ret = context->pressed[9] || context->button_pressed[9];
++ break;
++ case OBS_KEY_MOUSE7:
++ ret = context->pressed[10] || context->button_pressed[10];
++ break;
++ case OBS_KEY_MOUSE8:
++ ret = context->pressed[11] || context->button_pressed[11];
++ break;
++ case OBS_KEY_MOUSE9:
++ ret = context->pressed[12] || context->button_pressed[12];
++ break;
++ case OBS_KEY_MOUSE10:
++ ret = context->pressed[13] || context->button_pressed[13];
++ break;
++ case OBS_KEY_MOUSE11:
++ ret = context->pressed[14] || context->button_pressed[14];
++ break;
++ case OBS_KEY_MOUSE12:
++ ret = context->pressed[15] || context->button_pressed[15];
++ break;
++ case OBS_KEY_MOUSE13:
++ ret = context->pressed[16] || context->button_pressed[16];
++ break;
++ case OBS_KEY_MOUSE14:
++ ret = context->pressed[17] || context->button_pressed[17];
++ break;
++ case OBS_KEY_MOUSE15:
++ ret = context->pressed[18] || context->button_pressed[18];
++ break;
++ case OBS_KEY_MOUSE16:
++ ret = context->pressed[19] || context->button_pressed[19];
++ break;
++ case OBS_KEY_MOUSE17:
++ ret = context->pressed[20] || context->button_pressed[20];
++ break;
++ case OBS_KEY_MOUSE18:
++ ret = context->pressed[21] || context->button_pressed[21];
++ break;
++ case OBS_KEY_MOUSE19:
++ ret = context->pressed[22] || context->button_pressed[22];
++ break;
++ case OBS_KEY_MOUSE20:
++ ret = context->pressed[23] || context->button_pressed[23];
++ break;
++ case OBS_KEY_MOUSE21:
++ ret = context->pressed[24] || context->button_pressed[24];
++ break;
++ case OBS_KEY_MOUSE22:
++ ret = context->pressed[25] || context->button_pressed[25];
++ break;
++ case OBS_KEY_MOUSE23:
++ ret = context->pressed[26] || context->button_pressed[26];
++ break;
++ case OBS_KEY_MOUSE24:
++ ret = context->pressed[27] || context->button_pressed[27];
++ break;
++ case OBS_KEY_MOUSE25:
++ ret = context->pressed[28] || context->button_pressed[28];
++ break;
++ case OBS_KEY_MOUSE26:
++ ret = context->pressed[29] || context->button_pressed[29];
++ break;
++ case OBS_KEY_MOUSE27:
++ ret = context->pressed[30] || context->button_pressed[30];
++ break;
++ case OBS_KEY_MOUSE28:
++ ret = context->pressed[31] || context->button_pressed[31];
++ break;
++ case OBS_KEY_MOUSE29:
++ ret = context->pressed[32] || context->button_pressed[32];
++ break;
++ default:
++ break;
++ }
++
++ for (int i = 0; i != XINPUT_MOUSE_LEN; i++)
++ if (context->update[i])
++ context->button_pressed[i] = context->pressed[i];
++#else
++ xcb_generic_error_t *error = NULL;
++ xcb_query_pointer_cookie_t qpc;
++ xcb_query_pointer_reply_t *reply;
++
++ qpc = xcb_query_pointer(connection, root_window(context, connection));
++ reply = xcb_query_pointer_reply(connection, qpc, &error);
++
++ if (error) {
++ blog(LOG_WARNING, "xcb_query_pointer_reply failed");
++ } else {
++ uint16_t buttons = reply->mask;
++
++ switch (key) {
++ case OBS_KEY_MOUSE1:
++ ret = buttons & XCB_BUTTON_MASK_1;
++ break;
++ case OBS_KEY_MOUSE2:
++ ret = buttons & XCB_BUTTON_MASK_3;
++ break;
++ case OBS_KEY_MOUSE3:
++ ret = buttons & XCB_BUTTON_MASK_2;
++ break;
++ default:;
++ }
++ }
++
++ free(reply);
++ free(error);
++#endif
++ return ret;
++}
++
++static inline bool keycode_pressed(xcb_query_keymap_reply_t *reply,
++ xcb_keycode_t code)
++{
++ return (reply->keys[code / 8] & (1 << (code % 8))) != 0;
++}
++
++static bool key_pressed(xcb_connection_t *connection,
++ obs_hotkeys_platform_t *context, obs_key_t key)
++{
++ struct keycode_list *codes = &context->keycodes[key];
++ xcb_generic_error_t *error = NULL;
++ xcb_query_keymap_reply_t *reply;
++ bool pressed = false;
++
++ reply = xcb_query_keymap_reply(connection, xcb_query_keymap(connection),
++ &error);
++ if (error) {
++ blog(LOG_WARNING, "xcb_query_keymap failed");
++
++ } else if (key == OBS_KEY_META) {
++ pressed = keycode_pressed(reply, context->super_l_code) ||
++ keycode_pressed(reply, context->super_r_code);
++
++ } else {
++ for (size_t i = 0; i < codes->list.num; i++) {
++ if (keycode_pressed(reply, codes->list.array[i])) {
++ pressed = true;
++ break;
++ }
++ }
++ }
++
++ free(reply);
++ free(error);
++ return pressed;
++}
++
++static bool
++obs_nix_x11_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
++ obs_key_t key)
++{
++ xcb_connection_t *conn = XGetXCBConnection(context->display);
++
++ if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
++ return mouse_button_pressed(conn, context, key);
++ } else {
++ return key_pressed(conn, context, key);
++ }
++}
++
++static bool get_key_translation(struct dstr *dstr, xcb_keycode_t keycode)
++{
++ xcb_connection_t *connection;
++ char name[128];
++
++ connection = XGetXCBConnection(obs->hotkeys.platform_context->display);
++
++ XKeyEvent event = {0};
++ event.type = KeyPress;
++ event.display = obs->hotkeys.platform_context->display;
++ event.keycode = keycode;
++ event.root = root_window(obs->hotkeys.platform_context, connection);
++ event.window = event.root;
++
++ if (keycode) {
++ int len = XLookupString(&event, name, 128, NULL, NULL);
++ if (len) {
++ dstr_ncopy(dstr, name, len);
++ dstr_to_upper(dstr);
++ return true;
++ }
++ }
++
++ return false;
++}
++
++static void obs_nix_x11_key_to_str(obs_key_t key, struct dstr *dstr)
++{
++ if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
++ if (obs->hotkeys.translations[key]) {
++ dstr_copy(dstr, obs->hotkeys.translations[key]);
++ } else {
++ dstr_printf(dstr, "Mouse %d",
++ (int)(key - OBS_KEY_MOUSE1 + 1));
++ }
++ return;
++ }
++
++ if (key >= OBS_KEY_NUM0 && key <= OBS_KEY_NUM9) {
++ if (obs->hotkeys.translations[key]) {
++ dstr_copy(dstr, obs->hotkeys.translations[key]);
++ } else {
++ dstr_printf(dstr, "Numpad %d",
++ (int)(key - OBS_KEY_NUM0));
++ }
++ return;
++ }
++
++#define translate_key(key, def) \
++ dstr_copy(dstr, obs_get_hotkey_translation(key, def))
++
++ switch (key) {
++ case OBS_KEY_INSERT:
++ return translate_key(key, "Insert");
++ case OBS_KEY_DELETE:
++ return translate_key(key, "Delete");
++ case OBS_KEY_HOME:
++ return translate_key(key, "Home");
++ case OBS_KEY_END:
++ return translate_key(key, "End");
++ case OBS_KEY_PAGEUP:
++ return translate_key(key, "Page Up");
++ case OBS_KEY_PAGEDOWN:
++ return translate_key(key, "Page Down");
++ case OBS_KEY_NUMLOCK:
++ return translate_key(key, "Num Lock");
++ case OBS_KEY_SCROLLLOCK:
++ return translate_key(key, "Scroll Lock");
++ case OBS_KEY_CAPSLOCK:
++ return translate_key(key, "Caps Lock");
++ case OBS_KEY_BACKSPACE:
++ return translate_key(key, "Backspace");
++ case OBS_KEY_TAB:
++ return translate_key(key, "Tab");
++ case OBS_KEY_PRINT:
++ return translate_key(key, "Print");
++ case OBS_KEY_PAUSE:
++ return translate_key(key, "Pause");
++ case OBS_KEY_LEFT:
++ return translate_key(key, "Left");
++ case OBS_KEY_RIGHT:
++ return translate_key(key, "Right");
++ case OBS_KEY_UP:
++ return translate_key(key, "Up");
++ case OBS_KEY_DOWN:
++ return translate_key(key, "Down");
++ case OBS_KEY_SHIFT:
++ return translate_key(key, "Shift");
++ case OBS_KEY_ALT:
++ return translate_key(key, "Alt");
++ case OBS_KEY_CONTROL:
++ return translate_key(key, "Control");
++ case OBS_KEY_META:
++ return translate_key(key, "Super");
++ case OBS_KEY_MENU:
++ return translate_key(key, "Menu");
++ case OBS_KEY_NUMASTERISK:
++ return translate_key(key, "Numpad *");
++ case OBS_KEY_NUMPLUS:
++ return translate_key(key, "Numpad +");
++ case OBS_KEY_NUMCOMMA:
++ return translate_key(key, "Numpad ,");
++ case OBS_KEY_NUMPERIOD:
++ return translate_key(key, "Numpad .");
++ case OBS_KEY_NUMSLASH:
++ return translate_key(key, "Numpad /");
++ case OBS_KEY_SPACE:
++ return translate_key(key, "Space");
++ case OBS_KEY_ESCAPE:
++ return translate_key(key, "Escape");
++ default:;
++ }
++
++ if (key >= OBS_KEY_F1 && key <= OBS_KEY_F35) {
++ dstr_printf(dstr, "F%d", (int)(key - OBS_KEY_F1 + 1));
++ return;
++ }
++
++ obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
++ struct keycode_list *keycodes = &context->keycodes[key];
++
++ for (size_t i = 0; i < keycodes->list.num; i++) {
++ if (get_key_translation(dstr, keycodes->list.array[i])) {
++ break;
++ }
++ }
++
++ if (key != OBS_KEY_NONE && dstr_is_empty(dstr)) {
++ dstr_copy(dstr, obs_key_to_name(key));
++ }
++}
++
++static obs_key_t key_from_keycode(obs_hotkeys_platform_t *context,
++ xcb_keycode_t code)
++{
++ for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
++ struct keycode_list *codes = &context->keycodes[i];
++
++ for (size_t j = 0; j < codes->list.num; j++) {
++ if (codes->list.array[j] == code) {
++ return (obs_key_t)i;
++ }
++ }
++ }
++
++ return OBS_KEY_NONE;
++}
++
++static obs_key_t obs_nix_x11_key_from_virtual_key(int sym)
++{
++ obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
++ const xcb_keysym_t *keysyms = context->keysyms;
++ int syms_per_code = context->syms_per_code;
++ int num_keysyms = context->num_keysyms;
++
++ if (sym == 0)
++ return OBS_KEY_NONE;
++
++ for (int i = 0; i < num_keysyms; i++) {
++ if (keysyms[i] == (xcb_keysym_t)sym) {
++ xcb_keycode_t code = (xcb_keycode_t)(i / syms_per_code);
++ code += context->min_keycode;
++ obs_key_t key = key_from_keycode(context, code);
++
++ return key;
++ }
++ }
++
++ return OBS_KEY_NONE;
++}
++
++static int obs_nix_x11_key_to_virtual_key(obs_key_t key)
++{
++ if (key == OBS_KEY_META)
++ return XK_Super_L;
++
++ return (int)obs->hotkeys.platform_context->base_keysyms[(int)key];
++}
++
++static const struct obs_nix_hotkeys_vtable x11_hotkeys_vtable = {
++ .init = obs_nix_x11_hotkeys_platform_init,
++ .free = obs_nix_x11_hotkeys_platform_free,
++ .is_pressed = obs_nix_x11_hotkeys_platform_is_pressed,
++ .key_to_str = obs_nix_x11_key_to_str,
++ .key_from_virtual_key = obs_nix_x11_key_from_virtual_key,
++ .key_to_virtual_key = obs_nix_x11_key_to_virtual_key,
++};
++
++const struct obs_nix_hotkeys_vtable *obs_nix_x11_get_hotkeys_vtable(void)
++{
++ return &x11_hotkeys_vtable;
++}
+diff --git a/libobs/obs-nix-x11.h b/libobs/obs-nix-x11.h
+new file mode 100644
+index 00000000..34b9dd0f
+--- /dev/null
++++ b/libobs/obs-nix-x11.h
+@@ -0,0 +1,22 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "obs-nix.h"
++
++void obs_nix_x11_log_info(void);
++
++const struct obs_nix_hotkeys_vtable *obs_nix_x11_get_hotkeys_vtable(void);
+diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c
+index de039c15..df1d99df 100644
+--- a/libobs/obs-nix.c
++++ b/libobs/obs-nix.c
+@@ -17,6 +17,8 @@
+ ******************************************************************************/
+
+ #include "obs-internal.h"
++#include "obs-nix.h"
++#include "obs-nix-x11.h"
+ #if defined(__FreeBSD__)
+ #define _GNU_SOURCE
+ #endif
+@@ -28,15 +30,6 @@
+ #endif
+ #include <sys/sysinfo.h>
+ #include <sys/utsname.h>
+-#include <xcb/xcb.h>
+-#if USE_XINPUT
+-#include <xcb/xinput.h>
+-#endif
+-#include <X11/Xlib.h>
+-#include <X11/Xutil.h>
+-#include <X11/Xlib-xcb.h>
+-#include <X11/XF86keysym.h>
+-#include <X11/Sunkeysym.h>
+ #include <inttypes.h>
+
+ const char *get_module_extension(void)
+@@ -62,6 +55,8 @@ static const char *module_data[] = {
+ static const int module_patterns_size =
+ sizeof(module_bin) / sizeof(module_bin[0]);
+
++static const struct obs_nix_hotkeys_vtable *hotkeys_vtable = NULL;
++
+ void add_default_module_paths(void)
+ {
+ for (int i = 0; i < module_patterns_size; i++)
+@@ -238,37 +233,6 @@ static void log_kernel_version(void)
+ blog(LOG_INFO, "Kernel Version: %s %s", info.sysname, info.release);
+ }
+
+-static void log_x_info(void)
+-{
+- Display *dpy = XOpenDisplay(NULL);
+- if (!dpy) {
+- blog(LOG_INFO, "Unable to open X display");
+- return;
+- }
+-
+- int protocol_version = ProtocolVersion(dpy);
+- int protocol_revision = ProtocolRevision(dpy);
+- int vendor_release = VendorRelease(dpy);
+- const char *vendor_name = ServerVendor(dpy);
+-
+- if (strstr(vendor_name, "X.Org")) {
+- blog(LOG_INFO,
+- "Window System: X%d.%d, Vendor: %s, Version: %d"
+- ".%d.%d",
+- protocol_version, protocol_revision, vendor_name,
+- vendor_release / 10000000, (vendor_release / 100000) % 100,
+- (vendor_release / 1000) % 100);
+- } else {
+- blog(LOG_INFO,
+- "Window System: X%d.%d - vendor string: %s - "
+- "vendor release: %d",
+- protocol_version, protocol_revision, vendor_name,
+- vendor_release);
+- }
+-
+- XCloseDisplay(dpy);
+-}
+-
+ #if defined(__linux__)
+ static void log_distribution_info(void)
+ {
+@@ -325,1203 +289,41 @@ void log_system_info(void)
+ #if defined(__linux__)
+ log_distribution_info();
+ #endif
+- log_x_info();
+-}
+-
+-/* So here's how linux works with key mapping:
+- *
+- * First, there's a global key symbol enum (xcb_keysym_t) which has unique
+- * values for all possible symbols keys can have (e.g., '1' and '!' are
+- * different values).
+- *
+- * Then there's a key code (xcb_keycode_t), which is basically an index to the
+- * actual key itself on the keyboard (e.g., '1' and '!' will share the same
+- * value).
+- *
+- * xcb_keysym_t values should be given to libobs, and libobs will translate it
+- * to an obs_key_t, and although xcb_keysym_t can differ ('!' vs '1'), it will
+- * get the obs_key_t value that represents the actual key pressed; in other
+- * words it will be based on the key code rather than the key symbol. The same
+- * applies to checking key press states.
+- */
+-
+-struct keycode_list {
+- DARRAY(xcb_keycode_t) list;
+-};
+-
+-struct obs_hotkeys_platform {
+- Display *display;
+- xcb_keysym_t base_keysyms[OBS_KEY_LAST_VALUE];
+- struct keycode_list keycodes[OBS_KEY_LAST_VALUE];
+- xcb_keycode_t min_keycode;
+- xcb_keycode_t super_l_code;
+- xcb_keycode_t super_r_code;
+-
+- /* stores a copy of the keysym map for keycodes */
+- xcb_keysym_t *keysyms;
+- int num_keysyms;
+- int syms_per_code;
+-
+-#if USE_XINPUT
+- bool pressed[XINPUT_MOUSE_LEN];
+- bool update[XINPUT_MOUSE_LEN];
+- bool button_pressed[XINPUT_MOUSE_LEN];
+-#endif
+-};
+-
+-#define MOUSE_1 (1 << 16)
+-#define MOUSE_2 (2 << 16)
+-#define MOUSE_3 (3 << 16)
+-#define MOUSE_4 (4 << 16)
+-#define MOUSE_5 (5 << 16)
+-
+-static int get_keysym(obs_key_t key)
+-{
+- switch (key) {
+- case OBS_KEY_RETURN:
+- return XK_Return;
+- case OBS_KEY_ESCAPE:
+- return XK_Escape;
+- case OBS_KEY_TAB:
+- return XK_Tab;
+- case OBS_KEY_BACKSPACE:
+- return XK_BackSpace;
+- case OBS_KEY_INSERT:
+- return XK_Insert;
+- case OBS_KEY_DELETE:
+- return XK_Delete;
+- case OBS_KEY_PAUSE:
+- return XK_Pause;
+- case OBS_KEY_PRINT:
+- return XK_Print;
+- case OBS_KEY_HOME:
+- return XK_Home;
+- case OBS_KEY_END:
+- return XK_End;
+- case OBS_KEY_LEFT:
+- return XK_Left;
+- case OBS_KEY_UP:
+- return XK_Up;
+- case OBS_KEY_RIGHT:
+- return XK_Right;
+- case OBS_KEY_DOWN:
+- return XK_Down;
+- case OBS_KEY_PAGEUP:
+- return XK_Prior;
+- case OBS_KEY_PAGEDOWN:
+- return XK_Next;
+-
+- case OBS_KEY_SHIFT:
+- return XK_Shift_L;
+- case OBS_KEY_CONTROL:
+- return XK_Control_L;
+- case OBS_KEY_ALT:
+- return XK_Alt_L;
+- case OBS_KEY_CAPSLOCK:
+- return XK_Caps_Lock;
+- case OBS_KEY_NUMLOCK:
+- return XK_Num_Lock;
+- case OBS_KEY_SCROLLLOCK:
+- return XK_Scroll_Lock;
+-
+- case OBS_KEY_F1:
+- return XK_F1;
+- case OBS_KEY_F2:
+- return XK_F2;
+- case OBS_KEY_F3:
+- return XK_F3;
+- case OBS_KEY_F4:
+- return XK_F4;
+- case OBS_KEY_F5:
+- return XK_F5;
+- case OBS_KEY_F6:
+- return XK_F6;
+- case OBS_KEY_F7:
+- return XK_F7;
+- case OBS_KEY_F8:
+- return XK_F8;
+- case OBS_KEY_F9:
+- return XK_F9;
+- case OBS_KEY_F10:
+- return XK_F10;
+- case OBS_KEY_F11:
+- return XK_F11;
+- case OBS_KEY_F12:
+- return XK_F12;
+- case OBS_KEY_F13:
+- return XK_F13;
+- case OBS_KEY_F14:
+- return XK_F14;
+- case OBS_KEY_F15:
+- return XK_F15;
+- case OBS_KEY_F16:
+- return XK_F16;
+- case OBS_KEY_F17:
+- return XK_F17;
+- case OBS_KEY_F18:
+- return XK_F18;
+- case OBS_KEY_F19:
+- return XK_F19;
+- case OBS_KEY_F20:
+- return XK_F20;
+- case OBS_KEY_F21:
+- return XK_F21;
+- case OBS_KEY_F22:
+- return XK_F22;
+- case OBS_KEY_F23:
+- return XK_F23;
+- case OBS_KEY_F24:
+- return XK_F24;
+- case OBS_KEY_F25:
+- return XK_F25;
+- case OBS_KEY_F26:
+- return XK_F26;
+- case OBS_KEY_F27:
+- return XK_F27;
+- case OBS_KEY_F28:
+- return XK_F28;
+- case OBS_KEY_F29:
+- return XK_F29;
+- case OBS_KEY_F30:
+- return XK_F30;
+- case OBS_KEY_F31:
+- return XK_F31;
+- case OBS_KEY_F32:
+- return XK_F32;
+- case OBS_KEY_F33:
+- return XK_F33;
+- case OBS_KEY_F34:
+- return XK_F34;
+- case OBS_KEY_F35:
+- return XK_F35;
+-
+- case OBS_KEY_MENU:
+- return XK_Menu;
+- case OBS_KEY_HYPER_L:
+- return XK_Hyper_L;
+- case OBS_KEY_HYPER_R:
+- return XK_Hyper_R;
+- case OBS_KEY_HELP:
+- return XK_Help;
+- case OBS_KEY_CANCEL:
+- return XK_Cancel;
+- case OBS_KEY_FIND:
+- return XK_Find;
+- case OBS_KEY_REDO:
+- return XK_Redo;
+- case OBS_KEY_UNDO:
+- return XK_Undo;
+- case OBS_KEY_SPACE:
+- return XK_space;
+-
+- case OBS_KEY_COPY:
+- return XF86XK_Copy;
+- case OBS_KEY_CUT:
+- return XF86XK_Cut;
+- case OBS_KEY_OPEN:
+- return XF86XK_Open;
+- case OBS_KEY_PASTE:
+- return XF86XK_Paste;
+- case OBS_KEY_FRONT:
+- return SunXK_Front;
+- case OBS_KEY_PROPS:
+- return SunXK_Props;
+-
+- case OBS_KEY_EXCLAM:
+- return XK_exclam;
+- case OBS_KEY_QUOTEDBL:
+- return XK_quotedbl;
+- case OBS_KEY_NUMBERSIGN:
+- return XK_numbersign;
+- case OBS_KEY_DOLLAR:
+- return XK_dollar;
+- case OBS_KEY_PERCENT:
+- return XK_percent;
+- case OBS_KEY_AMPERSAND:
+- return XK_ampersand;
+- case OBS_KEY_APOSTROPHE:
+- return XK_apostrophe;
+- case OBS_KEY_PARENLEFT:
+- return XK_parenleft;
+- case OBS_KEY_PARENRIGHT:
+- return XK_parenright;
+- case OBS_KEY_ASTERISK:
+- return XK_asterisk;
+- case OBS_KEY_PLUS:
+- return XK_plus;
+- case OBS_KEY_COMMA:
+- return XK_comma;
+- case OBS_KEY_MINUS:
+- return XK_minus;
+- case OBS_KEY_PERIOD:
+- return XK_period;
+- case OBS_KEY_SLASH:
+- return XK_slash;
+- case OBS_KEY_0:
+- return XK_0;
+- case OBS_KEY_1:
+- return XK_1;
+- case OBS_KEY_2:
+- return XK_2;
+- case OBS_KEY_3:
+- return XK_3;
+- case OBS_KEY_4:
+- return XK_4;
+- case OBS_KEY_5:
+- return XK_5;
+- case OBS_KEY_6:
+- return XK_6;
+- case OBS_KEY_7:
+- return XK_7;
+- case OBS_KEY_8:
+- return XK_8;
+- case OBS_KEY_9:
+- return XK_9;
+- case OBS_KEY_NUMEQUAL:
+- return XK_KP_Equal;
+- case OBS_KEY_NUMASTERISK:
+- return XK_KP_Multiply;
+- case OBS_KEY_NUMPLUS:
+- return XK_KP_Add;
+- case OBS_KEY_NUMCOMMA:
+- return XK_KP_Separator;
+- case OBS_KEY_NUMMINUS:
+- return XK_KP_Subtract;
+- case OBS_KEY_NUMPERIOD:
+- return XK_KP_Decimal;
+- case OBS_KEY_NUMSLASH:
+- return XK_KP_Divide;
+- case OBS_KEY_NUM0:
+- return XK_KP_0;
+- case OBS_KEY_NUM1:
+- return XK_KP_1;
+- case OBS_KEY_NUM2:
+- return XK_KP_2;
+- case OBS_KEY_NUM3:
+- return XK_KP_3;
+- case OBS_KEY_NUM4:
+- return XK_KP_4;
+- case OBS_KEY_NUM5:
+- return XK_KP_5;
+- case OBS_KEY_NUM6:
+- return XK_KP_6;
+- case OBS_KEY_NUM7:
+- return XK_KP_7;
+- case OBS_KEY_NUM8:
+- return XK_KP_8;
+- case OBS_KEY_NUM9:
+- return XK_KP_9;
+- case OBS_KEY_COLON:
+- return XK_colon;
+- case OBS_KEY_SEMICOLON:
+- return XK_semicolon;
+- case OBS_KEY_LESS:
+- return XK_less;
+- case OBS_KEY_EQUAL:
+- return XK_equal;
+- case OBS_KEY_GREATER:
+- return XK_greater;
+- case OBS_KEY_QUESTION:
+- return XK_question;
+- case OBS_KEY_AT:
+- return XK_at;
+- case OBS_KEY_A:
+- return XK_A;
+- case OBS_KEY_B:
+- return XK_B;
+- case OBS_KEY_C:
+- return XK_C;
+- case OBS_KEY_D:
+- return XK_D;
+- case OBS_KEY_E:
+- return XK_E;
+- case OBS_KEY_F:
+- return XK_F;
+- case OBS_KEY_G:
+- return XK_G;
+- case OBS_KEY_H:
+- return XK_H;
+- case OBS_KEY_I:
+- return XK_I;
+- case OBS_KEY_J:
+- return XK_J;
+- case OBS_KEY_K:
+- return XK_K;
+- case OBS_KEY_L:
+- return XK_L;
+- case OBS_KEY_M:
+- return XK_M;
+- case OBS_KEY_N:
+- return XK_N;
+- case OBS_KEY_O:
+- return XK_O;
+- case OBS_KEY_P:
+- return XK_P;
+- case OBS_KEY_Q:
+- return XK_Q;
+- case OBS_KEY_R:
+- return XK_R;
+- case OBS_KEY_S:
+- return XK_S;
+- case OBS_KEY_T:
+- return XK_T;
+- case OBS_KEY_U:
+- return XK_U;
+- case OBS_KEY_V:
+- return XK_V;
+- case OBS_KEY_W:
+- return XK_W;
+- case OBS_KEY_X:
+- return XK_X;
+- case OBS_KEY_Y:
+- return XK_Y;
+- case OBS_KEY_Z:
+- return XK_Z;
+- case OBS_KEY_BRACKETLEFT:
+- return XK_bracketleft;
+- case OBS_KEY_BACKSLASH:
+- return XK_backslash;
+- case OBS_KEY_BRACKETRIGHT:
+- return XK_bracketright;
+- case OBS_KEY_ASCIICIRCUM:
+- return XK_asciicircum;
+- case OBS_KEY_UNDERSCORE:
+- return XK_underscore;
+- case OBS_KEY_QUOTELEFT:
+- return XK_quoteleft;
+- case OBS_KEY_BRACELEFT:
+- return XK_braceleft;
+- case OBS_KEY_BAR:
+- return XK_bar;
+- case OBS_KEY_BRACERIGHT:
+- return XK_braceright;
+- case OBS_KEY_ASCIITILDE:
+- return XK_grave;
+- case OBS_KEY_NOBREAKSPACE:
+- return XK_nobreakspace;
+- case OBS_KEY_EXCLAMDOWN:
+- return XK_exclamdown;
+- case OBS_KEY_CENT:
+- return XK_cent;
+- case OBS_KEY_STERLING:
+- return XK_sterling;
+- case OBS_KEY_CURRENCY:
+- return XK_currency;
+- case OBS_KEY_YEN:
+- return XK_yen;
+- case OBS_KEY_BROKENBAR:
+- return XK_brokenbar;
+- case OBS_KEY_SECTION:
+- return XK_section;
+- case OBS_KEY_DIAERESIS:
+- return XK_diaeresis;
+- case OBS_KEY_COPYRIGHT:
+- return XK_copyright;
+- case OBS_KEY_ORDFEMININE:
+- return XK_ordfeminine;
+- case OBS_KEY_GUILLEMOTLEFT:
+- return XK_guillemotleft;
+- case OBS_KEY_NOTSIGN:
+- return XK_notsign;
+- case OBS_KEY_HYPHEN:
+- return XK_hyphen;
+- case OBS_KEY_REGISTERED:
+- return XK_registered;
+- case OBS_KEY_MACRON:
+- return XK_macron;
+- case OBS_KEY_DEGREE:
+- return XK_degree;
+- case OBS_KEY_PLUSMINUS:
+- return XK_plusminus;
+- case OBS_KEY_TWOSUPERIOR:
+- return XK_twosuperior;
+- case OBS_KEY_THREESUPERIOR:
+- return XK_threesuperior;
+- case OBS_KEY_ACUTE:
+- return XK_acute;
+- case OBS_KEY_MU:
+- return XK_mu;
+- case OBS_KEY_PARAGRAPH:
+- return XK_paragraph;
+- case OBS_KEY_PERIODCENTERED:
+- return XK_periodcentered;
+- case OBS_KEY_CEDILLA:
+- return XK_cedilla;
+- case OBS_KEY_ONESUPERIOR:
+- return XK_onesuperior;
+- case OBS_KEY_MASCULINE:
+- return XK_masculine;
+- case OBS_KEY_GUILLEMOTRIGHT:
+- return XK_guillemotright;
+- case OBS_KEY_ONEQUARTER:
+- return XK_onequarter;
+- case OBS_KEY_ONEHALF:
+- return XK_onehalf;
+- case OBS_KEY_THREEQUARTERS:
+- return XK_threequarters;
+- case OBS_KEY_QUESTIONDOWN:
+- return XK_questiondown;
+- case OBS_KEY_AGRAVE:
+- return XK_Agrave;
+- case OBS_KEY_AACUTE:
+- return XK_Aacute;
+- case OBS_KEY_ACIRCUMFLEX:
+- return XK_Acircumflex;
+- case OBS_KEY_ATILDE:
+- return XK_Atilde;
+- case OBS_KEY_ADIAERESIS:
+- return XK_Adiaeresis;
+- case OBS_KEY_ARING:
+- return XK_Aring;
+- case OBS_KEY_AE:
+- return XK_AE;
+- case OBS_KEY_CCEDILLA:
+- return XK_cedilla;
+- case OBS_KEY_EGRAVE:
+- return XK_Egrave;
+- case OBS_KEY_EACUTE:
+- return XK_Eacute;
+- case OBS_KEY_ECIRCUMFLEX:
+- return XK_Ecircumflex;
+- case OBS_KEY_EDIAERESIS:
+- return XK_Ediaeresis;
+- case OBS_KEY_IGRAVE:
+- return XK_Igrave;
+- case OBS_KEY_IACUTE:
+- return XK_Iacute;
+- case OBS_KEY_ICIRCUMFLEX:
+- return XK_Icircumflex;
+- case OBS_KEY_IDIAERESIS:
+- return XK_Idiaeresis;
+- case OBS_KEY_ETH:
+- return XK_ETH;
+- case OBS_KEY_NTILDE:
+- return XK_Ntilde;
+- case OBS_KEY_OGRAVE:
+- return XK_Ograve;
+- case OBS_KEY_OACUTE:
+- return XK_Oacute;
+- case OBS_KEY_OCIRCUMFLEX:
+- return XK_Ocircumflex;
+- case OBS_KEY_ODIAERESIS:
+- return XK_Odiaeresis;
+- case OBS_KEY_MULTIPLY:
+- return XK_multiply;
+- case OBS_KEY_OOBLIQUE:
+- return XK_Ooblique;
+- case OBS_KEY_UGRAVE:
+- return XK_Ugrave;
+- case OBS_KEY_UACUTE:
+- return XK_Uacute;
+- case OBS_KEY_UCIRCUMFLEX:
+- return XK_Ucircumflex;
+- case OBS_KEY_UDIAERESIS:
+- return XK_Udiaeresis;
+- case OBS_KEY_YACUTE:
+- return XK_Yacute;
+- case OBS_KEY_THORN:
+- return XK_Thorn;
+- case OBS_KEY_SSHARP:
+- return XK_ssharp;
+- case OBS_KEY_DIVISION:
+- return XK_division;
+- case OBS_KEY_YDIAERESIS:
+- return XK_Ydiaeresis;
+- case OBS_KEY_MULTI_KEY:
+- return XK_Multi_key;
+- case OBS_KEY_CODEINPUT:
+- return XK_Codeinput;
+- case OBS_KEY_SINGLECANDIDATE:
+- return XK_SingleCandidate;
+- case OBS_KEY_MULTIPLECANDIDATE:
+- return XK_MultipleCandidate;
+- case OBS_KEY_PREVIOUSCANDIDATE:
+- return XK_PreviousCandidate;
+- case OBS_KEY_MODE_SWITCH:
+- return XK_Mode_switch;
+- case OBS_KEY_KANJI:
+- return XK_Kanji;
+- case OBS_KEY_MUHENKAN:
+- return XK_Muhenkan;
+- case OBS_KEY_HENKAN:
+- return XK_Henkan;
+- case OBS_KEY_ROMAJI:
+- return XK_Romaji;
+- case OBS_KEY_HIRAGANA:
+- return XK_Hiragana;
+- case OBS_KEY_KATAKANA:
+- return XK_Katakana;
+- case OBS_KEY_HIRAGANA_KATAKANA:
+- return XK_Hiragana_Katakana;
+- case OBS_KEY_ZENKAKU:
+- return XK_Zenkaku;
+- case OBS_KEY_HANKAKU:
+- return XK_Hankaku;
+- case OBS_KEY_ZENKAKU_HANKAKU:
+- return XK_Zenkaku_Hankaku;
+- case OBS_KEY_TOUROKU:
+- return XK_Touroku;
+- case OBS_KEY_MASSYO:
+- return XK_Massyo;
+- case OBS_KEY_KANA_LOCK:
+- return XK_Kana_Lock;
+- case OBS_KEY_KANA_SHIFT:
+- return XK_Kana_Shift;
+- case OBS_KEY_EISU_SHIFT:
+- return XK_Eisu_Shift;
+- case OBS_KEY_EISU_TOGGLE:
+- return XK_Eisu_toggle;
+- case OBS_KEY_HANGUL:
+- return XK_Hangul;
+- case OBS_KEY_HANGUL_START:
+- return XK_Hangul_Start;
+- case OBS_KEY_HANGUL_END:
+- return XK_Hangul_End;
+- case OBS_KEY_HANGUL_HANJA:
+- return XK_Hangul_Hanja;
+- case OBS_KEY_HANGUL_JAMO:
+- return XK_Hangul_Jamo;
+- case OBS_KEY_HANGUL_ROMAJA:
+- return XK_Hangul_Romaja;
+- case OBS_KEY_HANGUL_BANJA:
+- return XK_Hangul_Banja;
+- case OBS_KEY_HANGUL_PREHANJA:
+- return XK_Hangul_PreHanja;
+- case OBS_KEY_HANGUL_POSTHANJA:
+- return XK_Hangul_PostHanja;
+- case OBS_KEY_HANGUL_SPECIAL:
+- return XK_Hangul_Special;
+- case OBS_KEY_DEAD_GRAVE:
+- return XK_dead_grave;
+- case OBS_KEY_DEAD_ACUTE:
+- return XK_dead_acute;
+- case OBS_KEY_DEAD_CIRCUMFLEX:
+- return XK_dead_circumflex;
+- case OBS_KEY_DEAD_TILDE:
+- return XK_dead_tilde;
+- case OBS_KEY_DEAD_MACRON:
+- return XK_dead_macron;
+- case OBS_KEY_DEAD_BREVE:
+- return XK_dead_breve;
+- case OBS_KEY_DEAD_ABOVEDOT:
+- return XK_dead_abovedot;
+- case OBS_KEY_DEAD_DIAERESIS:
+- return XK_dead_diaeresis;
+- case OBS_KEY_DEAD_ABOVERING:
+- return XK_dead_abovering;
+- case OBS_KEY_DEAD_DOUBLEACUTE:
+- return XK_dead_doubleacute;
+- case OBS_KEY_DEAD_CARON:
+- return XK_dead_caron;
+- case OBS_KEY_DEAD_CEDILLA:
+- return XK_dead_cedilla;
+- case OBS_KEY_DEAD_OGONEK:
+- return XK_dead_ogonek;
+- case OBS_KEY_DEAD_IOTA:
+- return XK_dead_iota;
+- case OBS_KEY_DEAD_VOICED_SOUND:
+- return XK_dead_voiced_sound;
+- case OBS_KEY_DEAD_SEMIVOICED_SOUND:
+- return XK_dead_semivoiced_sound;
+- case OBS_KEY_DEAD_BELOWDOT:
+- return XK_dead_belowdot;
+- case OBS_KEY_DEAD_HOOK:
+- return XK_dead_hook;
+- case OBS_KEY_DEAD_HORN:
+- return XK_dead_horn;
+-
+- case OBS_KEY_MOUSE1:
+- return MOUSE_1;
+- case OBS_KEY_MOUSE2:
+- return MOUSE_2;
+- case OBS_KEY_MOUSE3:
+- return MOUSE_3;
+- case OBS_KEY_MOUSE4:
+- return MOUSE_4;
+- case OBS_KEY_MOUSE5:
+- return MOUSE_5;
+-
+- /* TODO: Implement keys for non-US keyboards */
+- default:;
+- }
+- return 0;
+-}
+-
+-static inline void fill_base_keysyms(struct obs_core_hotkeys *hotkeys)
+-{
+- for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
+- hotkeys->platform_context->base_keysyms[i] = get_keysym(i);
+-}
+-
+-static obs_key_t key_from_base_keysym(obs_hotkeys_platform_t *context,
+- xcb_keysym_t code)
+-{
+- for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
+- if (context->base_keysyms[i] == (xcb_keysym_t)code) {
+- return (obs_key_t)i;
+- }
+- }
+-
+- return OBS_KEY_NONE;
+-}
+-
+-static inline void add_key(obs_hotkeys_platform_t *context, obs_key_t key,
+- int code)
+-{
+- xcb_keycode_t kc = (xcb_keycode_t)code;
+- da_push_back(context->keycodes[key].list, &kc);
+-
+- if (context->keycodes[key].list.num > 1) {
+- blog(LOG_DEBUG,
+- "found alternate keycode %d for %s "
+- "which already has keycode %d",
+- code, obs_key_to_name(key),
+- (int)context->keycodes[key].list.array[0]);
+- }
++ obs_nix_x11_log_info();
+ }
+
+-static inline bool fill_keycodes(struct obs_core_hotkeys *hotkeys)
+-{
+- obs_hotkeys_platform_t *context = hotkeys->platform_context;
+- xcb_connection_t *connection = XGetXCBConnection(context->display);
+- const struct xcb_setup_t *setup = xcb_get_setup(connection);
+- xcb_get_keyboard_mapping_cookie_t cookie;
+- xcb_get_keyboard_mapping_reply_t *reply;
+- xcb_generic_error_t *error = NULL;
+- int code;
+-
+- int mincode = setup->min_keycode;
+- int maxcode = setup->max_keycode;
+-
+- context->min_keycode = setup->min_keycode;
+-
+- cookie = xcb_get_keyboard_mapping(connection, mincode,
+- maxcode - mincode + 1);
+-
+- reply = xcb_get_keyboard_mapping_reply(connection, cookie, &error);
+-
+- if (error || !reply) {
+- blog(LOG_WARNING, "xcb_get_keyboard_mapping_reply failed");
+- goto error1;
+- }
+-
+- const xcb_keysym_t *keysyms = xcb_get_keyboard_mapping_keysyms(reply);
+- int syms_per_code = (int)reply->keysyms_per_keycode;
+-
+- context->num_keysyms = (maxcode - mincode + 1) * syms_per_code;
+- context->syms_per_code = syms_per_code;
+- context->keysyms =
+- bmemdup(keysyms, sizeof(xcb_keysym_t) * context->num_keysyms);
+-
+- for (code = mincode; code <= maxcode; code++) {
+- const xcb_keysym_t *sym;
+- obs_key_t key;
+-
+- sym = &keysyms[(code - mincode) * syms_per_code];
+-
+- for (int i = 0; i < syms_per_code; i++) {
+- if (!sym[i])
+- break;
+-
+- if (sym[i] == XK_Super_L) {
+- context->super_l_code = code;
+- break;
+- } else if (sym[i] == XK_Super_R) {
+- context->super_r_code = code;
+- break;
+- } else {
+- key = key_from_base_keysym(context, sym[i]);
+-
+- if (key != OBS_KEY_NONE) {
+- add_key(context, key, code);
+- break;
+- }
+- }
+- }
+- }
+-
+-error1:
+- free(reply);
+- free(error);
+-
+- return error != NULL || reply == NULL;
+-}
+-
+-static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context,
+- xcb_connection_t *connection)
+-{
+- int def_screen_idx = XDefaultScreen(context->display);
+- xcb_screen_iterator_t iter;
+-
+- iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+- while (iter.rem) {
+- if (def_screen_idx-- == 0)
+- return iter.data;
+-
+- xcb_screen_next(&iter);
+- }
+-
+- return NULL;
+-}
+-
+-static inline xcb_window_t root_window(obs_hotkeys_platform_t *context,
+- xcb_connection_t *connection)
+-{
+- xcb_screen_t *screen = default_screen(context, connection);
+- if (screen)
+- return screen->root;
+- return 0;
+-}
+-
+-#if USE_XINPUT
+-static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)
+-{
+- obs_hotkeys_platform_t *context = hotkeys->platform_context;
+- xcb_connection_t *connection = XGetXCBConnection(context->display);
+- xcb_window_t window = root_window(context, connection);
+-
+- struct {
+- xcb_input_event_mask_t head;
+- xcb_input_xi_event_mask_t mask;
+- } mask;
+- mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
+- mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
+- mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
+- XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE;
+-
+- xcb_input_xi_select_events(connection, window, 1, &mask.head);
+- xcb_flush(connection);
+-}
+-#endif
+-
+ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
+ {
+- Display *display = XOpenDisplay(NULL);
+- if (!display)
+- return false;
++ hotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();
+
+- hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
+- hotkeys->platform_context->display = display;
+-
+-#if USE_XINPUT
+- registerMouseEvents(hotkeys);
+-#endif
+- fill_base_keysyms(hotkeys);
+- fill_keycodes(hotkeys);
+- return true;
++ return hotkeys_vtable->init(hotkeys);
+ }
+
+ void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
+ {
+- obs_hotkeys_platform_t *context = hotkeys->platform_context;
+-
+- for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
+- da_free(context->keycodes[i].list);
+-
+- XCloseDisplay(context->display);
+- bfree(context->keysyms);
+- bfree(context);
+-
+- hotkeys->platform_context = NULL;
+-}
+-
+-static bool mouse_button_pressed(xcb_connection_t *connection,
+- obs_hotkeys_platform_t *context, obs_key_t key)
+-{
+- bool ret = false;
+-
+-#if USE_XINPUT
+- memset(context->pressed, 0, XINPUT_MOUSE_LEN);
+- memset(context->update, 0, XINPUT_MOUSE_LEN);
+-
+- xcb_generic_event_t *ev;
+- while ((ev = xcb_poll_for_event(connection))) {
+- if ((ev->response_type & ~80) == XCB_GE_GENERIC) {
+- switch (((xcb_ge_event_t *)ev)->event_type) {
+- case XCB_INPUT_RAW_BUTTON_PRESS: {
+- xcb_input_raw_button_press_event_t *mot;
+- mot = (xcb_input_raw_button_press_event_t *)ev;
+- if (mot->detail < XINPUT_MOUSE_LEN) {
+- context->pressed[mot->detail - 1] =
+- true;
+- context->update[mot->detail - 1] = true;
+- } else {
+- blog(LOG_WARNING, "Unsupported button");
+- }
+- break;
+- }
+- case XCB_INPUT_RAW_BUTTON_RELEASE: {
+- xcb_input_raw_button_release_event_t *mot;
+- mot = (xcb_input_raw_button_release_event_t *)ev;
+- if (mot->detail < XINPUT_MOUSE_LEN)
+- context->update[mot->detail - 1] = true;
+- else
+- blog(LOG_WARNING, "Unsupported button");
+- break;
+- }
+- default:
+- break;
+- }
+- }
+- free(ev);
+- }
+-
+- // Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click.
+- // Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored.
+- switch (key) {
+- case OBS_KEY_MOUSE1:
+- ret = context->pressed[0] || context->button_pressed[0];
+- break;
+- case OBS_KEY_MOUSE2:
+- ret = context->pressed[2] || context->button_pressed[2];
+- break;
+- case OBS_KEY_MOUSE3:
+- ret = context->pressed[1] || context->button_pressed[1];
+- break;
+- case OBS_KEY_MOUSE4:
+- ret = context->pressed[7] || context->button_pressed[7];
+- break;
+- case OBS_KEY_MOUSE5:
+- ret = context->pressed[8] || context->button_pressed[8];
+- break;
+- case OBS_KEY_MOUSE6:
+- ret = context->pressed[9] || context->button_pressed[9];
+- break;
+- case OBS_KEY_MOUSE7:
+- ret = context->pressed[10] || context->button_pressed[10];
+- break;
+- case OBS_KEY_MOUSE8:
+- ret = context->pressed[11] || context->button_pressed[11];
+- break;
+- case OBS_KEY_MOUSE9:
+- ret = context->pressed[12] || context->button_pressed[12];
+- break;
+- case OBS_KEY_MOUSE10:
+- ret = context->pressed[13] || context->button_pressed[13];
+- break;
+- case OBS_KEY_MOUSE11:
+- ret = context->pressed[14] || context->button_pressed[14];
+- break;
+- case OBS_KEY_MOUSE12:
+- ret = context->pressed[15] || context->button_pressed[15];
+- break;
+- case OBS_KEY_MOUSE13:
+- ret = context->pressed[16] || context->button_pressed[16];
+- break;
+- case OBS_KEY_MOUSE14:
+- ret = context->pressed[17] || context->button_pressed[17];
+- break;
+- case OBS_KEY_MOUSE15:
+- ret = context->pressed[18] || context->button_pressed[18];
+- break;
+- case OBS_KEY_MOUSE16:
+- ret = context->pressed[19] || context->button_pressed[19];
+- break;
+- case OBS_KEY_MOUSE17:
+- ret = context->pressed[20] || context->button_pressed[20];
+- break;
+- case OBS_KEY_MOUSE18:
+- ret = context->pressed[21] || context->button_pressed[21];
+- break;
+- case OBS_KEY_MOUSE19:
+- ret = context->pressed[22] || context->button_pressed[22];
+- break;
+- case OBS_KEY_MOUSE20:
+- ret = context->pressed[23] || context->button_pressed[23];
+- break;
+- case OBS_KEY_MOUSE21:
+- ret = context->pressed[24] || context->button_pressed[24];
+- break;
+- case OBS_KEY_MOUSE22:
+- ret = context->pressed[25] || context->button_pressed[25];
+- break;
+- case OBS_KEY_MOUSE23:
+- ret = context->pressed[26] || context->button_pressed[26];
+- break;
+- case OBS_KEY_MOUSE24:
+- ret = context->pressed[27] || context->button_pressed[27];
+- break;
+- case OBS_KEY_MOUSE25:
+- ret = context->pressed[28] || context->button_pressed[28];
+- break;
+- case OBS_KEY_MOUSE26:
+- ret = context->pressed[29] || context->button_pressed[29];
+- break;
+- case OBS_KEY_MOUSE27:
+- ret = context->pressed[30] || context->button_pressed[30];
+- break;
+- case OBS_KEY_MOUSE28:
+- ret = context->pressed[31] || context->button_pressed[31];
+- break;
+- case OBS_KEY_MOUSE29:
+- ret = context->pressed[32] || context->button_pressed[32];
+- break;
+- default:
+- break;
+- }
+-
+- for (int i = 0; i != XINPUT_MOUSE_LEN; i++)
+- if (context->update[i])
+- context->button_pressed[i] = context->pressed[i];
+-#else
+- xcb_generic_error_t *error = NULL;
+- xcb_query_pointer_cookie_t qpc;
+- xcb_query_pointer_reply_t *reply;
+-
+- qpc = xcb_query_pointer(connection, root_window(context, connection));
+- reply = xcb_query_pointer_reply(connection, qpc, &error);
+-
+- if (error) {
+- blog(LOG_WARNING, "xcb_query_pointer_reply failed");
+- } else {
+- uint16_t buttons = reply->mask;
+-
+- switch (key) {
+- case OBS_KEY_MOUSE1:
+- ret = buttons & XCB_BUTTON_MASK_1;
+- break;
+- case OBS_KEY_MOUSE2:
+- ret = buttons & XCB_BUTTON_MASK_3;
+- break;
+- case OBS_KEY_MOUSE3:
+- ret = buttons & XCB_BUTTON_MASK_2;
+- break;
+- default:;
+- }
+- }
+-
+- free(reply);
+- free(error);
+-#endif
+- return ret;
+-}
+-
+-static inline bool keycode_pressed(xcb_query_keymap_reply_t *reply,
+- xcb_keycode_t code)
+-{
+- return (reply->keys[code / 8] & (1 << (code % 8))) != 0;
+-}
+-
+-static bool key_pressed(xcb_connection_t *connection,
+- obs_hotkeys_platform_t *context, obs_key_t key)
+-{
+- struct keycode_list *codes = &context->keycodes[key];
+- xcb_generic_error_t *error = NULL;
+- xcb_query_keymap_reply_t *reply;
+- bool pressed = false;
+-
+- reply = xcb_query_keymap_reply(connection, xcb_query_keymap(connection),
+- &error);
+- if (error) {
+- blog(LOG_WARNING, "xcb_query_keymap failed");
+-
+- } else if (key == OBS_KEY_META) {
+- pressed = keycode_pressed(reply, context->super_l_code) ||
+- keycode_pressed(reply, context->super_r_code);
+-
+- } else {
+- for (size_t i = 0; i < codes->list.num; i++) {
+- if (keycode_pressed(reply, codes->list.array[i])) {
+- pressed = true;
+- break;
+- }
+- }
+- }
+-
+- free(reply);
+- free(error);
+- return pressed;
++ hotkeys_vtable->free(hotkeys);
++ hotkeys_vtable = NULL;
+ }
+
+ bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
+ obs_key_t key)
+ {
+- xcb_connection_t *conn = XGetXCBConnection(context->display);
+-
+- if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
+- return mouse_button_pressed(conn, context, key);
+- } else {
+- return key_pressed(conn, context, key);
+- }
+-}
+-
+-static bool get_key_translation(struct dstr *dstr, xcb_keycode_t keycode)
+-{
+- xcb_connection_t *connection;
+- char name[128];
+-
+- connection = XGetXCBConnection(obs->hotkeys.platform_context->display);
+-
+- XKeyEvent event = {0};
+- event.type = KeyPress;
+- event.display = obs->hotkeys.platform_context->display;
+- event.keycode = keycode;
+- event.root = root_window(obs->hotkeys.platform_context, connection);
+- event.window = event.root;
+-
+- if (keycode) {
+- int len = XLookupString(&event, name, 128, NULL, NULL);
+- if (len) {
+- dstr_ncopy(dstr, name, len);
+- dstr_to_upper(dstr);
+- return true;
+- }
+- }
+-
+- return false;
++ return hotkeys_vtable->is_pressed(context, key);
+ }
+
+ void obs_key_to_str(obs_key_t key, struct dstr *dstr)
+ {
+- if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
+- if (obs->hotkeys.translations[key]) {
+- dstr_copy(dstr, obs->hotkeys.translations[key]);
+- } else {
+- dstr_printf(dstr, "Mouse %d",
+- (int)(key - OBS_KEY_MOUSE1 + 1));
+- }
+- return;
+- }
+-
+- if (key >= OBS_KEY_NUM0 && key <= OBS_KEY_NUM9) {
+- if (obs->hotkeys.translations[key]) {
+- dstr_copy(dstr, obs->hotkeys.translations[key]);
+- } else {
+- dstr_printf(dstr, "Numpad %d",
+- (int)(key - OBS_KEY_NUM0));
+- }
+- return;
+- }
+-
+-#define translate_key(key, def) \
+- dstr_copy(dstr, obs_get_hotkey_translation(key, def))
+-
+- switch (key) {
+- case OBS_KEY_INSERT:
+- return translate_key(key, "Insert");
+- case OBS_KEY_DELETE:
+- return translate_key(key, "Delete");
+- case OBS_KEY_HOME:
+- return translate_key(key, "Home");
+- case OBS_KEY_END:
+- return translate_key(key, "End");
+- case OBS_KEY_PAGEUP:
+- return translate_key(key, "Page Up");
+- case OBS_KEY_PAGEDOWN:
+- return translate_key(key, "Page Down");
+- case OBS_KEY_NUMLOCK:
+- return translate_key(key, "Num Lock");
+- case OBS_KEY_SCROLLLOCK:
+- return translate_key(key, "Scroll Lock");
+- case OBS_KEY_CAPSLOCK:
+- return translate_key(key, "Caps Lock");
+- case OBS_KEY_BACKSPACE:
+- return translate_key(key, "Backspace");
+- case OBS_KEY_TAB:
+- return translate_key(key, "Tab");
+- case OBS_KEY_PRINT:
+- return translate_key(key, "Print");
+- case OBS_KEY_PAUSE:
+- return translate_key(key, "Pause");
+- case OBS_KEY_LEFT:
+- return translate_key(key, "Left");
+- case OBS_KEY_RIGHT:
+- return translate_key(key, "Right");
+- case OBS_KEY_UP:
+- return translate_key(key, "Up");
+- case OBS_KEY_DOWN:
+- return translate_key(key, "Down");
+- case OBS_KEY_SHIFT:
+- return translate_key(key, "Shift");
+- case OBS_KEY_ALT:
+- return translate_key(key, "Alt");
+- case OBS_KEY_CONTROL:
+- return translate_key(key, "Control");
+- case OBS_KEY_META:
+- return translate_key(key, "Super");
+- case OBS_KEY_MENU:
+- return translate_key(key, "Menu");
+- case OBS_KEY_NUMASTERISK:
+- return translate_key(key, "Numpad *");
+- case OBS_KEY_NUMPLUS:
+- return translate_key(key, "Numpad +");
+- case OBS_KEY_NUMCOMMA:
+- return translate_key(key, "Numpad ,");
+- case OBS_KEY_NUMPERIOD:
+- return translate_key(key, "Numpad .");
+- case OBS_KEY_NUMSLASH:
+- return translate_key(key, "Numpad /");
+- case OBS_KEY_SPACE:
+- return translate_key(key, "Space");
+- case OBS_KEY_ESCAPE:
+- return translate_key(key, "Escape");
+- default:;
+- }
+-
+- if (key >= OBS_KEY_F1 && key <= OBS_KEY_F35) {
+- dstr_printf(dstr, "F%d", (int)(key - OBS_KEY_F1 + 1));
+- return;
+- }
+-
+- obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
+- struct keycode_list *keycodes = &context->keycodes[key];
+-
+- for (size_t i = 0; i < keycodes->list.num; i++) {
+- if (get_key_translation(dstr, keycodes->list.array[i])) {
+- break;
+- }
+- }
+-
+- if (key != OBS_KEY_NONE && dstr_is_empty(dstr)) {
+- dstr_copy(dstr, obs_key_to_name(key));
+- }
+-}
+-
+-static obs_key_t key_from_keycode(obs_hotkeys_platform_t *context,
+- xcb_keycode_t code)
+-{
+- for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
+- struct keycode_list *codes = &context->keycodes[i];
+-
+- for (size_t j = 0; j < codes->list.num; j++) {
+- if (codes->list.array[j] == code) {
+- return (obs_key_t)i;
+- }
+- }
+- }
+-
+- return OBS_KEY_NONE;
++ return hotkeys_vtable->key_to_str(key, dstr);
+ }
+
+ obs_key_t obs_key_from_virtual_key(int sym)
+ {
+- obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
+- const xcb_keysym_t *keysyms = context->keysyms;
+- int syms_per_code = context->syms_per_code;
+- int num_keysyms = context->num_keysyms;
+-
+- if (sym == 0)
+- return OBS_KEY_NONE;
+-
+- for (int i = 0; i < num_keysyms; i++) {
+- if (keysyms[i] == (xcb_keysym_t)sym) {
+- xcb_keycode_t code = (xcb_keycode_t)(i / syms_per_code);
+- code += context->min_keycode;
+- obs_key_t key = key_from_keycode(context, code);
+-
+- return key;
+- }
+- }
+-
+- return OBS_KEY_NONE;
++ return hotkeys_vtable->key_from_virtual_key(sym);
+ }
+
+ int obs_key_to_virtual_key(obs_key_t key)
+ {
+- if (key == OBS_KEY_META)
+- return XK_Super_L;
+-
+- return (int)obs->hotkeys.platform_context->base_keysyms[(int)key];
++ return hotkeys_vtable->key_to_virtual_key(key);
+ }
+
+ static inline void add_combo_key(obs_key_t key, struct dstr *str)
+diff --git a/libobs/obs-nix.h b/libobs/obs-nix.h
+new file mode 100644
+index 00000000..46eb9f3d
+--- /dev/null
++++ b/libobs/obs-nix.h
+@@ -0,0 +1,42 @@
++/******************************************************************************
++ Copyright (C) 2020 by Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include "obs-internal.h"
++
++struct obs_nix_hotkeys_vtable {
++ bool (*init)(struct obs_core_hotkeys *hotkeys);
++
++ void (*free)(struct obs_core_hotkeys *hotkeys);
++
++ bool (*is_pressed)(obs_hotkeys_platform_t *context, obs_key_t key);
++
++ void (*key_to_str)(obs_key_t key, struct dstr *dstr);
++
++ obs_key_t (*key_from_virtual_key)(int sym);
++
++ int (*key_to_virtual_key)(obs_key_t key);
++};
++
++#ifdef __cplusplus
++}
++#endif
+--
+2.28.0
+
diff --git a/0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch b/0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch
new file mode 100644
index 000000000000..30152b5f8644
--- /dev/null
+++ b/0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch
@@ -0,0 +1,258 @@
+From 2fe20c5488edf35c0507728d253b373bc1b842ab Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Fri, 6 Mar 2020 17:50:41 -0300
+Subject: [PATCH 08/15] libobs: Introduce the concept of a Unix platform
+
+This is a Unix-specific code. The only available platforms
+at this point are the X11/GLX and X11/EGL platforms.
+
+The concept of a platform display is also introduced. Again,
+the only display that is set right now is the X11 display.
+---
+ UI/obs-app.cpp | 12 +++++++++
+ libobs/CMakeLists.txt | 6 +++++
+ libobs/obs-nix-platform.c | 42 +++++++++++++++++++++++++++++++
+ libobs/obs-nix-platform.h | 52 +++++++++++++++++++++++++++++++++++++++
+ libobs/obs-nix-x11.c | 5 ++--
+ libobs/obs-nix.c | 15 +++++++++--
+ 6 files changed, 128 insertions(+), 4 deletions(-)
+ create mode 100644 libobs/obs-nix-platform.c
+ create mode 100644 libobs/obs-nix-platform.h
+
+diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp
+index b6f0b7d5..cf9ecabf 100644
+--- a/UI/obs-app.cpp
++++ b/UI/obs-app.cpp
+@@ -54,6 +54,11 @@
+ #include <pthread.h>
+ #endif
+
++#if !defined(_WIN32) && !defined(__APPLE__)
++#include <obs-nix-platform.h>
++#include <QX11Info>
++#endif
++
+ #include <iostream>
+
+ #include "ui-config.h"
+@@ -1346,6 +1351,13 @@ bool OBSApp::OBSInit()
+
+ qRegisterMetaType<VoidFunc>();
+
++#if !defined(_WIN32) && !defined(__APPLE__)
++ obs_set_nix_platform(OBS_NIX_PLATFORM_X11_GLX);
++ if (QApplication::platformName() == "xcb") {
++ obs_set_nix_platform_display(QX11Info::display());
++ }
++#endif
++
+ if (!StartupOBS(locale.c_str(), GetProfilerNameStore()))
+ return false;
+
+diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt
+index 1625363f..489334c0 100644
+--- a/libobs/CMakeLists.txt
++++ b/libobs/CMakeLists.txt
+@@ -180,13 +180,18 @@ elseif(APPLE)
+ elseif(UNIX)
+ set(libobs_PLATFORM_SOURCES
+ obs-nix.c
++ obs-nix-platform.c
+ obs-nix-x11.c
+ util/threading-posix.c
+ util/pipe-posix.c
+ util/platform-nix.c)
+
++ set(libobs_PLATFORM_HEADERS
++ obs-nix-platform.h)
++
+ if(NEEDS_SIMDE)
+ set(libobs_PLATFORM_HEADERS
++ ${libobs_PLATFORM_HEADERS}
+ util/simde/check.h
+ util/simde/hedley.h
+ util/simde/mmx.h
+@@ -197,6 +202,7 @@ elseif(UNIX)
+ util/threading-posix.h)
+ else()
+ set(libobs_PLATFORM_HEADERS
++ ${libobs_PLATFORM_HEADERS}
+ util/threading-posix.h)
+ endif()
+
+diff --git a/libobs/obs-nix-platform.c b/libobs/obs-nix-platform.c
+new file mode 100644
+index 00000000..e07a4d7b
+--- /dev/null
++++ b/libobs/obs-nix-platform.c
+@@ -0,0 +1,42 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "obs-nix-platform.h"
++
++static enum obs_nix_platform_type obs_nix_platform = OBS_NIX_PLATFORM_X11_GLX;
++
++static void *obs_nix_platform_display = NULL;
++
++void obs_set_nix_platform(enum obs_nix_platform_type platform)
++{
++ obs_nix_platform = platform;
++}
++
++enum obs_nix_platform_type obs_get_nix_platform(void)
++{
++ return obs_nix_platform;
++}
++
++void obs_set_nix_platform_display(void *display)
++{
++ obs_nix_platform_display = display;
++}
++
++void *obs_get_nix_platform_display(void)
++{
++ return obs_nix_platform_display;
++}
+diff --git a/libobs/obs-nix-platform.h b/libobs/obs-nix-platform.h
+new file mode 100644
+index 00000000..4cf9d8cd
+--- /dev/null
++++ b/libobs/obs-nix-platform.h
+@@ -0,0 +1,52 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "util/c99defs.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++enum obs_nix_platform_type {
++ OBS_NIX_PLATFORM_X11_GLX,
++ OBS_NIX_PLATFORM_X11_EGL,
++};
++
++/**
++ * Sets the Unix platform.
++ * @param platform The platform to select.
++ */
++EXPORT void obs_set_nix_platform(enum obs_nix_platform_type platform);
++/**
++ * Gets the host platform.
++ */
++EXPORT enum obs_nix_platform_type obs_get_nix_platform(void);
++/**
++ * Sets the host platform's display connection.
++ * @param display The host display connection.
++ */
++EXPORT void obs_set_nix_platform_display(void *display);
++/**
++ * Gets the host platform's display connection.
++ */
++EXPORT void *obs_get_nix_platform_display(void);
++
++#ifdef __cplusplus
++}
++#endif
+diff --git a/libobs/obs-nix-x11.c b/libobs/obs-nix-x11.c
+index 29aa3c7f..bb3bc0b7 100644
+--- a/libobs/obs-nix-x11.c
++++ b/libobs/obs-nix-x11.c
+@@ -18,6 +18,7 @@
+ ******************************************************************************/
+
+ #include "obs-internal.h"
++#include "obs-nix-platform.h"
+ #include "obs-nix-x11.h"
+
+ #include <xcb/xcb.h>
+@@ -32,7 +33,7 @@
+
+ void obs_nix_x11_log_info(void)
+ {
+- Display *dpy = XOpenDisplay(NULL);
++ Display *dpy = obs_get_nix_platform_display();
+ if (!dpy) {
+ blog(LOG_INFO, "Unable to open X display");
+ return;
+@@ -827,7 +828,7 @@ static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)
+
+ static bool obs_nix_x11_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
+ {
+- Display *display = XOpenDisplay(NULL);
++ Display *display = obs_get_nix_platform_display();
+ if (!display)
+ return false;
+
+diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c
+index df1d99df..9c52279a 100644
+--- a/libobs/obs-nix.c
++++ b/libobs/obs-nix.c
+@@ -18,6 +18,7 @@
+
+ #include "obs-internal.h"
+ #include "obs-nix.h"
++#include "obs-nix-platform.h"
+ #include "obs-nix-x11.h"
+ #if defined(__FreeBSD__)
+ #define _GNU_SOURCE
+@@ -289,12 +290,22 @@ void log_system_info(void)
+ #if defined(__linux__)
+ log_distribution_info();
+ #endif
+- obs_nix_x11_log_info();
++ switch (obs_get_nix_platform()) {
++ case OBS_NIX_PLATFORM_X11_GLX:
++ case OBS_NIX_PLATFORM_X11_EGL:
++ obs_nix_x11_log_info();
++ break;
++ }
+ }
+
+ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
+ {
+- hotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();
++ switch (obs_get_nix_platform()) {
++ case OBS_NIX_PLATFORM_X11_GLX:
++ case OBS_NIX_PLATFORM_X11_EGL:
++ hotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();
++ break;
++ }
+
+ return hotkeys_vtable->init(hotkeys);
+ }
+--
+2.28.0
+
diff --git a/0009-UI-Set-the-Unix-platform-on-startup.patch b/0009-UI-Set-the-Unix-platform-on-startup.patch
new file mode 100644
index 000000000000..d18eccb9a527
--- /dev/null
+++ b/0009-UI-Set-the-Unix-platform-on-startup.patch
@@ -0,0 +1,67 @@
+From 25d37ebfbe7d9aa583bd8bd9655892c2d73f13dc Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Tue, 7 Apr 2020 23:17:02 -0300
+Subject: [PATCH 09/15] UI: Set the Unix platform on startup
+
+Move the OBS_USE_EGL environment variable check to obs-app.cpp,
+and set the OBS platform to be either OBS_NIX_PLATFORM_X11_GLX
+or OBS_NIX_PLATFORM_X11_EGL.
+---
+ UI/obs-app.cpp | 4 ++++
+ libobs-opengl/gl-nix.c | 10 ++++++----
+ libobs-opengl/gl-nix.h | 2 ++
+ 3 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp
+index cf9ecabf..c62ba1da 100644
+--- a/UI/obs-app.cpp
++++ b/UI/obs-app.cpp
+@@ -1354,6 +1354,10 @@ bool OBSApp::OBSInit()
+ #if !defined(_WIN32) && !defined(__APPLE__)
+ obs_set_nix_platform(OBS_NIX_PLATFORM_X11_GLX);
+ if (QApplication::platformName() == "xcb") {
++ if (getenv("OBS_USE_EGL")) {
++ blog(LOG_INFO, "Using EGL/X11");
++ obs_set_nix_platform(OBS_NIX_PLATFORM_X11_EGL);
++ }
+ obs_set_nix_platform_display(QX11Info::display());
+ }
+ #endif
+diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
+index 4b616ef1..9ed3d198 100644
+--- a/libobs-opengl/gl-nix.c
++++ b/libobs-opengl/gl-nix.c
+@@ -25,11 +25,13 @@ static void init_winsys(void)
+ {
+ assert(gl_vtable == NULL);
+
+- if (getenv("OBS_USE_EGL")) {
+- gl_vtable = gl_x11_egl_get_winsys_vtable();
+- blog(LOG_INFO, "Using EGL/X11");
+- } else {
++ switch (obs_get_nix_platform()) {
++ case OBS_NIX_PLATFORM_X11_GLX:
+ gl_vtable = gl_x11_glx_get_winsys_vtable();
++ break;
++ case OBS_NIX_PLATFORM_X11_EGL:
++ gl_vtable = gl_x11_egl_get_winsys_vtable();
++ break;
+ }
+
+ assert(gl_vtable != NULL);
+diff --git a/libobs-opengl/gl-nix.h b/libobs-opengl/gl-nix.h
+index 209cc308..f5532719 100644
+--- a/libobs-opengl/gl-nix.h
++++ b/libobs-opengl/gl-nix.h
+@@ -17,6 +17,8 @@
+
+ #pragma once
+
++#include <obs-nix-platform.h>
++
+ #include "gl-subsystem.h"
+
+ struct gl_winsys_vtable {
+--
+2.28.0
+
diff --git a/0010-linux-capture-Fail-to-load-when-running-on-EGL.patch b/0010-linux-capture-Fail-to-load-when-running-on-EGL.patch
new file mode 100644
index 000000000000..2576277a4bd7
--- /dev/null
+++ b/0010-linux-capture-Fail-to-load-when-running-on-EGL.patch
@@ -0,0 +1,38 @@
+From d07e62894d3aa78430a2ee543443a81c70153067 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Wed, 8 Apr 2020 23:58:43 -0300
+Subject: [PATCH 10/15] linux-capture: Fail to load when running on EGL
+
+Right now, linux-capture hard-depends on GLX. Disable it when
+running under EGL.
+---
+ plugins/linux-capture/linux-capture.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/plugins/linux-capture/linux-capture.c b/plugins/linux-capture/linux-capture.c
+index ce49ee72..56ff485c 100644
+--- a/plugins/linux-capture/linux-capture.c
++++ b/plugins/linux-capture/linux-capture.c
+@@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ #include <obs-module.h>
++#include <obs-nix-platform.h>
+
+ OBS_DECLARE_MODULE()
+ OBS_MODULE_USE_DEFAULT_LOCALE("linux-xshm", "en-US")
+@@ -30,6 +31,11 @@ extern void xcomposite_unload(void);
+
+ bool obs_module_load(void)
+ {
++ if (obs_get_nix_platform() != OBS_NIX_PLATFORM_X11_GLX) {
++ blog(LOG_ERROR, "linux-capture cannot run on EGL platforms");
++ return false;
++ }
++
+ obs_register_source(&xshm_input);
+ xcomposite_load();
+ return true;
+--
+2.28.0
+
diff --git a/0011-libobs-Add-a-Wayland-platform.patch b/0011-libobs-Add-a-Wayland-platform.patch
new file mode 100644
index 000000000000..d95c85d1518b
--- /dev/null
+++ b/0011-libobs-Add-a-Wayland-platform.patch
@@ -0,0 +1,422 @@
+From de53eda26763e1aa7ec82803fd231c0cf9c0a6c4 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Fri, 6 Mar 2020 18:53:42 -0300
+Subject: [PATCH 11/15] libobs: Add a Wayland platform
+
+Introduce the OBS_NIX_PLATFORM_WAYLAND enum value, and try to detect
+it when OBS Studio runs by looking into the platform name.
+---
+ CMakeLists.txt | 2 +
+ UI/CMakeLists.txt | 5 ++
+ UI/obs-app.cpp | 17 +++++++
+ cmake/Modules/FindWayland.cmake | 78 +++++++++++++++++++++++++++++
+ libobs-opengl/CMakeLists.txt | 2 +-
+ libobs-opengl/gl-nix.c | 5 ++
+ libobs/CMakeLists.txt | 15 ++++++
+ libobs/obs-nix-platform.h | 4 ++
+ libobs/obs-nix-wayland.c | 88 +++++++++++++++++++++++++++++++++
+ libobs/obs-nix-wayland.h | 24 +++++++++
+ libobs/obs-nix.c | 14 ++++++
+ libobs/obsconfig.h.in | 2 +
+ 12 files changed, 255 insertions(+), 1 deletion(-)
+ create mode 100644 cmake/Modules/FindWayland.cmake
+ create mode 100644 libobs/obs-nix-wayland.c
+ create mode 100644 libobs/obs-nix-wayland.h
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index b56c2bba..9ab530ce 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -189,6 +189,8 @@ if(APPLE)
+ list(APPEND CMAKE_INSTALL_RPATH "@loader_path/" "@executable_path/")
+ elseif(UNIX)
+ option(USE_XDG "Utilize XDG Base Directory Specification" ON)
++ option(ENABLE_WAYLAND "Build support for Wayland" ON)
++
+ if(USE_XDG)
+ add_definitions(-DUSE_XDG)
+ endif()
+diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt
+index a660f326..171efa2b 100644
+--- a/UI/CMakeLists.txt
++++ b/UI/CMakeLists.txt
+@@ -403,6 +403,11 @@ if (APPLE)
+ set_target_properties(obs PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
+ endif()
+
++if (ENABLE_WAYLAND)
++ find_package(Qt5Gui)
++ include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
++endif()
++
+ define_graphic_modules(obs)
+
+ install_obs_core(obs)
+diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp
+index c62ba1da..8335c2ce 100644
+--- a/UI/obs-app.cpp
++++ b/UI/obs-app.cpp
+@@ -57,6 +57,11 @@
+ #if !defined(_WIN32) && !defined(__APPLE__)
+ #include <obs-nix-platform.h>
+ #include <QX11Info>
++
++#ifdef ENABLE_WAYLAND
++#include <qpa/qplatformnativeinterface.h>
++#endif
++
+ #endif
+
+ #include <iostream>
+@@ -1360,6 +1365,18 @@ bool OBSApp::OBSInit()
+ }
+ obs_set_nix_platform_display(QX11Info::display());
+ }
++
++#ifdef ENABLE_WAYLAND
++ if (QApplication::platformName().contains("wayland")) {
++ obs_set_nix_platform(OBS_NIX_PLATFORM_WAYLAND);
++ QPlatformNativeInterface *native =
++ QGuiApplication::platformNativeInterface();
++ obs_set_nix_platform_display(
++ native->nativeResourceForIntegration("display"));
++
++ blog(LOG_INFO, "Platform: Wayland");
++ }
++#endif
+ #endif
+
+ if (!StartupOBS(locale.c_str(), GetProfilerNameStore()))
+diff --git a/cmake/Modules/FindWayland.cmake b/cmake/Modules/FindWayland.cmake
+new file mode 100644
+index 00000000..377f0545
+--- /dev/null
++++ b/cmake/Modules/FindWayland.cmake
+@@ -0,0 +1,78 @@
++# Try to find Wayland on a Unix system
++#
++# This will define:
++#
++# WAYLAND_FOUND - True if Wayland is found
++# WAYLAND_LIBRARIES - Link these to use Wayland
++# WAYLAND_INCLUDE_DIRS - Include directory for Wayland
++# WAYLAND_DEFINITIONS - Compiler flags for using Wayland
++#
++# In addition the following more fine grained variables will be defined:
++#
++# Wayland_Client_FOUND WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES
++# Wayland_Server_FOUND WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES
++# Wayland_EGL_FOUND WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES
++# Wayland_Cursor_FOUND WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES
++#
++# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
++# 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
++#
++# Redistribution and use is allowed according to the terms of the BSD license.
++# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
++
++IF (NOT WIN32)
++
++ # Use pkg-config to get the directories and then use these values
++ # in the find_path() and find_library() calls
++ find_package(PkgConfig)
++ PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
++
++ set(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
++
++ find_path(WAYLAND_CLIENT_INCLUDE_DIRS NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
++ find_library(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
++ if(WAYLAND_CLIENT_INCLUDE_DIRS AND WAYLAND_CLIENT_LIBRARIES)
++ set(Wayland_Client_FOUND TRUE)
++ else()
++ set(Wayland_Client_FOUND FALSE)
++ endif()
++ mark_as_advanced(WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES)
++
++ find_path(WAYLAND_CURSOR_INCLUDE_DIRS NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
++ find_library(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
++ if(WAYLAND_CURSOR_INCLUDE_DIRS AND WAYLAND_CURSOR_LIBRARIES)
++ set(Wayland_Cursor_FOUND TRUE)
++ else()
++ set(Wayland_Cursor_FOUND FALSE)
++ endif()
++ mark_as_advanced(WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES)
++
++ find_path(WAYLAND_EGL_INCLUDE_DIRS NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
++ find_library(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
++ if(WAYLAND_EGL_INCLUDE_DIRS AND WAYLAND_EGL_LIBRARIES)
++ set(Wayland_EGL_FOUND TRUE)
++ else()
++ set(Wayland_EGL_FOUND FALSE)
++ endif()
++ mark_as_advanced(WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES)
++
++ find_path(WAYLAND_SERVER_INCLUDE_DIRS NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
++ find_library(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
++ if(WAYLAND_SERVER_INCLUDE_DIRS AND WAYLAND_SERVER_LIBRARIES)
++ set(Wayland_Server_FOUND TRUE)
++ else()
++ set(Wayland_Server_FOUND FALSE)
++ endif()
++ mark_as_advanced(WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES)
++
++ set(WAYLAND_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS} ${WAYLAND_SERVER_INCLUDE_DIRS} ${WAYLAND_EGL_INCLUDE_DIRS} ${WAYLAND_CURSOR_INCLUDE_DIRS})
++ set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
++ mark_as_advanced(WAYLAND_INCLUDE_DIRS WAYLAND_LIBRARIES)
++
++ list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIRS)
++
++ include(FindPackageHandleStandardArgs)
++
++ find_package_handle_standard_args(Wayland REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIRS HANDLE_COMPONENTS)
++
++ENDIF ()
+diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
+index 9c645c3d..f84636cf 100644
+--- a/libobs-opengl/CMakeLists.txt
++++ b/libobs-opengl/CMakeLists.txt
+@@ -32,7 +32,7 @@ elseif(APPLE)
+ ${COCOA}
+ ${IOSURF}
+ ${OPENGL_gl_LIBRARY})
+-else() #This needs to change to be more specific to get ready for Wayland
++else()
+ find_package(XCB COMPONENTS XCB REQUIRED)
+ find_package(X11_XCB REQUIRED)
+
+diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
+index 9ed3d198..581e16a4 100644
+--- a/libobs-opengl/gl-nix.c
++++ b/libobs-opengl/gl-nix.c
+@@ -32,6 +32,11 @@ static void init_winsys(void)
+ case OBS_NIX_PLATFORM_X11_EGL:
+ gl_vtable = gl_x11_egl_get_winsys_vtable();
+ break;
++#ifdef ENABLE_WAYLAND
++ case OBS_NIX_PLATFORM_WAYLAND:
++ blog(LOG_ERROR, "EGL/Wayland not implemented yet");
++ break;
++#endif
+ }
+
+ assert(gl_vtable != NULL);
+diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt
+index 489334c0..1cb1583b 100644
+--- a/libobs/CMakeLists.txt
++++ b/libobs/CMakeLists.txt
+@@ -189,6 +189,21 @@ elseif(UNIX)
+ set(libobs_PLATFORM_HEADERS
+ obs-nix-platform.h)
+
++ if(ENABLE_WAYLAND)
++ find_package(Wayland COMPONENTS Client REQUIRED)
++
++ set(libobs_PLATFORM_SOURCES ${libobs_PLATFORM_SOURCES}
++ obs-nix-wayland.c)
++
++ include_directories(
++ ${WAYLAND_CLIENT_INCLUDE_DIR})
++ add_definitions(
++ ${WAYLAND_DEFINITIONS})
++ set(libobs_PLATFORM_DEPS
++ ${libobs_PLATFORM_DEPS}
++ ${WAYLAND_CLIENT_LIBRARIES})
++ endif()
++
+ if(NEEDS_SIMDE)
+ set(libobs_PLATFORM_HEADERS
+ ${libobs_PLATFORM_HEADERS}
+diff --git a/libobs/obs-nix-platform.h b/libobs/obs-nix-platform.h
+index 4cf9d8cd..cef700d7 100644
+--- a/libobs/obs-nix-platform.h
++++ b/libobs/obs-nix-platform.h
+@@ -26,6 +26,10 @@ extern "C" {
+ enum obs_nix_platform_type {
+ OBS_NIX_PLATFORM_X11_GLX,
+ OBS_NIX_PLATFORM_X11_EGL,
++#ifdef ENABLE_WAYLAND
++ OBS_NIX_PLATFORM_WAYLAND,
++#endif
++
+ };
+
+ /**
+diff --git a/libobs/obs-nix-wayland.c b/libobs/obs-nix-wayland.c
+new file mode 100644
+index 00000000..b242017f
+--- /dev/null
++++ b/libobs/obs-nix-wayland.c
+@@ -0,0 +1,88 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "obs-internal.h"
++#include "obs-nix-platform.h"
++#include "obs-nix-wayland.h"
++
++#include <wayland-client.h>
++
++void obs_nix_wayland_log_info(void)
++{
++ struct wl_display *display = obs_get_nix_platform_display();
++ if (display == NULL) {
++ blog(LOG_INFO, "Unable to connect to Wayland server");
++ return;
++ }
++ //TODO: query some information about the wayland server if possible
++ blog(LOG_INFO, "Connected to Wayland server");
++}
++
++static bool
++obs_nix_wayland_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
++{
++ UNUSED_PARAMETER(hotkeys);
++ return true;
++}
++
++static void
++obs_nix_wayland_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
++{
++ UNUSED_PARAMETER(hotkeys);
++}
++
++static bool
++obs_nix_wayland_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
++ obs_key_t key)
++{
++ UNUSED_PARAMETER(context);
++ UNUSED_PARAMETER(key);
++ return false;
++}
++
++static void obs_nix_wayland_key_to_str(obs_key_t key, struct dstr *dstr)
++{
++ UNUSED_PARAMETER(key);
++ UNUSED_PARAMETER(dstr);
++}
++
++static obs_key_t obs_nix_wayland_key_from_virtual_key(int sym)
++{
++ UNUSED_PARAMETER(sym);
++ return OBS_KEY_NONE;
++}
++
++static int obs_nix_wayland_key_to_virtual_key(obs_key_t key)
++{
++ UNUSED_PARAMETER(key);
++ return 0;
++}
++
++static const struct obs_nix_hotkeys_vtable wayland_hotkeys_vtable = {
++ .init = obs_nix_wayland_hotkeys_platform_init,
++ .free = obs_nix_wayland_hotkeys_platform_free,
++ .is_pressed = obs_nix_wayland_hotkeys_platform_is_pressed,
++ .key_to_str = obs_nix_wayland_key_to_str,
++ .key_from_virtual_key = obs_nix_wayland_key_from_virtual_key,
++ .key_to_virtual_key = obs_nix_wayland_key_to_virtual_key,
++
++};
++
++const struct obs_nix_hotkeys_vtable *obs_nix_wayland_get_hotkeys_vtable(void)
++{
++ return &wayland_hotkeys_vtable;
++}
+diff --git a/libobs/obs-nix-wayland.h b/libobs/obs-nix-wayland.h
+new file mode 100644
+index 00000000..d44720c5
+--- /dev/null
++++ b/libobs/obs-nix-wayland.h
+@@ -0,0 +1,24 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "obs-nix.h"
++
++void obs_nix_wayland_log_info(void);
++
++const struct obs_nix_hotkeys_vtable *obs_nix_wayland_get_hotkeys_vtable(void);
+diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c
+index 9c52279a..ec7f8e25 100644
+--- a/libobs/obs-nix.c
++++ b/libobs/obs-nix.c
+@@ -20,6 +20,11 @@
+ #include "obs-nix.h"
+ #include "obs-nix-platform.h"
+ #include "obs-nix-x11.h"
++
++#ifdef ENABLE_WAYLAND
++#include "obs-nix-wayland.h"
++#endif
++
+ #if defined(__FreeBSD__)
+ #define _GNU_SOURCE
+ #endif
+@@ -295,6 +300,10 @@ void log_system_info(void)
+ case OBS_NIX_PLATFORM_X11_EGL:
+ obs_nix_x11_log_info();
+ break;
++#ifdef ENABLE_WAYLAND
++ case OBS_NIX_PLATFORM_WAYLAND:
++ break;
++#endif
+ }
+ }
+
+@@ -305,6 +314,11 @@ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
+ case OBS_NIX_PLATFORM_X11_EGL:
+ hotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();
+ break;
++#ifdef ENABLE_WAYLAND
++ case OBS_NIX_PLATFORM_WAYLAND:
++ hotkeys_vtable = obs_nix_wayland_get_hotkeys_vtable();
++ break;
++#endif
+ }
+
+ return hotkeys_vtable->init(hotkeys);
+diff --git a/libobs/obsconfig.h.in b/libobs/obsconfig.h.in
+index 1a09ebea..82d2e3af 100644
+--- a/libobs/obsconfig.h.in
++++ b/libobs/obsconfig.h.in
+@@ -24,6 +24,8 @@
+ #define LIBOBS_IMAGEMAGICK_DIR_STYLE_7GE 7
+ #define LIBOBS_IMAGEMAGICK_DIR_STYLE @LIBOBS_IMAGEMAGICK_DIR_STYLE@
+
++#cmakedefine ENABLE_WAYLAND
++
+ /* NOTE: Release candidate version numbers internally are always the previous
+ * main release number! For example, if the current public release is 21.0 and
+ * the build is 22.0 release candidate 1, internally the build number (defined
+--
+2.28.0
+
diff --git a/0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch b/0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch
new file mode 100644
index 000000000000..8f138caedb6b
--- /dev/null
+++ b/0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch
@@ -0,0 +1,50 @@
+From 5bf0de83d59b4147e91feceba2ec957522d83736 Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Sat, 11 Apr 2020 23:32:16 -0300
+Subject: [PATCH 12/15] libobs-opengl: Try to use the platform display if
+ available
+
+We need to ensure we're running all X11 code on the same display.
+---
+ libobs-opengl/gl-x11-egl.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/libobs-opengl/gl-x11-egl.c b/libobs-opengl/gl-x11-egl.c
+index 5b413995..47b8e420 100644
+--- a/libobs-opengl/gl-x11-egl.c
++++ b/libobs-opengl/gl-x11-egl.c
+@@ -324,14 +324,19 @@ static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
+ bfree(info);
+ }
+
+-static Display *open_windowless_display(void)
++static Display *open_windowless_display(Display *platform_display)
+ {
+- Display *display = XOpenDisplay(NULL);
++ Display *display;
+ xcb_connection_t *xcb_conn;
+ xcb_screen_iterator_t screen_iterator;
+ xcb_screen_t *screen;
+ int screen_num;
+
++ if (platform_display)
++ display = platform_display;
++ else
++ display = XOpenDisplay(NULL);
++
+ if (!display) {
+ blog(LOG_ERROR, "Unable to open new X connection!");
+ return NULL;
+@@ -392,7 +397,8 @@ static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
+ For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
+ Essentially, EGL requires Xlib. Everything else we use xcb. */
+ struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
+- Display *display = open_windowless_display();
++ Display *platform_display = obs_get_nix_platform_display();
++ Display *display = open_windowless_display(platform_display);
+
+ if (!display) {
+ goto fail_display_open;
+--
+2.28.0
+
diff --git a/0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch b/0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch
new file mode 100644
index 000000000000..26ac4909d196
--- /dev/null
+++ b/0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch
@@ -0,0 +1,474 @@
+From 7fd9f95fee70ec8d604797db4a0de77e58c6e32d Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 21:09:11 -0300
+Subject: [PATCH 13/15] libobs-opengl: Introduce an EGL/Wayland renderer
+
+Introduce a new Wayland/EGL renderer.
+---
+ libobs-opengl/CMakeLists.txt | 23 +++
+ libobs-opengl/gl-nix.c | 7 +-
+ libobs-opengl/gl-nix.h | 1 +
+ libobs-opengl/gl-wayland-egl.c | 350 +++++++++++++++++++++++++++++++++
+ libobs-opengl/gl-wayland-egl.h | 22 +++
+ 5 files changed, 402 insertions(+), 1 deletion(-)
+ create mode 100644 libobs-opengl/gl-wayland-egl.c
+ create mode 100644 libobs-opengl/gl-wayland-egl.h
+
+diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
+index f84636cf..0f694b6c 100644
+--- a/libobs-opengl/CMakeLists.txt
++++ b/libobs-opengl/CMakeLists.txt
+@@ -52,6 +52,29 @@ else()
+ gl-nix.c
+ gl-x11-egl.c
+ gl-x11-glx.c)
++
++ if(ENABLE_WAYLAND)
++ find_package(EGL REQUIRED)
++ find_package(Wayland REQUIRED)
++
++ include_directories(
++ ${WAYLAND_CLIENT_INCLUDE_DIRS}
++ ${WAYLAND_EGL_INCLUDE_DIRS}
++ ${EGL_INCLUDE_DIRS})
++
++ add_definitions(
++ ${WAYLAND_DEFINITIONS})
++
++ set(libobs-opengl_PLATFORM_DEPS
++ ${libobs-opengl_PLATFORM_DEPS}
++ ${WAYLAND_CLIENT_LIBRARIES}
++ ${WAYLAND_EGL_LIBRARIES}
++ ${EGL_LIBRARIES})
++
++ set(libobs-opengl_PLATFORM_SOURCES
++ ${libobs-opengl_PLATFORM_SOURCES}
++ gl-wayland-egl.c)
++ endif()
+ endif()
+
+ set(libobs-opengl_SOURCES
+diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
+index 581e16a4..6c272c3d 100644
+--- a/libobs-opengl/gl-nix.c
++++ b/libobs-opengl/gl-nix.c
+@@ -19,6 +19,10 @@
+ #include "gl-x11-glx.h"
+ #include "gl-x11-egl.h"
+
++#ifdef ENABLE_WAYLAND
++#include "gl-wayland-egl.h"
++#endif
++
+ static const struct gl_winsys_vtable *gl_vtable = NULL;
+
+ static void init_winsys(void)
+@@ -34,7 +38,8 @@ static void init_winsys(void)
+ break;
+ #ifdef ENABLE_WAYLAND
+ case OBS_NIX_PLATFORM_WAYLAND:
+- blog(LOG_ERROR, "EGL/Wayland not implemented yet");
++ gl_vtable = gl_wayland_egl_get_winsys_vtable();
++ blog(LOG_INFO, "Using EGL/Wayland");
+ break;
+ #endif
+ }
+diff --git a/libobs-opengl/gl-nix.h b/libobs-opengl/gl-nix.h
+index f5532719..741154da 100644
+--- a/libobs-opengl/gl-nix.h
++++ b/libobs-opengl/gl-nix.h
+@@ -17,6 +17,7 @@
+
+ #pragma once
+
++#include <obs.h>
+ #include <obs-nix-platform.h>
+
+ #include "gl-subsystem.h"
+diff --git a/libobs-opengl/gl-wayland-egl.c b/libobs-opengl/gl-wayland-egl.c
+new file mode 100644
+index 00000000..98bb483f
+--- /dev/null
++++ b/libobs-opengl/gl-wayland-egl.c
+@@ -0,0 +1,350 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "gl-wayland-egl.h"
++
++#include <wayland-client.h>
++#include <wayland-egl.h>
++
++#include <EGL/egl.h>
++#include <EGL/eglplatform.h>
++#include <EGL/eglext.h>
++
++static const EGLint config_attribs[] = {EGL_SURFACE_TYPE,
++ EGL_WINDOW_BIT,
++ EGL_RENDERABLE_TYPE,
++ EGL_OPENGL_BIT,
++ EGL_STENCIL_SIZE,
++ 0,
++ EGL_DEPTH_SIZE,
++ 0,
++ EGL_BUFFER_SIZE,
++ 32,
++ EGL_ALPHA_SIZE,
++ 8,
++ EGL_NATIVE_RENDERABLE,
++ EGL_TRUE,
++ EGL_NONE};
++
++static const EGLint ctx_attribs[] = {
++#ifdef _DEBUG
++ EGL_CONTEXT_OPENGL_DEBUG,
++ EGL_TRUE,
++#endif
++ EGL_CONTEXT_OPENGL_PROFILE_MASK,
++ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
++ EGL_CONTEXT_MAJOR_VERSION,
++ 3,
++ EGL_CONTEXT_MINOR_VERSION,
++ 3,
++ EGL_NONE};
++
++#ifdef EGL_KHR_create_context
++static const EGLint khr_ctx_attribs[] = {
++#ifdef _DEBUG
++ EGL_CONTEXT_FLAGS_KHR,
++ EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
++#endif
++ EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
++ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
++ EGL_CONTEXT_MAJOR_VERSION_KHR,
++ 3,
++ EGL_CONTEXT_MINOR_VERSION_KHR,
++ 3,
++ EGL_NONE};
++#endif
++
++struct gl_windowinfo {
++ struct wl_egl_window *window;
++ EGLSurface egl_surface;
++};
++
++struct gl_platform {
++ struct wl_display *wl_display;
++ EGLDisplay display;
++ EGLConfig config;
++ EGLContext context;
++};
++
++struct gl_windowinfo *
++gl_wayland_egl_windowinfo_create(const struct gs_init_data *info)
++{
++ struct wl_egl_window *window =
++ wl_egl_window_create(info->window.display, info->cx, info->cy);
++ if (window == NULL) {
++ blog(LOG_ERROR, "wl_egl_window_create failed");
++ return NULL;
++ }
++
++ struct gl_windowinfo *wi = bmalloc(sizeof(struct gl_windowinfo));
++ wi->window = window;
++ return wi;
++}
++
++static void gl_wayland_egl_windowinfo_destroy(struct gl_windowinfo *info)
++{
++ wl_egl_window_destroy(info->window);
++ bfree(info);
++}
++
++static bool egl_make_current(EGLDisplay display, EGLSurface surface,
++ EGLContext context)
++{
++ if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
++ blog(LOG_ERROR, "eglBindAPI failed");
++ }
++
++ if (!eglMakeCurrent(display, surface, surface, context)) {
++ blog(LOG_ERROR, "eglMakeCurrent failed");
++ return false;
++ }
++ return true;
++}
++
++static bool egl_context_create(struct gl_platform *plat, const EGLint *attribs)
++{
++ bool success = false;
++ EGLint num_config;
++
++ if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
++ blog(LOG_ERROR, "eglBindAPI failed");
++ }
++
++ EGLBoolean result = eglChooseConfig(plat->display, config_attribs,
++ &plat->config, 1, &num_config);
++ if (result != EGL_TRUE || num_config == 0) {
++ blog(LOG_ERROR, "eglChooseConfig failed");
++ goto error;
++ }
++
++ plat->context = eglCreateContext(plat->display, plat->config,
++ EGL_NO_CONTEXT, attribs);
++ if (plat->context == EGL_NO_CONTEXT) {
++ blog(LOG_ERROR, "eglCreateContext failed");
++ goto error;
++ }
++
++ success =
++ egl_make_current(plat->display, EGL_NO_SURFACE, plat->context);
++
++error:
++ return success;
++}
++
++static void egl_context_destroy(struct gl_platform *plat)
++{
++ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
++ eglDestroyContext(plat->display, plat->context);
++}
++
++static bool extension_supported(const char *extensions, const char *search)
++{
++ const char *result = strstr(extensions, search);
++ unsigned long len = strlen(search);
++ return result != NULL &&
++ (result == extensions || *(result - 1) == ' ') &&
++ (result[len] == ' ' || result[len] == '\0');
++}
++
++static struct gl_platform *gl_wayland_egl_platform_create(gs_device_t *device,
++ uint32_t adapter)
++{
++ struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
++
++ plat->wl_display = obs_get_nix_platform_display();
++
++ device->plat = plat;
++
++ plat->display = eglGetDisplay(plat->wl_display);
++ if (plat->display == EGL_NO_DISPLAY) {
++ blog(LOG_ERROR, "eglGetDisplay failed");
++ goto fail_display_init;
++ }
++
++ EGLint major;
++ EGLint minor;
++
++ if (eglInitialize(plat->display, &major, &minor) == EGL_FALSE) {
++ blog(LOG_ERROR, "eglInitialize failed");
++ goto fail_display_init;
++ }
++
++ blog(LOG_INFO, "Initialized EGL %d.%d", major, minor);
++
++ const char *extensions = eglQueryString(plat->display, EGL_EXTENSIONS);
++ blog(LOG_DEBUG, "Supported EGL Extensions: %s", extensions);
++
++ const EGLint *attribs = ctx_attribs;
++ if (major == 1 && minor == 4) {
++#ifdef EGL_KHR_create_context
++ if (extension_supported(extensions, "EGL_KHR_create_context")) {
++ attribs = khr_ctx_attribs;
++ } else {
++#endif
++ blog(LOG_ERROR,
++ "EGL_KHR_create_context extension is required to use EGL 1.4.");
++ goto fail_context_create;
++#ifdef EGL_KHR_create_context
++ }
++#endif
++ } else if (major < 1 || (major == 1 && minor < 4)) {
++ blog(LOG_ERROR, "EGL 1.4 or higher is required.");
++ goto fail_context_create;
++ }
++
++ if (!egl_context_create(plat, attribs)) {
++ goto fail_context_create;
++ }
++
++ if (!gladLoadGL()) {
++ blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
++ goto fail_load_gl;
++ }
++
++ goto success;
++
++fail_load_gl:
++ egl_context_destroy(plat);
++fail_context_create:
++ eglTerminate(plat->display);
++fail_display_init:
++ bfree(plat);
++ plat = NULL;
++success:
++ UNUSED_PARAMETER(adapter);
++ return plat;
++}
++
++static void gl_wayland_egl_platform_destroy(struct gl_platform *plat)
++{
++ if (plat) {
++ egl_context_destroy(plat);
++ eglTerminate(plat->display);
++ bfree(plat);
++ }
++}
++
++static bool gl_wayland_egl_platform_init_swapchain(struct gs_swap_chain *swap)
++{
++ struct gl_platform *plat = swap->device->plat;
++ EGLSurface egl_surface = eglCreateWindowSurface(
++ plat->display, plat->config, swap->wi->window, NULL);
++ if (egl_surface == EGL_NO_SURFACE) {
++ blog(LOG_ERROR, "eglCreateWindowSurface failed");
++ return false;
++ }
++ swap->wi->egl_surface = egl_surface;
++ return true;
++}
++
++static void
++gl_wayland_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
++{
++ struct gl_platform *plat = swap->device->plat;
++ eglDestroySurface(plat->display, swap->wi->egl_surface);
++}
++
++static void gl_wayland_egl_device_enter_context(gs_device_t *device)
++{
++ struct gl_platform *plat = device->plat;
++ EGLSurface surface = EGL_NO_SURFACE;
++ if (device->cur_swap != NULL)
++ surface = device->cur_swap->wi->egl_surface;
++ egl_make_current(plat->display, surface, plat->context);
++}
++
++static void gl_wayland_egl_device_leave_context(gs_device_t *device)
++{
++ struct gl_platform *plat = device->plat;
++ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
++}
++
++static void *gl_wayland_egl_device_get_device_obj(gs_device_t *device)
++{
++ return device->plat->context;
++}
++
++static void gl_wayland_egl_getclientsize(const struct gs_swap_chain *swap,
++ uint32_t *width, uint32_t *height)
++{
++ wl_egl_window_get_attached_size(swap->wi->window, (void *)width,
++ (void *)height);
++}
++
++static void gl_wayland_egl_clear_context(gs_device_t *device)
++{
++ struct gl_platform *plat = device->plat;
++ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
++}
++
++static void gl_wayland_egl_update(gs_device_t *device)
++{
++ wl_egl_window_resize(device->cur_swap->wi->window,
++ device->cur_swap->info.cx,
++ device->cur_swap->info.cy, 0, 0);
++}
++
++static void gl_wayland_egl_device_load_swapchain(gs_device_t *device,
++ gs_swapchain_t *swap)
++{
++ if (device->cur_swap == swap)
++ return;
++
++ device->cur_swap = swap;
++
++ struct gl_platform *plat = device->plat;
++ if (swap == NULL) {
++ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
++ } else {
++ egl_make_current(plat->display, swap->wi->egl_surface,
++ plat->context);
++ }
++}
++
++static void gl_wayland_egl_device_present(gs_device_t *device)
++{
++ struct gl_platform *plat = device->plat;
++ struct gl_windowinfo *wi = device->cur_swap->wi;
++ if (eglSwapInterval(plat->display, 0) == EGL_FALSE) {
++ blog(LOG_ERROR, "eglSwapInterval failed");
++ }
++ if (eglSwapBuffers(plat->display, wi->egl_surface) == EGL_FALSE) {
++ blog(LOG_ERROR, "eglSwapBuffers failed");
++ }
++}
++
++static const struct gl_winsys_vtable egl_wayland_winsys_vtable = {
++ .windowinfo_create = gl_wayland_egl_windowinfo_create,
++ .windowinfo_destroy = gl_wayland_egl_windowinfo_destroy,
++ .platform_create = gl_wayland_egl_platform_create,
++ .platform_destroy = gl_wayland_egl_platform_destroy,
++ .platform_init_swapchain = gl_wayland_egl_platform_init_swapchain,
++ .platform_cleanup_swapchain = gl_wayland_egl_platform_cleanup_swapchain,
++ .device_enter_context = gl_wayland_egl_device_enter_context,
++ .device_leave_context = gl_wayland_egl_device_leave_context,
++ .device_get_device_obj = gl_wayland_egl_device_get_device_obj,
++ .getclientsize = gl_wayland_egl_getclientsize,
++ .clear_context = gl_wayland_egl_clear_context,
++ .update = gl_wayland_egl_update,
++ .device_load_swapchain = gl_wayland_egl_device_load_swapchain,
++ .device_present = gl_wayland_egl_device_present,
++};
++
++const struct gl_winsys_vtable *gl_wayland_egl_get_winsys_vtable(void)
++{
++ return &egl_wayland_winsys_vtable;
++}
+diff --git a/libobs-opengl/gl-wayland-egl.h b/libobs-opengl/gl-wayland-egl.h
+new file mode 100644
+index 00000000..3384576f
+--- /dev/null
++++ b/libobs-opengl/gl-wayland-egl.h
+@@ -0,0 +1,22 @@
++/******************************************************************************
++ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
++
++ 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 2 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 <http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#pragma once
++
++#include "gl-nix.h"
++
++const struct gl_winsys_vtable *gl_wayland_egl_get_winsys_vtable(void);
+--
+2.28.0
+
diff --git a/0014-UI-Retrieve-Wayland-surface-from-QWindow.patch b/0014-UI-Retrieve-Wayland-surface-from-QWindow.patch
new file mode 100644
index 000000000000..b46f02f1feb7
--- /dev/null
+++ b/0014-UI-Retrieve-Wayland-surface-from-QWindow.patch
@@ -0,0 +1,107 @@
+From ded1e2a0201d3160ff1b427bab104870f29d516b Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+Date: Mon, 9 Mar 2020 21:23:37 -0300
+Subject: [PATCH 14/15] UI: Retrieve Wayland surface from QWindow
+
+On Wayland, we want to query the window's underlying
+platform for the Wayland surface, instead of foolishly
+retrieving the X11 display.
+
+Pass QWindow instead of WId directly, and set the surface
+as the platform data on Wayland systems.
+---
+ UI/qt-display.cpp | 2 +-
+ UI/qt-wrappers.cpp | 28 +++++++++++++++++++++++-----
+ UI/qt-wrappers.hpp | 3 ++-
+ 3 files changed, 26 insertions(+), 7 deletions(-)
+
+diff --git a/UI/qt-display.cpp b/UI/qt-display.cpp
+index 1bb97c7b..685ee6f9 100644
+--- a/UI/qt-display.cpp
++++ b/UI/qt-display.cpp
+@@ -89,7 +89,7 @@ void OBSQTDisplay::CreateDisplay()
+ info.format = GS_BGRA;
+ info.zsformat = GS_ZS_NONE;
+
+- QTToGSWindow(winId(), info.window);
++ QTToGSWindow(windowHandle(), info.window);
+
+ display = obs_display_create(&info, backgroundColor);
+
+diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp
+index e8a51d37..1485a181 100644
+--- a/UI/qt-wrappers.cpp
++++ b/UI/qt-wrappers.cpp
+@@ -27,9 +27,14 @@
+ #include <QKeyEvent>
+
+ #if !defined(_WIN32) && !defined(__APPLE__)
++#include <obs-nix-platform.h>
+ #include <QX11Info>
+ #endif
+
++#ifdef ENABLE_WAYLAND
++#include <qpa/qplatformnativeinterface.h>
++#endif
++
+ static inline void OBSErrorBoxva(QWidget *parent, const char *msg, va_list args)
+ {
+ char full_message[4096];
+@@ -105,15 +110,28 @@ void OBSMessageBox::critical(QWidget *parent, const QString &title,
+ mb.exec();
+ }
+
+-void QTToGSWindow(WId windowId, gs_window &gswindow)
++void QTToGSWindow(QWindow *window, gs_window &gswindow)
+ {
+ #ifdef _WIN32
+- gswindow.hwnd = (HWND)windowId;
++ gswindow.hwnd = (HWND)window->winId();
+ #elif __APPLE__
+- gswindow.view = (id)windowId;
++ gswindow.view = (id)window->winId();
+ #else
+- gswindow.id = windowId;
+- gswindow.display = QX11Info::display();
++ switch (obs_get_nix_platform()) {
++ case OBS_NIX_PLATFORM_X11_GLX:
++ case OBS_NIX_PLATFORM_X11_EGL:
++ gswindow.id = window->winId();
++ gswindow.display = obs_get_nix_platform_display();
++ break;
++#ifdef ENABLE_WAYLAND
++ case OBS_NIX_PLATFORM_WAYLAND:
++ QPlatformNativeInterface *native =
++ QGuiApplication::platformNativeInterface();
++ gswindow.display =
++ native->nativeResourceForWindow("surface", window);
++ break;
++#endif
++ }
+ #endif
+ }
+
+diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp
+index 69996deb..f191f8f9 100644
+--- a/UI/qt-wrappers.hpp
++++ b/UI/qt-wrappers.hpp
+@@ -20,6 +20,7 @@
+ #include <QApplication>
+ #include <QMessageBox>
+ #include <QWidget>
++#include <QWindow>
+ #include <QThread>
+ #include <obs.hpp>
+
+@@ -55,7 +56,7 @@ public:
+
+ void OBSErrorBox(QWidget *parent, const char *msg, ...);
+
+-void QTToGSWindow(WId windowId, gs_window &gswindow);
++void QTToGSWindow(QWindow *window, gs_window &gswindow);
+
+ uint32_t TranslateQtKeyboardEventModifiers(Qt::KeyboardModifiers mods);
+
+--
+2.28.0
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..3baf4fdd2c99
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,84 @@
+# Maintainer: Antoine Damhet <xdbob at lse.epita.fr>
+# Original-Maintainer: Jonathan Steel <jsteel at archlinux.org>
+# Contributor: Benjamin Klettbach <b.klettbach@gmail.com>
+
+_pkgname=obs-studio
+pkgname=$_pkgname-wayland
+provides=("$_pkgname")
+conflicts=("$_pkgname")
+pkgver=25.0.8
+pkgrel=1
+pkgdesc="Free, open source software for live streaming and recording (with wayland patches)"
+arch=('x86_64')
+url="https://obsproject.com"
+license=('GPL2')
+depends=('ffmpeg' 'jansson' 'libxinerama' 'libxkbcommon-x11' 'mbedtls'
+ 'qt5-svg' 'qt5-x11extras' 'curl' 'jack' 'gtk-update-icon-cache')
+makedepends=('cmake' 'libfdk-aac' 'libxcomposite' 'x264' 'vlc' 'swig' 'python' 'luajit')
+optdepends=('libfdk-aac: FDK AAC codec support'
+ 'libxcomposite: XComposite capture support'
+ 'libva-intel-driver: hardware encoding'
+ 'libva-mesa-driver: hardware encoding'
+ 'luajit: scripting support'
+ 'python: scripting support'
+ 'vlc: VLC Media Source support'
+ 'obs-xdg-portal-git: screen capture with xdg-desktop-portal interface'
+ 'xdg-desktop-portal-wlr: screen capture on wlroots compositors')
+source=(
+ $_pkgname-$pkgver.tar.gz::https://github.com/jp9000/obs-studio/archive/$pkgver.tar.gz
+ https://github.com/obsproject/obs-studio/commit/8a1429e29ebd6bf31ad6ae63c6992e2c03893767.patch
+ 0001-deps-glad-Add-EGL.patch
+ 0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch
+ 0003-libobs-opengl-Factor-out-GLX-winsys.patch
+ 0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch
+ 0005-deps-glad-Make-X11-required-as-well.patch
+ 0006-ci-Install-qtbase5-private-dev-on-Linux.patch
+ 0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch
+ 0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch
+ 0009-UI-Set-the-Unix-platform-on-startup.patch
+ 0010-linux-capture-Fail-to-load-when-running-on-EGL.patch
+ 0011-libobs-Add-a-Wayland-platform.patch
+ 0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch
+ 0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch
+ 0014-UI-Retrieve-Wayland-surface-from-QWindow.patch
+)
+sha512sums=('a97c03dc218a4e03e48f6a7dc82b4a59ebeee2039f17be66bb847681ce9ff3d25e6e015be4af78fe44739f6fad5089b6e683d7657c2e4fde8e547df9a2594a08'
+ '1ff0e088eed61554268009f3d8c5a23c0888bfbe860d6cb288ddf348108446c152fd87e2cb8f54613a88378d8474550632c90f924005d5e0343bf1a801339ccc'
+ 'bfe2b0e6da69ffdca95229eb4015515148fdda909355add1d2dec71cf97e9fdabdfc832c74f455a890846708f28d5bcbec64589e853904d539a438b2dcbd7a18'
+ '5221b6a7a46f99c58cde1c5406f83d50def2d5b3a2e97be7db759d94d74a5be46da092209e6a4122a6de4b704632c3f013535f80b570349b029ea4124151c4f6'
+ 'c9a0660c95bd18a02620fb0b870032563669544e7a721e4d91dafb8aebb96d1735414a9e37ed56355fc5afeb8f437a434b4fd5f147c9658cc6974e8e8bab4463'
+ '0b404ff252f94bcdd957d43db26c54c6b47de5a8f810f4febdb0aa5b873c48f23ef2817361e5ce9c09a189e770978cfca24767167604434ece771d759e7c4270'
+ '47f5bffb469ece2b961000cd2d8656b82cba8ac0fa09fa7703c662e0cee2e48744d5b8aa93a4b4508436ea5edfe3038fa7aa88a3b43466f88c7504e6a8ba51ed'
+ 'd15c21968a3024888ce4c8e884d861f147358e95a42c1de557251a4c2fccbdddf9cf5a285deedbf73cffbd25fdaad44dd972cb10bf9a5b23a0049b239e75961f'
+ 'c1f94ccd836c51ff83735df614bf6d3e2c310c599685e700ae5726ace20434edd04ef0c9be0a8c0f4c458dd164ad1ac817fd32bcbeeefb0107a6ce4cbce9cb08'
+ '6ce870404a6d2bfbb25935a6da59a07447307f8592dd1dc1aaebba2b9f959633565ba4cdc7d50ee3c4e5b4c169397298daa5804c3060fc780dba52099f687393'
+ '6374229b662949e2989eb372a922fda872d2a08e817690b2262f99dc8a02261a75aeeacfc40da2b68a04228b38cda4aeaca4212068e8605b7532662dc459abb4'
+ '16dfa319e9e18ef8e946b9723e27d1ea1f56e4de8656d8112571bc87aa7ade8dbda4293f064c2477cdaf92c60fca4484b2c7ac322835bf402657275933f6ab52'
+ 'c81a421475293d3d5c64a744c10a925dc26975a6dfd46e1b3b2a92931da43c311d0a153548889b4e9831bee61ab8b0f5fc504ad3f0ed7f0628f93287e12ad3d3'
+ 'ea36fee6228d582f5f3b886a3de61ad8b139691c3bf30e24a7b20f1eab2f9e43b0dfbf6f254dcef00e2bfbf6826f223a957d3e78524ebd864c64433529e40441'
+ 'a93f186ed24ee979a4297aa063c435ae541f5f1958b86373f6534a2dd85e2178d6f151f115200c987b5e1d999ebd94d6ce0597ef1e7b3588bcb161c53dd4878e'
+ 'c4e6a23edf080076c27599e02909a068b11094848f733297496e7ea0061df56be4becdb58449ec7a05ff2a659fa4c0f75f4006cb204578477308d24d764fba41')
+prepare() {
+ cd $_pkgname-$pkgver
+
+ for patch in ../*.patch; do
+ patch -Np1 -i "$patch"
+ done
+}
+
+build() {
+ cd $_pkgname-$pkgver
+
+ mkdir -p build; cd build
+
+ cmake -DCMAKE_INSTALL_PREFIX="/usr" \
+ -DOBS_VERSION_OVERRIDE="$pkgver-$pkgrel" ..
+
+ make
+}
+
+package() {
+ cd $_pkgname-$pkgver/build
+
+ make install DESTDIR="$pkgdir"
+}