//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// FunctionsEGLDL.cpp: Implements the FunctionsEGLDL class.

#include "libANGLE/renderer/gl/egl/FunctionsEGLDL.h"

#include <dlfcn.h>

namespace rx
{
namespace
{
// In ideal world, we would want this to be a member of FunctionsEGLDL,
// and call dlclose() on it in ~FunctionsEGLDL().
// However, some GL implementations are broken and don't allow multiple
// load/unload cycles, but only static linking with them.
// That's why we dlopen() this handle once and never dlclose() it.
// This is consistent with Chromium's CleanupNativeLibraries() code,
// referencing crbug.com/250813 and http://www.xfree86.org/4.3.0/DRI11.html
void *nativeEGLHandle;
}  // anonymous namespace

FunctionsEGLDL::FunctionsEGLDL() : mGetProcAddressPtr(nullptr) {}

FunctionsEGLDL::~FunctionsEGLDL() {}

egl::Error FunctionsEGLDL::initialize(EGLNativeDisplayType nativeDisplay,
                                      const char *libName,
                                      void *eglHandle)
{

    if (eglHandle)
    {
        // If the handle is provided, use it.
        // Caller has already dlopened the vendor library.
        nativeEGLHandle = eglHandle;
    }

    if (!nativeEGLHandle)
    {
        nativeEGLHandle = dlopen(libName, RTLD_NOW);
        if (!nativeEGLHandle)
        {
            return egl::EglNotInitialized() << "Could not dlopen native EGL: " << dlerror();
        }
    }

    mGetProcAddressPtr =
        reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(dlsym(nativeEGLHandle, "eglGetProcAddress"));
    if (!mGetProcAddressPtr)
    {
        return egl::EglNotInitialized() << "Could not find eglGetProcAddress";
    }

    return FunctionsEGL::initialize(nativeDisplay);
}

void *FunctionsEGLDL::getProcAddress(const char *name) const
{
    void *f = reinterpret_cast<void *>(mGetProcAddressPtr(name));
    if (f)
    {
        return f;
    }
    return dlsym(nativeEGLHandle, name);
}

}  // namespace rx
