summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/X11
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/windowing/X11
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/windowing/X11')
-rw-r--r--xbmc/windowing/X11/CMakeLists.txt37
-rw-r--r--xbmc/windowing/X11/GLContext.cpp20
-rw-r--r--xbmc/windowing/X11/GLContext.h42
-rw-r--r--xbmc/windowing/X11/GLContextEGL.cpp542
-rw-r--r--xbmc/windowing/X11/GLContextEGL.h63
-rw-r--r--xbmc/windowing/X11/GLContextGLX.cpp293
-rw-r--r--xbmc/windowing/X11/GLContextGLX.h49
-rw-r--r--xbmc/windowing/X11/OSScreenSaverX11.cpp36
-rw-r--r--xbmc/windowing/X11/OSScreenSaverX11.h28
-rw-r--r--xbmc/windowing/X11/OptionalsReg.cpp256
-rw-r--r--xbmc/windowing/X11/OptionalsReg.h78
-rw-r--r--xbmc/windowing/X11/VideoSyncGLX.cpp277
-rw-r--r--xbmc/windowing/X11/VideoSyncGLX.h62
-rw-r--r--xbmc/windowing/X11/VideoSyncOML.cpp83
-rw-r--r--xbmc/windowing/X11/VideoSyncOML.h46
-rw-r--r--xbmc/windowing/X11/WinEventsX11.cpp673
-rw-r--r--xbmc/windowing/X11/WinEventsX11.h60
-rw-r--r--xbmc/windowing/X11/WinSystemX11.cpp1081
-rw-r--r--xbmc/windowing/X11/WinSystemX11.h117
-rw-r--r--xbmc/windowing/X11/WinSystemX11GLContext.cpp358
-rw-r--r--xbmc/windowing/X11/WinSystemX11GLContext.h79
-rw-r--r--xbmc/windowing/X11/WinSystemX11GLESContext.cpp293
-rw-r--r--xbmc/windowing/X11/WinSystemX11GLESContext.h62
-rw-r--r--xbmc/windowing/X11/X11DPMSSupport.cpp96
-rw-r--r--xbmc/windowing/X11/X11DPMSSupport.h20
-rw-r--r--xbmc/windowing/X11/XRandR.cpp518
-rw-r--r--xbmc/windowing/X11/XRandR.h109
27 files changed, 5378 insertions, 0 deletions
diff --git a/xbmc/windowing/X11/CMakeLists.txt b/xbmc/windowing/X11/CMakeLists.txt
new file mode 100644
index 0000000..91e13d6
--- /dev/null
+++ b/xbmc/windowing/X11/CMakeLists.txt
@@ -0,0 +1,37 @@
+set(SOURCES GLContextEGL.cpp
+ GLContext.cpp
+ OptionalsReg.cpp
+ OSScreenSaverX11.cpp
+ WinEventsX11.cpp
+ WinSystemX11.cpp
+ XRandR.cpp
+ X11DPMSSupport.cpp)
+
+set(HEADERS GLContext.h
+ GLContextEGL.h
+ OptionalsReg.h
+ OSScreenSaverX11.h
+ WinEventsX11.h
+ WinSystemX11.h
+ XRandR.h
+ X11DPMSSupport.h)
+
+if(GLX_FOUND)
+ list(APPEND SOURCES GLContextGLX.cpp
+ VideoSyncGLX.cpp)
+ list(APPEND HEADERS GLContextGLX.h
+ VideoSyncGLX.h)
+endif()
+
+if(OPENGL_FOUND)
+ list(APPEND SOURCES WinSystemX11GLContext.cpp)
+ list(APPEND HEADERS WinSystemX11GLContext.h)
+ list(APPEND SOURCES VideoSyncOML.cpp)
+ list(APPEND HEADERS VideoSyncOML.h)
+endif()
+if(OPENGLES_FOUND)
+ list(APPEND SOURCES WinSystemX11GLESContext.cpp)
+ list(APPEND HEADERS WinSystemX11GLESContext.h)
+endif()
+
+core_add_library(windowing_X11)
diff --git a/xbmc/windowing/X11/GLContext.cpp b/xbmc/windowing/X11/GLContext.cpp
new file mode 100644
index 0000000..3dac508
--- /dev/null
+++ b/xbmc/windowing/X11/GLContext.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GLContext.h"
+
+bool CGLContext::IsExtSupported(const char* extension) const
+{
+ std::string name;
+
+ name = " ";
+ name += extension;
+ name += " ";
+
+ return m_extensions.find(name) != std::string::npos;
+}
diff --git a/xbmc/windowing/X11/GLContext.h b/xbmc/windowing/X11/GLContext.h
new file mode 100644
index 0000000..95e22ec
--- /dev/null
+++ b/xbmc/windowing/X11/GLContext.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+#include <X11/Xlib.h>
+
+class CGLContext
+{
+public:
+ explicit CGLContext(Display *dpy)
+ {
+ m_dpy = dpy;
+ }
+ virtual ~CGLContext() = default;
+ virtual bool Refresh(bool force, int screen, Window glWindow, bool &newContext) = 0;
+ virtual bool CreatePB() { return false; }
+ virtual void Destroy() = 0;
+ virtual void Detach() = 0;
+ virtual void SetVSync(bool enable) = 0;
+ virtual void SwapBuffers() = 0;
+ virtual void QueryExtensions() = 0;
+ virtual uint64_t GetVblankTiming(uint64_t& msc, uint64_t& interval) { return 0; }
+ bool IsExtSupported(const char* extension) const;
+
+ std::string ExtPrefix() { return m_extPrefix; }
+ std::string m_extPrefix;
+ std::string m_extensions;
+
+ Display *m_dpy;
+
+protected:
+ bool m_omlSync = true;
+};
diff --git a/xbmc/windowing/X11/GLContextEGL.cpp b/xbmc/windowing/X11/GLContextEGL.cpp
new file mode 100644
index 0000000..66de374
--- /dev/null
+++ b/xbmc/windowing/X11/GLContextEGL.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+// always define GL_GLEXT_PROTOTYPES before include gl headers
+#if !defined(GL_GLEXT_PROTOTYPES)
+ #define GL_GLEXT_PROTOTYPES
+#endif
+
+#include "GLContextEGL.h"
+
+#include "ServiceBroker.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/log.h"
+
+#include <clocale>
+#include <mutex>
+
+#include <EGL/eglext.h>
+#include <unistd.h>
+
+#include "PlatformDefs.h"
+#include "system_gl.h"
+
+#define EGL_NO_CONFIG (EGLConfig)0
+
+CGLContextEGL::CGLContextEGL(Display* dpy, EGLint renderingApi)
+ : CGLContext(dpy), m_renderingApi(renderingApi)
+{
+ m_extPrefix = "EGL_";
+ m_eglConfig = EGL_NO_CONFIG;
+
+ m_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+ const auto settings = CServiceBroker::GetSettingsComponent();
+ if (settings)
+ {
+ m_omlSync = settings->GetAdvancedSettings()->m_omlSync;
+ }
+}
+
+CGLContextEGL::~CGLContextEGL()
+{
+ Destroy();
+}
+
+bool CGLContextEGL::Refresh(bool force, int screen, Window glWindow, bool &newContext)
+{
+ m_sync.cont = 0;
+
+ // refresh context
+ if (m_eglContext && !force)
+ {
+ if (m_eglSurface == EGL_NO_SURFACE)
+ {
+ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
+ if (m_eglSurface == EGL_NO_SURFACE)
+ {
+ CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
+ return false;
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshEGLContext: refreshing context");
+ eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
+ return true;
+ }
+
+ Destroy();
+ newContext = true;
+
+ if (m_eglGetPlatformDisplayEXT)
+ {
+ EGLint attribs[] =
+ {
+ EGL_PLATFORM_X11_SCREEN_EXT, screen,
+ EGL_NONE
+ };
+ m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
+ attribs);
+ }
+ else
+ m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
+
+ if (m_eglDisplay == EGL_NO_DISPLAY)
+ {
+ CLog::Log(LOGERROR, "failed to get egl display");
+ return false;
+ }
+ if (!eglInitialize(m_eglDisplay, NULL, NULL))
+ {
+ CLog::Log(LOGERROR, "failed to initialize egl");
+ Destroy();
+ return false;
+ }
+ if (!eglBindAPI(m_renderingApi))
+ {
+ CLog::Log(LOGERROR, "failed to bind rendering API");
+ Destroy();
+ return false;
+ }
+
+ // create context
+
+ XVisualInfo vMask;
+ XVisualInfo *vInfo = nullptr;
+ int availableVisuals = 0;
+ vMask.screen = screen;
+ XWindowAttributes winAttr;
+
+ if (!XGetWindowAttributes(m_dpy, glWindow, &winAttr))
+ {
+ CLog::Log(LOGWARNING, "Failed to get window attributes");
+ Destroy();
+ return false;
+ }
+
+ vMask.visualid = XVisualIDFromVisual(winAttr.visual);
+ vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals);
+ if (!vInfo)
+ {
+ CLog::Log(LOGERROR, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid);
+ Destroy();
+ return false;
+ }
+
+ unsigned int visualid = static_cast<unsigned int>(vInfo->visualid);
+ m_eglConfig = GetEGLConfig(m_eglDisplay, vInfo);
+ XFree(vInfo);
+
+ if (m_eglConfig == EGL_NO_CONFIG)
+ {
+ CLog::Log(LOGERROR, "failed to get suitable eglconfig for visual 0x{:x}", visualid);
+ Destroy();
+ return false;
+ }
+
+ CLog::Log(LOGINFO, "Using visual 0x{:x}", visualid);
+
+ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
+ if (m_eglSurface == EGL_NO_SURFACE)
+ {
+ CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
+ Destroy();
+ return false;
+ }
+
+ EGLint contextAttributes[] =
+ {
+ EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
+ EGL_CONTEXT_MINOR_VERSION_KHR, 2,
+ EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
+ EGL_NONE
+ };
+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
+ if (m_eglContext == EGL_NO_CONTEXT)
+ {
+ EGLint contextAttributes[] =
+ {
+ EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
+ EGL_NONE
+ };
+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
+
+ if (m_eglContext == EGL_NO_CONTEXT)
+ {
+ CLog::Log(LOGERROR, "failed to create EGL context");
+ Destroy();
+ return false;
+ }
+
+ CLog::Log(LOGWARNING, "Failed to get an OpenGL context supporting core profile 3.2, "
+ "using legacy mode with reduced feature set");
+ }
+
+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
+ {
+ CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
+ fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
+ Destroy();
+ return false;
+ }
+
+ m_eglGetSyncValuesCHROMIUM = (PFNEGLGETSYNCVALUESCHROMIUMPROC)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
+
+ m_usePB = false;
+ return true;
+}
+
+bool CGLContextEGL::CreatePB()
+{
+ const EGLint configAttribs[] =
+ {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_BLUE_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_RED_SIZE, 8,
+ EGL_DEPTH_SIZE, 8,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+ EGL_NONE
+ };
+
+ const EGLint pbufferAttribs[] =
+ {
+ EGL_WIDTH, 9,
+ EGL_HEIGHT, 9,
+ EGL_NONE,
+ };
+
+ Destroy();
+
+ if (m_eglGetPlatformDisplayEXT)
+ {
+ m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
+ NULL);
+ }
+ else
+ m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
+
+ if (m_eglDisplay == EGL_NO_DISPLAY)
+ {
+ CLog::Log(LOGERROR, "failed to get egl display");
+ return false;
+ }
+ if (!eglInitialize(m_eglDisplay, NULL, NULL))
+ {
+ CLog::Log(LOGERROR, "failed to initialize egl");
+ Destroy();
+ return false;
+ }
+ if (!eglBindAPI(m_renderingApi))
+ {
+ CLog::Log(LOGERROR, "failed to bind rendering API");
+ Destroy();
+ return false;
+ }
+
+ EGLint numConfigs;
+
+ eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs);
+ m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttribs);
+ if (m_eglSurface == EGL_NO_SURFACE)
+ {
+ CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
+ Destroy();
+ return false;
+ }
+
+ EGLint contextAttributes[] =
+ {
+ EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
+ EGL_CONTEXT_MINOR_VERSION_KHR, 2,
+ EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
+ EGL_NONE
+ };
+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
+ if (m_eglContext == EGL_NO_CONTEXT)
+ {
+ EGLint contextAttributes[] =
+ {
+ EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
+ EGL_NONE
+ };
+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
+
+ if (m_eglContext == EGL_NO_CONTEXT)
+ {
+ CLog::Log(LOGERROR, "failed to create EGL context");
+ Destroy();
+ return false;
+ }
+ }
+
+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
+ {
+ CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
+ fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
+ Destroy();
+ return false;
+ }
+
+ m_usePB = true;
+ return true;
+}
+
+void CGLContextEGL::Destroy()
+{
+ if (m_eglContext)
+ {
+ glFinish();
+ eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(m_eglDisplay, m_eglContext);
+ m_eglContext = EGL_NO_CONTEXT;
+ }
+
+ if (m_eglSurface)
+ {
+ eglDestroySurface(m_eglDisplay, m_eglSurface);
+ m_eglSurface = EGL_NO_SURFACE;
+ }
+
+ if (m_eglDisplay)
+ {
+ eglTerminate(m_eglDisplay);
+ m_eglDisplay = EGL_NO_DISPLAY;
+ }
+
+ m_eglConfig = EGL_NO_CONFIG;
+}
+
+void CGLContextEGL::Detach()
+{
+ if (m_eglContext)
+ {
+ glFinish();
+ eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ if (m_eglSurface)
+ {
+ eglDestroySurface(m_eglDisplay, m_eglSurface);
+ m_eglSurface = EGL_NO_SURFACE;
+ }
+}
+
+bool CGLContextEGL::SuitableCheck(EGLDisplay eglDisplay, EGLConfig config)
+{
+ if (config == EGL_NO_CONFIG)
+ return false;
+
+ EGLint value;
+ if (!eglGetConfigAttrib(eglDisplay, config, EGL_RED_SIZE, &value) || value < 8)
+ return false;
+ if (!eglGetConfigAttrib(eglDisplay, config, EGL_GREEN_SIZE, &value) || value < 8)
+ return false;
+ if (!eglGetConfigAttrib(eglDisplay, config, EGL_BLUE_SIZE, &value) || value < 8)
+ return false;
+ if (!eglGetConfigAttrib(eglDisplay, config, EGL_DEPTH_SIZE, &value) || value < 24)
+ return false;
+
+ return true;
+}
+
+EGLConfig CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay, XVisualInfo *vInfo)
+{
+ EGLint numConfigs;
+
+ if (!eglGetConfigs(eglDisplay, nullptr, 0, &numConfigs))
+ {
+ CLog::Log(LOGERROR, "Failed to query number of egl configs");
+ return EGL_NO_CONFIG;
+ }
+ if (numConfigs == 0)
+ {
+ CLog::Log(LOGERROR, "No suitable egl configs found");
+ return EGL_NO_CONFIG;
+ }
+
+ EGLConfig *eglConfigs;
+ eglConfigs = (EGLConfig*)malloc(numConfigs * sizeof(EGLConfig));
+ if (!eglConfigs)
+ {
+ CLog::Log(LOGERROR, "eglConfigs malloc failed");
+ return EGL_NO_CONFIG;
+ }
+ EGLConfig eglConfig = EGL_NO_CONFIG;
+ if (!eglGetConfigs(eglDisplay, eglConfigs, numConfigs, &numConfigs))
+ {
+ CLog::Log(LOGERROR, "Failed to query egl configs");
+ goto Exit;
+ }
+ for (EGLint i = 0; i < numConfigs; ++i)
+ {
+ if (!SuitableCheck(eglDisplay, eglConfigs[i]))
+ continue;
+
+ EGLint value;
+ if (!eglGetConfigAttrib(eglDisplay, eglConfigs[i], EGL_NATIVE_VISUAL_ID, &value))
+ {
+ CLog::Log(LOGERROR, "Failed to query EGL_NATIVE_VISUAL_ID for egl config.");
+ break;
+ }
+ if (value == (EGLint)vInfo->visualid)
+ {
+ eglConfig = eglConfigs[i];
+ break;
+ }
+ }
+
+Exit:
+ free(eglConfigs);
+ return eglConfig;
+}
+
+void CGLContextEGL::SetVSync(bool enable)
+{
+ eglSwapInterval(m_eglDisplay, enable ? 1 : 0);
+}
+
+void CGLContextEGL::SwapBuffers()
+{
+ if ((m_eglDisplay == EGL_NO_DISPLAY) || (m_eglSurface == EGL_NO_SURFACE))
+ return;
+
+ if (m_usePB)
+ {
+ eglSwapBuffers(m_eglDisplay, m_eglSurface);
+ usleep(20 * 1000);
+ return;
+ }
+
+ uint64_t ust1, ust2;
+ uint64_t msc1, msc2;
+ uint64_t sbc1, sbc2;
+ struct timespec nowTs;
+ uint64_t now;
+ uint64_t cont = m_sync.cont;
+ uint64_t interval = m_sync.interval;
+
+ if (m_eglGetSyncValuesCHROMIUM)
+ {
+ m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust1, &msc1, &sbc1);
+ }
+
+ eglSwapBuffers(m_eglDisplay, m_eglSurface);
+
+ if (!m_eglGetSyncValuesCHROMIUM)
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &nowTs);
+ now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
+
+ m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust2, &msc2, &sbc2);
+
+ if ((msc1 - m_sync.msc1) > 2)
+ {
+ cont = 0;
+ }
+
+ // we want to block in SwapBuffers
+ // if a vertical retrace occurs 5 times in a row outside
+ // of this function, we take action
+ if (m_sync.cont < 5)
+ {
+ if ((msc1 - m_sync.msc1) == 2)
+ {
+ cont = 0;
+ }
+ else if ((msc1 - m_sync.msc1) == 1)
+ {
+ interval = (ust1 - m_sync.ust1) / (msc1 - m_sync.msc1);
+ cont++;
+ }
+ }
+ else if (m_sync.cont == 5 && m_omlSync)
+ {
+ CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync check blocking");
+
+ if (msc2 == msc1)
+ {
+ // if no vertical retrace has occurred in eglSwapBuffers,
+ // sleep until next vertical retrace
+ uint64_t lastIncrement = (now / 1000 - ust2);
+ if (lastIncrement > m_sync.interval)
+ {
+ lastIncrement = m_sync.interval;
+ CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval");
+ }
+ uint64_t sleeptime = m_sync.interval - lastIncrement;
+ usleep(sleeptime);
+ cont++;
+ msc2++;
+ CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync sleep: {}", sleeptime);
+ }
+ }
+ else if ((m_sync.cont > 5) && (msc2 == m_sync.msc2))
+ {
+ // sleep until next vertical retrace
+ // this avoids blocking outside of this function
+ uint64_t lastIncrement = (now / 1000 - ust2);
+ if (lastIncrement > m_sync.interval)
+ {
+ lastIncrement = m_sync.interval;
+ CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval (1)");
+ }
+ uint64_t sleeptime = m_sync.interval - lastIncrement;
+ usleep(sleeptime);
+ msc2++;
+ }
+ {
+ std::unique_lock<CCriticalSection> lock(m_syncLock);
+ m_sync.ust1 = ust1;
+ m_sync.ust2 = ust2;
+ m_sync.msc1 = msc1;
+ m_sync.msc2 = msc2;
+ m_sync.interval = interval;
+ m_sync.cont = cont;
+ }
+}
+
+uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc, uint64_t &interval)
+{
+ struct timespec nowTs;
+ uint64_t now;
+ clock_gettime(CLOCK_MONOTONIC, &nowTs);
+ now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
+ now /= 1000;
+
+ std::unique_lock<CCriticalSection> lock(m_syncLock);
+ msc = m_sync.msc2;
+
+ interval = (m_sync.cont >= 5) ? m_sync.interval : m_sync.ust2 - m_sync.ust1;
+ if (interval == 0)
+ return 0;
+
+ if (now < m_sync.ust2)
+ {
+ return 0;
+ }
+
+ uint64_t ret = now - m_sync.ust2;
+ while (ret > interval)
+ {
+ ret -= interval;
+ msc++;
+ }
+
+ return ret;
+}
+
+void CGLContextEGL::QueryExtensions()
+{
+ std::string extensions = eglQueryString(m_eglDisplay, EGL_EXTENSIONS);
+ m_extensions = std::string(" ") + extensions + " ";
+
+ CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_extensions);
+}
diff --git a/xbmc/windowing/X11/GLContextEGL.h b/xbmc/windowing/X11/GLContextEGL.h
new file mode 100644
index 0000000..441787b
--- /dev/null
+++ b/xbmc/windowing/X11/GLContextEGL.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "GLContext.h"
+#include "system_egl.h"
+#include "threads/CriticalSection.h"
+
+#include <cstdint>
+
+#include <EGL/eglext.h>
+#ifdef HAVE_EGLEXTANGLE
+#include <EGL/eglext_angle.h>
+#else
+#include <EGL/eglextchromium.h>
+#endif
+#include <X11/Xutil.h>
+
+class CGLContextEGL : public CGLContext
+{
+public:
+ explicit CGLContextEGL(Display *dpy, EGLint renderingApi);
+ ~CGLContextEGL() override;
+ bool Refresh(bool force, int screen, Window glWindow, bool &newContext) override;
+ bool CreatePB() override;
+ void Destroy() override;
+ void Detach() override;
+ void SetVSync(bool enable) override;
+ void SwapBuffers() override;
+ void QueryExtensions() override;
+ uint64_t GetVblankTiming(uint64_t &msc, uint64_t &interval) override;
+
+ EGLint m_renderingApi;
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
+ EGLSurface m_eglSurface = EGL_NO_SURFACE;
+ EGLContext m_eglContext = EGL_NO_CONTEXT;
+ EGLConfig m_eglConfig;
+protected:
+ bool SuitableCheck(EGLDisplay eglDisplay, EGLConfig config);
+ EGLConfig GetEGLConfig(EGLDisplay eglDisplay, XVisualInfo *vInfo);
+ PFNEGLGETSYNCVALUESCHROMIUMPROC m_eglGetSyncValuesCHROMIUM = nullptr;
+ PFNEGLGETPLATFORMDISPLAYEXTPROC m_eglGetPlatformDisplayEXT = nullptr;
+
+ struct Sync
+ {
+ uint64_t cont = 0;
+ uint64_t ust1 = 0;
+ uint64_t ust2 = 0;
+ uint64_t msc1 = 0;
+ uint64_t msc2 = 0;
+ uint64_t interval = 0;
+ } m_sync;
+
+ CCriticalSection m_syncLock;
+
+ bool m_usePB = false;
+};
diff --git a/xbmc/windowing/X11/GLContextGLX.cpp b/xbmc/windowing/X11/GLContextGLX.cpp
new file mode 100644
index 0000000..3c31c22
--- /dev/null
+++ b/xbmc/windowing/X11/GLContextGLX.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GLContextGLX.h"
+
+#include "utils/log.h"
+
+#include <GL/glx.h>
+
+#include "system_gl.h"
+
+using namespace KODI::WINDOWING::X11;
+
+CGLContextGLX::CGLContextGLX(Display *dpy) : CGLContext(dpy)
+{
+ m_extPrefix = "GLX_";
+ m_vsyncMode = 0;
+}
+
+bool CGLContextGLX::Refresh(bool force, int screen, Window glWindow, bool &newContext)
+{
+ bool retVal = false;
+ m_glxWindow = glWindow;
+ m_nScreen = screen;
+
+ // refresh context
+ if (m_glxContext && !force)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshGlxContext: refreshing context");
+ glXMakeCurrent(m_dpy, None, NULL);
+ glXMakeCurrent(m_dpy, glWindow, m_glxContext);
+ return true;
+ }
+
+ // create context
+
+ XVisualInfo vMask;
+ XVisualInfo *visuals;
+ XVisualInfo *vInfo = NULL;
+ int availableVisuals = 0;
+ vMask.screen = screen;
+ XWindowAttributes winAttr;
+
+ /* Assume a depth of 24 in case the below calls to XGetWindowAttributes()
+ or XGetVisualInfo() fail. That shouldn't happen unless something is
+ fatally wrong, but lets prepare for everything. */
+ vMask.depth = 24;
+
+ if (XGetWindowAttributes(m_dpy, glWindow, &winAttr))
+ {
+ vMask.visualid = XVisualIDFromVisual(winAttr.visual);
+ vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals);
+ if (!vInfo)
+ CLog::Log(LOGWARNING, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid);
+ else if(!IsSuitableVisual(vInfo))
+ {
+ CLog::Log(LOGWARNING,
+ "Visual 0x{:x} of the window is not suitable, looking for another one...",
+ (unsigned)vInfo->visualid);
+ vMask.depth = vInfo->depth;
+ XFree(vInfo);
+ vInfo = NULL;
+ }
+ }
+ else
+ CLog::Log(LOGWARNING, "Failed to get window attributes");
+
+ /* As per glXMakeCurrent documentation, we have to use the same visual as
+ m_glWindow. Since that was not suitable for use, we try to use another
+ one with the same depth and hope that the used implementation is less
+ strict than the documentation. */
+ if (!vInfo)
+ {
+ visuals = XGetVisualInfo(m_dpy, VisualScreenMask | VisualDepthMask, &vMask, &availableVisuals);
+ for (int i = 0; i < availableVisuals; i++)
+ {
+ if (IsSuitableVisual(&visuals[i]))
+ {
+ vMask.visualid = visuals[i].visualid;
+ vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals);
+ break;
+ }
+ }
+ XFree(visuals);
+ }
+
+ if (vInfo)
+ {
+ CLog::Log(LOGINFO, "Using visual 0x{:x}", (unsigned)vInfo->visualid);
+ if (m_glxContext)
+ {
+ glXMakeCurrent(m_dpy, None, NULL);
+ glXDestroyContext(m_dpy, m_glxContext);
+ XSync(m_dpy, False);
+ }
+
+ if ((m_glxContext = glXCreateContext(m_dpy, vInfo, NULL, True)))
+ {
+ // make this context current
+ glXMakeCurrent(m_dpy, glWindow, m_glxContext);
+ retVal = true;
+ newContext = true;
+ }
+ else
+ CLog::Log(LOGERROR, "GLX Error: Could not create context");
+
+ XFree(vInfo);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "GLX Error: vInfo is NULL!");
+ }
+
+ return retVal;
+}
+
+void CGLContextGLX::Destroy()
+{
+ glXMakeCurrent(m_dpy, None, NULL);
+ glXDestroyContext(m_dpy, m_glxContext);
+ m_glxContext = 0;
+}
+
+void CGLContextGLX::Detach()
+{
+ glXMakeCurrent(m_dpy, None, NULL);
+}
+
+bool CGLContextGLX::IsSuitableVisual(XVisualInfo *vInfo)
+{
+ int value;
+
+ if (glXGetConfig(m_dpy, vInfo, GLX_RGBA, &value) || !value)
+ return false;
+ if (glXGetConfig(m_dpy, vInfo, GLX_DOUBLEBUFFER, &value) || !value)
+ return false;
+ if (glXGetConfig(m_dpy, vInfo, GLX_RED_SIZE, &value) || value < 8)
+ return false;
+ if (glXGetConfig(m_dpy, vInfo, GLX_GREEN_SIZE, &value) || value < 8)
+ return false;
+ if (glXGetConfig(m_dpy, vInfo, GLX_BLUE_SIZE, &value) || value < 8)
+ return false;
+ if (glXGetConfig(m_dpy, vInfo, GLX_DEPTH_SIZE, &value) || value < 24)
+ return false;
+
+ return true;
+}
+
+void CGLContextGLX::SetVSync(bool enable)
+{
+ // turn of current setting first
+ if(m_glXSwapIntervalEXT)
+ m_glXSwapIntervalEXT(m_dpy, m_glxWindow, 0);
+ else if(m_glXSwapIntervalMESA)
+ m_glXSwapIntervalMESA(0);
+
+ m_iVSyncErrors = 0;
+
+ if(!enable)
+ return;
+
+ if (m_glXSwapIntervalEXT)
+ {
+ m_glXSwapIntervalEXT(m_dpy, m_glxWindow, 1);
+ m_vsyncMode = 6;
+ }
+ if (m_glXSwapIntervalMESA)
+ {
+ if(m_glXSwapIntervalMESA(1) == 0)
+ m_vsyncMode = 2;
+ else
+ CLog::Log(LOGWARNING, "{} - glXSwapIntervalMESA failed", __FUNCTION__);
+ }
+ if (m_glXWaitVideoSyncSGI && m_glXGetVideoSyncSGI && !m_vsyncMode)
+ {
+ unsigned int count;
+ if(m_glXGetVideoSyncSGI(&count) == 0)
+ m_vsyncMode = 3;
+ else
+ CLog::Log(LOGWARNING, "{} - glXGetVideoSyncSGI failed, glcontext probably not direct",
+ __FUNCTION__);
+ }
+}
+
+void CGLContextGLX::SwapBuffers()
+{
+ if (m_vsyncMode == 3)
+ {
+ glFinish();
+ unsigned int before = 0, after = 0;
+ if (m_glXGetVideoSyncSGI(&before) != 0)
+ CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count",
+ __FUNCTION__);
+
+ glXSwapBuffers(m_dpy, m_glxWindow);
+ glFinish();
+
+ if(m_glXGetVideoSyncSGI(&after) != 0)
+ CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count",
+ __FUNCTION__);
+
+ if (after == before)
+ m_iVSyncErrors = 1;
+ else
+ m_iVSyncErrors--;
+
+ if (m_iVSyncErrors > 0)
+ {
+ CLog::Log(LOGINFO, "GL: retrace count didn't change after buffer swap, switching to vsync mode 4");
+ m_iVSyncErrors = 0;
+ m_vsyncMode = 4;
+ }
+
+ if (m_iVSyncErrors < -200)
+ {
+ CLog::Log(
+ LOGINFO,
+ "GL: retrace count change for {} consecutive buffer swap, switching to vsync mode 2",
+ -m_iVSyncErrors);
+ m_iVSyncErrors = 0;
+ m_vsyncMode = 2;
+ }
+ }
+ else if (m_vsyncMode == 4)
+ {
+ glFinish();
+ unsigned int before = 0, swap = 0, after = 0;
+ if (m_glXGetVideoSyncSGI(&before) != 0)
+ CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count",
+ __FUNCTION__);
+
+ if(m_glXWaitVideoSyncSGI(2, (before+1)%2, &swap) != 0)
+ CLog::Log(LOGERROR, "{} - glXWaitVideoSyncSGI - Returned error", __FUNCTION__);
+
+ glXSwapBuffers(m_dpy, m_glxWindow);
+ glFinish();
+
+ if (m_glXGetVideoSyncSGI(&after) != 0)
+ CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count",
+ __FUNCTION__);
+
+ if (after == before)
+ CLog::Log(LOGERROR, "{} - glXWaitVideoSyncSGI - Woke up early", __FUNCTION__);
+
+ if (after > before + 1)
+ m_iVSyncErrors++;
+ else
+ m_iVSyncErrors = 0;
+
+ if (m_iVSyncErrors > 30)
+ {
+ CLog::Log(LOGINFO, "GL: retrace count seems to be changing due to the swapbuffers call, switching to vsync mode 3");
+ m_vsyncMode = 3;
+ m_iVSyncErrors = 0;
+ }
+ }
+ else
+ glXSwapBuffers(m_dpy, m_glxWindow);
+}
+
+void CGLContextGLX::QueryExtensions()
+{
+ m_extensions = " ";
+ m_extensions += glXQueryExtensionsString(m_dpy, m_nScreen);
+ m_extensions += " ";
+
+ CLog::Log(LOGDEBUG, "GLX_EXTENSIONS:{}", m_extensions);
+
+ if (IsExtSupported("GLX_SGI_video_sync"))
+ m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI");
+ else
+ m_glXWaitVideoSyncSGI = NULL;
+
+ if (IsExtSupported("GLX_SGI_video_sync"))
+ m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI");
+ else
+ m_glXGetVideoSyncSGI = NULL;
+
+ if (IsExtSupported("GLX_MESA_swap_control"))
+ m_glXSwapIntervalMESA = (int (*)(int))glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
+ else
+ m_glXSwapIntervalMESA = NULL;
+
+ if (IsExtSupported("GLX_EXT_swap_control"))
+ m_glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
+ else
+ m_glXSwapIntervalEXT = NULL;
+}
diff --git a/xbmc/windowing/X11/GLContextGLX.h b/xbmc/windowing/X11/GLContextGLX.h
new file mode 100644
index 0000000..6fd41b3
--- /dev/null
+++ b/xbmc/windowing/X11/GLContextGLX.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "GLContext.h"
+
+#include <GL/glx.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CGLContextGLX : public CGLContext
+{
+public:
+ explicit CGLContextGLX(Display *dpy);
+ bool Refresh(bool force, int screen, Window glWindow, bool &newContext) override;
+ void Destroy() override;
+ void Detach() override;
+ void SetVSync(bool enable) override;
+ void SwapBuffers() override;
+ void QueryExtensions() override;
+ GLXWindow m_glxWindow = 0;
+ GLXContext m_glxContext = 0;
+
+protected:
+ bool IsSuitableVisual(XVisualInfo *vInfo);
+
+ int (*m_glXGetVideoSyncSGI)(unsigned int*);
+ int (*m_glXWaitVideoSyncSGI)(int, int, unsigned int*);
+ int (*m_glXSwapIntervalMESA)(int);
+ PFNGLXSWAPINTERVALEXTPROC m_glXSwapIntervalEXT;
+ int m_nScreen;
+ int m_iVSyncErrors;
+ int m_vsyncMode;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/OSScreenSaverX11.cpp b/xbmc/windowing/X11/OSScreenSaverX11.cpp
new file mode 100644
index 0000000..3395e46
--- /dev/null
+++ b/xbmc/windowing/X11/OSScreenSaverX11.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OSScreenSaverX11.h"
+
+#include <cassert>
+
+using namespace std::chrono_literals;
+
+COSScreenSaverX11::COSScreenSaverX11(Display* dpy)
+: m_dpy(dpy), m_screensaverResetTimer(std::bind(&COSScreenSaverX11::ResetScreenSaver, this))
+{
+ assert(m_dpy);
+}
+
+void COSScreenSaverX11::Inhibit()
+{
+ // disallow the screensaver by periodically calling XResetScreenSaver(),
+ // for some reason setting a 0 timeout with XSetScreenSaver doesn't work with gnome
+ m_screensaverResetTimer.Start(5000ms, true);
+}
+
+void COSScreenSaverX11::Uninhibit()
+{
+ m_screensaverResetTimer.Stop(true);
+}
+
+void COSScreenSaverX11::ResetScreenSaver()
+{
+ XResetScreenSaver(m_dpy);
+}
diff --git a/xbmc/windowing/X11/OSScreenSaverX11.h b/xbmc/windowing/X11/OSScreenSaverX11.h
new file mode 100644
index 0000000..60c9b45
--- /dev/null
+++ b/xbmc/windowing/X11/OSScreenSaverX11.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "../OSScreenSaver.h"
+#include "threads/Timer.h"
+
+#include <X11/Xlib.h>
+
+class COSScreenSaverX11 : public KODI::WINDOWING::IOSScreenSaver
+{
+public:
+ explicit COSScreenSaverX11(Display* dpy);
+ void Inhibit() override;
+ void Uninhibit() override;
+
+private:
+ void ResetScreenSaver();
+
+ Display* m_dpy;
+ CTimer m_screensaverResetTimer;
+};
diff --git a/xbmc/windowing/X11/OptionalsReg.cpp b/xbmc/windowing/X11/OptionalsReg.cpp
new file mode 100644
index 0000000..ecfd780
--- /dev/null
+++ b/xbmc/windowing/X11/OptionalsReg.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OptionalsReg.h"
+
+//-----------------------------------------------------------------------------
+// VAAPI
+//-----------------------------------------------------------------------------
+#if defined (HAVE_LIBVA)
+#include <va/va_x11.h>
+#include "cores/VideoPlayer/DVDCodecs/Video/VAAPI.h"
+#if defined(HAS_GL)
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.h"
+#endif
+#if defined(HAS_GLES)
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.h"
+#endif
+
+using namespace KODI::WINDOWING::X11;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinSystemX11GLContext;
+
+class CVaapiProxy : public VAAPI::IVaapiWinSystem
+{
+public:
+ CVaapiProxy() = default;
+ virtual ~CVaapiProxy() = default;
+ VADisplay GetVADisplay() override { return vaGetDisplay(dpy); };
+ void *GetEGLDisplay() override { return eglDisplay; };
+
+ Display *dpy;
+ void *eglDisplay;
+};
+
+CVaapiProxy* VaapiProxyCreate()
+{
+ return new CVaapiProxy();
+}
+
+void VaapiProxyDelete(CVaapiProxy *proxy)
+{
+ delete proxy;
+}
+
+void VaapiProxyConfig(CVaapiProxy *proxy, void *dpy, void *eglDpy)
+{
+ proxy->dpy = static_cast<Display*>(dpy);
+ proxy->eglDisplay = eglDpy;
+}
+
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor)
+{
+ VAAPI::CDecoder::Register(winSystem, deepColor);
+}
+
+#if defined(HAS_GL)
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+ EGLDisplay eglDpy = winSystem->eglDisplay;
+ VADisplay vaDpy = vaGetDisplay(winSystem->dpy);
+ CRendererVAAPIGL::Register(winSystem, vaDpy, eglDpy, general, deepColor);
+}
+#endif
+
+#if defined(HAS_GLES)
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+ EGLDisplay eglDpy = winSystem->eglDisplay;
+ VADisplay vaDpy = vaGetDisplay(winSystem->dpy);
+ CRendererVAAPIGLES::Register(winSystem, vaDpy, eglDpy, general, deepColor);
+}
+#endif
+}
+}
+}
+
+#else
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CVaapiProxy
+{
+};
+
+CVaapiProxy* VaapiProxyCreate()
+{
+ return nullptr;
+}
+
+void VaapiProxyDelete(CVaapiProxy *proxy)
+{
+}
+
+void VaapiProxyConfig(CVaapiProxy *proxy, void *dpy, void *eglDpy)
+{
+}
+
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor)
+{
+}
+
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+}
+
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+}
+
+}
+}
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// GLX
+//-----------------------------------------------------------------------------
+
+#ifdef HAS_GLX
+#include <GL/glx.h>
+#include "VideoSyncGLX.h"
+#include "GLContextGLX.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+XID GLXGetWindow(void* context)
+{
+ return static_cast<CGLContextGLX*>(context)->m_glxWindow;
+}
+
+void* GLXGetContext(void* context)
+{
+ return static_cast<CGLContextGLX*>(context)->m_glxContext;
+}
+
+CGLContext* GLXContextCreate(Display *dpy)
+{
+ return new CGLContextGLX(dpy);
+}
+
+
+CVideoSync* GLXVideoSyncCreate(void* clock, CWinSystemX11GLContext& winSystem)
+{
+ return new CVideoSyncGLX(clock, winSystem);
+}
+
+}
+}
+}
+#else
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+XID GLXGetWindow(void* context)
+{
+ return 0;
+}
+
+void* GLXGetContext(void* context)
+{
+ return nullptr;
+}
+
+CGLContext* GLXContextCreate(Display *dpy)
+{
+ return nullptr;
+}
+
+CVideoSync* GLXVideoSyncCreate(void* clock, CWinSystemX11GLContext& winSystem)
+{
+ return nullptr;
+}
+
+}
+}
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// VDPAU
+//-----------------------------------------------------------------------------
+
+#if defined (HAVE_LIBVDPAU)
+#include "cores/VideoPlayer/DVDCodecs/Video/VDPAU.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+void VDPAURegisterRender()
+{
+ CRendererVDPAU::Register();
+}
+
+void VDPAURegister()
+{
+ VDPAU::CDecoder::Register();
+}
+
+}
+}
+}
+#else
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+void VDPAURegisterRender()
+{
+
+}
+
+void VDPAURegister()
+{
+
+}
+
+}
+}
+}
+#endif
+
diff --git a/xbmc/windowing/X11/OptionalsReg.h b/xbmc/windowing/X11/OptionalsReg.h
new file mode 100644
index 0000000..2c4f15b
--- /dev/null
+++ b/xbmc/windowing/X11/OptionalsReg.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <X11/Xlib.h>
+
+//-----------------------------------------------------------------------------
+// VAAPI
+//-----------------------------------------------------------------------------
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CVaapiProxy;
+
+CVaapiProxy* VaapiProxyCreate();
+void VaapiProxyDelete(CVaapiProxy *proxy);
+void VaapiProxyConfig(CVaapiProxy *proxy, void *dpy, void *eglDpy);
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor);
+#if defined(HAS_GL)
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor);
+#endif
+#if defined(HAS_GLES)
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor);
+#endif
+}
+}
+}
+
+//-----------------------------------------------------------------------------
+// GLX
+//-----------------------------------------------------------------------------
+
+class CVideoSync;
+class CGLContext;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinSystemX11GLContext;
+
+XID GLXGetWindow(void* context);
+void* GLXGetContext(void* context);
+CGLContext* GLXContextCreate(Display *dpy);
+CVideoSync* GLXVideoSyncCreate(void *clock, CWinSystemX11GLContext& winSystem);
+}
+}
+}
+
+//-----------------------------------------------------------------------------
+// VDPAU
+//-----------------------------------------------------------------------------
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+void VDPAURegisterRender();
+void VDPAURegister();
+}
+}
+}
diff --git a/xbmc/windowing/X11/VideoSyncGLX.cpp b/xbmc/windowing/X11/VideoSyncGLX.cpp
new file mode 100644
index 0000000..0e29a85
--- /dev/null
+++ b/xbmc/windowing/X11/VideoSyncGLX.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoSyncGLX.h"
+
+#include "utils/TimeUtils.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/X11/WinSystemX11GLContext.h"
+
+#include <mutex>
+#include <sstream>
+
+#include <X11/extensions/Xrandr.h>
+
+using namespace KODI::WINDOWING::X11;
+
+using namespace std::chrono_literals;
+
+Display* CVideoSyncGLX::m_Dpy = NULL;
+
+void CVideoSyncGLX::OnLostDisplay()
+{
+ if (!m_displayLost)
+ {
+ m_displayLost = true;
+ m_lostEvent.Wait();
+ }
+}
+
+void CVideoSyncGLX::OnResetDisplay()
+{
+ m_displayReset = true;
+}
+
+bool CVideoSyncGLX::Setup(PUPDATECLOCK func)
+{
+ std::unique_lock<CCriticalSection> lock(m_winSystem.GetGfxContext());
+
+ m_glXWaitVideoSyncSGI = NULL;
+ m_glXGetVideoSyncSGI = NULL;
+ m_vInfo = NULL;
+ m_Window = 0;
+ m_Context = NULL;
+ UpdateClock = func;
+
+ int singleBufferAttributes[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 0,
+ GLX_GREEN_SIZE, 0,
+ GLX_BLUE_SIZE, 0,
+ None
+ };
+
+ int ReturnV, SwaMask;
+ unsigned int GlxTest;
+ XSetWindowAttributes Swa;
+
+ m_vInfo = NULL;
+ m_Context = NULL;
+ m_Window = 0;
+
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up GLX");
+
+ static_cast<CWinSystemX11*>(&m_winSystem)->Register(this);
+
+ m_displayLost = false;
+ m_displayReset = false;
+ m_lostEvent.Reset();
+
+ if (!m_Dpy)
+ {
+ m_Dpy = XOpenDisplay(NULL);
+ if (!m_Dpy)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Unable to open display");
+ return false;
+ }
+ }
+
+ if (!glXQueryExtension(m_Dpy, NULL, NULL))
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX");
+ return false;
+ }
+
+ bool ExtensionFound = false;
+ std::istringstream Extensions(glXQueryExtensionsString(m_Dpy, m_winSystem.GetScreen()));
+ std::string ExtensionStr;
+
+ while (!ExtensionFound)
+ {
+ Extensions >> ExtensionStr;
+ if (Extensions.fail())
+ break;
+
+ if (ExtensionStr == "GLX_SGI_video_sync")
+ ExtensionFound = true;
+ }
+
+ if (!ExtensionFound)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX_SGI_video_sync");
+ return false;
+ }
+
+ m_vInfo = glXChooseVisual(m_Dpy, m_winSystem.GetScreen(), singleBufferAttributes);
+ if (!m_vInfo)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXChooseVisual returned NULL");
+ return false;
+ }
+
+ Swa.border_pixel = 0;
+ Swa.event_mask = StructureNotifyMask;
+ Swa.colormap = XCreateColormap(m_Dpy, m_winSystem.GetWindow(), m_vInfo->visual, AllocNone );
+ SwaMask = CWBorderPixel | CWColormap | CWEventMask;
+
+ m_Window = XCreateWindow(m_Dpy, m_winSystem.GetWindow(), 0, 0, 256, 256, 0,
+ m_vInfo->depth, InputOutput, m_vInfo->visual, SwaMask, &Swa);
+
+ m_Context = glXCreateContext(m_Dpy, m_vInfo, NULL, True);
+ if (!m_Context)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXCreateContext returned NULL");
+ return false;
+ }
+
+ ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context);
+ if (ReturnV != True)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV);
+ return false;
+ }
+
+ m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI");
+ if (!m_glXWaitVideoSyncSGI)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI not found");
+ return false;
+ }
+
+ ReturnV = m_glXWaitVideoSyncSGI(2, 0, &GlxTest);
+ if (ReturnV)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned {}", ReturnV);
+ return false;
+ }
+
+ m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI");
+ if (!m_glXGetVideoSyncSGI)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI not found");
+ return false;
+ }
+
+ ReturnV = m_glXGetVideoSyncSGI(&GlxTest);
+ if (ReturnV)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI returned {}", ReturnV);
+ return false;
+ }
+
+ return true;
+}
+
+void CVideoSyncGLX::Run(CEvent& stopEvent)
+{
+ unsigned int PrevVblankCount;
+ unsigned int VblankCount;
+ int ReturnV;
+ bool IsReset = false;
+ int64_t Now;
+
+ //get the current vblank counter
+ m_glXGetVideoSyncSGI(&VblankCount);
+ PrevVblankCount = VblankCount;
+
+ while(!stopEvent.Signaled() && !m_displayLost && !m_displayReset)
+ {
+ //wait for the next vblank
+ ReturnV = m_glXWaitVideoSyncSGI(2, (VblankCount + 1) % 2, &VblankCount);
+ m_glXGetVideoSyncSGI(&VblankCount); //the vblank count returned by glXWaitVideoSyncSGI is not always correct
+ Now = CurrentHostCounter(); //get the timestamp of this vblank
+
+ if(ReturnV)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned {}", ReturnV);
+ return;
+ }
+
+ if (VblankCount > PrevVblankCount)
+ {
+ UpdateClock((int)(VblankCount - PrevVblankCount), Now, m_refClock);
+ IsReset = false;
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Vblank counter has reset");
+
+ //only try reattaching once
+ if (IsReset)
+ return;
+
+ //because of a bug in the nvidia driver, glXWaitVideoSyncSGI breaks when the vblank counter resets
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detaching glX context");
+ ReturnV = glXMakeCurrent(m_Dpy, None, NULL);
+ if (ReturnV != True)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV);
+ return;
+ }
+
+ //sleep here so we don't busy spin when this constantly happens, for example when the display went to sleep
+ KODI::TIME::Sleep(1s);
+
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Attaching glX context");
+ ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context);
+ if (ReturnV != True)
+ {
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV);
+ return;
+ }
+
+ m_glXGetVideoSyncSGI(&VblankCount);
+
+ IsReset = true;
+ }
+ PrevVblankCount = VblankCount;
+ }
+ m_lostEvent.Set();
+ while(!stopEvent.Signaled() && m_displayLost && !m_displayReset)
+ {
+ KODI::TIME::Sleep(10ms);
+ }
+}
+
+void CVideoSyncGLX::Cleanup()
+{
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up GLX");
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_winSystem.GetGfxContext());
+
+ if (m_vInfo)
+ {
+ XFree(m_vInfo);
+ m_vInfo = NULL;
+ }
+ if (m_Context)
+ {
+ glXMakeCurrent(m_Dpy, None, NULL);
+ glXDestroyContext(m_Dpy, m_Context);
+ m_Context = NULL;
+ }
+ if (m_Window)
+ {
+ XDestroyWindow(m_Dpy, m_Window);
+ m_Window = 0;
+ }
+ }
+
+ m_lostEvent.Set();
+ m_winSystem.Unregister(this);
+}
+
+float CVideoSyncGLX::GetFps()
+{
+ m_fps = m_winSystem.GetGfxContext().GetFPS();
+ return m_fps;
+}
diff --git a/xbmc/windowing/X11/VideoSyncGLX.h b/xbmc/windowing/X11/VideoSyncGLX.h
new file mode 100644
index 0000000..06d348e
--- /dev/null
+++ b/xbmc/windowing/X11/VideoSyncGLX.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "threads/Event.h"
+#include "windowing/VideoSync.h"
+
+#include <GL/glx.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include "system_gl.h"
+
+
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinSystemX11GLContext;
+
+class CVideoSyncGLX : public CVideoSync, IDispResource
+{
+public:
+ explicit CVideoSyncGLX(void* clock, CWinSystemX11GLContext& winSystem)
+ : CVideoSync(clock), m_winSystem(winSystem)
+ {
+ }
+ bool Setup(PUPDATECLOCK func) override;
+ void Run(CEvent& stopEvent) override;
+ void Cleanup() override;
+ float GetFps() override;
+ void OnLostDisplay() override;
+ void OnResetDisplay() override;
+
+private:
+ int (*m_glXWaitVideoSyncSGI) (int, int, unsigned int*);
+ int (*m_glXGetVideoSyncSGI) (unsigned int*);
+
+ static Display* m_Dpy;
+ CWinSystemX11GLContext &m_winSystem;
+ XVisualInfo *m_vInfo;
+ Window m_Window;
+ GLXContext m_Context;
+ volatile bool m_displayLost;
+ volatile bool m_displayReset;
+ CEvent m_lostEvent;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/VideoSyncOML.cpp b/xbmc/windowing/X11/VideoSyncOML.cpp
new file mode 100644
index 0000000..2dc91fe
--- /dev/null
+++ b/xbmc/windowing/X11/VideoSyncOML.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoSyncOML.h"
+
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/X11/WinSystemX11GLContext.h"
+
+#include <unistd.h>
+
+using namespace KODI::WINDOWING::X11;
+
+bool CVideoSyncOML::Setup(PUPDATECLOCK func)
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncOML::{} - setting up OML", __FUNCTION__);
+
+ UpdateClock = func;
+
+ m_abort = false;
+
+ static_cast<CWinSystemX11*>(&m_winSystem)->Register(this);
+
+ return true;
+}
+
+void CVideoSyncOML::Run(CEvent& stopEvent)
+{
+ uint64_t interval, timeSinceVblank, msc;
+
+ timeSinceVblank = m_winSystem.GetVblankTiming(msc, interval);
+
+ while (!stopEvent.Signaled() && !m_abort)
+ {
+ if (interval == 0)
+ {
+ usleep(10000);
+ }
+ else
+ {
+ usleep(interval - timeSinceVblank + 1000);
+ }
+ uint64_t newMsc;
+ timeSinceVblank = m_winSystem.GetVblankTiming(newMsc, interval);
+
+ if (newMsc == msc)
+ {
+ newMsc++;
+ }
+ else if (newMsc < msc)
+ {
+ timeSinceVblank = interval;
+ continue;
+ }
+
+ uint64_t now = CurrentHostCounter();
+ UpdateClock(newMsc - msc, now, m_refClock);
+ msc = newMsc;
+ }
+}
+
+void CVideoSyncOML::Cleanup()
+{
+ m_winSystem.Unregister(this);
+}
+
+void CVideoSyncOML::OnResetDisplay()
+{
+ m_abort = true;
+}
+
+float CVideoSyncOML::GetFps()
+{
+ m_fps = m_winSystem.GetGfxContext().GetFPS();
+ return m_fps;
+}
+
diff --git a/xbmc/windowing/X11/VideoSyncOML.h b/xbmc/windowing/X11/VideoSyncOML.h
new file mode 100644
index 0000000..a04bd1d
--- /dev/null
+++ b/xbmc/windowing/X11/VideoSyncOML.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "windowing/VideoSync.h"
+
+#include <atomic>
+
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinSystemX11GLContext;
+
+class CVideoSyncOML : public CVideoSync, IDispResource
+{
+public:
+ explicit CVideoSyncOML(void* clock, CWinSystemX11GLContext& winSystem)
+ : CVideoSync(clock), m_winSystem(winSystem)
+ {
+ }
+ bool Setup(PUPDATECLOCK func) override;
+ void Run(CEvent& stopEvent) override;
+ void Cleanup() override;
+ float GetFps() override;
+ void OnResetDisplay() override;
+
+private:
+ std::atomic_bool m_abort;
+ CWinSystemX11GLContext &m_winSystem;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/WinEventsX11.cpp b/xbmc/windowing/X11/WinEventsX11.cpp
new file mode 100644
index 0000000..faffd99
--- /dev/null
+++ b/xbmc/windowing/X11/WinEventsX11.cpp
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinEventsX11.h"
+
+#include "ServiceBroker.h"
+#include "application/AppInboundProtocol.h"
+#include "application/Application.h"
+#include "cores/AudioEngine/Interfaces/AE.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/InputManager.h"
+#include "input/mouse/MouseStat.h"
+#include "messaging/ApplicationMessenger.h"
+#include "utils/CharsetConverter.h"
+#include "utils/log.h"
+#include "windowing/WinEvents.h"
+#include "windowing/X11/WinSystemX11.h"
+
+#include <stdexcept>
+
+#include <X11/XF86keysym.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/keysymdef.h>
+
+using namespace KODI::WINDOWING::X11;
+
+static uint32_t SymMappingsX11[][2] =
+{
+ {XK_BackSpace, XBMCK_BACKSPACE}
+, {XK_Tab, XBMCK_TAB}
+, {XK_Clear, XBMCK_CLEAR}
+, {XK_Return, XBMCK_RETURN}
+, {XK_Pause, XBMCK_PAUSE}
+, {XK_Escape, XBMCK_ESCAPE}
+, {XK_Delete, XBMCK_DELETE}
+// multi-media keys
+, {XF86XK_Back, XBMCK_BROWSER_BACK}
+, {XF86XK_Forward, XBMCK_BROWSER_FORWARD}
+, {XF86XK_Refresh, XBMCK_BROWSER_REFRESH}
+, {XF86XK_Stop, XBMCK_BROWSER_STOP}
+, {XF86XK_Search, XBMCK_BROWSER_SEARCH}
+, {XF86XK_Favorites, XBMCK_BROWSER_FAVORITES}
+, {XF86XK_HomePage, XBMCK_BROWSER_HOME}
+, {XF86XK_AudioMute, XBMCK_VOLUME_MUTE}
+, {XF86XK_AudioLowerVolume, XBMCK_VOLUME_DOWN}
+, {XF86XK_AudioRaiseVolume, XBMCK_VOLUME_UP}
+, {XF86XK_AudioNext, XBMCK_MEDIA_NEXT_TRACK}
+, {XF86XK_AudioPrev, XBMCK_MEDIA_PREV_TRACK}
+, {XF86XK_AudioStop, XBMCK_MEDIA_STOP}
+, {XF86XK_AudioPause, XBMCK_MEDIA_PLAY_PAUSE}
+, {XF86XK_Mail, XBMCK_LAUNCH_MAIL}
+, {XF86XK_Select, XBMCK_LAUNCH_MEDIA_SELECT}
+, {XF86XK_Launch0, XBMCK_LAUNCH_APP1}
+, {XF86XK_Launch1, XBMCK_LAUNCH_APP2}
+, {XF86XK_WWW, XBMCK_LAUNCH_FILE_BROWSER}
+, {XF86XK_AudioMedia, XBMCK_LAUNCH_MEDIA_CENTER }
+ // Numeric keypad
+, {XK_KP_0, XBMCK_KP0}
+, {XK_KP_1, XBMCK_KP1}
+, {XK_KP_2, XBMCK_KP2}
+, {XK_KP_3, XBMCK_KP3}
+, {XK_KP_4, XBMCK_KP4}
+, {XK_KP_5, XBMCK_KP5}
+, {XK_KP_6, XBMCK_KP6}
+, {XK_KP_7, XBMCK_KP7}
+, {XK_KP_8, XBMCK_KP8}
+, {XK_KP_9, XBMCK_KP9}
+, {XK_KP_Separator, XBMCK_KP_PERIOD}
+, {XK_KP_Divide, XBMCK_KP_DIVIDE}
+, {XK_KP_Multiply, XBMCK_KP_MULTIPLY}
+, {XK_KP_Subtract, XBMCK_KP_MINUS}
+, {XK_KP_Add, XBMCK_KP_PLUS}
+, {XK_KP_Enter, XBMCK_KP_ENTER}
+, {XK_KP_Equal, XBMCK_KP_EQUALS}
+ // Arrows + Home/End pad
+, {XK_Up, XBMCK_UP}
+, {XK_Down, XBMCK_DOWN}
+, {XK_Right, XBMCK_RIGHT}
+, {XK_Left, XBMCK_LEFT}
+, {XK_Insert, XBMCK_INSERT}
+, {XK_Home, XBMCK_HOME}
+, {XK_End, XBMCK_END}
+, {XK_Page_Up, XBMCK_PAGEUP}
+, {XK_Page_Down, XBMCK_PAGEDOWN}
+ // Function keys
+, {XK_F1, XBMCK_F1}
+, {XK_F2, XBMCK_F2}
+, {XK_F3, XBMCK_F3}
+, {XK_F4, XBMCK_F4}
+, {XK_F5, XBMCK_F5}
+, {XK_F6, XBMCK_F6}
+, {XK_F7, XBMCK_F7}
+, {XK_F8, XBMCK_F8}
+, {XK_F9, XBMCK_F9}
+, {XK_F10, XBMCK_F10}
+, {XK_F11, XBMCK_F11}
+, {XK_F12, XBMCK_F12}
+, {XK_F13, XBMCK_F13}
+, {XK_F14, XBMCK_F14}
+, {XK_F15, XBMCK_F15}
+ // Key state modifier keys
+, {XK_Num_Lock, XBMCK_NUMLOCK}
+, {XK_Caps_Lock, XBMCK_CAPSLOCK}
+, {XK_Scroll_Lock, XBMCK_SCROLLOCK}
+, {XK_Shift_R, XBMCK_RSHIFT}
+, {XK_Shift_L, XBMCK_LSHIFT}
+, {XK_Control_R, XBMCK_RCTRL}
+, {XK_Control_L, XBMCK_LCTRL}
+, {XK_Alt_R, XBMCK_RALT}
+, {XK_Alt_L, XBMCK_LALT}
+, {XK_Meta_R, XBMCK_RMETA}
+, {XK_Meta_L, XBMCK_LMETA}
+, {XK_Super_L, XBMCK_LSUPER}
+, {XK_Super_R, XBMCK_RSUPER}
+, {XK_Mode_switch, XBMCK_MODE}
+, {XK_Multi_key, XBMCK_COMPOSE}
+ // Miscellaneous function keys
+, {XK_Help, XBMCK_HELP}
+, {XK_Print, XBMCK_PRINT}
+//, {0, XBMCK_SYSREQ}
+, {XK_Break, XBMCK_BREAK}
+, {XK_Menu, XBMCK_MENU}
+, {XF86XK_PowerOff, XBMCK_POWER}
+, {XF86XK_Sleep, XBMCK_SLEEP}
+, {XK_EcuSign, XBMCK_EURO}
+, {XK_Undo, XBMCK_UNDO}
+ /* Media keys */
+, {XF86XK_Eject, XBMCK_EJECT}
+, {XF86XK_Stop, XBMCK_STOP}
+, {XF86XK_AudioRecord, XBMCK_RECORD}
+, {XF86XK_AudioRewind, XBMCK_REWIND}
+, {XF86XK_Phone, XBMCK_PHONE}
+, {XF86XK_AudioPlay, XBMCK_PLAY}
+, {XF86XK_AudioRandomPlay, XBMCK_SHUFFLE}
+, {XF86XK_AudioForward, XBMCK_FASTFORWARD}
+};
+
+CWinEventsX11::CWinEventsX11(CWinSystemX11& winSystem) : m_winSystem(winSystem)
+{
+}
+
+CWinEventsX11::~CWinEventsX11()
+{
+ Quit();
+}
+
+bool CWinEventsX11::Init(Display *dpy, Window win)
+{
+ if (m_display)
+ return true;
+
+ m_display = dpy;
+ m_window = win;
+ m_keybuf_len = 32*sizeof(char);
+ m_keybuf = (char*)malloc(m_keybuf_len);
+ m_keymodState = 0;
+ m_wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ m_structureChanged = false;
+ m_xrrEventPending = false;
+
+ // open input method
+ char *old_locale = NULL, *old_modifiers = NULL;
+ char res_name[8];
+ const char *p;
+
+ // set resource name to xbmc, not used
+ strcpy(res_name, "xbmc");
+
+ // save current locale, this should be "C"
+ p = setlocale(LC_ALL, NULL);
+ if (p)
+ {
+ old_locale = (char*)malloc(strlen(p) +1);
+ strcpy(old_locale, p);
+ }
+ p = XSetLocaleModifiers(NULL);
+ if (p)
+ {
+ old_modifiers = (char*)malloc(strlen(p) +1);
+ strcpy(old_modifiers, p);
+ }
+
+ // set users preferences and open input method
+ p = setlocale(LC_ALL, "");
+ XSetLocaleModifiers("");
+ m_xim = XOpenIM(m_display, NULL, res_name, res_name);
+
+ // restore old locale
+ if (old_locale)
+ {
+ setlocale(LC_ALL, old_locale);
+ free(old_locale);
+ }
+ if (old_modifiers)
+ {
+ XSetLocaleModifiers(old_modifiers);
+ free(old_modifiers);
+ }
+
+ m_xic = NULL;
+ if (m_xim)
+ {
+ m_xic = XCreateIC(m_xim,
+ XNClientWindow, m_window,
+ XNFocusWindow, m_window,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNResourceName, res_name,
+ XNResourceClass, res_name,
+ nullptr);
+ }
+
+ if (!m_xic)
+ CLog::Log(LOGWARNING,"CWinEventsX11::Init - no input method found");
+
+ // build Keysym lookup table
+ for (const auto& symMapping : SymMappingsX11)
+ {
+ m_symLookupTable[symMapping[0]] = symMapping[1];
+ }
+
+ // register for xrandr events
+ int iReturn;
+ XRRQueryExtension(m_display, &m_RREventBase, &iReturn);
+ int numScreens = XScreenCount(m_display);
+ for (int i = 0; i < numScreens; i++)
+ {
+ XRRSelectInput(m_display, RootWindow(m_display, i), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask | RROutputPropertyNotifyMask);
+ }
+
+ return true;
+}
+
+void CWinEventsX11::Quit()
+{
+ free(m_keybuf);
+ m_keybuf = nullptr;
+
+ if (m_xic)
+ {
+ XUnsetICFocus(m_xic);
+ XDestroyIC(m_xic);
+ m_xic = nullptr;
+ }
+
+ if (m_xim)
+ {
+ XCloseIM(m_xim);
+ m_xim = nullptr;
+ }
+
+ m_symLookupTable.clear();
+
+ m_display = nullptr;
+}
+
+bool CWinEventsX11::HasStructureChanged()
+{
+ if (!m_display)
+ return false;
+
+ bool ret = m_structureChanged;
+ m_structureChanged = false;
+ return ret;
+}
+
+void CWinEventsX11::SetXRRFailSafeTimer(std::chrono::milliseconds duration)
+{
+ if (!m_display)
+ return;
+
+ m_xrrFailSafeTimer.Set(duration);
+ m_xrrEventPending = true;
+}
+
+bool CWinEventsX11::MessagePump()
+{
+ if (!m_display)
+ return false;
+
+ bool ret = false;
+ XEvent xevent;
+ unsigned long serial = 0;
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+
+ while (m_display && XPending(m_display))
+ {
+ memset(&xevent, 0, sizeof (XEvent));
+ XNextEvent(m_display, &xevent);
+
+ if (m_display && (xevent.type == m_RREventBase + RRScreenChangeNotify))
+ {
+ if (xevent.xgeneric.serial == serial)
+ continue;
+
+ if (m_xrrEventPending)
+ {
+ m_winSystem.NotifyXRREvent();
+ m_xrrEventPending = false;
+ serial = xevent.xgeneric.serial;
+ }
+
+ continue;
+ }
+ else if (m_display && (xevent.type == m_RREventBase + RRNotify))
+ {
+ if (xevent.xgeneric.serial == serial)
+ continue;
+
+ XRRNotifyEvent* rrEvent = reinterpret_cast<XRRNotifyEvent*>(&xevent);
+ if (rrEvent->subtype == RRNotify_OutputChange)
+ {
+ XRROutputChangeNotifyEvent* changeEvent = reinterpret_cast<XRROutputChangeNotifyEvent*>(&xevent);
+ if (changeEvent->connection == RR_Connected ||
+ changeEvent->connection == RR_Disconnected)
+ {
+ m_winSystem.NotifyXRREvent();
+ CServiceBroker::GetActiveAE()->DeviceChange();
+ serial = xevent.xgeneric.serial;
+ }
+ }
+
+ continue;
+ }
+
+ if (XFilterEvent(&xevent, m_window))
+ continue;
+
+ switch (xevent.type)
+ {
+ case MapNotify:
+ {
+ if (appPort)
+ appPort->SetRenderGUI(true);
+ break;
+ }
+
+ case UnmapNotify:
+ {
+ if (appPort)
+ appPort->SetRenderGUI(false);
+ break;
+ }
+
+ case FocusIn:
+ {
+ if (m_xic)
+ XSetICFocus(m_xic);
+ g_application.m_AppFocused = true;
+ m_keymodState = 0;
+ if (serial == xevent.xfocus.serial)
+ break;
+ m_winSystem.NotifyAppFocusChange(g_application.m_AppFocused);
+ break;
+ }
+
+ case FocusOut:
+ {
+ if (m_xic)
+ XUnsetICFocus(m_xic);
+ g_application.m_AppFocused = false;
+ m_winSystem.NotifyAppFocusChange(g_application.m_AppFocused);
+ serial = xevent.xfocus.serial;
+ break;
+ }
+
+ case Expose:
+ {
+ CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
+ break;
+ }
+
+ case ConfigureNotify:
+ {
+ if (xevent.xconfigure.window != m_window)
+ break;
+
+ m_structureChanged = true;
+ XBMC_Event newEvent = {};
+ newEvent.type = XBMC_VIDEORESIZE;
+ newEvent.resize.w = xevent.xconfigure.width;
+ newEvent.resize.h = xevent.xconfigure.height;
+ if (appPort)
+ ret |= appPort->OnEvent(newEvent);
+ CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
+ break;
+ }
+
+ case ClientMessage:
+ {
+ if ((unsigned int)xevent.xclient.data.l[0] == m_wmDeleteMessage)
+ if (!g_application.m_bStop)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
+ break;
+ }
+
+ case KeyPress:
+ {
+ XBMC_Event newEvent = {};
+ newEvent.type = XBMC_KEYDOWN;
+ KeySym xkeysym;
+
+ // fallback if we have no IM
+ if (!m_xic)
+ {
+ static XComposeStatus state;
+ char keybuf[32];
+ XLookupString(&xevent.xkey, NULL, 0, &xkeysym, NULL);
+ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym);
+ newEvent.key.keysym.scancode = xevent.xkey.keycode;
+ if (XLookupString(&xevent.xkey, keybuf, sizeof(keybuf), NULL, &state))
+ {
+ newEvent.key.keysym.unicode = keybuf[0];
+ }
+ ret |= ProcessKey(newEvent);
+ break;
+ }
+
+ Status status;
+ int len;
+ len = Xutf8LookupString(m_xic, &xevent.xkey,
+ m_keybuf, m_keybuf_len,
+ &xkeysym, &status);
+ if (status == XBufferOverflow)
+ {
+ m_keybuf_len = len;
+ m_keybuf = (char*)realloc(m_keybuf, m_keybuf_len);
+ if (m_keybuf == nullptr)
+ throw std::runtime_error("Failed to realloc memory, insufficient memory available");
+ len = Xutf8LookupString(m_xic, &xevent.xkey,
+ m_keybuf, m_keybuf_len,
+ &xkeysym, &status);
+ }
+ switch (status)
+ {
+ case XLookupNone:
+ break;
+ case XLookupChars:
+ case XLookupBoth:
+ {
+ std::string data(m_keybuf, len);
+ std::wstring keys;
+ g_charsetConverter.utf8ToW(data, keys, false);
+
+ if (keys.length() == 0)
+ {
+ break;
+ }
+
+ for (unsigned int i = 0; i < keys.length() - 1; i++)
+ {
+ newEvent.key.keysym.sym = XBMCK_UNKNOWN;
+ newEvent.key.keysym.unicode = keys[i];
+ ret |= ProcessKey(newEvent);
+ }
+ if (keys.length() > 0)
+ {
+ newEvent.key.keysym.scancode = xevent.xkey.keycode;
+ XLookupString(&xevent.xkey, NULL, 0, &xkeysym, NULL);
+ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym);
+ newEvent.key.keysym.unicode = keys[keys.length() - 1];
+
+ ret |= ProcessKey(newEvent);
+ }
+ break;
+ }
+
+ case XLookupKeySym:
+ {
+ newEvent.key.keysym.scancode = xevent.xkey.keycode;
+ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym);
+ ret |= ProcessKey(newEvent);
+ break;
+ }
+
+ }// switch status
+ break;
+ } //KeyPress
+
+ case KeyRelease:
+ {
+ // if we have a queued press directly after, this is a repeat
+ if (XEventsQueued(m_display, QueuedAfterReading))
+ {
+ XEvent next_event;
+ XPeekEvent(m_display, &next_event);
+ if (next_event.type == KeyPress &&
+ next_event.xkey.window == xevent.xkey.window &&
+ next_event.xkey.keycode == xevent.xkey.keycode &&
+ (next_event.xkey.time - xevent.xkey.time < 2))
+ continue;
+ }
+
+ XBMC_Event newEvent = {};
+ KeySym xkeysym;
+ newEvent.type = XBMC_KEYUP;
+ xkeysym = XLookupKeysym(&xevent.xkey, 0);
+ newEvent.key.keysym.scancode = xevent.xkey.keycode;
+ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym);
+ ret |= ProcessKey(newEvent);
+ break;
+ }
+
+ case EnterNotify:
+ {
+ break;
+ }
+
+ // lose mouse coverage
+ case LeaveNotify:
+ {
+ CServiceBroker::GetInputManager().SetMouseActive(false);
+ break;
+ }
+
+ case MotionNotify:
+ {
+ if (xevent.xmotion.window != m_window)
+ break;
+ XBMC_Event newEvent = {};
+ newEvent.type = XBMC_MOUSEMOTION;
+ newEvent.motion.x = (int16_t)xevent.xmotion.x;
+ newEvent.motion.y = (int16_t)xevent.xmotion.y;
+ if (appPort)
+ ret |= appPort->OnEvent(newEvent);
+ break;
+ }
+
+ case ButtonPress:
+ {
+ XBMC_Event newEvent = {};
+ newEvent.type = XBMC_MOUSEBUTTONDOWN;
+ newEvent.button.button = (unsigned char)xevent.xbutton.button;
+ newEvent.button.x = (int16_t)xevent.xbutton.x;
+ newEvent.button.y = (int16_t)xevent.xbutton.y;
+ if (appPort)
+ ret |= appPort->OnEvent(newEvent);
+ break;
+ }
+
+ case ButtonRelease:
+ {
+ XBMC_Event newEvent = {};
+ newEvent.type = XBMC_MOUSEBUTTONUP;
+ newEvent.button.button = (unsigned char)xevent.xbutton.button;
+ newEvent.button.x = (int16_t)xevent.xbutton.x;
+ newEvent.button.y = (int16_t)xevent.xbutton.y;
+ if (appPort)
+ ret |= appPort->OnEvent(newEvent);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }// switch event.type
+ }// while
+
+ if (m_display && m_xrrEventPending && m_xrrFailSafeTimer.IsTimePast())
+ {
+ CLog::Log(LOGERROR,"CWinEventsX11::MessagePump - missed XRR Events");
+ m_winSystem.NotifyXRREvent();
+ m_xrrEventPending = false;
+ }
+
+ return ret;
+}
+
+bool CWinEventsX11::ProcessKey(XBMC_Event &event)
+{
+ if (event.type == XBMC_KEYDOWN)
+ {
+ // check key modifiers
+ switch(event.key.keysym.sym)
+ {
+ case XBMCK_LSHIFT:
+ m_keymodState |= XBMCKMOD_LSHIFT;
+ break;
+ case XBMCK_RSHIFT:
+ m_keymodState |= XBMCKMOD_RSHIFT;
+ break;
+ case XBMCK_LCTRL:
+ m_keymodState |= XBMCKMOD_LCTRL;
+ break;
+ case XBMCK_RCTRL:
+ m_keymodState |= XBMCKMOD_RCTRL;
+ break;
+ case XBMCK_LALT:
+ m_keymodState |= XBMCKMOD_LALT;
+ break;
+ case XBMCK_RALT:
+ m_keymodState |= XBMCKMOD_RCTRL;
+ break;
+ case XBMCK_LMETA:
+ m_keymodState |= XBMCKMOD_LMETA;
+ break;
+ case XBMCK_RMETA:
+ m_keymodState |= XBMCKMOD_RMETA;
+ break;
+ case XBMCK_MODE:
+ m_keymodState |= XBMCKMOD_MODE;
+ break;
+ default:
+ break;
+ }
+ event.key.keysym.mod = (XBMCMod)m_keymodState;
+ }
+ else if (event.type == XBMC_KEYUP)
+ {
+ switch(event.key.keysym.sym)
+ {
+ case XBMCK_LSHIFT:
+ m_keymodState &= ~XBMCKMOD_LSHIFT;
+ break;
+ case XBMCK_RSHIFT:
+ m_keymodState &= ~XBMCKMOD_RSHIFT;
+ break;
+ case XBMCK_LCTRL:
+ m_keymodState &= ~XBMCKMOD_LCTRL;
+ break;
+ case XBMCK_RCTRL:
+ m_keymodState &= ~XBMCKMOD_RCTRL;
+ break;
+ case XBMCK_LALT:
+ m_keymodState &= ~XBMCKMOD_LALT;
+ break;
+ case XBMCK_RALT:
+ m_keymodState &= ~XBMCKMOD_RCTRL;
+ break;
+ case XBMCK_LMETA:
+ m_keymodState &= ~XBMCKMOD_LMETA;
+ break;
+ case XBMCK_RMETA:
+ m_keymodState &= ~XBMCKMOD_RMETA;
+ break;
+ case XBMCK_MODE:
+ m_keymodState &= ~XBMCKMOD_MODE;
+ break;
+ default:
+ break;
+ }
+ event.key.keysym.mod = (XBMCMod)m_keymodState;
+ }
+
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+ if (appPort)
+ appPort->OnEvent(event);
+ return true;
+}
+
+XBMCKey CWinEventsX11::LookupXbmcKeySym(KeySym keysym)
+{
+ // try direct mapping first
+ std::map<uint32_t, uint32_t>::iterator it;
+ it = m_symLookupTable.find(keysym);
+ if (it != m_symLookupTable.end())
+ {
+ return (XBMCKey)(it->second);
+ }
+
+ // try ascii mappings
+ if (keysym>>8 == 0x00)
+ return (XBMCKey)tolower(keysym & 0xFF);
+
+ return (XBMCKey)keysym;
+}
diff --git a/xbmc/windowing/X11/WinEventsX11.h b/xbmc/windowing/X11/WinEventsX11.h
new file mode 100644
index 0000000..b2111da
--- /dev/null
+++ b/xbmc/windowing/X11/WinEventsX11.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "threads/SystemClock.h"
+#include "windowing/WinEvents.h"
+#include "windowing/X11/WinSystemX11.h"
+
+#include <clocale>
+#include <map>
+
+#include <X11/Xlib.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinEventsX11 : public IWinEvents
+{
+public:
+ CWinEventsX11(CWinSystemX11& winSystem);
+ ~CWinEventsX11() override;
+ bool MessagePump() override;
+ bool Init(Display *dpy, Window win);
+ void Quit();
+ bool HasStructureChanged();
+ void PendingResize(int width, int height);
+ void SetXRRFailSafeTimer(std::chrono::milliseconds duration);
+
+protected:
+ XBMCKey LookupXbmcKeySym(KeySym keysym);
+ bool ProcessKey(XBMC_Event &event);
+ Display *m_display = nullptr;
+ Window m_window = 0;
+ Atom m_wmDeleteMessage;
+ char *m_keybuf = nullptr;
+ size_t m_keybuf_len = 0;
+ XIM m_xim = nullptr;
+ XIC m_xic = nullptr;
+ std::map<uint32_t,uint32_t> m_symLookupTable;
+ int m_keymodState;
+ bool m_structureChanged;
+ int m_RREventBase;
+ XbmcThreads::EndTime<> m_xrrFailSafeTimer;
+ bool m_xrrEventPending;
+ CWinSystemX11& m_winSystem;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/WinSystemX11.cpp b/xbmc/windowing/X11/WinSystemX11.cpp
new file mode 100644
index 0000000..43a8ebb
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11.cpp
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemX11.h"
+
+#include "CompileInfo.h"
+#include "OSScreenSaverX11.h"
+#include "ServiceBroker.h"
+#include "WinEventsX11.h"
+#include "XRandR.h"
+#include "guilib/DispResource.h"
+#include "guilib/Texture.h"
+#include "input/InputManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/StringUtils.h"
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrandr.h>
+
+using namespace KODI::WINDOWING::X11;
+
+using namespace std::chrono_literals;
+
+#define EGL_NO_CONFIG (EGLConfig)0
+
+CWinSystemX11::CWinSystemX11() : CWinSystemBase()
+{
+ m_dpy = NULL;
+ m_bWasFullScreenBeforeMinimize = false;
+ m_minimized = false;
+ m_bIgnoreNextFocusMessage = false;
+ m_bIsInternalXrr = false;
+ m_delayDispReset = false;
+
+ XSetErrorHandler(XErrorHandler);
+
+ m_winEventsX11 = new CWinEventsX11(*this);
+ m_winEvents.reset(m_winEventsX11);
+}
+
+CWinSystemX11::~CWinSystemX11() = default;
+
+bool CWinSystemX11::InitWindowSystem()
+{
+ const char* env = getenv("DISPLAY");
+ if (!env)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemX11::{} - DISPLAY env not set", __FUNCTION__);
+ return false;
+ }
+
+ if ((m_dpy = XOpenDisplay(NULL)))
+ {
+ bool ret = CWinSystemBase::InitWindowSystem();
+
+ CServiceBroker::GetSettingsComponent()
+ ->GetSettings()
+ ->GetSetting(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE)
+ ->SetVisible(true);
+
+ return ret;
+ }
+ else
+ CLog::Log(LOGERROR, "X11 Error: No Display found");
+
+ return false;
+}
+
+bool CWinSystemX11::DestroyWindowSystem()
+{
+ //restore desktop resolution on exit
+ if (m_bFullScreen)
+ {
+ XOutput out;
+ XMode mode;
+ out.name = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strOutput;
+ mode.w = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth;
+ mode.h = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight;
+ mode.hz = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).fRefreshRate;
+ mode.id = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId;
+ g_xrandr.SetMode(out, mode);
+ }
+
+ return true;
+}
+
+bool CWinSystemX11::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ if(!SetFullScreen(fullScreen, res, false))
+ return false;
+
+ m_bWindowCreated = true;
+ return true;
+}
+
+bool CWinSystemX11::DestroyWindow()
+{
+ if (!m_mainWindow)
+ return true;
+
+ if (m_invisibleCursor)
+ {
+ XUndefineCursor(m_dpy, m_mainWindow);
+ XFreeCursor(m_dpy, m_invisibleCursor);
+ m_invisibleCursor = 0;
+ }
+
+ m_winEventsX11->Quit();
+
+ XUnmapWindow(m_dpy, m_mainWindow);
+ XDestroyWindow(m_dpy, m_glWindow);
+ XDestroyWindow(m_dpy, m_mainWindow);
+ m_glWindow = 0;
+ m_mainWindow = 0;
+
+ if (m_icon)
+ XFreePixmap(m_dpy, m_icon);
+
+ return true;
+}
+
+bool CWinSystemX11::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ m_userOutput = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ XOutput *out = NULL;
+ if (m_userOutput.compare("Default") != 0)
+ {
+ out = g_xrandr.GetOutput(m_userOutput);
+ if (out)
+ {
+ XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
+ if (!mode.isCurrent)
+ {
+ out = NULL;
+ }
+ }
+ }
+ if (!out)
+ {
+ std::vector<XOutput> outputs = g_xrandr.GetModes();
+ if (!outputs.empty())
+ {
+ m_userOutput = outputs[0].name;
+ }
+ }
+
+ if (!SetWindow(newWidth, newHeight, false, m_userOutput))
+ {
+ return false;
+ }
+
+ m_nWidth = newWidth;
+ m_nHeight = newHeight;
+ m_bFullScreen = false;
+ m_currentOutput = m_userOutput;
+
+ return true;
+}
+
+void CWinSystemX11::FinishWindowResize(int newWidth, int newHeight)
+{
+ m_userOutput = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ XOutput *out = NULL;
+ if (m_userOutput.compare("Default") != 0)
+ {
+ out = g_xrandr.GetOutput(m_userOutput);
+ if (out)
+ {
+ XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
+ if (!mode.isCurrent)
+ {
+ out = NULL;
+ }
+ }
+ }
+ if (!out)
+ {
+ std::vector<XOutput> outputs = g_xrandr.GetModes();
+ if (!outputs.empty())
+ {
+ m_userOutput = outputs[0].name;
+ }
+ }
+
+ XResizeWindow(m_dpy, m_glWindow, newWidth, newHeight);
+ UpdateCrtc();
+
+ if (m_userOutput.compare(m_currentOutput) != 0)
+ {
+ SetWindow(newWidth, newHeight, false, m_userOutput);
+ }
+
+ m_nWidth = newWidth;
+ m_nHeight = newHeight;
+}
+
+bool CWinSystemX11::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ XOutput out;
+ XMode mode;
+
+ if (fullScreen)
+ {
+ out.name = res.strOutput;
+ mode.w = res.iWidth;
+ mode.h = res.iHeight;
+ mode.hz = res.fRefreshRate;
+ mode.id = res.strId;
+ }
+ else
+ {
+ out.name = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strOutput;
+ mode.w = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth;
+ mode.h = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight;
+ mode.hz = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).fRefreshRate;
+ mode.id = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId;
+ }
+
+ XMode currmode = g_xrandr.GetCurrentMode(out.name);
+ if (!currmode.name.empty())
+ {
+ // flip h/w when rotated
+ if (m_bIsRotated)
+ {
+ int w = mode.w;
+ mode.w = mode.h;
+ mode.h = w;
+ }
+
+ // only call xrandr if mode changes
+ if (m_mainWindow)
+ {
+ if (currmode.w != mode.w || currmode.h != mode.h ||
+ currmode.hz != mode.hz || currmode.id != mode.id)
+ {
+ CLog::Log(LOGINFO, "CWinSystemX11::SetFullScreen - calling xrandr");
+
+ // remember last position of mouse
+ Window root_return, child_return;
+ int root_x_return, root_y_return;
+ int win_x_return, win_y_return;
+ unsigned int mask_return;
+ bool isInWin = XQueryPointer(m_dpy, m_mainWindow, &root_return, &child_return,
+ &root_x_return, &root_y_return,
+ &win_x_return, &win_y_return,
+ &mask_return);
+
+ if (isInWin)
+ {
+ m_MouseX = win_x_return;
+ m_MouseY = win_y_return;
+ }
+ else
+ {
+ m_MouseX = -1;
+ m_MouseY = -1;
+ }
+
+ OnLostDevice();
+ m_bIsInternalXrr = true;
+ g_xrandr.SetMode(out, mode);
+ auto delay =
+ std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ "videoscreen.delayrefreshchange") *
+ 100);
+ if (delay > 0ms)
+ {
+ m_delayDispReset = true;
+ m_dispResetTimer.Set(delay);
+ }
+ return true;
+ }
+ }
+ }
+
+ if (!SetWindow(res.iWidth, res.iHeight, fullScreen, m_userOutput))
+ return false;
+
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_bFullScreen = fullScreen;
+ m_currentOutput = m_userOutput;
+
+ return true;
+}
+
+void CWinSystemX11::UpdateResolutions()
+{
+ CWinSystemBase::UpdateResolutions();
+
+ int numScreens = XScreenCount(m_dpy);
+ g_xrandr.SetNumScreens(numScreens);
+
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ bool switchOnOff = settings->GetBool(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS);
+ m_userOutput = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ if (m_userOutput.compare("Default") == 0)
+ switchOnOff = false;
+
+ if(g_xrandr.Query(true, !switchOnOff))
+ {
+ XOutput *out = NULL;
+ if (m_userOutput.compare("Default") != 0)
+ {
+ out = g_xrandr.GetOutput(m_userOutput);
+ if (out)
+ {
+ XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
+ if (!mode.isCurrent && !switchOnOff)
+ {
+ out = NULL;
+ }
+ }
+ }
+ if (!out)
+ {
+ m_userOutput = g_xrandr.GetModes()[0].name;
+ out = g_xrandr.GetOutput(m_userOutput);
+ }
+
+ if (switchOnOff)
+ {
+ // switch on output
+ g_xrandr.TurnOnOutput(m_userOutput);
+
+ // switch off other outputs
+ std::vector<XOutput> outputs = g_xrandr.GetModes();
+ for (size_t i=0; i<outputs.size(); i++)
+ {
+ if (StringUtils::EqualsNoCase(outputs[i].name, m_userOutput))
+ continue;
+ g_xrandr.TurnOffOutput(outputs[i].name);
+ }
+ }
+
+ XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
+ if (mode.id.empty())
+ mode = g_xrandr.GetPreferredMode(m_userOutput);
+ m_bIsRotated = out->isRotated;
+ if (!m_bIsRotated)
+ UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), out->name, mode.w, mode.h, mode.hz, 0);
+ else
+ UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), out->name, mode.h, mode.w, mode.hz, 0);
+ CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId = mode.id;
+ }
+ else
+ {
+ m_userOutput = "No Output";
+ m_screen = DefaultScreen(m_dpy);
+ int w = DisplayWidth(m_dpy, m_screen);
+ int h = DisplayHeight(m_dpy, m_screen);
+ UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), m_userOutput, w, h, 0.0, 0);
+ }
+
+ // erase previous stored modes
+ CDisplaySettings::GetInstance().ClearCustomResolutions();
+
+ CLog::Log(LOGINFO, "Available videomodes (xrandr):");
+
+ XOutput *out = g_xrandr.GetOutput(m_userOutput);
+ if (out != NULL)
+ {
+ CLog::Log(LOGINFO, "Output '{}' has {} modes", out->name, out->modes.size());
+
+ for (auto mode : out->modes)
+ {
+ CLog::Log(LOGINFO, "ID:{} Name:{} Refresh:{:f} Width:{} Height:{}", mode.id, mode.name,
+ mode.hz, mode.w, mode.h);
+ RESOLUTION_INFO res;
+ res.dwFlags = 0;
+
+ if (mode.IsInterlaced())
+ res.dwFlags |= D3DPRESENTFLAG_INTERLACED;
+
+ if (!m_bIsRotated)
+ {
+ res.iWidth = mode.w;
+ res.iHeight = mode.h;
+ res.iScreenWidth = mode.w;
+ res.iScreenHeight = mode.h;
+ }
+ else
+ {
+ res.iWidth = mode.h;
+ res.iHeight = mode.w;
+ res.iScreenWidth = mode.h;
+ res.iScreenHeight = mode.w;
+ }
+
+ if (mode.h > 0 && mode.w > 0 && out->hmm > 0 && out->wmm > 0)
+ res.fPixelRatio = ((float)out->wmm/(float)mode.w) / (((float)out->hmm/(float)mode.h));
+ else
+ res.fPixelRatio = 1.0f;
+
+ CLog::Log(LOGINFO, "Pixel Ratio: {:f}", res.fPixelRatio);
+
+ res.strMode = StringUtils::Format("{}: {} @ {:.2f}Hz", out->name, mode.name, mode.hz);
+ res.strOutput = out->name;
+ res.strId = mode.id;
+ res.iSubtitles = mode.h;
+ res.fRefreshRate = mode.hz;
+ res.bFullScreen = true;
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
+ CDisplaySettings::GetInstance().AddResolutionInfo(res);
+ }
+ }
+ CDisplaySettings::GetInstance().ApplyCalibrations();
+}
+
+bool CWinSystemX11::HasCalibration(const RESOLUTION_INFO &resInfo)
+{
+ XOutput *out = g_xrandr.GetOutput(m_currentOutput);
+
+ // keep calibrations done on a not connected output
+ if (!StringUtils::EqualsNoCase(out->name, resInfo.strOutput))
+ return true;
+
+ // keep calibrations not updated with resolution data
+ if (resInfo.iWidth == 0)
+ return true;
+
+ float fPixRatio;
+ if (resInfo.iHeight>0 && resInfo.iWidth>0 && out->hmm>0 && out->wmm>0)
+ fPixRatio = ((float)out->wmm/(float)resInfo.iWidth) / (((float)out->hmm/(float)resInfo.iHeight));
+ else
+ fPixRatio = 1.0f;
+
+ if (resInfo.Overscan.left != 0)
+ return true;
+ if (resInfo.Overscan.top != 0)
+ return true;
+ if (resInfo.Overscan.right != resInfo.iWidth)
+ return true;
+ if (resInfo.Overscan.bottom != resInfo.iHeight)
+ return true;
+ if (resInfo.fPixelRatio != fPixRatio)
+ return true;
+ if (resInfo.iSubtitles != resInfo.iHeight)
+ return true;
+
+ return false;
+}
+
+bool CWinSystemX11::UseLimitedColor()
+{
+ return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE);
+}
+
+std::vector<std::string> CWinSystemX11::GetConnectedOutputs()
+{
+ std::vector<std::string> outputs;
+ std::vector<XOutput> outs;
+ g_xrandr.Query(true);
+ outs = g_xrandr.GetModes();
+ outputs.emplace_back("Default");
+ for(unsigned int i=0; i<outs.size(); ++i)
+ {
+ outputs.emplace_back(outs[i].name);
+ }
+
+ return outputs;
+}
+
+bool CWinSystemX11::IsCurrentOutput(const std::string& output)
+{
+ return (StringUtils::EqualsNoCase(output, "Default")) || (m_currentOutput.compare(output.c_str()) == 0);
+}
+
+void CWinSystemX11::ShowOSMouse(bool show)
+{
+ if (show)
+ XUndefineCursor(m_dpy,m_mainWindow);
+ else if (m_invisibleCursor)
+ XDefineCursor(m_dpy,m_mainWindow, m_invisibleCursor);
+}
+
+std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> CWinSystemX11::GetOSScreenSaverImpl()
+{
+ std::unique_ptr<IOSScreenSaver> ret;
+ if (m_dpy)
+ {
+ ret.reset(new COSScreenSaverX11(m_dpy));
+ }
+ return ret;
+}
+
+void CWinSystemX11::NotifyAppActiveChange(bool bActivated)
+{
+ if (bActivated && m_bWasFullScreenBeforeMinimize && !m_bFullScreen)
+ {
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+
+ m_bWasFullScreenBeforeMinimize = false;
+ }
+ m_minimized = !bActivated;
+}
+
+void CWinSystemX11::NotifyAppFocusChange(bool bGaining)
+{
+ if (bGaining && m_bWasFullScreenBeforeMinimize && !m_bIgnoreNextFocusMessage &&
+ !m_bFullScreen)
+ {
+ m_bWasFullScreenBeforeMinimize = false;
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+ m_minimized = false;
+ }
+ if (!bGaining)
+ m_bIgnoreNextFocusMessage = false;
+}
+
+bool CWinSystemX11::Minimize()
+{
+ m_bWasFullScreenBeforeMinimize = m_bFullScreen;
+ if (m_bWasFullScreenBeforeMinimize)
+ {
+ m_bIgnoreNextFocusMessage = true;
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+ }
+
+ XIconifyWindow(m_dpy, m_mainWindow, m_screen);
+
+ m_minimized = true;
+ return true;
+}
+bool CWinSystemX11::Restore()
+{
+ return false;
+}
+bool CWinSystemX11::Hide()
+{
+ XUnmapWindow(m_dpy, m_mainWindow);
+ XFlush(m_dpy);
+ return true;
+}
+bool CWinSystemX11::Show(bool raise)
+{
+ XMapWindow(m_dpy, m_mainWindow);
+ XFlush(m_dpy);
+ m_minimized = false;
+ return true;
+}
+
+void CWinSystemX11::NotifyXRREvent()
+{
+ CLog::Log(LOGDEBUG, "{} - notify display reset event", __FUNCTION__);
+
+ std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
+
+ if (!g_xrandr.Query(true))
+ {
+ CLog::Log(LOGERROR, "WinSystemX11::RefreshWindow - failed to query xrandr");
+ return;
+ }
+
+ // if external event update resolutions
+ if (!m_bIsInternalXrr)
+ {
+ UpdateResolutions();
+ }
+
+ RecreateWindow();
+}
+
+void CWinSystemX11::RecreateWindow()
+{
+ m_windowDirty = true;
+
+ std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
+
+ XOutput *out = g_xrandr.GetOutput(m_userOutput);
+ XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
+
+ if (out)
+ CLog::Log(LOGDEBUG, "{} - current output: {}, mode: {}, refresh: {:.3f}", __FUNCTION__,
+ out->name, mode.id, mode.hz);
+ else
+ CLog::Log(LOGWARNING, "{} - output name not set", __FUNCTION__);
+
+ RESOLUTION_INFO res;
+ unsigned int i;
+ bool found(false);
+ for (i = RES_DESKTOP; i < CDisplaySettings::GetInstance().ResolutionInfoSize(); ++i)
+ {
+ res = CDisplaySettings::GetInstance().GetResolutionInfo(i);
+ if (StringUtils::EqualsNoCase(CDisplaySettings::GetInstance().GetResolutionInfo(i).strId, mode.id))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::RecreateWindow - could not find resolution");
+ i = RES_DESKTOP;
+ }
+
+ if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution((RESOLUTION)i, true);
+ else
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(RES_WINDOW, true);
+}
+
+void CWinSystemX11::OnLostDevice()
+{
+ CLog::Log(LOGDEBUG, "{} - notify display change event", __FUNCTION__);
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnLostDisplay();
+ }
+
+ m_winEventsX11->SetXRRFailSafeTimer(3s);
+}
+
+void CWinSystemX11::Register(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void CWinSystemX11::Unregister(IDispResource* resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ m_resources.erase(i);
+}
+
+int CWinSystemX11::XErrorHandler(Display* dpy, XErrorEvent* error)
+{
+ char buf[1024];
+ XGetErrorText(error->display, error->error_code, buf, sizeof(buf));
+ CLog::Log(LOGERROR,
+ "CWinSystemX11::XErrorHandler: {}, type:{}, serial:{}, error_code:{}, request_code:{} "
+ "minor_code:{}",
+ buf, error->type, error->serial, (int)error->error_code, (int)error->request_code,
+ (int)error->minor_code);
+
+ return 0;
+}
+
+bool CWinSystemX11::SetWindow(int width, int height, bool fullscreen, const std::string &output, int *winstate)
+{
+ bool changeWindow = false;
+ bool changeSize = false;
+ float mouseX = 0.5;
+ float mouseY = 0.5;
+
+ if (!m_mainWindow)
+ {
+ CServiceBroker::GetInputManager().SetMouseActive(false);
+ }
+
+ if (m_mainWindow && ((m_bFullScreen != fullscreen) || m_currentOutput.compare(output) != 0 || m_windowDirty))
+ {
+ // set mouse to last known position
+ // we can't trust values after an xrr event
+ if (m_bIsInternalXrr && m_MouseX >= 0 && m_MouseY >= 0)
+ {
+ mouseX = (float)m_MouseX/m_nWidth;
+ mouseY = (float)m_MouseY/m_nHeight;
+ }
+ else if (!m_windowDirty)
+ {
+ Window root_return, child_return;
+ int root_x_return, root_y_return;
+ int win_x_return, win_y_return;
+ unsigned int mask_return;
+ bool isInWin = XQueryPointer(m_dpy, m_mainWindow, &root_return, &child_return,
+ &root_x_return, &root_y_return,
+ &win_x_return, &win_y_return,
+ &mask_return);
+
+ if (isInWin)
+ {
+ mouseX = (float)win_x_return/m_nWidth;
+ mouseY = (float)win_y_return/m_nHeight;
+ }
+ }
+
+ CServiceBroker::GetInputManager().SetMouseActive(false);
+ OnLostDevice();
+ DestroyWindow();
+ m_windowDirty = true;
+ }
+
+ // create main window
+ if (!m_mainWindow)
+ {
+ Colormap cmap;
+ XSetWindowAttributes swa;
+ XVisualInfo *vi;
+ int x0 = 0;
+ int y0 = 0;
+
+ XOutput *out = g_xrandr.GetOutput(output);
+ if (!out)
+ out = g_xrandr.GetOutput(m_currentOutput);
+ if (out)
+ {
+ m_screen = out->screen;
+ x0 = out->x;
+ y0 = out->y;
+ }
+
+ vi = GetVisual();
+ if (!vi)
+ {
+ CLog::Log(LOGERROR, "Failed to find matching visual");
+ return false;
+ }
+
+ cmap = XCreateColormap(m_dpy, RootWindow(m_dpy, vi->screen), vi->visual, AllocNone);
+
+ bool hasWM = HasWindowManager();
+
+ int def_vis = (vi->visual == DefaultVisual(m_dpy, vi->screen));
+ swa.override_redirect = hasWM ? False : True;
+ swa.border_pixel = fullscreen ? 0 : 5;
+ swa.background_pixel = def_vis ? BlackPixel(m_dpy, vi->screen) : 0;
+ swa.colormap = cmap;
+ swa.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
+ PropertyChangeMask | StructureNotifyMask | KeymapStateMask |
+ EnterWindowMask | LeaveWindowMask | ExposureMask;
+ unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWEventMask;
+
+ m_mainWindow = XCreateWindow(m_dpy, RootWindow(m_dpy, vi->screen),
+ x0, y0, width, height, 0, vi->depth,
+ InputOutput, vi->visual,
+ mask, &swa);
+
+ swa.override_redirect = False;
+ swa.border_pixel = 0;
+ swa.event_mask = ExposureMask;
+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWColormap | CWEventMask;
+
+ m_glWindow = XCreateWindow(m_dpy, m_mainWindow,
+ 0, 0, width, height, 0, vi->depth,
+ InputOutput, vi->visual,
+ mask, &swa);
+
+ if (fullscreen && hasWM)
+ {
+ Atom fs = XInternAtom(m_dpy, "_NET_WM_STATE_FULLSCREEN", True);
+ XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_NET_WM_STATE", True), XA_ATOM, 32, PropModeReplace, (unsigned char *) &fs, 1);
+ // disable desktop compositing for KDE, when Kodi is in full-screen mode
+ int one = 1;
+ Atom composite = XInternAtom(m_dpy, "_KDE_NET_WM_BLOCK_COMPOSITING", True);
+ if (composite != None)
+ {
+ XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_KDE_NET_WM_BLOCK_COMPOSITING", True), XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char*) &one, 1);
+ }
+ composite = XInternAtom(m_dpy, "_NET_WM_BYPASS_COMPOSITOR", True);
+ if (composite != None)
+ {
+ // standard way for Gnome 3
+ XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_NET_WM_BYPASS_COMPOSITOR", True), XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char*) &one, 1);
+ }
+ }
+
+ // define invisible cursor
+ Pixmap bitmapNoData;
+ XColor black;
+ static char noData[] = { 0,0,0,0,0,0,0,0 };
+ black.red = black.green = black.blue = 0;
+
+ bitmapNoData = XCreateBitmapFromData(m_dpy, m_mainWindow, noData, 8, 8);
+ m_invisibleCursor = XCreatePixmapCursor(m_dpy, bitmapNoData, bitmapNoData,
+ &black, &black, 0, 0);
+ XFreePixmap(m_dpy, bitmapNoData);
+ XDefineCursor(m_dpy,m_mainWindow, m_invisibleCursor);
+ XFree(vi);
+
+ //init X11 events
+ m_winEventsX11->Init(m_dpy, m_mainWindow);
+
+ changeWindow = true;
+ changeSize = true;
+ }
+
+ if (!m_winEventsX11->HasStructureChanged() && ((width != m_nWidth) || (height != m_nHeight)))
+ {
+ changeSize = true;
+ }
+
+ if (changeSize || changeWindow)
+ {
+ XResizeWindow(m_dpy, m_mainWindow, width, height);
+ }
+
+ if ((width != m_nWidth) || (height != m_nHeight) || changeWindow)
+ {
+ XResizeWindow(m_dpy, m_glWindow, width, height);
+ }
+
+ if (changeWindow)
+ {
+ m_icon = None;
+ {
+ CreateIconPixmap();
+ XWMHints *wm_hints;
+ XClassHint *class_hints;
+ XTextProperty windowName, iconName;
+
+ std::string titleString = CCompileInfo::GetAppName();
+ const std::string& classString = titleString;
+ char *title = const_cast<char*>(titleString.c_str());
+
+ XStringListToTextProperty(&title, 1, &windowName);
+ XStringListToTextProperty(&title, 1, &iconName);
+
+ wm_hints = XAllocWMHints();
+ wm_hints->initial_state = NormalState;
+ wm_hints->icon_pixmap = m_icon;
+ wm_hints->flags = StateHint | IconPixmapHint;
+
+ class_hints = XAllocClassHint();
+ class_hints->res_class = const_cast<char*>(classString.c_str());
+ class_hints->res_name = const_cast<char*>(classString.c_str());
+
+ XSetWMProperties(m_dpy, m_mainWindow, &windowName, &iconName,
+ NULL, 0, NULL, wm_hints,
+ class_hints);
+ XFree(class_hints);
+ XFree(wm_hints);
+ XFree(iconName.value);
+ XFree(windowName.value);
+
+ // register interest in the delete window message
+ Atom wmDeleteMessage = XInternAtom(m_dpy, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(m_dpy, m_mainWindow, &wmDeleteMessage, 1);
+ }
+
+ // placement of window may follow mouse
+ XWarpPointer(m_dpy, None, m_mainWindow, 0, 0, 0, 0, mouseX*width, mouseY*height);
+
+ XMapRaised(m_dpy, m_glWindow);
+ XMapRaised(m_dpy, m_mainWindow);
+
+ // discard events generated by creating the window, i.e. xrr events
+ XSync(m_dpy, True);
+
+ if (winstate)
+ *winstate = 1;
+ }
+
+ UpdateCrtc();
+
+ return true;
+}
+
+bool CWinSystemX11::CreateIconPixmap()
+{
+ int depth;
+ XImage *img = NULL;
+ Visual *vis;
+ XWindowAttributes wndattribs;
+ XVisualInfo visInfo;
+ double rRatio;
+ double gRatio;
+ double bRatio;
+ int outIndex = 0;
+ unsigned int i,j;
+ unsigned char *buf;
+ uint32_t *newBuf = 0;
+ size_t numNewBufBytes;
+
+ // Get visual Info
+ XGetWindowAttributes(m_dpy, m_glWindow, &wndattribs);
+ visInfo.visualid = wndattribs.visual->visualid;
+ int nvisuals = 0;
+ XVisualInfo* visuals = XGetVisualInfo(m_dpy, VisualIDMask, &visInfo, &nvisuals);
+ if (nvisuals != 1)
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not find visual");
+ return false;
+ }
+ visInfo = visuals[0];
+ XFree(visuals);
+
+ depth = visInfo.depth;
+ vis = visInfo.visual;
+
+ if (depth < 15)
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - no suitable depth");
+ return false;
+ }
+
+ rRatio = vis->red_mask / 255.0;
+ gRatio = vis->green_mask / 255.0;
+ bRatio = vis->blue_mask / 255.0;
+
+ std::unique_ptr<CTexture> iconTexture =
+ CTexture::LoadFromFile("special://xbmc/media/icon256x256.png");
+
+ if (!iconTexture)
+ return false;
+
+ buf = iconTexture->GetPixels();
+
+ if (depth>=24)
+ numNewBufBytes = (4 * (iconTexture->GetWidth() * iconTexture->GetHeight()));
+ else
+ numNewBufBytes = (2 * (iconTexture->GetWidth() * iconTexture->GetHeight()));
+
+ newBuf = (uint32_t*)malloc(numNewBufBytes);
+ if (!newBuf)
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - malloc failed");
+ return false;
+ }
+
+ for (i=0; i<iconTexture->GetHeight();++i)
+ {
+ for (j=0; j<iconTexture->GetWidth();++j)
+ {
+ unsigned int pos = i*iconTexture->GetPitch()+j*4;
+ unsigned int r, g, b;
+ r = (buf[pos+2] * rRatio);
+ g = (buf[pos+1] * gRatio);
+ b = (buf[pos+0] * bRatio);
+ r &= vis->red_mask;
+ g &= vis->green_mask;
+ b &= vis->blue_mask;
+ newBuf[outIndex] = r | g | b;
+ ++outIndex;
+ }
+ }
+ img = XCreateImage(m_dpy, vis, depth,ZPixmap, 0, (char *)newBuf,
+ iconTexture->GetWidth(), iconTexture->GetHeight(),
+ (depth>=24)?32:16, 0);
+ if (!img)
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not create image");
+ free(newBuf);
+ return false;
+ }
+ if (!XInitImage(img))
+ {
+ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - init image failed");
+ XDestroyImage(img);
+ return false;
+ }
+
+ // set byte order
+ union
+ {
+ char c[sizeof(short)];
+ short s;
+ } order;
+ order.s = 1;
+ if ((1 == order.c[0]))
+ {
+ img->byte_order = LSBFirst;
+ }
+ else
+ {
+ img->byte_order = MSBFirst;
+ }
+
+ // create icon pixmap from image
+ m_icon = XCreatePixmap(m_dpy, m_glWindow, img->width, img->height, depth);
+ GC gc = XCreateGC(m_dpy, m_glWindow, 0, NULL);
+ XPutImage(m_dpy, m_icon, gc, img, 0, 0, 0, 0, img->width, img->height);
+ XFreeGC(m_dpy, gc);
+ XDestroyImage(img); // this also frees newBuf
+
+ return true;
+}
+
+bool CWinSystemX11::HasWindowManager()
+{
+ Window wm_check;
+ unsigned char *data;
+ int status, real_format;
+ Atom real_type, prop;
+ unsigned long items_read, items_left;
+
+ prop = XInternAtom(m_dpy, "_NET_SUPPORTING_WM_CHECK", True);
+ if (prop == None)
+ return false;
+ status = XGetWindowProperty(m_dpy, DefaultRootWindow(m_dpy), prop,
+ 0L, 1L, False, XA_WINDOW, &real_type, &real_format,
+ &items_read, &items_left, &data);
+ if(status != Success || ! items_read)
+ {
+ if(status == Success)
+ XFree(data);
+ return false;
+ }
+
+ wm_check = ((Window*)data)[0];
+ XFree(data);
+
+ status = XGetWindowProperty(m_dpy, wm_check, prop,
+ 0L, 1L, False, XA_WINDOW, &real_type, &real_format,
+ &items_read, &items_left, &data);
+
+ if(status != Success || !items_read)
+ {
+ if(status == Success)
+ XFree(data);
+ return false;
+ }
+
+ if(wm_check != ((Window*)data)[0])
+ {
+ XFree(data);
+ return false;
+ }
+
+ XFree(data);
+
+ prop = XInternAtom(m_dpy, "_NET_WM_NAME", True);
+ if (prop == None)
+ {
+ CLog::Log(LOGDEBUG,"Window Manager Name: ");
+ return true;
+ }
+
+ status = XGetWindowProperty(m_dpy, wm_check, prop,
+ 0L, (~0L), False, AnyPropertyType, &real_type, &real_format,
+ &items_read, &items_left, &data);
+
+ if(status == Success && items_read)
+ {
+ const char* s;
+
+ s = reinterpret_cast<const char*>(data);
+ CLog::Log(LOGDEBUG, "Window Manager Name: {}", s);
+ }
+ else
+ CLog::Log(LOGDEBUG,"Window Manager Name: ");
+
+ if(status == Success)
+ XFree(data);
+
+ return true;
+}
+
+void CWinSystemX11::UpdateCrtc()
+{
+ XWindowAttributes winattr;
+ int posx, posy;
+ float fps = 0.0f;
+ Window child;
+ XGetWindowAttributes(m_dpy, m_mainWindow, &winattr);
+ XTranslateCoordinates(m_dpy, m_mainWindow, RootWindow(m_dpy, m_screen), winattr.x, winattr.y,
+ &posx, &posy, &child);
+
+ m_crtc = g_xrandr.GetCrtc(posx+winattr.width/2, posy+winattr.height/2, fps);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(fps);
+}
+
+bool CWinSystemX11::MessagePump()
+{
+ return m_winEvents->MessagePump();
+}
diff --git a/xbmc/windowing/X11/WinSystemX11.h b/xbmc/windowing/X11/WinSystemX11.h
new file mode 100644
index 0000000..f65538d
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingCallback.h"
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+#include "utils/Stopwatch.h"
+#include "windowing/WinSystem.h"
+
+#include <string>
+#include <vector>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+class IDispResource;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinEventsX11;
+
+class CWinSystemX11 : public CWinSystemBase
+{
+public:
+ CWinSystemX11();
+ ~CWinSystemX11() override;
+
+ const std::string GetName() override { return "x11"; }
+
+ // CWinSystemBase
+ bool InitWindowSystem() override;
+ bool DestroyWindowSystem() override;
+ bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
+ bool DestroyWindow() override;
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ void FinishWindowResize(int newWidth, int newHeight) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ void UpdateResolutions() override;
+ void ShowOSMouse(bool show) override;
+
+ void NotifyAppActiveChange(bool bActivated) override;
+ void NotifyAppFocusChange(bool bGaining) override;
+
+ bool Minimize() override;
+ bool Restore() override;
+ bool Hide() override;
+ bool Show(bool raise = true) override;
+ void Register(IDispResource *resource) override;
+ void Unregister(IDispResource *resource) override;
+ bool HasCalibration(const RESOLUTION_INFO &resInfo) override;
+ bool UseLimitedColor() override;
+
+ std::vector<std::string> GetConnectedOutputs() override;
+
+ // Local to WinSystemX11 only
+ Display* GetDisplay() { return m_dpy; }
+ int GetScreen() { return m_screen; }
+ void NotifyXRREvent();
+ bool IsCurrentOutput(const std::string& output);
+ void RecreateWindow();
+ int GetCrtc() { return m_crtc; }
+
+ // winevents override
+ bool MessagePump() override;
+
+protected:
+ std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> GetOSScreenSaverImpl() override;
+
+ virtual bool SetWindow(int width, int height, bool fullscreen, const std::string &output, int *winstate = NULL) = 0;
+ virtual XVisualInfo* GetVisual() = 0;
+
+ void OnLostDevice();
+
+ Window m_glWindow = 0, m_mainWindow = 0;
+ int m_screen = 0;
+ Display *m_dpy;
+ Cursor m_invisibleCursor = 0;
+ Pixmap m_icon;
+ bool m_bIsRotated;
+ bool m_bWasFullScreenBeforeMinimize;
+ bool m_minimized;
+ bool m_bIgnoreNextFocusMessage;
+ CCriticalSection m_resourceSection;
+ std::vector<IDispResource*> m_resources;
+ bool m_delayDispReset;
+ XbmcThreads::EndTime<> m_dispResetTimer;
+ std::string m_currentOutput;
+ std::string m_userOutput;
+ bool m_windowDirty;
+ bool m_bIsInternalXrr;
+ int m_MouseX, m_MouseY;
+ int m_crtc;
+ CWinEventsX11 *m_winEventsX11;
+
+private:
+ bool IsSuitableVisual(XVisualInfo *vInfo);
+ static int XErrorHandler(Display* dpy, XErrorEvent* error);
+ bool CreateIconPixmap();
+ bool HasWindowManager();
+ void UpdateCrtc();
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/WinSystemX11GLContext.cpp b/xbmc/windowing/X11/WinSystemX11GLContext.cpp
new file mode 100644
index 0000000..ad27aca
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11GLContext.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemX11GLContext.h"
+
+#include "GLContextEGL.h"
+#include "OptionalsReg.h"
+#include "VideoSyncOML.h"
+#include "X11DPMSSupport.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationSkinHandling.h"
+#include "cores/RetroPlayer/process/X11/RPProcessInfoX11.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/Process/X11/ProcessInfoX11.h"
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "guilib/DispResource.h"
+#include "rendering/gl/ScreenshotSurfaceGL.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include <mutex>
+#include <vector>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+using namespace KODI;
+using namespace KODI::WINDOWING::X11;
+
+
+void CWinSystemX11GLContext::Register()
+{
+ KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem, "x11");
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemX11GLContext::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemX11GLContext>();
+}
+
+CWinSystemX11GLContext::~CWinSystemX11GLContext()
+{
+ delete m_pGLContext;
+}
+
+void CWinSystemX11GLContext::PresentRenderImpl(bool rendered)
+{
+ if (rendered)
+ m_pGLContext->SwapBuffers();
+
+ if (m_delayDispReset && m_dispResetTimer.IsTimePast())
+ {
+ m_delayDispReset = false;
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ // tell any shared resources
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnResetDisplay();
+ }
+}
+
+void CWinSystemX11GLContext::SetVSyncImpl(bool enable)
+{
+ m_pGLContext->SetVSync(enable);
+}
+
+bool CWinSystemX11GLContext::IsExtSupported(const char* extension) const
+{
+ if(strncmp(extension, m_pGLContext->ExtPrefix().c_str(), 4) != 0)
+ return CRenderSystemGL::IsExtSupported(extension);
+
+ return m_pGLContext->IsExtSupported(extension);
+}
+
+XID CWinSystemX11GLContext::GetWindow() const
+{
+ return GLXGetWindow(m_pGLContext);
+}
+
+void* CWinSystemX11GLContext::GetGlxContext() const
+{
+ return GLXGetContext(m_pGLContext);
+}
+
+EGLDisplay CWinSystemX11GLContext::GetEGLDisplay() const
+{
+ return static_cast<CGLContextEGL*>(m_pGLContext)->m_eglDisplay;
+}
+
+EGLSurface CWinSystemX11GLContext::GetEGLSurface() const
+{
+ return static_cast<CGLContextEGL*>(m_pGLContext)->m_eglSurface;
+}
+
+EGLContext CWinSystemX11GLContext::GetEGLContext() const
+{
+ return static_cast<CGLContextEGL*>(m_pGLContext)->m_eglContext;
+}
+
+EGLConfig CWinSystemX11GLContext::GetEGLConfig() const
+{
+ return static_cast<CGLContextEGL*>(m_pGLContext)->m_eglConfig;
+}
+
+bool CWinSystemX11GLContext::SetWindow(int width, int height, bool fullscreen, const std::string &output, int *winstate)
+{
+ int newwin = 0;
+
+ CWinSystemX11::SetWindow(width, height, fullscreen, output, &newwin);
+ if (newwin)
+ {
+ RefreshGLContext(m_currentOutput.compare(output) != 0);
+ XSync(m_dpy, False);
+ CServiceBroker::GetWinSystem()->GetGfxContext().Clear(0);
+ CServiceBroker::GetWinSystem()->GetGfxContext().Flip(true, false);
+ ResetVSync();
+
+ m_windowDirty = false;
+ m_bIsInternalXrr = false;
+
+ if (!m_delayDispReset)
+ {
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ // tell any shared resources
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnResetDisplay();
+ }
+ }
+ return true;
+}
+
+bool CWinSystemX11GLContext::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ if(!CWinSystemX11::CreateNewWindow(name, fullScreen, res))
+ return false;
+
+ m_pGLContext->QueryExtensions();
+ return true;
+}
+
+bool CWinSystemX11GLContext::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ m_newGlContext = false;
+ CWinSystemX11::ResizeWindow(newWidth, newHeight, newLeft, newTop);
+ CRenderSystemGL::ResetRenderSystem(newWidth, newHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+
+ return true;
+}
+
+void CWinSystemX11GLContext::FinishWindowResize(int newWidth, int newHeight)
+{
+ m_newGlContext = false;
+ CWinSystemX11::FinishWindowResize(newWidth, newHeight);
+ CRenderSystemGL::ResetRenderSystem(newWidth, newHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+}
+
+bool CWinSystemX11GLContext::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ m_newGlContext = false;
+ CWinSystemX11::SetFullScreen(fullScreen, res, blankOtherDisplays);
+ CRenderSystemGL::ResetRenderSystem(res.iWidth, res.iHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+
+ return true;
+}
+
+bool CWinSystemX11GLContext::DestroyWindowSystem()
+{
+ if (m_pGLContext)
+ m_pGLContext->Destroy();
+ return CWinSystemX11::DestroyWindowSystem();
+}
+
+bool CWinSystemX11GLContext::DestroyWindow()
+{
+ if (m_pGLContext)
+ m_pGLContext->Detach();
+ return CWinSystemX11::DestroyWindow();
+}
+
+XVisualInfo* CWinSystemX11GLContext::GetVisual()
+{
+ int count = 0;
+ XVisualInfo vTemplate;
+ XVisualInfo *visual = nullptr;
+
+ int vMask = VisualScreenMask | VisualDepthMask | VisualClassMask;
+
+ vTemplate.screen = m_screen;
+ vTemplate.depth = 24;
+ vTemplate.c_class = TrueColor;
+
+ visual = XGetVisualInfo(m_dpy, vMask, &vTemplate, &count);
+
+ if (!visual)
+ {
+ vTemplate.depth = 30;
+ visual = XGetVisualInfo(m_dpy, vMask, &vTemplate, &count);
+ }
+
+ return visual;
+}
+
+bool CWinSystemX11GLContext::RefreshGLContext(bool force)
+{
+ bool success = false;
+ if (m_pGLContext)
+ {
+ if (force)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->UnloadSkin();
+ CRenderSystemGL::DestroyRenderSystem();
+ }
+ success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
+ if (!success)
+ {
+ success = m_pGLContext->CreatePB();
+ m_newGlContext = true;
+ }
+ if (force)
+ CRenderSystemGL::InitRenderSystem();
+ return success;
+ }
+
+ m_dpms = std::make_shared<CX11DPMSSupport>();
+ VIDEOPLAYER::CProcessInfoX11::Register();
+ RETRO::CRPProcessInfoX11::Register();
+ RETRO::CRPProcessInfoX11::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGL);
+ CDVDFactoryCodec::ClearHWAccels();
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CLinuxRendererGL::Register();
+
+ CScreenshotSurfaceGL::Register();
+
+ std::string gpuvendor;
+ const char* vend = (const char*) glGetString(GL_VENDOR);
+ if (vend)
+ gpuvendor = vend;
+ std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
+ bool isNvidia = (gpuvendor.compare(0, 6, "nvidia") == 0);
+ bool isIntel = (gpuvendor.compare(0, 5, "intel") == 0);
+ std::string gli = (getenv("KODI_GL_INTERFACE") != nullptr) ? getenv("KODI_GL_INTERFACE") : "";
+
+ if (gli != "GLX")
+ {
+ m_pGLContext = new CGLContextEGL(m_dpy, EGL_OPENGL_API);
+ success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
+ if (success)
+ {
+ if (!isNvidia)
+ {
+ m_vaapiProxy.reset(VaapiProxyCreate());
+ VaapiProxyConfig(m_vaapiProxy.get(), GetDisplay(),
+ static_cast<CGLContextEGL*>(m_pGLContext)->m_eglDisplay);
+ bool general = false;
+ bool deepColor = false;
+ VAAPIRegisterRenderGL(m_vaapiProxy.get(), general, deepColor);
+ if (general)
+ {
+ VAAPIRegister(m_vaapiProxy.get(), deepColor);
+ return true;
+ }
+ if (isIntel || gli == "EGL")
+ return true;
+ }
+ }
+ else if (gli == "EGL_PB")
+ {
+ success = m_pGLContext->CreatePB();
+ if (success)
+ return true;
+ }
+ }
+
+ delete m_pGLContext;
+
+ // fallback for vdpau
+ m_pGLContext = GLXContextCreate(m_dpy);
+ success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
+ if (success)
+ {
+ VDPAURegister();
+ VDPAURegisterRender();
+ }
+ return success;
+}
+
+std::unique_ptr<CVideoSync> CWinSystemX11GLContext::GetVideoSync(void *clock)
+{
+ std::unique_ptr<CVideoSync> pVSync;
+
+ if (dynamic_cast<CGLContextEGL*>(m_pGLContext))
+ {
+ pVSync.reset(new CVideoSyncOML(clock, *this));
+ }
+ else
+ {
+ pVSync.reset(GLXVideoSyncCreate(clock, *this));
+ }
+
+ return pVSync;
+}
+
+float CWinSystemX11GLContext::GetFrameLatencyAdjustment()
+{
+ if (m_pGLContext)
+ {
+ uint64_t msc, interval;
+ float micros = m_pGLContext->GetVblankTiming(msc, interval);
+ return micros / 1000;
+ }
+ return 0;
+}
+
+uint64_t CWinSystemX11GLContext::GetVblankTiming(uint64_t &msc, uint64_t &interval)
+{
+ if (m_pGLContext)
+ {
+ float micros = m_pGLContext->GetVblankTiming(msc, interval);
+ return micros;
+ }
+ msc = 0;
+ interval = 0;
+ return 0;
+}
+
+void CWinSystemX11GLContext::delete_CVaapiProxy::operator()(CVaapiProxy *p) const
+{
+ VaapiProxyDelete(p);
+}
diff --git a/xbmc/windowing/X11/WinSystemX11GLContext.h b/xbmc/windowing/X11/WinSystemX11GLContext.h
new file mode 100644
index 0000000..7d227b2
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11GLContext.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "WinSystemX11.h"
+#include "rendering/gl/RenderSystemGL.h"
+#include "system_egl.h"
+
+#include <memory>
+
+class CGLContext;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CVaapiProxy;
+
+class CWinSystemX11GLContext : public CWinSystemX11, public CRenderSystemGL
+{
+public:
+ CWinSystemX11GLContext() = default;
+ ~CWinSystemX11GLContext() override;
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystem via CWinSystemX11
+ CRenderSystemBase *GetRenderSystem() override { return this; }
+ bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ void FinishWindowResize(int newWidth, int newHeight) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ bool DestroyWindowSystem() override;
+ bool DestroyWindow() override;
+
+ bool IsExtSupported(const char* extension) const override;
+
+ // videosync
+ std::unique_ptr<CVideoSync> GetVideoSync(void *clock) override;
+ float GetFrameLatencyAdjustment() override;
+ uint64_t GetVblankTiming(uint64_t &msc, uint64_t &interval);
+
+ XID GetWindow() const;
+ void* GetGlxContext() const;
+ EGLDisplay GetEGLDisplay() const;
+ EGLSurface GetEGLSurface() const;
+ EGLContext GetEGLContext() const;
+ EGLConfig GetEGLConfig() const;
+
+protected:
+ bool SetWindow(int width, int height, bool fullscreen, const std::string &output, int *winstate = NULL) override;
+ void PresentRenderImpl(bool rendered) override;
+ void SetVSyncImpl(bool enable) override;
+ bool RefreshGLContext(bool force);
+ XVisualInfo* GetVisual() override;
+
+ CGLContext *m_pGLContext = nullptr;
+ bool m_newGlContext;
+
+ struct delete_CVaapiProxy
+ {
+ void operator()(CVaapiProxy *p) const;
+ };
+ std::unique_ptr<CVaapiProxy, delete_CVaapiProxy> m_vaapiProxy;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/X11/WinSystemX11GLESContext.cpp b/xbmc/windowing/X11/WinSystemX11GLESContext.cpp
new file mode 100644
index 0000000..e2367d3
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11GLESContext.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemX11GLESContext.h"
+
+#include "GLContextEGL.h"
+#include "OptionalsReg.h"
+#include "X11DPMSSupport.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationSkinHandling.h"
+#include "cores/RetroPlayer/process/X11/RPProcessInfoX11.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/Process/X11/ProcessInfoX11.h"
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "guilib/DispResource.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include <mutex>
+
+using namespace KODI;
+using namespace KODI::WINDOWING::X11;
+
+void CWinSystemX11GLESContext::Register()
+{
+ KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem, "x11");
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemX11GLESContext::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemX11GLESContext>();
+}
+
+CWinSystemX11GLESContext::~CWinSystemX11GLESContext()
+{
+ delete m_pGLContext;
+}
+
+void CWinSystemX11GLESContext::PresentRenderImpl(bool rendered)
+{
+ if (rendered && m_pGLContext)
+ m_pGLContext->SwapBuffers();
+
+ if (m_delayDispReset && m_dispResetTimer.IsTimePast())
+ {
+ m_delayDispReset = false;
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ // tell any shared resources
+ for (std::vector<IDispResource*>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnResetDisplay();
+ }
+}
+
+void CWinSystemX11GLESContext::SetVSyncImpl(bool enable)
+{
+ m_pGLContext->SetVSync(enable);
+}
+
+bool CWinSystemX11GLESContext::IsExtSupported(const char* extension) const
+{
+ if (strncmp(extension, m_pGLContext->ExtPrefix().c_str(), 4) != 0)
+ return CRenderSystemGLES::IsExtSupported(extension);
+
+ return m_pGLContext->IsExtSupported(extension);
+}
+
+EGLDisplay CWinSystemX11GLESContext::GetEGLDisplay() const
+{
+ return m_pGLContext->m_eglDisplay;
+}
+
+EGLSurface CWinSystemX11GLESContext::GetEGLSurface() const
+{
+ return m_pGLContext->m_eglSurface;
+}
+
+EGLContext CWinSystemX11GLESContext::GetEGLContext() const
+{
+ return m_pGLContext->m_eglContext;
+}
+
+EGLConfig CWinSystemX11GLESContext::GetEGLConfig() const
+{
+ return m_pGLContext->m_eglConfig;
+}
+
+bool CWinSystemX11GLESContext::SetWindow(int width, int height, bool fullscreen, const std::string& output, int* winstate)
+{
+ int newwin = 0;
+
+ CWinSystemX11::SetWindow(width, height, fullscreen, output, &newwin);
+ if (newwin)
+ {
+ RefreshGLContext(m_currentOutput.compare(output) != 0);
+ XSync(m_dpy, false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().Clear(0);
+ CServiceBroker::GetWinSystem()->GetGfxContext().Flip(true, false);
+ ResetVSync();
+
+ m_windowDirty = false;
+ m_bIsInternalXrr = false;
+
+ if (!m_delayDispReset)
+ {
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ // tell any shared resources
+ for (std::vector<IDispResource*>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnResetDisplay();
+ }
+ }
+ return true;
+}
+
+bool CWinSystemX11GLESContext::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ CLog::Log(LOGINFO, "CWinSystemX11GLESContext::CreateNewWindow");
+ if (!CWinSystemX11::CreateNewWindow(name, fullScreen, res) || !m_pGLContext)
+ return false;
+
+ m_pGLContext->QueryExtensions();
+ return true;
+}
+
+bool CWinSystemX11GLESContext::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ m_newGlContext = false;
+ CWinSystemX11::ResizeWindow(newWidth, newHeight, newLeft, newTop);
+ CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+
+ return true;
+}
+
+void CWinSystemX11GLESContext::FinishWindowResize(int newWidth, int newHeight)
+{
+ m_newGlContext = false;
+ CWinSystemX11::FinishWindowResize(newWidth, newHeight);
+ CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+}
+
+bool CWinSystemX11GLESContext::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ m_newGlContext = false;
+ CWinSystemX11::SetFullScreen(fullScreen, res, blankOtherDisplays);
+ CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
+
+ if (m_newGlContext)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+ }
+
+ return true;
+}
+
+bool CWinSystemX11GLESContext::DestroyWindowSystem()
+{
+ if (m_pGLContext)
+ m_pGLContext->Destroy();
+ return CWinSystemX11::DestroyWindowSystem();
+}
+
+bool CWinSystemX11GLESContext::DestroyWindow()
+{
+ if (m_pGLContext)
+ m_pGLContext->Detach();
+ return CWinSystemX11::DestroyWindow();
+}
+
+XVisualInfo* CWinSystemX11GLESContext::GetVisual()
+{
+ EGLDisplay eglDisplay;
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+ reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ if (eglGetPlatformDisplayEXT)
+ {
+ EGLint attribs[] =
+ {
+ EGL_PLATFORM_X11_SCREEN_EXT, m_screen,
+ EGL_NONE
+ };
+ eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,static_cast<EGLNativeDisplayType>(m_dpy), attribs);
+ }
+ else
+ eglDisplay = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_dpy));
+
+ if (eglDisplay == EGL_NO_DISPLAY)
+ {
+ CLog::Log(LOGERROR, "failed to get egl display");
+ return nullptr;
+ }
+ if (!eglInitialize(eglDisplay, nullptr, nullptr))
+ {
+ CLog::Log(LOGERROR, "failed to initialize egl display");
+ return nullptr;
+ }
+
+ GLint att[] =
+ {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_BUFFER_SIZE, 32,
+ EGL_DEPTH_SIZE, 24,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+ EGLint numConfigs;
+ EGLConfig eglConfig = 0;
+ if (!eglChooseConfig(eglDisplay, att, &eglConfig, 1, &numConfigs) || numConfigs == 0)
+ {
+ CLog::Log(LOGERROR, "Failed to choose a config {}", eglGetError());
+ return nullptr;
+ }
+
+ XVisualInfo x11_visual_info_template;
+ memset(&x11_visual_info_template, 0, sizeof(XVisualInfo));
+
+ if (!eglGetConfigAttrib(eglDisplay, eglConfig,
+ EGL_NATIVE_VISUAL_ID, reinterpret_cast<EGLint*>(&x11_visual_info_template.visualid)))
+ {
+ CLog::Log(LOGERROR, "Failed to query native visual id");
+ return nullptr;
+ }
+ int num_visuals;
+ XVisualInfo* visual =
+ XGetVisualInfo(m_dpy, VisualIDMask, &x11_visual_info_template, &num_visuals);
+ return visual;
+}
+
+bool CWinSystemX11GLESContext::RefreshGLContext(bool force)
+{
+ bool success = false;
+ if (m_pGLContext)
+ {
+ success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
+ if (!success)
+ {
+ success = m_pGLContext->CreatePB();
+ m_newGlContext = true;
+ }
+ return success;
+ }
+
+ m_dpms = std::make_shared<CX11DPMSSupport>();
+ VIDEOPLAYER::CProcessInfoX11::Register();
+ RETRO::CRPProcessInfoX11::Register();
+ RETRO::CRPProcessInfoX11::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
+ CDVDFactoryCodec::ClearHWAccels();
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CLinuxRendererGLES::Register();
+
+ std::string gli = (getenv("KODI_GL_INTERFACE") != nullptr) ? getenv("KODI_GL_INTERFACE") : "";
+
+ m_pGLContext = new CGLContextEGL(m_dpy, EGL_OPENGL_ES_API);
+ success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
+ if (!success && gli == "EGL_PB")
+ {
+ success = m_pGLContext->CreatePB();
+ m_newGlContext = true;
+ }
+
+ if (!success)
+ {
+ delete m_pGLContext;
+ m_pGLContext = nullptr;
+ }
+ return success;
+}
diff --git a/xbmc/windowing/X11/WinSystemX11GLESContext.h b/xbmc/windowing/X11/WinSystemX11GLESContext.h
new file mode 100644
index 0000000..cdb1cc4
--- /dev/null
+++ b/xbmc/windowing/X11/WinSystemX11GLESContext.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "EGL/egl.h"
+#include "WinSystemX11.h"
+#include "rendering/gles/RenderSystemGLES.h"
+
+class CGLContextEGL;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace X11
+{
+
+class CWinSystemX11GLESContext : public CWinSystemX11, public CRenderSystemGLES
+{
+public:
+ CWinSystemX11GLESContext() = default;
+ virtual ~CWinSystemX11GLESContext() override;
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystem via CWinSystemX11
+ CRenderSystemBase* GetRenderSystem() override { return this; }
+ bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ void FinishWindowResize(int newWidth, int newHeight) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ bool DestroyWindowSystem() override;
+ bool DestroyWindow() override;
+
+ bool IsExtSupported(const char* extension) const override;
+
+ EGLDisplay GetEGLDisplay() const;
+ EGLSurface GetEGLSurface() const;
+ EGLContext GetEGLContext() const;
+ EGLConfig GetEGLConfig() const;
+
+protected:
+ bool SetWindow(int width, int height, bool fullscreen, const std::string& output, int* winstate = nullptr) override;
+ void PresentRenderImpl(bool rendered) override;
+ void SetVSyncImpl(bool enable) override;
+ bool RefreshGLContext(bool force);
+ XVisualInfo* GetVisual() override;
+
+ CGLContextEGL* m_pGLContext = nullptr;
+ bool m_newGlContext;
+};
+
+} // namespace X11
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/X11/X11DPMSSupport.cpp b/xbmc/windowing/X11/X11DPMSSupport.cpp
new file mode 100644
index 0000000..7512bfa
--- /dev/null
+++ b/xbmc/windowing/X11/X11DPMSSupport.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "X11DPMSSupport.h"
+
+#include "ServiceBroker.h"
+#include "utils/log.h"
+#include "windowing/X11/WinSystemX11.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/dpms.h>
+
+using namespace KODI::WINDOWING::X11;
+
+namespace
+{
+// Mapping of PowerSavingMode to X11's mode constants.
+const CARD16 X_DPMS_MODES[] =
+{
+ DPMSModeStandby,
+ DPMSModeSuspend,
+ DPMSModeOff
+};
+}
+
+CX11DPMSSupport::CX11DPMSSupport()
+{
+ CWinSystemX11* winSystem = dynamic_cast<CWinSystemX11*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return;
+
+ Display* dpy = winSystem->GetDisplay();
+ if (!dpy)
+ return;
+
+ int event_base, error_base; // we ignore these
+ if (!DPMSQueryExtension(dpy, &event_base, &error_base))
+ {
+ CLog::Log(LOGINFO, "DPMS: X11 extension not present, power-saving will not be available");
+ return;
+ }
+
+ if (!DPMSCapable(dpy))
+ {
+ CLog::Log(LOGINFO, "DPMS: display does not support power-saving");
+ return;
+ }
+
+ m_supportedModes.push_back(SUSPEND); // best compromise
+ m_supportedModes.push_back(OFF); // next best
+ m_supportedModes.push_back(STANDBY); // rather lame, < 80% power according to DPMS spec
+}
+
+bool CX11DPMSSupport::EnablePowerSaving(PowerSavingMode mode)
+{
+ CWinSystemX11* winSystem = dynamic_cast<CWinSystemX11*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return false;
+
+ Display* dpy = winSystem->GetDisplay();
+ if (!dpy)
+ return false;
+
+ // This is not needed on my ATI Radeon, but the docs say that DPMSForceLevel
+ // after a DPMSDisable (from SDL) should not normally work.
+ DPMSEnable(dpy);
+ DPMSForceLevel(dpy, X_DPMS_MODES[mode]);
+ // There shouldn't be any errors if we called DPMSEnable; if they do happen,
+ // they're asynchronous and messy to detect.
+ XFlush(dpy);
+ return true;
+}
+
+bool CX11DPMSSupport::DisablePowerSaving()
+{
+ CWinSystemX11* winSystem = dynamic_cast<CWinSystemX11*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return false;
+
+ Display* dpy = winSystem->GetDisplay();
+ if (!dpy)
+ return false;
+
+ DPMSForceLevel(dpy, DPMSModeOn);
+ DPMSDisable(dpy);
+ XFlush(dpy);
+
+ winSystem->RecreateWindow();
+
+ return true;
+}
diff --git a/xbmc/windowing/X11/X11DPMSSupport.h b/xbmc/windowing/X11/X11DPMSSupport.h
new file mode 100644
index 0000000..c6ce8df
--- /dev/null
+++ b/xbmc/windowing/X11/X11DPMSSupport.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "xbmc/powermanagement/DPMSSupport.h"
+
+class CX11DPMSSupport : public CDPMSSupport
+{
+public:
+ CX11DPMSSupport();
+ ~CX11DPMSSupport() override = default;
+
+protected:
+ bool EnablePowerSaving(PowerSavingMode mode) override;
+ bool DisablePowerSaving() override;
+};
diff --git a/xbmc/windowing/X11/XRandR.cpp b/xbmc/windowing/X11/XRandR.cpp
new file mode 100644
index 0000000..ab20da3
--- /dev/null
+++ b/xbmc/windowing/X11/XRandR.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "XRandR.h"
+
+#include "CompileInfo.h"
+#include "threads/SystemClock.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+
+#include <string.h>
+
+#include <sys/wait.h>
+
+#include "PlatformDefs.h"
+
+#if defined(TARGET_FREEBSD)
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+using namespace std::chrono_literals;
+
+CXRandR::CXRandR(bool query)
+{
+ m_bInit = false;
+ m_numScreens = 1;
+ if (query)
+ Query();
+}
+
+bool CXRandR::Query(bool force, bool ignoreoff)
+{
+ if (!force)
+ if (m_bInit)
+ return m_outputs.size() > 0;
+
+ m_bInit = true;
+
+ if (getenv("KODI_BIN_HOME") == NULL)
+ return false;
+
+ m_outputs.clear();
+ // query all screens
+ // we are happy if at least one screen returns results
+ bool success = false;
+ for(unsigned int screennum=0; screennum<m_numScreens; ++screennum)
+ {
+ if(Query(force, screennum, ignoreoff))
+ success = true;
+ }
+ return success;
+}
+
+bool CXRandR::Query(bool force, int screennum, bool ignoreoff)
+{
+ std::string cmd;
+ std::string appname = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appname);
+ if (getenv("KODI_BIN_HOME"))
+ {
+ cmd = getenv("KODI_BIN_HOME");
+ cmd += "/" + appname + "-xrandr";
+ cmd = StringUtils::Format("{} -q --screen {}", cmd, screennum);
+ }
+
+ FILE* file = popen(cmd.c_str(),"r");
+ if (!file)
+ {
+ CLog::Log(LOGERROR, "CXRandR::Query - unable to execute xrandr tool");
+ return false;
+ }
+
+
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(file, TIXML_DEFAULT_ENCODING))
+ {
+ CLog::Log(LOGERROR, "CXRandR::Query - unable to open xrandr xml");
+ pclose(file);
+ return false;
+ }
+ pclose(file);
+
+ TiXmlElement *pRootElement = xmlDoc.RootElement();
+ if (atoi(pRootElement->Attribute("id")) != screennum)
+ {
+ //! @todo ERROR
+ return false;
+ }
+
+ for (TiXmlElement* output = pRootElement->FirstChildElement("output"); output; output = output->NextSiblingElement("output"))
+ {
+ XOutput xoutput;
+ xoutput.name = output->Attribute("name");
+ StringUtils::Trim(xoutput.name);
+ xoutput.isConnected = (StringUtils::CompareNoCase(output->Attribute("connected"), "true") == 0);
+ xoutput.screen = screennum;
+ xoutput.w = (output->Attribute("w") != NULL ? atoi(output->Attribute("w")) : 0);
+ xoutput.h = (output->Attribute("h") != NULL ? atoi(output->Attribute("h")) : 0);
+ xoutput.x = (output->Attribute("x") != NULL ? atoi(output->Attribute("x")) : 0);
+ xoutput.y = (output->Attribute("y") != NULL ? atoi(output->Attribute("y")) : 0);
+ xoutput.crtc = (output->Attribute("crtc") != NULL ? atoi(output->Attribute("crtc")) : 0);
+ xoutput.wmm = (output->Attribute("wmm") != NULL ? atoi(output->Attribute("wmm")) : 0);
+ xoutput.hmm = (output->Attribute("hmm") != NULL ? atoi(output->Attribute("hmm")) : 0);
+ if (output->Attribute("rotation") != NULL &&
+ (StringUtils::CompareNoCase(output->Attribute("rotation"), "left") == 0 ||
+ StringUtils::CompareNoCase(output->Attribute("rotation"), "right") == 0))
+ {
+ xoutput.isRotated = true;
+ }
+ else
+ xoutput.isRotated = false;
+
+ if (!xoutput.isConnected)
+ continue;
+
+ bool hascurrent = false;
+ for (TiXmlElement* mode = output->FirstChildElement("mode"); mode; mode = mode->NextSiblingElement("mode"))
+ {
+ XMode xmode;
+ xmode.id = mode->Attribute("id");
+ xmode.name = mode->Attribute("name");
+ xmode.hz = atof(mode->Attribute("hz"));
+ xmode.w = atoi(mode->Attribute("w"));
+ xmode.h = atoi(mode->Attribute("h"));
+ xmode.isPreferred = (StringUtils::CompareNoCase(mode->Attribute("preferred"), "true") == 0);
+ xmode.isCurrent = (StringUtils::CompareNoCase(mode->Attribute("current"), "true") == 0);
+ xoutput.modes.push_back(xmode);
+ if (xmode.isCurrent)
+ hascurrent = true;
+ }
+ if (hascurrent || !ignoreoff)
+ m_outputs.push_back(xoutput);
+ else
+ CLog::Log(LOGWARNING, "CXRandR::Query - output {} has no current mode, assuming disconnected",
+ xoutput.name);
+ }
+ return m_outputs.size() > 0;
+}
+
+bool CXRandR::TurnOffOutput(const std::string& name)
+{
+ XOutput *output = GetOutput(name);
+ if (!output)
+ return false;
+
+ std::string cmd;
+ std::string appname = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appname);
+
+ if (getenv("KODI_BIN_HOME"))
+ {
+ cmd = getenv("KODI_BIN_HOME");
+ cmd += "/" + appname + "-xrandr";
+ cmd = StringUtils::Format("{} --screen {} --output {} --off", cmd, output->screen, name);
+ }
+
+ int status = system(cmd.c_str());
+ if (status == -1)
+ return false;
+
+ if (WEXITSTATUS(status) != 0)
+ return false;
+
+ return true;
+}
+
+bool CXRandR::TurnOnOutput(const std::string& name)
+{
+ XOutput *output = GetOutput(name);
+ if (!output)
+ return false;
+
+ XMode mode = GetCurrentMode(output->name);
+ if (mode.isCurrent)
+ return true;
+
+ // get preferred mode
+ for (unsigned int j = 0; j < m_outputs.size(); j++)
+ {
+ if (m_outputs[j].name == output->name)
+ {
+ for (unsigned int i = 0; i < m_outputs[j].modes.size(); i++)
+ {
+ if (m_outputs[j].modes[i].isPreferred)
+ {
+ mode = m_outputs[j].modes[i];
+ break;
+ }
+ }
+ }
+ }
+
+ if (!mode.isPreferred)
+ return false;
+
+ if (!SetMode(*output, mode))
+ return false;
+
+ XbmcThreads::EndTime<> timeout(5s);
+ while (!timeout.IsTimePast())
+ {
+ if (!Query(true))
+ return false;
+
+ output = GetOutput(name);
+ if (output && output->h > 0)
+ return true;
+
+ KODI::TIME::Sleep(200ms);
+ }
+
+ return false;
+}
+
+std::vector<XOutput> CXRandR::GetModes(void)
+{
+ Query();
+ return m_outputs;
+}
+
+void CXRandR::SaveState()
+{
+ Query(true);
+}
+
+bool CXRandR::SetMode(const XOutput& output, const XMode& mode)
+{
+ if ((output.name == "" && mode.id == ""))
+ return true;
+
+ Query();
+
+ // Make sure the output exists, if not -- complain and exit
+ bool isOutputFound = false;
+ XOutput outputFound;
+ for (size_t i = 0; i < m_outputs.size(); i++)
+ {
+ if (m_outputs[i].name == output.name)
+ {
+ isOutputFound = true;
+ outputFound = m_outputs[i];
+ }
+ }
+
+ if (!isOutputFound)
+ {
+ CLog::Log(LOGERROR,
+ "CXRandR::SetMode: asked to change resolution for non existing output: {} mode: {}",
+ output.name, mode.id);
+ return false;
+ }
+
+ // try to find the same exact mode (same id, resolution, hz)
+ bool isModeFound = false;
+ XMode modeFound;
+ for (size_t i = 0; i < outputFound.modes.size(); i++)
+ {
+ if (outputFound.modes[i].id == mode.id)
+ {
+ if (outputFound.modes[i].w == mode.w &&
+ outputFound.modes[i].h == mode.h &&
+ outputFound.modes[i].hz == mode.hz)
+ {
+ isModeFound = true;
+ modeFound = outputFound.modes[i];
+ }
+ else
+ {
+ CLog::Log(LOGERROR,
+ "CXRandR::SetMode: asked to change resolution for mode that exists but with "
+ "different w/h/hz: {} mode: {}. Searching for similar modes...",
+ output.name, mode.id);
+ break;
+ }
+ }
+ }
+
+ if (!isModeFound)
+ {
+ for (size_t i = 0; i < outputFound.modes.size(); i++)
+ {
+ if (outputFound.modes[i].w == mode.w &&
+ outputFound.modes[i].h == mode.h &&
+ outputFound.modes[i].hz == mode.hz)
+ {
+ isModeFound = true;
+ modeFound = outputFound.modes[i];
+ CLog::Log(LOGWARNING, "CXRandR::SetMode: found alternative mode (same hz): {} mode: {}.",
+ output.name, outputFound.modes[i].id);
+ }
+ }
+ }
+
+ if (!isModeFound)
+ {
+ for (size_t i = 0; i < outputFound.modes.size(); i++)
+ {
+ if (outputFound.modes[i].w == mode.w &&
+ outputFound.modes[i].h == mode.h)
+ {
+ isModeFound = true;
+ modeFound = outputFound.modes[i];
+ CLog::Log(LOGWARNING,
+ "CXRandR::SetMode: found alternative mode (different hz): {} mode: {}.",
+ output.name, outputFound.modes[i].id);
+ }
+ }
+ }
+
+ // Let's try finding a mode that is the same
+ if (!isModeFound)
+ {
+ CLog::Log(LOGERROR,
+ "CXRandR::SetMode: asked to change resolution for non existing mode: {} mode: {}",
+ output.name, mode.id);
+ return false;
+ }
+
+ m_currentOutput = outputFound.name;
+ m_currentMode = modeFound.id;
+ std::string appname = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appname);
+ char cmd[255];
+
+ if (getenv("KODI_BIN_HOME"))
+ snprintf(cmd, sizeof(cmd), "%s/%s-xrandr --screen %d --output %s --mode %s",
+ getenv("KODI_BIN_HOME"),appname.c_str(),
+ outputFound.screen, outputFound.name.c_str(), modeFound.id.c_str());
+ else
+ return false;
+ CLog::Log(LOGINFO, "XRANDR: {}", cmd);
+ int status = system(cmd);
+ if (status == -1)
+ return false;
+
+ if (WEXITSTATUS(status) != 0)
+ return false;
+
+ return true;
+}
+
+XMode CXRandR::GetCurrentMode(const std::string& outputName)
+{
+ Query();
+ XMode result;
+
+ for (unsigned int j = 0; j < m_outputs.size(); j++)
+ {
+ if (m_outputs[j].name == outputName || outputName == "")
+ {
+ for (unsigned int i = 0; i < m_outputs[j].modes.size(); i++)
+ {
+ if (m_outputs[j].modes[i].isCurrent)
+ {
+ result = m_outputs[j].modes[i];
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+XMode CXRandR::GetPreferredMode(const std::string& outputName)
+{
+ Query();
+ XMode result;
+
+ for (unsigned int j = 0; j < m_outputs.size(); j++)
+ {
+ if (m_outputs[j].name == outputName || outputName == "")
+ {
+ for (unsigned int i = 0; i < m_outputs[j].modes.size(); i++)
+ {
+ if (m_outputs[j].modes[i].isPreferred)
+ {
+ result = m_outputs[j].modes[i];
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+void CXRandR::LoadCustomModeLinesToAllOutputs(void)
+{
+ Query();
+ CXBMCTinyXML xmlDoc;
+
+ if (!xmlDoc.LoadFile("special://xbmc/userdata/ModeLines.xml"))
+ {
+ return;
+ }
+
+ TiXmlElement *pRootElement = xmlDoc.RootElement();
+ if (StringUtils::CompareNoCase(pRootElement->Value(), "modelines") != 0)
+ {
+ //! @todo ERROR
+ return;
+ }
+
+ char cmd[255];
+ std::string name;
+ std::string strModeLine;
+
+ for (TiXmlElement* modeline = pRootElement->FirstChildElement("modeline"); modeline; modeline = modeline->NextSiblingElement("modeline"))
+ {
+ name = modeline->Attribute("label");
+ StringUtils::Trim(name);
+ strModeLine = modeline->FirstChild()->Value();
+ StringUtils::Trim(strModeLine);
+ std::string appname = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appname);
+
+ if (getenv("KODI_BIN_HOME"))
+ {
+ snprintf(cmd, sizeof(cmd), "%s/%s-xrandr --newmode \"%s\" %s > /dev/null 2>&1", getenv("KODI_BIN_HOME"),
+ appname.c_str(), name.c_str(), strModeLine.c_str());
+ if (system(cmd) != 0)
+ CLog::Log(LOGERROR, "Unable to create modeline \"{}\"", name);
+ }
+
+ for (unsigned int i = 0; i < m_outputs.size(); i++)
+ {
+ if (getenv("KODI_BIN_HOME"))
+ {
+ snprintf(cmd, sizeof(cmd), "%s/%s-xrandr --addmode %s \"%s\" > /dev/null 2>&1", getenv("KODI_BIN_HOME"),
+ appname.c_str(), m_outputs[i].name.c_str(), name.c_str());
+ if (system(cmd) != 0)
+ CLog::Log(LOGERROR, "Unable to add modeline \"{}\"", name);
+ }
+ }
+ }
+}
+
+void CXRandR::SetNumScreens(unsigned int num)
+{
+ m_numScreens = num;
+ m_bInit = false;
+}
+
+bool CXRandR::IsOutputConnected(const std::string& name)
+{
+ bool result = false;
+ Query();
+
+ for (unsigned int i = 0; i < m_outputs.size(); ++i)
+ {
+ if (m_outputs[i].name == name)
+ {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+XOutput* CXRandR::GetOutput(const std::string& outputName)
+{
+ XOutput *result = 0;
+ Query();
+ for (unsigned int i = 0; i < m_outputs.size(); ++i)
+ {
+ if (m_outputs[i].name == outputName)
+ {
+ result = &m_outputs[i];
+ break;
+ }
+ }
+ return result;
+}
+
+int CXRandR::GetCrtc(int x, int y, float &hz)
+{
+ int crtc = 0;
+ for (unsigned int i = 0; i < m_outputs.size(); ++i)
+ {
+ if (!m_outputs[i].isConnected)
+ continue;
+
+ if ((m_outputs[i].x <= x && (m_outputs[i].x+m_outputs[i].w) > x) &&
+ (m_outputs[i].y <= y && (m_outputs[i].y+m_outputs[i].h) > y))
+ {
+ crtc = m_outputs[i].crtc;
+ for (const auto& mode : m_outputs[i].modes)
+ {
+ if (mode.isCurrent)
+ {
+ hz = mode.hz;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return crtc;
+}
+
+CXRandR g_xrandr;
+
+/*
+ int main()
+ {
+ CXRandR r;
+ r.LoadCustomModeLinesToAllOutputs();
+ }
+*/
diff --git a/xbmc/windowing/X11/XRandR.h b/xbmc/windowing/X11/XRandR.h
new file mode 100644
index 0000000..6f5f74c
--- /dev/null
+++ b/xbmc/windowing/X11/XRandR.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+class XMode
+{
+public:
+ XMode()
+ {
+ hz=0.0f;
+ isPreferred=false;
+ isCurrent=false;
+ w=h=0;
+ }
+ bool operator==(XMode& mode) const
+ {
+ if (id != mode.id)
+ return false;
+ if (name != mode.name)
+ return false;
+ if (hz != mode.hz)
+ return false;
+ if (isPreferred != mode.isPreferred)
+ return false;
+ if (isCurrent != mode.isCurrent)
+ return false;
+ if (w != mode.w)
+ return false;
+ if (h != mode.h)
+ return false;
+ return true;
+ }
+ bool IsInterlaced()
+ {
+ return name.back() == 'i';
+ }
+ std::string id;
+ std::string name;
+ float hz;
+ bool isPreferred;
+ bool isCurrent;
+ unsigned int w;
+ unsigned int h;
+};
+
+class XOutput
+{
+public:
+ XOutput()
+ {
+ isConnected = false;
+ w = h = x = y = wmm = hmm = 0;
+ }
+ std::string name;
+ bool isConnected;
+ int screen;
+ int w;
+ int h;
+ int x;
+ int y;
+ int crtc;
+ int wmm;
+ int hmm;
+ std::vector<XMode> modes;
+ bool isRotated;
+};
+
+class CXRandR
+{
+public:
+ explicit CXRandR(bool query=false);
+ bool Query(bool force=false, bool ignoreoff=true);
+ bool Query(bool force, int screennum, bool ignoreoff=true);
+ std::vector<XOutput> GetModes(void);
+ XMode GetCurrentMode(const std::string& outputName);
+ XMode GetPreferredMode(const std::string& outputName);
+ XOutput *GetOutput(const std::string& outputName);
+ bool SetMode(const XOutput& output, const XMode& mode);
+ void LoadCustomModeLinesToAllOutputs(void);
+ void SaveState();
+ void SetNumScreens(unsigned int num);
+ bool IsOutputConnected(const std::string& name);
+ bool TurnOffOutput(const std::string& name);
+ bool TurnOnOutput(const std::string& name);
+ int GetCrtc(int x, int y, float &hz);
+ //bool Has1080i();
+ //bool Has1080p();
+ //bool Has720p();
+ //bool Has480p();
+
+private:
+ bool m_bInit;
+ std::vector<XOutput> m_outputs;
+ std::string m_currentOutput;
+ std::string m_currentMode;
+ unsigned int m_numScreens;
+};
+
+extern CXRandR g_xrandr;