Commit eac64633 by Corentin Wallez

Add a basic GLX EGL implementation

BUG=angleproject:892 Change-Id: Ife9955a457d4a6fb3adce17757ccb0de7d0dd274 Reviewed-on: https://chromium-review.googlesource.com/269413Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tested-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent f229cb9b
...@@ -6,37 +6,50 @@ ...@@ -6,37 +6,50 @@
// DisplayGLX.h: GLX implementation of egl::Display // DisplayGLX.h: GLX implementation of egl::Display
#define GLX_GLXEXT_PROTOTYPES
#include "libANGLE/renderer/gl/glx/DisplayGLX.h" #include "libANGLE/renderer/gl/glx/DisplayGLX.h"
#include <GL/glxext.h>
#include <EGL/eglext.h>
#include <algorithm>
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/Config.h" #include "libANGLE/Config.h"
#include "libANGLE/Display.h" #include "libANGLE/Display.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
namespace rx namespace rx
{ {
class FunctionsGLX : public FunctionsGL class FunctionsGLGLX : public FunctionsGL
{ {
public: public:
FunctionsGLX() FunctionsGLGLX(PFNGLXGETPROCADDRESSPROC getProc)
: mGetProc(getProc)
{ {
} }
virtual ~FunctionsGLX() virtual ~FunctionsGLGLX()
{ {
} }
private: private:
void *loadProcAddress(const std::string &function) override void *loadProcAddress(const std::string &function) override
{ {
return nullptr; return reinterpret_cast<void*>(mGetProc(reinterpret_cast<const unsigned char*>(function.c_str())));
} }
PFNGLXGETPROCADDRESSPROC mGetProc;
}; };
DisplayGLX::DisplayGLX() DisplayGLX::DisplayGLX()
: DisplayGL(), : DisplayGL(),
mFunctionsGL(nullptr) mFunctionsGL(nullptr),
mContext(nullptr),
mDummyPbuffer(0),
mEGLDisplay(nullptr),
mXDisplay(nullptr)
{ {
} }
...@@ -46,16 +59,111 @@ DisplayGLX::~DisplayGLX() ...@@ -46,16 +59,111 @@ DisplayGLX::~DisplayGLX()
egl::Error DisplayGLX::initialize(egl::Display *display) egl::Error DisplayGLX::initialize(egl::Display *display)
{ {
mFunctionsGL = new FunctionsGLX; mEGLDisplay = display;
mXDisplay = display->getNativeDisplayId();
egl::Error glxInitResult = mGLX.initialize(mXDisplay);
if (glxInitResult.isError())
{
return glxInitResult;
}
// Check we have the needed extensions
{
if (mGLX.minorVersion == 3 && !mGLX.hasExtension("GLX_ARB_multisample"))
{
return egl::Error(EGL_NOT_INITIALIZED, "GLX doesn't support ARB_multisample.");
}
// Require ARB_create_context which has been supported since Mesa 9 unconditionnaly
// and is present in Mesa 8 in an almost always on compile flag. Also assume proprietary
// drivers have it.
if (!mGLX.hasExtension("GLX_ARB_create_context"))
{
return egl::Error(EGL_NOT_INITIALIZED, "GLX doesn't support ARB_create_context.");
}
}
GLXFBConfig contextConfig;
// When glXMakeCurrent is called the visual of the context FBConfig and of
// the drawable must match. This means that when generating the list of EGL
// configs, they must all have the same visual id as our unique GL context.
// Here we find a GLX framebuffer config we like to create our GL context
// so that we are sure there is a decent config given back to the application
// when it queries EGL.
{
int nConfigs;
int attribList[] =
{
// We want at least RGBA8 and DEPTH24_STENCIL8
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
// We want RGBA rendering (vs COLOR_INDEX) and doublebuffer
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
// All of these must be supported for full EGL support
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
// This makes sure the config have an associated visual Id
GLX_X_RENDERABLE, True,
GLX_CONFIG_CAVEAT, GLX_NONE,
None
};
GLXFBConfig* candidates = mGLX.chooseFBConfig(mXDisplay, DefaultScreen(mXDisplay), attribList, &nConfigs);
if (nConfigs == 0)
{
XFree(candidates);
return egl::Error(EGL_NOT_INITIALIZED, "Could not find a decent GLX FBConfig to create the context.");
}
contextConfig = candidates[0];
XFree(candidates);
}
mContextVisualId = getGLXFBConfigAttrib(contextConfig, GLX_VISUAL_ID);
mContext = mGLX.createContextAttribsARB(mXDisplay, contextConfig, nullptr, True, nullptr);
if (!mContext)
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context.");
}
// FunctionsGL and DisplayGL need to make a few GL calls, for example to
// query the version of the context so we need to make the context current.
// glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer
// (of size 0, 0) for the duration of these calls.
// TODO(cwallez) error checking here
// TODO(cwallez) during the initialization of ANGLE we need a gl context current
// to query things like limits. Ideally we would want to unset the current context
// and destroy the pbuffer before going back to the application but this is TODO
mDummyPbuffer = mGLX.createPbuffer(mXDisplay, contextConfig, nullptr);
mGLX.makeCurrent(mXDisplay, mDummyPbuffer, mContext);
mFunctionsGL = new FunctionsGLGLX(mGLX.getProc);
mFunctionsGL->initialize(); mFunctionsGL->initialize();
return egl::Error(EGL_SUCCESS); return DisplayGL::initialize(display);
} }
void DisplayGLX::terminate() void DisplayGLX::terminate()
{ {
DisplayGL::terminate(); DisplayGL::terminate();
if (mDummyPbuffer)
{
mGLX.destroyPbuffer(mXDisplay, mDummyPbuffer);
mDummyPbuffer = 0;
}
if (mContext)
{
mGLX.destroyContext(mXDisplay, mContext);
mContext = nullptr;
}
mGLX.terminate();
SafeDelete(mFunctionsGL); SafeDelete(mFunctionsGL);
} }
...@@ -63,13 +171,16 @@ SurfaceImpl *DisplayGLX::createWindowSurface(const egl::Config *configuration, ...@@ -63,13 +171,16 @@ SurfaceImpl *DisplayGLX::createWindowSurface(const egl::Config *configuration,
EGLNativeWindowType window, EGLNativeWindowType window,
const egl::AttributeMap &attribs) const egl::AttributeMap &attribs)
{ {
UNIMPLEMENTED(); ASSERT(configIdToGLXConfig.count(configuration->configID) > 0);
return nullptr; GLXFBConfig fbConfig = configIdToGLXConfig[configuration->configID];
return new WindowSurfaceGLX(mGLX, window, mXDisplay, mContext, fbConfig);
} }
SurfaceImpl *DisplayGLX::createPbufferSurface(const egl::Config *configuration, SurfaceImpl *DisplayGLX::createPbufferSurface(const egl::Config *configuration,
const egl::AttributeMap &attribs) const egl::AttributeMap &attribs)
{ {
//TODO(cwallez) WGL implements it
UNIMPLEMENTED(); UNIMPLEMENTED();
return nullptr; return nullptr;
} }
...@@ -98,20 +209,122 @@ egl::Error DisplayGLX::getDevice(DeviceImpl **device) ...@@ -98,20 +209,122 @@ egl::Error DisplayGLX::getDevice(DeviceImpl **device)
egl::ConfigSet DisplayGLX::generateConfigs() const egl::ConfigSet DisplayGLX::generateConfigs() const
{ {
UNIMPLEMENTED();
egl::ConfigSet configs; egl::ConfigSet configs;
configIdToGLXConfig.clear();
// GLX_EXT_texture_from_pixmap is required for the "bind to rgb(a)" attributes
bool hasTextureFromPixmap = mGLX.hasExtension("GLX_EXT_texture_from_pixmap");
int glxConfigCount;
GLXFBConfig *glxConfigs = mGLX.getFBConfigs(mXDisplay, DefaultScreen(mXDisplay), &glxConfigCount);
for (int i = 0; i < glxConfigCount; i++)
{
GLXFBConfig glxConfig = glxConfigs[i];
egl::Config config;
// Native stuff
int visualId = getGLXFBConfigAttrib(glxConfig, GLX_VISUAL_ID);
if (visualId != mContextVisualId)
{
// Filter out the configs that are incompatible with our GL context
continue;
}
config.nativeVisualID = visualId;
config.nativeVisualType = getGLXFBConfigAttrib(glxConfig, GLX_X_VISUAL_TYPE);
config.nativeRenderable = EGL_TRUE;
// Buffer sizes
config.redSize = getGLXFBConfigAttrib(glxConfig, GLX_RED_SIZE);
config.greenSize = getGLXFBConfigAttrib(glxConfig, GLX_GREEN_SIZE);
config.blueSize = getGLXFBConfigAttrib(glxConfig, GLX_BLUE_SIZE);
config.alphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ALPHA_SIZE);
config.depthSize = getGLXFBConfigAttrib(glxConfig, GLX_DEPTH_SIZE);
config.stencilSize = getGLXFBConfigAttrib(glxConfig, GLX_STENCIL_SIZE);
config.colorBufferType = EGL_RGB_BUFFER;
config.luminanceSize = 0;
config.alphaMaskSize = 0;
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
// Transparency
if (getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_TYPE) == GLX_TRANSPARENT_RGB)
{
config.transparentType = EGL_TRANSPARENT_RGB;
config.transparentRedValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_RED_VALUE);
config.transparentGreenValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_GREEN_VALUE);
config.transparentBlueValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_BLUE_VALUE);
}
else
{
config.transparentType = EGL_NONE;
}
// Pbuffer
config.maxPBufferWidth = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_WIDTH);
config.maxPBufferHeight = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_HEIGHT);
config.maxPBufferPixels = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_PIXELS);
// Caveat
config.configCaveat = EGL_NONE;
int caveat = getGLXFBConfigAttrib(glxConfig, GLX_CONFIG_CAVEAT);
if (caveat == GLX_SLOW_CONFIG)
{
config.configCaveat = EGL_SLOW_CONFIG;
}
else if (caveat == GLX_NON_CONFORMANT_CONFIG)
{
continue;
}
// Misc
config.sampleBuffers = getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS);
config.samples = getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES);
config.level = getGLXFBConfigAttrib(glxConfig, GLX_LEVEL);
config.bindToTextureRGB = EGL_FALSE;
config.bindToTextureRGBA = EGL_FALSE;
if (hasTextureFromPixmap)
{
config.bindToTextureRGB = getGLXFBConfigAttrib(glxConfig, GLX_BIND_TO_TEXTURE_RGB_EXT);
config.bindToTextureRGBA = getGLXFBConfigAttrib(glxConfig, GLX_BIND_TO_TEXTURE_RGBA_EXT);
}
int glxDrawable = getGLXFBConfigAttrib(glxConfig, GLX_DRAWABLE_TYPE);
config.surfaceType = 0 |
(glxDrawable & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0) |
(glxDrawable & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0) |
(glxDrawable & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0);
// In GLX_EXT_swap_control querying these is done on a GLXWindow so we just set a default value.
config.maxSwapInterval = 1;
config.minSwapInterval = 1;
// TODO(cwallez) wildly guessing these formats, another TODO says they should be removed anyway
config.renderTargetFormat = GL_RGBA8;
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
// TODO(cwallez) Fill after determining the GL version we are using and what ES version it supports
config.conformant = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR;
config.renderableType = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR;
// TODO(cwallez) I have no idea what this is
config.matchNativePixmap = EGL_NONE;
int id = configs.add(config);
configIdToGLXConfig[id] = glxConfig;
}
return configs; return configs;
} }
bool DisplayGLX::isDeviceLost() const bool DisplayGLX::isDeviceLost() const
{ {
UNIMPLEMENTED(); // UNIMPLEMENTED();
return false; return false;
} }
bool DisplayGLX::testDeviceLost() bool DisplayGLX::testDeviceLost()
{ {
UNIMPLEMENTED(); // UNIMPLEMENTED();
return false; return false;
} }
...@@ -123,30 +336,53 @@ egl::Error DisplayGLX::restoreLostDevice() ...@@ -123,30 +336,53 @@ egl::Error DisplayGLX::restoreLostDevice()
bool DisplayGLX::isValidNativeWindow(EGLNativeWindowType window) const bool DisplayGLX::isValidNativeWindow(EGLNativeWindowType window) const
{ {
UNIMPLEMENTED(); // There is no function in Xlib to check the validity of a Window directly.
return true; // However a small number of functions used to obtain window information
// return a status code (0 meaning failure) and guarantee that they will
// fail if the window doesn't exist (the rational is that these function
// are used by window managers). Out of these function we use XQueryTree
// as it seems to be the simplest; a drawback is that it will allocate
// memory for the list of children, becasue we use a child window for
// WindowSurface.
Window root;
Window parent;
Window *children = nullptr;
unsigned nChildren;
int status = XQueryTree(mXDisplay, window, &root, &parent, &children, &nChildren);
if (children)
{
XFree(children);
}
return status != 0;
} }
std::string DisplayGLX::getVendorString() const std::string DisplayGLX::getVendorString() const
{ {
UNIMPLEMENTED(); // UNIMPLEMENTED();
return ""; return "";
} }
const FunctionsGL *DisplayGLX::getFunctionsGL() const const FunctionsGL *DisplayGLX::getFunctionsGL() const
{ {
UNIMPLEMENTED();
return mFunctionsGL; return mFunctionsGL;
} }
void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const
{ {
UNIMPLEMENTED(); // UNIMPLEMENTED();
} }
void DisplayGLX::generateCaps(egl::Caps *outCaps) const void DisplayGLX::generateCaps(egl::Caps *outCaps) const
{ {
UNIMPLEMENTED(); // UNIMPLEMENTED();
outCaps->textureNPOT = true;
}
int DisplayGLX::getGLXFBConfigAttrib(GLXFBConfig config, int attrib) const
{
int result;
mGLX.getFBConfigAttrib(mXDisplay, config, attrib, &result);
return result;
} }
} }
...@@ -9,7 +9,11 @@ ...@@ -9,7 +9,11 @@
#ifndef LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_ #ifndef LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_
#define LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_ #define LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_
#include <string>
#include <vector>
#include "libANGLE/renderer/gl/DisplayGL.h" #include "libANGLE/renderer/gl/DisplayGL.h"
#include "libANGLE/renderer/gl/glx/FunctionsGLX.h"
namespace rx namespace rx
{ {
...@@ -55,7 +59,22 @@ class DisplayGLX : public DisplayGL ...@@ -55,7 +59,22 @@ class DisplayGLX : public DisplayGL
void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
void generateCaps(egl::Caps *outCaps) const override; void generateCaps(egl::Caps *outCaps) const override;
int getGLXFBConfigAttrib(GLXFBConfig config, int attrib) const;
FunctionsGL *mFunctionsGL; FunctionsGL *mFunctionsGL;
//TODO(cwallez) yuck, change generateConfigs to be non-const or add a userdata member to egl::Config?
mutable std::map<int, GLXFBConfig> configIdToGLXConfig;
// The ID of the visual used to create the context
int mContextVisualId;
GLXContext mContext;
// A pbuffer the context is current on during ANGLE initialization
GLXPbuffer mDummyPbuffer;
FunctionsGLX mGLX;
egl::Display *mEGLDisplay;
Display *mXDisplay;
}; };
} }
......
...@@ -10,33 +10,97 @@ ...@@ -10,33 +10,97 @@
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/renderer/gl/glx/FunctionsGLX.h"
namespace rx namespace rx
{ {
WindowSurfaceGLX::WindowSurfaceGLX() WindowSurfaceGLX::WindowSurfaceGLX(const FunctionsGLX &glx, EGLNativeWindowType window, Display* display, GLXContext context, GLXFBConfig fbConfig)
: SurfaceGL() : SurfaceGL(),
mGLX(glx),
mParent(window),
mDisplay(display),
mContext(context),
mFBConfig(fbConfig),
mWindow(0),
mGLXWindow(0)
{ {
} }
WindowSurfaceGLX::~WindowSurfaceGLX() WindowSurfaceGLX::~WindowSurfaceGLX()
{ {
if (mWindow)
{
XDestroyWindow(mDisplay, mWindow);
}
if (mGLXWindow)
{
mGLX.destroyWindow(mDisplay, mGLXWindow);
}
} }
egl::Error WindowSurfaceGLX::initialize() egl::Error WindowSurfaceGLX::initialize()
{ {
UNIMPLEMENTED(); // The visual of the X window, GLX window and GLX context must match,
// however we received a user-created window that can have any visual
// and wouldn't work with our GLX context. To work in all cases, we
// create a child window with the right visual that covers all of its
// parent.
XVisualInfo* visualInfo = mGLX.getVisualFromFBConfig(mDisplay, mFBConfig);
if (!visualInfo)
{
return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to get the XVisualInfo for the child window.");
}
Visual* visual = visualInfo->visual;
XWindowAttributes parentAttribs;
XGetWindowAttributes(mDisplay, mParent, &parentAttribs);
// The depth, colormap and visual must match otherwise we get a X error
// so we specify the colormap attribute. Also we do not want the window
// to be taken into account for input so we specify the event and
// do-not-propagate masks to 0 (the defaults).
XSetWindowAttributes attributes;
unsigned long attributeMask = CWColormap;
Colormap colormap = XCreateColormap(mDisplay, mParent, visual, AllocNone);
if(!colormap)
{
XFree(visualInfo);
return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to create the Colormap for the child window.");
}
attributes.colormap = colormap;
//TODO(cwallez) set up our own error handler to see if the call failed
mWindow = XCreateWindow(mDisplay, mParent, 0, 0, parentAttribs.width, parentAttribs.height,
0, visualInfo->depth, InputOutput, visual, attributeMask, &attributes);
mGLXWindow = mGLX.createWindow(mDisplay, mFBConfig, mWindow, nullptr);
XMapWindow(mDisplay, mWindow);
XFlush(mDisplay);
XFree(visualInfo);
XFreeColormap(mDisplay, colormap);
return egl::Error(EGL_SUCCESS); return egl::Error(EGL_SUCCESS);
} }
egl::Error WindowSurfaceGLX::makeCurrent() egl::Error WindowSurfaceGLX::makeCurrent()
{ {
UNIMPLEMENTED(); if (mGLX.makeCurrent(mDisplay, mGLXWindow, mContext) != True)
{
return egl::Error(EGL_BAD_DISPLAY);
}
return egl::Error(EGL_SUCCESS); return egl::Error(EGL_SUCCESS);
} }
egl::Error WindowSurfaceGLX::swap() egl::Error WindowSurfaceGLX::swap()
{ {
UNIMPLEMENTED(); //TODO(cwallez) resize support
//TODO(cwallez) set up our own error handler to see if the call failed
mGLX.swapBuffers(mDisplay, mGLXWindow);
return egl::Error(EGL_SUCCESS); return egl::Error(EGL_SUCCESS);
} }
...@@ -66,19 +130,31 @@ egl::Error WindowSurfaceGLX::releaseTexImage(EGLint buffer) ...@@ -66,19 +130,31 @@ egl::Error WindowSurfaceGLX::releaseTexImage(EGLint buffer)
void WindowSurfaceGLX::setSwapInterval(EGLint interval) void WindowSurfaceGLX::setSwapInterval(EGLint interval)
{ {
UNIMPLEMENTED(); // TODO(cwallez) WGL has this, implement it
} }
EGLint WindowSurfaceGLX::getWidth() const EGLint WindowSurfaceGLX::getWidth() const
{ {
UNIMPLEMENTED(); Window root;
return 0; int x, y;
unsigned width, height, border, depth;
if (!XGetGeometry(mDisplay, mParent, &root, &x, &y, &width, &height, &border, &depth))
{
return 0;
}
return width;
} }
EGLint WindowSurfaceGLX::getHeight() const EGLint WindowSurfaceGLX::getHeight() const
{ {
UNIMPLEMENTED(); Window root;
return 0; int x, y;
unsigned width, height, border, depth;
if (!XGetGeometry(mDisplay, mParent, &root, &x, &y, &width, &height, &border, &depth))
{
return 0;
}
return height;
} }
EGLint WindowSurfaceGLX::isPostSubBufferSupported() const EGLint WindowSurfaceGLX::isPostSubBufferSupported() const
......
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
namespace rx namespace rx
{ {
class FunctionsGLX;
class WindowSurfaceGLX : public SurfaceGL class WindowSurfaceGLX : public SurfaceGL
{ {
public: public:
WindowSurfaceGLX(); WindowSurfaceGLX(const FunctionsGLX &glx, Window window, Display* display, GLXContext context, GLXFBConfig fbConfig);
~WindowSurfaceGLX() override; ~WindowSurfaceGLX() override;
egl::Error initialize(); egl::Error initialize();
...@@ -35,6 +37,14 @@ class WindowSurfaceGLX : public SurfaceGL ...@@ -35,6 +37,14 @@ class WindowSurfaceGLX : public SurfaceGL
EGLint getHeight() const override; EGLint getHeight() const override;
EGLint isPostSubBufferSupported() const override; EGLint isPostSubBufferSupported() const override;
private:
const FunctionsGLX &mGLX;
Window mParent;
Display *mDisplay;
GLXContext mContext;
GLXFBConfig mFBConfig;
Window mWindow;
GLXWindow mGLXWindow;
}; };
} }
......
...@@ -15,4 +15,8 @@ ...@@ -15,4 +15,8 @@
#include <GL/glx.h> #include <GL/glx.h>
#undef __glext_h_ #undef __glext_h_
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/Xutil.h>
#endif // LIBANGLE_RENDERER_GL_GLX_PLATFORMGLX_H_ #endif // LIBANGLE_RENDERER_GL_GLX_PLATFORMGLX_H_
...@@ -742,6 +742,17 @@ ...@@ -742,6 +742,17 @@
{ {
'msvs_enable_winphone' : '1', 'msvs_enable_winphone' : '1',
}], }],
['OS=="linux"',
{
'link_settings': {
'ldflags': [
'<!@(pkg-config --libs-only-L --libs-only-other x11 xi)',
],
'libraries': [
'<!@(pkg-config --libs-only-l x11 xi)',
],
},
}],
], ],
}, },
], ],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment