text: /*
   *  gles2_help.c - gles2_help.c - utility to set up gles2 drawing context
   *
   *  Copyright (C) 2014 Intel Corporation
   *    Author: Zhao, Halley<halley.zhao@intel.com>
   *
   *  This library is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Lesser General Public License
   *  as published by the Free Software Foundation; either version 2.1
   *  of the License, or (at your option) any later version.
   *
   *  This library is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free
   *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   *  Boston, MA 02110-1301 USA
   */
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif
  
  #include "gles2_help.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <assert.h>
  
  #ifndef USE_INTERNAL_LOG
  #include "common/log.h"
  #else
  #define ERROR printf
  #define INFO printf
  #define DEBUG printf
  #define ASSERT assert
  #endif
  
  #define EGL_CHECK_RESULT_RET(result, promptStr, ret) do {   \
      if (result != EGL_TRUE) {                               \
          ERROR("%s failed", promptStr);                                         \
          goto error;                                          \
      }                                                       \
  }while(0)
  #define CHECK_HANDLE_RET(handle, invalid, promptStr, ret) do {  \
      if (handle == invalid) {                                    \
          ERROR("%s failed", promptStr);                                                   \
          goto error;                                              \
      }                                                           \
  } while(0)
  
  static const char *fragShaderText_rgba =
    "precision mediump float;\n"
    "uniform sampler2D tex0;\n"
    "varying vec2 v_texcoord;\n"
    "void main() {\n"
    "   gl_FragColor = texture2D(tex0, v_texcoord);\n"
    "   gl_FragColor.a = 1.0;\n"
    "}\n";
  static const char *fragShaderText_rgba_ext =
    "#extension GL_OES_EGL_image_external : require\n"
    "precision mediump float;\n"
    "uniform samplerExternalOES tex0;\n"
    "varying vec2 v_texcoord;\n"
    "void main() {\n"
    "   gl_FragColor = texture2D(tex0, v_texcoord);\n"
    "   gl_FragColor.a = 1.0;\n"
    "}\n";
  
  static const char *vertexShaderText_rgba =
    "attribute vec4 pos;\n"
    "attribute vec2 texcoord;\n"
    "varying vec2 v_texcoord;\n"
    "void main() {\n"
    "   gl_Position = pos;\n"
    "   v_texcoord  = texcoord;\n"
    "}\n";
  
  static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DProc = NULL;
  
  void imageTargetTexture2D(EGLenum target, EGLImageKHR image)
  {
      if (!imageTargetTexture2DProc) {
          imageTargetTexture2DProc =
              (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
      }
      imageTargetTexture2DProc(target, image);
  }
  
  GLuint
  createTextureFromPixmap(EGLContextType *context, XID pixmap)
  {
      GLuint textureId;
  
      EGLImageKHR image = createImage(context->eglContext.display, context->eglContext.context, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)pixmap, NULL);
      glGenTextures(1, &textureId );
      glBindTexture(GL_TEXTURE_2D, textureId);
      imageTargetTexture2D(GL_TEXTURE_2D, image);
  
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      return textureId;
  }
  
  static GLProgram*
  createShaders(const char *vertexShaderText, const char *fragShaderText, int texCount)
  {
      GLProgram *glProgram = NULL;
      GLint stat;
      #define BUFFER_SIZE 256
      char log[BUFFER_SIZE];
      GLsizei logSize;
  
      glProgram = calloc(1, sizeof(GLProgram));
      if (!glProgram)
          return NULL;
  
      glProgram->vertexShader = glCreateShader(GL_VERTEX_SHADER);
      glShaderSource(glProgram->vertexShader, 1, (const char **) &vertexShaderText, NULL);
      glCompileShader(glProgram->vertexShader);
      glGetShaderiv(glProgram->vertexShader, GL_COMPILE_STATUS, &stat);
      if (!stat) {
          glGetShaderInfoLog(glProgram->vertexShader, BUFFER_SIZE, &logSize, log);
          ERROR(" vertex shader fail to compile!: %s", log);
          free(glProgram);
          return NULL;
      }
  
      glProgram->fragShader = glCreateShader(GL_FRAGMENT_SHADER);
      glShaderSource(glProgram->fragShader, 1, (const char **) &fragShaderText, NULL);
      glCompileShader(glProgram->fragShader);
      glGetShaderiv(glProgram->fragShader, GL_COMPILE_STATUS, &stat);
      if (!stat) {
          glGetShaderInfoLog(glProgram->fragShader, BUFFER_SIZE, &logSize, log);
          ERROR(" fragment shader fail to compile!: %s", log);
          free(glProgram);
          return NULL;
      }
  
      glProgram->program = glCreateProgram();
      glAttachShader(glProgram->program, glProgram->fragShader);
      glAttachShader(glProgram->program, glProgram->vertexShader);
      glLinkProgram(glProgram->program);
  
      glGetProgramiv(glProgram->program, GL_LINK_STATUS, &stat);
      if (!stat) {
         glGetProgramInfoLog(glProgram->program, BUFFER_SIZE, &logSize, log);
         printf("Shader fail to link!: %s\n", log);
         free(glProgram);
         return NULL;
      }
  
      glUseProgram(glProgram->program);
  
      glProgram->texCount = texCount;
      glProgram->attrPosition = glGetAttribLocation(glProgram->program, "pos");
      glProgram->attrTexCoord = glGetAttribLocation(glProgram->program, "texcoord");
      glProgram->uniformTex[0] = glGetUniformLocation(glProgram->program, "tex0");
      // glProgram->uniformTex[1] = glGetUniformLocation(glProgram->program, "tex1");
      // glProgram->uniformTex[2] = glGetUniformLocation(glProgram->program, "tex2");
  
      INFO("Attrib pos at %d\n", glProgram->attrPosition);
      INFO("Attrib texcoord at %d\n", glProgram->attrTexCoord);
      INFO("texture (0, %d), (1, %d), (2, %d) \n", glProgram->uniformTex[0], glProgram->uniformTex[1], glProgram->uniformTex[2]);
      return glProgram;
  }
  
  static void
  releaseShader(GLProgram *program)
  {
      if (!program)
          return;
  
      if (program->program)
          glDeleteProgram(program->program);
      if (program->fragShader)
          glDeleteShader(program->fragShader);
      if (program->vertexShader)
          glDeleteShader(program->vertexShader);
      free(program);
  }
  
  #define MAX_RECT_SIZE 100
  #define MIN_RECT_SIZE 10
  
  int
  drawTextures(EGLContextType *context, GLenum target, GLuint *textureIds, int texCount)
  {
      unsigned int i;
      static int rectSize1 = MAX_RECT_SIZE;
      static int rectSize2 = MIN_RECT_SIZE;
      static int direction = 1;
      const GLfloat positions[4][2] = {
          { -rectSize1/100.f, -rectSize1/100.f},
          {  rectSize2/100.f, -rectSize2/100.f},
          {  rectSize1/100.f,  rectSize1/100.f},
          { -rectSize2/100.f,  rectSize2/100.f}
      };
      static const GLfloat texcoords[4][2] = {
          {  0.0f,  1.0f },
          {  1.0f,  1.0f },
          {  1.0f,  0.0f },
          {  0.0f,  0.0f }
      };
  
      if (direction) {
          rectSize1--;
          rectSize2++;
      } else {
          rectSize1++;
          rectSize2--;
      }
  
      if (rectSize1 < 10 || rectSize2 > 100) {
          rectSize1 = 10;
          rectSize2 = 100;
          direction = 0;
      }
      if (rectSize1 > 100 || rectSize2 < 10) {
          rectSize1 = 100;
          rectSize2 = 10;
          direction = 1;
      }
  
  
      if (!context)
          return -1;
      GLProgram *glProgram = context->glProgram;
  
      ASSERT(texCount == glProgram->texCount);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
      glUseProgram(glProgram->program);
      // glUniformMatrix4fv(program->proj_uniform, 1, GL_FALSE, egl->proj);
      glEnableVertexAttribArray(glProgram->attrPosition);
      glVertexAttribPointer(glProgram->attrPosition, 2, GL_FLOAT, GL_FALSE, 0, positions);
      glEnableVertexAttribArray(glProgram->attrTexCoord);
      glVertexAttribPointer(glProgram->attrTexCoord, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
  
      for (i = 0; i < glProgram->texCount; i++) {
          glActiveTexture(GL_TEXTURE0 + i);
          glBindTexture(target, textureIds[i]);
          glUniform1i(glProgram->uniformTex[i], i);
      }
      glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  
      glDisableVertexAttribArray(1);
      glDisableVertexAttribArray(0);
      glUseProgram(0);
  
      eglSwapBuffers(context->eglContext.display, context->eglContext.surface);
      int glError = glGetError();
      if (glError != GL_NO_ERROR)
          return glError;
  
      return 0;
  }
  
  EGLContextType *eglInit(Display *x11Display, XID x11Window, uint32_t fourcc, int isExternalTexture)
  {
      EGLContextType *context = NULL;
      GLProgram *glProgram = NULL;
  
      context = calloc(1, sizeof(EGLContextType));
      if (!context)
          return NULL;
      EGLDisplay eglDisplay = eglGetDisplay(x11Display);
      if (eglDisplay == EGL_NO_DISPLAY) {
          ERROR("eglGetDisplay fail");
      }
      CHECK_HANDLE_RET(eglDisplay, EGL_NO_DISPLAY, "eglGetDisplay", NULL);
      context->eglContext.display = eglDisplay;
  
      EGLint major, minor;
      EGLBoolean result = eglInitialize(eglDisplay, &major, &minor);
      if (result != EGL_TRUE) {
          ERROR("eglInitialize fail");
      }
      EGL_CHECK_RESULT_RET(result, "eglInitialize", NULL);
  
      // eglBindAPI(EGL_OPENGL_ES_API); // gles is the default one
  
      EGLint const eglConfigAttribs[] = {
           EGL_RED_SIZE, 8,
           EGL_GREEN_SIZE, 8,
           EGL_BLUE_SIZE, 8,
           EGL_ALPHA_SIZE, 8,
           EGL_DEPTH_SIZE, 8,
           EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
           EGL_NONE
      };
      EGLConfig eglConfig;
      EGLint eglConfigCount;
      result = eglChooseConfig(eglDisplay, eglConfigAttribs, &eglConfig, 1, &eglConfigCount);
      EGL_CHECK_RESULT_RET(result, "eglChooseConfig", NULL);
      context->eglContext.config = eglConfig;
  
      EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, x11Window, NULL);
      CHECK_HANDLE_RET(eglSurface, EGL_NO_SURFACE, "eglCreateWindowSurface", NULL);
      context->eglContext.surface = eglSurface;
  
      EGLint const eglContextAttribs[] = {
          EGL_CONTEXT_CLIENT_VERSION, 2,
          EGL_NONE
      };
      EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, eglContextAttribs);
      CHECK_HANDLE_RET(eglContext, EGL_NO_CONTEXT, "eglCreateContext", NULL);
      context->eglContext.context = eglContext;
  
      result = eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
      EGL_CHECK_RESULT_RET(result, "eglMakeCurrent", NULL);
  
      INFO("Runing GL version: %s, please make sure it support GL 2.0 API", glGetString(GL_VERSION));
  
      // clear to middle blue
      glClearColor(0.0, 0.0, 0.5, 0.0);
      glEnable(GL_DEPTH_TEST);
      glClearDepthf(1.0f);
      {
          unsigned int width=800, height=600;
  #ifdef __ENABLE_X11__
          Window root;
          int x, y;
          unsigned int borderWidth, depth;
          XGetGeometry(x11Display, x11Window, &root, &x, &y, &width, &height, &borderWidth, &depth);
  #endif
          glViewport(0, 0, width, height);
      }
      if (isExternalTexture)
          glProgram = createShaders(vertexShaderText_rgba, fragShaderText_rgba_ext, 1);
      else
          glProgram = createShaders(vertexShaderText_rgba, fragShaderText_rgba, 1);
      CHECK_HANDLE_RET(glProgram, NULL, "createShaders", NULL);
      context->glProgram = glProgram;
  
      return context;
  error:
      if (context)
          free(context);
      return NULL;
  }
  
  void eglRelease(EGLContextType *context)
  {
      if (!context)
          return;
  
      releaseShader(context->glProgram);
      eglMakeCurrent(context->eglContext.display, NULL, NULL, NULL);
      eglDestroySurface(context->eglContext.display, context->eglContext.surface);
      eglDestroyContext(context->eglContext.display, context->eglContext.context);
      eglTerminate(context->eglContext.display);
      free(context);
  }
  
  