summaryrefslogtreecommitdiffstats
path: root/vcl/opengl/x11
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/opengl/x11/X11DeviceInfo.cxx363
-rw-r--r--vcl/opengl/x11/cairotextrender.cxx84
-rw-r--r--vcl/opengl/x11/gdiimpl.cxx598
-rw-r--r--vcl/opengl/x11/salvd.cxx90
4 files changed, 1135 insertions, 0 deletions
diff --git a/vcl/opengl/x11/X11DeviceInfo.cxx b/vcl/opengl/x11/X11DeviceInfo.cxx
new file mode 100644
index 000000000..7f671952f
--- /dev/null
+++ b/vcl/opengl/x11/X11DeviceInfo.cxx
@@ -0,0 +1,363 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <opengl/x11/X11DeviceInfo.hxx>
+#include <opengl/x11/glxtest.hxx>
+
+#include <config_features.h>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include <desktop/crashreport.hxx>
+
+namespace glx {
+
+static int glxtest_pipe = 0;
+
+static pid_t glxtest_pid = 0;
+
+}
+
+pid_t* getGlxPid()
+{
+ return &glx::glxtest_pid;
+}
+
+int* getGlxPipe()
+{
+ return &glx::glxtest_pipe;
+}
+
+namespace {
+
+const char*
+strspnp_wrapper(const char* aDelims, const char* aStr)
+{
+ const char* d;
+ do
+ {
+ for (d = aDelims; *d != '\0'; ++d)
+ {
+ if (*aStr == *d)
+ {
+ ++aStr;
+ break;
+ }
+ }
+ } while (*d);
+
+ return aStr;
+}
+
+char* strtok_wrapper(const char* aDelims, char** aStr)
+{
+ if (!*aStr)
+ {
+ return nullptr;
+ }
+
+ char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
+
+ if (!*ret)
+ {
+ *aStr = ret;
+ return nullptr;
+ }
+
+ char* i = ret;
+ do
+ {
+ for (const char* d = aDelims; *d != '\0'; ++d)
+ {
+ if (*i == *d) {
+ *i = '\0';
+ *aStr = ++i;
+ return ret;
+ }
+ }
+ ++i;
+ } while (*i);
+
+ *aStr = nullptr;
+ return ret;
+}
+
+uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
+{
+ return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
+}
+
+}
+
+X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
+ mbIsMesa(false),
+ mbIsNVIDIA(false),
+ mbIsFGLRX(false),
+ mbIsNouveau(false),
+ mbIsIntel(false),
+ mbIsOldSwrast(false),
+ mbIsLlvmpipe(false),
+ mnGLMajorVersion(0),
+ mnMajorVersion(0),
+ mnMinorVersion(0),
+ mnRevisionVersion(0)
+{
+ GetData();
+}
+
+void X11OpenGLDeviceInfo::GetData()
+{
+ if (!glx::glxtest_pipe)
+ return;
+
+ // to understand this function, see bug moz#639842. We retrieve the OpenGL driver information in a
+ // separate process to protect against bad drivers.
+ enum { buf_size = 1024 };
+ char buf[buf_size];
+ ssize_t bytesread = read(glx::glxtest_pipe,
+ &buf,
+ buf_size-1); // -1 because we'll append a zero
+ close(glx::glxtest_pipe);
+ glx::glxtest_pipe = 0;
+
+ // bytesread < 0 would mean that the above read() call failed.
+ // This should never happen. If it did, the outcome would be to blacklist anyway.
+ if (bytesread < 0)
+ bytesread = 0;
+
+ // let buf be a zero-terminated string
+ buf[bytesread] = 0;
+
+ // Wait for the glxtest process to finish. This serves 2 purposes:
+ // * avoid having a zombie glxtest process laying around
+ // * get the glxtest process status info.
+ int glxtest_status = 0;
+ bool wait_for_glxtest_process = true;
+ bool waiting_for_glxtest_process_failed = false;
+ int waitpid_errno = 0;
+ while(wait_for_glxtest_process)
+ {
+ wait_for_glxtest_process = false;
+ if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1)
+ {
+ waitpid_errno = errno;
+ if (waitpid_errno == EINTR)
+ {
+ wait_for_glxtest_process = true;
+ }
+ else
+ {
+ // Bug moz#718629
+ // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
+ // as per bug moz#227246. This shouldn't matter, as we still seem to get the data
+ // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
+ waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
+ }
+ }
+ }
+
+ bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
+ WIFEXITED(glxtest_status) &&
+ WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
+ bool received_signal = !waiting_for_glxtest_process_failed &&
+ WIFSIGNALED(glxtest_status);
+
+ bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
+
+ OString textureFromPixmap;
+ OString *stringToFill = nullptr;
+ char *bufptr = buf;
+ if (!error)
+ {
+ while(true)
+ {
+ char *line = strtok_wrapper("\n", &bufptr);
+ if (!line)
+ break;
+ if (stringToFill) {
+ *stringToFill = OString(line);
+ stringToFill = nullptr;
+ }
+ else if(!strcmp(line, "VENDOR"))
+ stringToFill = &maVendor;
+ else if(!strcmp(line, "RENDERER"))
+ stringToFill = &maRenderer;
+ else if(!strcmp(line, "VERSION"))
+ stringToFill = &maVersion;
+ else if(!strcmp(line, "TFP"))
+ stringToFill = &textureFromPixmap;
+ }
+ }
+
+ // only useful for Linux kernel version check for FGLRX driver.
+ // assumes X client == X server, which is sad.
+ struct utsname unameobj;
+ if (!uname(&unameobj))
+ {
+ maOS = OString(unameobj.sysname);
+ maOSRelease = OString(unameobj.release);
+ }
+
+ // determine the major OpenGL version. That's the first integer in the version string.
+ mnGLMajorVersion = strtol(maVersion.getStr(), nullptr, 10);
+
+ // determine driver type (vendor) and where in the version string
+ // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
+ const char *whereToReadVersionNumbers = nullptr;
+ const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
+ if (Mesa_in_version_string)
+ {
+ mbIsMesa = true;
+ // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
+ // there is no actual driver version info.
+ whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
+ if (strcasestr(maVendor.getStr(), "nouveau"))
+ mbIsNouveau = true;
+ if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
+ mbIsIntel = true;
+ if (strcasestr(maRenderer.getStr(), "llvmpipe"))
+ mbIsLlvmpipe = true;
+ if (strcasestr(maRenderer.getStr(), "software rasterizer"))
+ mbIsOldSwrast = true;
+ }
+ else if (strstr(maVendor.getStr(), "NVIDIA Corporation"))
+ {
+ mbIsNVIDIA = true;
+ // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
+ // note that here the vendor and version strings behave differently, that's why we don't put this above
+ // alongside Mesa_in_version_string.
+ const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
+ if (NVIDIA_in_version_string)
+ whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
+ }
+ else if (strstr(maVendor.getStr(), "ATI Technologies Inc"))
+ {
+ mbIsFGLRX = true;
+ // with the FGLRX driver, the version string only gives an OpenGL version: so let's return that.
+ // that can at least give a rough idea of how old the driver is.
+ whereToReadVersionNumbers = maVersion.getStr();
+ }
+
+ // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
+ if (whereToReadVersionNumbers)
+ {
+ // copy into writable buffer, for tokenization
+ strncpy(buf, whereToReadVersionNumbers, buf_size-1);
+ buf[buf_size-1] = 0;
+ bufptr = buf;
+
+ // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
+ // been initialized as 0 anyways
+ char *token = strtok_wrapper(".", &bufptr);
+ if (token)
+ {
+ mnMajorVersion = strtol(token, nullptr, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token)
+ {
+ mnMinorVersion = strtol(token, nullptr, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token)
+ mnRevisionVersion = strtol(token, nullptr, 10);
+ }
+ }
+ }
+}
+
+bool X11OpenGLDeviceInfo::isDeviceBlocked()
+{
+ // don't even try to use OpenGL 1.x
+ if (mnGLMajorVersion == 1)
+ return true;
+
+ CrashReporter::addKeyValue("AdapterVendorId", OStringToOUString(maVendor, RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
+ CrashReporter::addKeyValue("AdapterDeviceId", OStringToOUString(maRenderer, RTL_TEXTENCODING_UTF8), CrashReporter::Write);
+
+ SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
+ SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
+ SAL_INFO("vcl.opengl", "Version: " << maVersion);
+ SAL_INFO("vcl.opengl", "OS: " << maOS);
+ SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
+
+ if (mbIsMesa)
+ {
+ if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
+ return true;
+ }
+ else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
+ return true;
+ }
+ else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
+ return true;
+ }
+ else if (mbIsOldSwrast)
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
+ return true;
+ }
+ else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1))
+ {
+ // bug moz#791905, Mesa bug 57733, fixed in Mesa 9.1 according to
+ // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
+ SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
+ return true;
+ }
+ }
+ else if (mbIsNVIDIA)
+ {
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
+ return true;
+ }
+ }
+ else if (mbIsFGLRX)
+ {
+ // FGLRX does not report a driver version number, so we have the OpenGL version instead.
+ // by requiring OpenGL 3, we effectively require recent drivers.
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
+ return true;
+ }
+ // Bug moz#724640: FGLRX + Linux 2.6.32 is a crashy combo
+ bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
+ bool badOS = maOS.indexOf("Linux") != -1 &&
+ maOSRelease.indexOf("2.6.32") != -1;
+ if (unknownOS || badOS)
+ {
+ SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
+ return true;
+ }
+ }
+ else
+ {
+ // like on windows, let's block unknown vendors. Think of virtual machines.
+ // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
+ SAL_WARN("vcl.opengl", "unknown vendor => blocked");
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/cairotextrender.cxx b/vcl/opengl/x11/cairotextrender.cxx
new file mode 100644
index 000000000..39b5f661d
--- /dev/null
+++ b/vcl/opengl/x11/cairotextrender.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <opengl/x11/cairotextrender.hxx>
+
+#include <opengl/gdiimpl.hxx>
+
+#include <cairo.h>
+
+OpenGLX11CairoTextRender::OpenGLX11CairoTextRender(X11SalGraphics& rParent)
+ : X11CairoTextRender(rParent)
+{
+}
+
+cairo_t* OpenGLX11CairoTextRender::getCairoContext()
+{
+ cairo_surface_t* surface = nullptr;
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if( pImpl )
+ {
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+ if( aClipRect.GetWidth() == 0 || aClipRect.GetHeight() == 0 )
+ {
+ aClipRect.setWidth( GetWidth() );
+ aClipRect.setHeight( GetHeight() );
+ }
+ surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, aClipRect.GetWidth(), aClipRect.GetHeight() );
+ }
+ if (!surface)
+ return nullptr;
+ cairo_t *cr = cairo_create(surface);
+ cairo_surface_destroy(surface);
+ return cr;
+}
+
+void OpenGLX11CairoTextRender::getSurfaceOffset( double& nDX, double& nDY )
+{
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if( pImpl )
+ {
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+ nDX = -aClipRect.Left();
+ nDY = -aClipRect.Top();
+ }
+}
+
+void OpenGLX11CairoTextRender::releaseCairoContext(cairo_t* cr)
+{
+ // XXX: lfrb: GLES 2.0 doesn't support GL_UNSIGNED_INT_8_8_8_8_REV
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if(!pImpl)
+ {
+ cairo_destroy(cr);
+ return;
+ }
+
+ cairo_surface_t* pSurface = cairo_get_target(cr);
+ int nWidth = cairo_image_surface_get_width( pSurface );
+ int nHeight = cairo_image_surface_get_height( pSurface );
+ cairo_surface_flush(pSurface);
+ unsigned char *pSrc = cairo_image_surface_get_data( pSurface );
+
+ // XXX: lfrb: GLES 2.0 doesn't support GL_UNSIGNED_INT_8_8_8_8_REV
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+
+ SalTwoRect aRect(0, 0, nWidth, nHeight,
+ aClipRect.Left(), aClipRect.Top(), nWidth, nHeight);
+
+ // Cairo surface data is ARGB with premultiplied alpha and is Y-inverted
+ OpenGLTexture aTexture( nWidth, nHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pSrc );
+ pImpl->PreDraw();
+ pImpl->DrawAlphaTexture( aTexture, aRect, true, true );
+ pImpl->PostDraw();
+
+ cairo_destroy(cr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/gdiimpl.cxx b/vcl/opengl/x11/gdiimpl.cxx
new file mode 100644
index 000000000..c00ff76e8
--- /dev/null
+++ b/vcl/opengl/x11/gdiimpl.cxx
@@ -0,0 +1,598 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <vcl/lazydelete.hxx>
+
+#include <svdata.hxx>
+
+#include <unx/saldisp.hxx>
+#include <unx/salframe.h>
+#include <unx/salgdi.h>
+#include <unx/salinst.h>
+#include <unx/salvd.h>
+#include <unx/x11/xlimits.hxx>
+
+#include <opengl/texture.hxx>
+#include <opengl/zone.hxx>
+#include <opengl/RenderState.hxx>
+#include <opengl/x11/gdiimpl.hxx>
+#include <opengl/x11/salvd.hxx>
+
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <sal/log.hxx>
+
+#include <o3tl/lru_map.hxx>
+#include <ControlCacheKey.hxx>
+
+static std::vector<GLXContext> g_vShareList;
+static bool g_bAnyCurrent;
+
+namespace {
+
+class X11OpenGLContext : public OpenGLContext
+{
+public:
+ void init(Display* dpy, Window win, int screen);
+ virtual void initWindow() override;
+private:
+ GLX11Window m_aGLWin;
+ virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
+ virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
+ virtual bool ImplInit() override;
+ void initGLWindow(Visual* pVisual);
+ virtual SystemWindowData generateWinData(vcl::Window* pParent, bool bRequestLegacyContext) override;
+ virtual void makeCurrent() override;
+ virtual void destroyCurrentContext() override;
+ virtual bool isCurrent() override;
+ virtual bool isAnyCurrent() override;
+ virtual void sync() override;
+ virtual void resetCurrent() override;
+ virtual void swapBuffers() override;
+};
+
+#ifdef DBG_UTIL
+ int unxErrorHandler(Display* dpy, XErrorEvent* event)
+ {
+ char err[256];
+ char req[256];
+ char minor[256];
+ XGetErrorText(dpy, event->error_code, err, 256);
+ XGetErrorText(dpy, event->request_code, req, 256);
+ XGetErrorText(dpy, event->minor_code, minor, 256);
+ SAL_WARN("vcl.opengl", "Error: " << err << ", Req: " << req << ", Minor: " << minor);
+ return 0;
+ }
+#endif
+
+ typedef int (*errorHandler)(Display* /*dpy*/, XErrorEvent* /*evnt*/);
+
+ class TempErrorHandler
+ {
+ private:
+ errorHandler oldErrorHandler;
+ Display* mdpy;
+
+ public:
+ TempErrorHandler(Display* dpy, errorHandler newErrorHandler)
+ : oldErrorHandler(nullptr)
+ , mdpy(dpy)
+ {
+ if (mdpy)
+ {
+ XLockDisplay(dpy);
+ XSync(dpy, false);
+ oldErrorHandler = XSetErrorHandler(newErrorHandler);
+ }
+ }
+
+ ~TempErrorHandler()
+ {
+ if (mdpy)
+ {
+ // sync so that we possibly get an XError
+ glXWaitGL();
+ XSync(mdpy, false);
+ XSetErrorHandler(oldErrorHandler);
+ XUnlockDisplay(mdpy);
+ }
+ }
+ };
+
+ static bool errorTriggered;
+ int oglErrorHandler( Display* /*dpy*/, XErrorEvent* /*evnt*/ )
+ {
+ errorTriggered = true;
+
+ return 0;
+ }
+
+ GLXFBConfig* getFBConfig(Display* dpy, Window win, int& nBestFBC)
+ {
+ OpenGLZone aZone;
+
+ if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
+ return nullptr;
+
+ VCL_GL_INFO("window: " << win);
+
+ XWindowAttributes xattr;
+ if( !XGetWindowAttributes( dpy, win, &xattr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes for fbconfig " << win);
+ xattr.screen = nullptr;
+ xattr.visual = nullptr;
+ }
+
+ int screen = XScreenNumberOfScreen( xattr.screen );
+
+ // TODO: moggi: Select colour channel depth based on visual attributes, not hardcoded */
+ static int visual_attribs[] =
+ {
+ GLX_DOUBLEBUFFER, True,
+ GLX_X_RENDERABLE, True,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_DEPTH_SIZE, 24,
+ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
+ None
+ };
+
+ int fbCount = 0;
+ GLXFBConfig* pFBC = glXChooseFBConfig( dpy,
+ screen,
+ visual_attribs, &fbCount );
+
+ if(!pFBC)
+ {
+ SAL_WARN("vcl.opengl", "no suitable fb format found");
+ return nullptr;
+ }
+
+ int best_num_samp = -1;
+ for(int i = 0; i < fbCount; ++i)
+ {
+ XVisualInfo* pVi = glXGetVisualFromFBConfig( dpy, pFBC[i] );
+ if(pVi && (xattr.visual && pVi->visualid == xattr.visual->visualid) )
+ {
+ // pick the one with the most samples per pixel
+ int nSampleBuf = 0;
+ int nSamples = 0;
+ glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLE_BUFFERS, &nSampleBuf );
+ glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLES , &nSamples );
+
+ if ( nBestFBC < 0 || (nSampleBuf && ( nSamples > best_num_samp )) )
+ {
+ nBestFBC = i;
+ best_num_samp = nSamples;
+ }
+ }
+ XFree( pVi );
+ }
+
+ return pFBC;
+ }
+
+ Visual* getVisual(Display* dpy, Window win)
+ {
+ OpenGLZone aZone;
+
+ XWindowAttributes xattr;
+ if( !XGetWindowAttributes( dpy, win, &xattr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes for getVisual " << win);
+ xattr.visual = nullptr;
+ }
+ VCL_GL_INFO("using VisualID " << xattr.visual);
+ return xattr.visual;
+ }
+}
+
+void X11OpenGLContext::sync()
+{
+ OpenGLZone aZone;
+ glXWaitGL();
+ XSync(m_aGLWin.dpy, false);
+}
+
+void X11OpenGLContext::swapBuffers()
+{
+ OpenGLZone aZone;
+
+ glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
+
+ BuffersSwapped();
+}
+
+void X11OpenGLContext::resetCurrent()
+{
+ clearCurrent();
+
+ OpenGLZone aZone;
+
+ if (m_aGLWin.dpy)
+ {
+ glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
+ g_bAnyCurrent = false;
+ }
+}
+
+bool X11OpenGLContext::isCurrent()
+{
+ OpenGLZone aZone;
+ return g_bAnyCurrent && m_aGLWin.ctx && glXGetCurrentContext() == m_aGLWin.ctx &&
+ glXGetCurrentDrawable() == m_aGLWin.win;
+}
+
+bool X11OpenGLContext::isAnyCurrent()
+{
+ return g_bAnyCurrent && glXGetCurrentContext() != None;
+}
+
+SystemWindowData X11OpenGLContext::generateWinData(vcl::Window* pParent, bool /*bRequestLegacyContext*/)
+{
+ OpenGLZone aZone;
+
+ SystemWindowData aWinData;
+ aWinData.pVisual = nullptr;
+ aWinData.bClipUsingNativeWidget = false;
+
+ const SystemEnvData* sysData(pParent->GetSystemData());
+
+ Display *dpy = static_cast<Display*>(sysData->pDisplay);
+ Window win = sysData->aWindow;
+
+ if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
+ return aWinData;
+
+ int best_fbc = -1;
+ GLXFBConfig* pFBC = getFBConfig(dpy, win, best_fbc);
+
+ if (!pFBC)
+ return aWinData;
+
+ XVisualInfo* vi = nullptr;
+ if( best_fbc != -1 )
+ vi = glXGetVisualFromFBConfig( dpy, pFBC[best_fbc] );
+
+ XFree(pFBC);
+
+ if( vi )
+ {
+ VCL_GL_INFO("using VisualID " << vi->visualid);
+ aWinData.pVisual = static_cast<void*>(vi->visual);
+ }
+
+ return aWinData;
+}
+
+bool X11OpenGLContext::ImplInit()
+{
+ if (!m_aGLWin.dpy)
+ return false;
+
+ OpenGLZone aZone;
+
+ GLXContext pSharedCtx( nullptr );
+#ifdef DBG_UTIL
+ TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
+#endif
+
+ VCL_GL_INFO("OpenGLContext::ImplInit----start");
+
+ if (!g_vShareList.empty())
+ pSharedCtx = g_vShareList.front();
+
+ //tdf#112166 for, e.g. VirtualBox GL, claiming OpenGL 2.1
+ static bool hasCreateContextAttribsARB = glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB")) != nullptr;
+ if (hasCreateContextAttribsARB && !mbRequestLegacyContext)
+ {
+ int best_fbc = -1;
+ GLXFBConfig* pFBC = getFBConfig(m_aGLWin.dpy, m_aGLWin.win, best_fbc);
+
+ if (pFBC && best_fbc != -1)
+ {
+ int const pContextAttribs[] =
+ {
+#if 0 // defined(DBG_UTIL)
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 2,
+#endif
+ None
+
+ };
+ m_aGLWin.ctx = glXCreateContextAttribsARB(m_aGLWin.dpy, pFBC[best_fbc], pSharedCtx, /* direct, not via X */ GL_TRUE, pContextAttribs);
+ SAL_INFO_IF(m_aGLWin.ctx, "vcl.opengl", "created a 3.2 core context");
+ }
+ else
+ SAL_WARN("vcl.opengl", "unable to find correct FBC");
+ }
+
+ if (!m_aGLWin.ctx)
+ {
+ if (!m_aGLWin.vi)
+ return false;
+
+ SAL_WARN("vcl.opengl", "attempting to create a non-double-buffered "
+ "visual matching the context");
+
+ m_aGLWin.ctx = glXCreateContext(m_aGLWin.dpy,
+ m_aGLWin.vi,
+ pSharedCtx,
+ GL_TRUE /* direct, not via X server */);
+ }
+
+ if( m_aGLWin.ctx )
+ {
+ g_vShareList.push_back( m_aGLWin.ctx );
+ }
+ else
+ {
+ SAL_WARN("vcl.opengl", "unable to create GLX context");
+ return false;
+ }
+
+ if( !glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ) )
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "unable to select current GLX context");
+ return false;
+ }
+
+ g_bAnyCurrent = true;
+
+ int glxMinor, glxMajor;
+ double nGLXVersion = 0;
+ if( glXQueryVersion( m_aGLWin.dpy, &glxMajor, &glxMinor ) )
+ nGLXVersion = glxMajor + 0.1*glxMinor;
+ SAL_INFO("vcl.opengl", "available GLX version: " << nGLXVersion);
+
+ SAL_INFO("vcl.opengl", "available GL extensions: " << glGetString(GL_EXTENSIONS));
+
+ XWindowAttributes aWinAttr;
+ if( !XGetWindowAttributes( m_aGLWin.dpy, m_aGLWin.win, &aWinAttr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes on " << m_aGLWin.win);
+ m_aGLWin.Width = 0;
+ m_aGLWin.Height = 0;
+ }
+ else
+ {
+ m_aGLWin.Width = aWinAttr.width;
+ m_aGLWin.Height = aWinAttr.height;
+ }
+
+ if( m_aGLWin.HasGLXExtension("GLX_SGI_swap_control" ) )
+ {
+ // enable vsync
+ typedef GLint (*glXSwapIntervalProc)(GLint);
+ glXSwapIntervalProc glXSwapInterval = reinterpret_cast<glXSwapIntervalProc>(glXGetProcAddress( reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI") ));
+ if( glXSwapInterval )
+ {
+ TempErrorHandler aLocalErrorHandler(m_aGLWin.dpy, oglErrorHandler);
+
+ errorTriggered = false;
+
+ glXSwapInterval( 1 );
+
+ if( errorTriggered )
+ SAL_WARN("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
+ else
+ VCL_GL_INFO("set swap interval to 1 (enable vsync)");
+ }
+ }
+
+ bool bRet = InitGL();
+ InitGLDebugging();
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ registerAsCurrent();
+
+ return bRet;
+}
+
+void X11OpenGLContext::makeCurrent()
+{
+ if (isCurrent())
+ return;
+
+ OpenGLZone aZone;
+
+ clearCurrent();
+
+#ifdef DBG_UTIL
+ TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
+#endif
+
+ if (m_aGLWin.dpy)
+ {
+ if (!glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ))
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "OpenGLContext::makeCurrent failed "
+ "on drawable " << m_aGLWin.win);
+ return;
+ }
+ g_bAnyCurrent = true;
+ }
+
+ registerAsCurrent();
+}
+
+void X11OpenGLContext::destroyCurrentContext()
+{
+ if(m_aGLWin.ctx)
+ {
+ std::vector<GLXContext>::iterator itr = std::remove( g_vShareList.begin(), g_vShareList.end(), m_aGLWin.ctx );
+ if (itr != g_vShareList.end())
+ g_vShareList.erase(itr);
+
+ glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
+ g_bAnyCurrent = false;
+ if( glGetError() != GL_NO_ERROR )
+ {
+ SAL_WARN("vcl.opengl", "glError: " << glGetError());
+ }
+ glXDestroyContext(m_aGLWin.dpy, m_aGLWin.ctx);
+ m_aGLWin.ctx = nullptr;
+ }
+}
+
+void X11OpenGLContext::init(Display* dpy, Window win, int screen)
+{
+ if (isInitialized())
+ return;
+
+ if (!dpy)
+ return;
+
+ OpenGLZone aZone;
+
+ m_aGLWin.dpy = dpy;
+ m_aGLWin.win = win;
+ m_aGLWin.screen = screen;
+
+ Visual* pVisual = getVisual(dpy, win);
+
+ initGLWindow(pVisual);
+
+ ImplInit();
+}
+
+void X11OpenGLContext::initGLWindow(Visual* pVisual)
+{
+ OpenGLZone aZone;
+
+ // Get visual info
+ {
+ XVisualInfo aTemplate;
+ aTemplate.visualid = XVisualIDFromVisual( pVisual );
+ int nVisuals = 0;
+ XVisualInfo* pInfo = XGetVisualInfo( m_aGLWin.dpy, VisualIDMask, &aTemplate, &nVisuals );
+ if( nVisuals != 1 )
+ SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
+ m_aGLWin.vi = pInfo;
+ }
+
+ // Check multisample support
+ /* TODO: moggi: This is not necessarily correct in the DBG_UTIL path, as it picks
+ * an FBConfig instead ... */
+ int nSamples = 0;
+ glXGetConfig(m_aGLWin.dpy, m_aGLWin.vi, GLX_SAMPLES, &nSamples);
+ if( nSamples > 0 )
+ m_aGLWin.bMultiSampleSupported = true;
+
+ m_aGLWin.GLXExtensions = glXQueryExtensionsString( m_aGLWin.dpy, m_aGLWin.screen );
+ SAL_INFO("vcl.opengl", "available GLX extensions: " << m_aGLWin.GLXExtensions);
+}
+
+void X11OpenGLContext::initWindow()
+{
+ const SystemEnvData* pChildSysData = nullptr;
+ SystemWindowData winData = generateWinData(mpWindow, false);
+ if( winData.pVisual )
+ {
+ if( !m_pChildWindow )
+ {
+ m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
+ }
+ pChildSysData = m_pChildWindow->GetSystemData();
+ }
+
+ if (!m_pChildWindow || !pChildSysData)
+ return;
+
+ InitChildWindow(m_pChildWindow.get());
+
+ m_aGLWin.dpy = static_cast<Display*>(pChildSysData->pDisplay);
+ m_aGLWin.win = pChildSysData->aWindow;
+ m_aGLWin.screen = pChildSysData->nScreen;
+
+ Visual* pVisual = static_cast<Visual*>(pChildSysData->pVisual);
+ initGLWindow(pVisual);
+}
+
+GLX11Window::GLX11Window()
+ : dpy(nullptr)
+ , screen(0)
+ , win(0)
+ , vi(nullptr)
+ , ctx(nullptr)
+{
+}
+
+bool GLX11Window::HasGLXExtension( const char* name ) const
+{
+ for (sal_Int32 i = 0; i != -1;) {
+ if (GLXExtensions.getToken(0, ' ', i) == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+GLX11Window::~GLX11Window()
+{
+ XFree(vi);
+}
+
+bool GLX11Window::Synchronize(bool bOnoff) const
+{
+ XSynchronize(dpy, bOnoff);
+ return true;
+}
+
+OpenGLContext* X11SalInstance::CreateOpenGLContext()
+{
+ return new X11OpenGLContext;
+}
+
+X11OpenGLSalGraphicsImpl::X11OpenGLSalGraphicsImpl( X11SalGraphics& rParent ):
+ OpenGLSalGraphicsImpl(rParent,rParent.GetGeometryProvider()),
+ mrX11Parent(rParent)
+{
+}
+
+X11OpenGLSalGraphicsImpl::~X11OpenGLSalGraphicsImpl()
+{
+}
+
+void X11OpenGLSalGraphicsImpl::Init()
+{
+ // The m_pFrame and m_pVDev pointers are updated late in X11
+ mpProvider = mrX11Parent.GetGeometryProvider();
+ OpenGLSalGraphicsImpl::Init();
+}
+
+rtl::Reference<OpenGLContext> X11OpenGLSalGraphicsImpl::CreateWinContext()
+{
+ NativeWindowHandleProvider *pProvider = dynamic_cast<NativeWindowHandleProvider*>(mrX11Parent.m_pFrame);
+
+ if( !pProvider )
+ return nullptr;
+
+ sal_uIntPtr aWin = pProvider->GetNativeWindowHandle();
+ rtl::Reference<X11OpenGLContext> xContext = new X11OpenGLContext;
+ xContext->setVCLOnly();
+ xContext->init( mrX11Parent.GetXDisplay(), aWin,
+ mrX11Parent.m_nXScreen.getXScreen() );
+ return rtl::Reference<OpenGLContext>(xContext.get());
+}
+
+void X11OpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ OpenGLSalGraphicsImpl *pImpl = pSrcGraphics ? static_cast< OpenGLSalGraphicsImpl* >(pSrcGraphics->GetImpl()) : static_cast< OpenGLSalGraphicsImpl *>(mrX11Parent.GetImpl());
+ OpenGLSalGraphicsImpl::DoCopyBits( rPosAry, *pImpl );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/salvd.cxx b/vcl/opengl/x11/salvd.cxx
new file mode 100644
index 000000000..a6ed5602f
--- /dev/null
+++ b/vcl/opengl/x11/salvd.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vcl/sysdata.hxx>
+
+#include <unx/salunx.h>
+#include <unx/saldisp.hxx>
+#include <unx/salgdi.h>
+#include <unx/salvd.h>
+
+#include <opengl/x11/salvd.hxx>
+
+void X11SalGraphics::Init( X11OpenGLSalVirtualDevice *pDevice )
+{
+ SalDisplay *pDisplay = pDevice->GetDisplay();
+
+ m_nXScreen = pDevice->GetXScreenNumber();
+ m_pColormap = &pDisplay->GetColormap( m_nXScreen );
+
+ m_pVDev = pDevice;
+ m_pFrame = nullptr;
+
+ bWindow_ = pDisplay->IsDisplay();
+ bVirDev_ = true;
+
+ mxImpl->Init();
+}
+
+X11OpenGLSalVirtualDevice::X11OpenGLSalVirtualDevice( SalGraphics const * pGraphics,
+ long nDX, long nDY,
+ const SystemGraphicsData *pData,
+ std::unique_ptr<X11SalGraphics> pNewGraphics) :
+ mpGraphics(std::move(pNewGraphics)),
+ mbGraphics( false ),
+ mnXScreen( 0 )
+{
+ assert(mpGraphics);
+
+ // TODO Check where a VirtualDevice is created from SystemGraphicsData
+ assert( pData == nullptr ); (void)pData;
+
+ mpDisplay = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ mnXScreen = pGraphics ? static_cast<X11SalGraphics const *>(pGraphics)->GetScreenNumber() :
+ vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDefaultXScreen();
+ mnWidth = nDX;
+ mnHeight = nDY;
+ mpGraphics->Init( this );
+}
+
+X11OpenGLSalVirtualDevice::~X11OpenGLSalVirtualDevice()
+{
+}
+
+SalGraphics* X11OpenGLSalVirtualDevice::AcquireGraphics()
+{
+ if( mbGraphics )
+ return nullptr;
+
+ if( mpGraphics )
+ mbGraphics = true;
+
+ return mpGraphics.get();
+}
+
+void X11OpenGLSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+
+bool X11OpenGLSalVirtualDevice::SetSize( long nDX, long nDY )
+{
+ if( !nDX ) nDX = 1;
+ if( !nDY ) nDY = 1;
+
+ mnWidth = nDX;
+ mnHeight = nDY;
+ if( mpGraphics )
+ mpGraphics->Init( this );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */