From fd61839bc1525cf1ba9ef99d701db342db38f6d7 Mon Sep 17 00:00:00 2001 From: Antoine Damhet Date: Sun, 9 Aug 2020 13:22:33 +0200 Subject: [PATCH] Initial commit Signed-off-by: Antoine Damhet --- .SRCINFO | 71 + 0001-deps-glad-Add-EGL.patch | 545 ++++ ...engl-Rename-gl-x11.c-to-gl-x11-glx.c.patch | 35 + ...-libobs-opengl-Factor-out-GLX-winsys.patch | 403 +++ ...-opengl-Introduce-the-X11-EGL-winsys.patch | 745 +++++ ...-deps-glad-Make-X11-required-as-well.patch | 26 + ...Install-qtbase5-private-dev-on-Linux.patch | 24 + ...e-X11-specific-code-to-obs-nix-x11.c.patch | 2683 +++++++++++++++++ ...oduce-the-concept-of-a-Unix-platform.patch | 258 ++ ...-UI-Set-the-Unix-platform-on-startup.patch | 67 + ...ure-Fail-to-load-when-running-on-EGL.patch | 38 + 0011-libobs-Add-a-Wayland-platform.patch | 422 +++ ...y-to-use-the-platform-display-if-ava.patch | 50 + ...gl-Introduce-an-EGL-Wayland-renderer.patch | 474 +++ ...etrieve-Wayland-surface-from-QWindow.patch | 107 + PKGBUILD | 84 + 16 files changed, 6032 insertions(+) create mode 100644 .SRCINFO create mode 100644 0001-deps-glad-Add-EGL.patch create mode 100644 0002-libobs-opengl-Rename-gl-x11.c-to-gl-x11-glx.c.patch create mode 100644 0003-libobs-opengl-Factor-out-GLX-winsys.patch create mode 100644 0004-libobs-opengl-Introduce-the-X11-EGL-winsys.patch create mode 100644 0005-deps-glad-Make-X11-required-as-well.patch create mode 100644 0006-ci-Install-qtbase5-private-dev-on-Linux.patch create mode 100644 0007-libobs-nix-Move-X11-specific-code-to-obs-nix-x11.c.patch create mode 100644 0008-libobs-Introduce-the-concept-of-a-Unix-platform.patch create mode 100644 0009-UI-Set-the-Unix-platform-on-startup.patch create mode 100644 0010-linux-capture-Fail-to-load-when-running-on-EGL.patch create mode 100644 0011-libobs-Add-a-Wayland-platform.patch create mode 100644 0012-libobs-opengl-Try-to-use-the-platform-display-if-ava.patch create mode 100644 0013-libobs-opengl-Introduce-an-EGL-Wayland-renderer.patch create mode 100644 0014-UI-Retrieve-Wayland-surface-from-QWindow.patch create mode 100644 PKGBUILD diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 0000000..fa0df7e --- /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 0000000..2a2c7bd --- /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 +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 ++#include ++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 ++#include ++#include ++#include ++ ++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 0000000..f73cc33 --- /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 +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 0000000..ae99c34 --- /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 +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 +--- + 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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 + +-#include "gl-subsystem.h" ++#include "gl-nix.h" + + #include + +@@ -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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 0000000..1dad1ee --- /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 +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 . +--- + 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 ++ ++ 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 . ++******************************************************************************/ ++ ++/* 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 ++#include ++ ++#include ++ ++#include ++ ++#include "gl-x11-egl.h" ++ ++#include ++ ++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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 0000000..f971e8c --- /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 +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 0000000..804e48b --- /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 +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 0000000..47c412c --- /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 +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 ++ Copyright (C) 2014 by Zachary Lund ++ Copyright (C) 2019 by Jason Francis ++ ++ 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 . ++******************************************************************************/ ++ ++#include "obs-internal.h" ++#include "obs-nix-x11.h" ++ ++#include ++#if USE_XINPUT ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 + #include +-#include +-#if USE_XINPUT +-#include +-#endif +-#include +-#include +-#include +-#include +-#include + #include + + 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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 0000000..30152b5 --- /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 +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 + #endif + ++#if !defined(_WIN32) && !defined(__APPLE__) ++#include ++#include ++#endif ++ + #include + + #include "ui-config.h" +@@ -1346,6 +1351,13 @@ bool OBSApp::OBSInit() + + qRegisterMetaType(); + ++#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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 +@@ -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 0000000..d18eccb --- /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 +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 ++ + #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 0000000..2576277 --- /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 +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 . + */ + #include ++#include + + 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 0000000..d95c85d --- /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 +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 + #include ++ ++#ifdef ENABLE_WAYLAND ++#include ++#endif ++ + #endif + + #include +@@ -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 ++# 2020 Georges Basile Stavracas Neto ++# ++# 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 ++ ++ 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 . ++******************************************************************************/ ++ ++#include "obs-internal.h" ++#include "obs-nix-platform.h" ++#include "obs-nix-wayland.h" ++ ++#include ++ ++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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 0000000..8f138ca --- /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 +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 0000000..26ac490 --- /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 +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 + #include + + #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 ++ ++ 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 . ++******************************************************************************/ ++ ++#include "gl-wayland-egl.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 ++ ++ 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 . ++******************************************************************************/ ++ ++#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 0000000..b46f02f --- /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 +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 + + #if !defined(_WIN32) && !defined(__APPLE__) ++#include + #include + #endif + ++#ifdef ENABLE_WAYLAND ++#include ++#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 + #include + #include ++#include + #include + #include + +@@ -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 0000000..3baf4fd --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,84 @@ +# Maintainer: Antoine Damhet +# Original-Maintainer: Jonathan Steel +# Contributor: Benjamin Klettbach + +_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" +}