Browse Source

Android support, part 2 (display module)

rdb 13 years ago
parent
commit
0631441b20

+ 1 - 1
panda/metalibs/pandagles/Sources.pp

@@ -9,7 +9,7 @@
 #define BUILD_DIRECTORY $[HAVE_GLES]
 
 #define COMPONENT_LIBS \
-    p3glesgsg p3egldisplay
+    p3glesgsg p3egldisplay p3androiddisplay
 
 #define LOCAL_LIBS p3gsgbase p3display p3express
 #define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \

+ 11 - 4
panda/metalibs/pandagles/pandagles.cxx

@@ -1,5 +1,5 @@
 // Filename: pandagles.cxx
-// Created by:  pro-rsoft (8Jun09)
+// Created by:  rdb (8Jun09)
 //
 ////////////////////////////////////////////////////////////////////
 
@@ -8,7 +8,10 @@
 #define OPENGLES_1
 #include "config_glesgsg.h"
 
-#ifdef HAVE_EGL
+#if defined(ANDROID)
+#include "config_androiddisplay.h"
+#include "androidGraphicsPipe.h"
+#elif defined(HAVE_EGL)
 #include "config_egldisplay.h"
 #include "eglGraphicsPipe.h"
 #endif
@@ -31,7 +34,9 @@ void
 init_libpandagles() {
   init_libglesgsg();
 
-#ifdef HAVE_EGL
+#if defined(ANDROID)
+  init_libandroiddisplay();
+#elif defined(HAVE_EGL)
   init_libegldisplay();
 #endif
 }
@@ -43,7 +48,9 @@ init_libpandagles() {
 ////////////////////////////////////////////////////////////////////
 int
 get_pipe_type_pandagles() {
-#ifdef HAVE_EGL
+#if defined(ANDROID)
+  return AndroidGraphicsPipe::get_class_type().get_index();
+#elif defined(HAVE_EGL)
   return eglGraphicsPipe::get_class_type().get_index();
 #endif
 

+ 14 - 0
panda/src/androiddisplay/androidGraphicsPipe.I

@@ -0,0 +1,14 @@
+// Filename: androidGraphicsPipe.I
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 233 - 0
panda/src/androiddisplay/androidGraphicsPipe.cxx

@@ -0,0 +1,233 @@
+// Filename: androidGraphicsPipe.cxx
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+//#include "androidGraphicsBuffer.h"
+#include "androidGraphicsPipe.h"
+//#include "androidGraphicsPixmap.h"
+#include "androidGraphicsWindow.h"
+#include "androidGraphicsStateGuardian.h"
+#include "config_androiddisplay.h"
+#include "frameBufferProperties.h"
+
+TypeHandle AndroidGraphicsPipe::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsPipe::
+AndroidGraphicsPipe() {
+  _is_valid = false;
+  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
+  _egl_display = NULL;
+
+  _display_width = 0;
+  _display_height = 0;
+
+  _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (!eglInitialize(_egl_display, NULL, NULL)) {
+    androiddisplay_cat.error()
+      << "Couldn't initialize the EGL display: "
+      << get_egl_error_string(eglGetError()) << "\n";
+  }
+
+  if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+    androiddisplay_cat.error()
+      << "Couldn't bind EGL to the OpenGL ES API: "
+      << get_egl_error_string(eglGetError()) << "\n";
+  }
+
+  _is_valid = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsPipe::
+~AndroidGraphicsPipe() {
+  if (_egl_display) {
+    if (!eglTerminate(_egl_display)) {
+      androiddisplay_cat.error() << "Failed to terminate EGL display: "
+        << get_egl_error_string(eglGetError()) << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::get_interface_name
+//       Access: Published, Virtual
+//  Description: Returns the name of the rendering interface
+//               associated with this GraphicsPipe.  This is used to
+//               present to the user to allow him/her to choose
+//               between several possible GraphicsPipes available on a
+//               particular platform, so the name should be meaningful
+//               and unique for a given platform.
+////////////////////////////////////////////////////////////////////
+string AndroidGraphicsPipe::
+get_interface_name() const {
+  return "OpenGL ES";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::pipe_constructor
+//       Access: Public, Static
+//  Description: This function is passed to the GraphicsPipeSelection
+//               object to allow the user to make a default
+//               AndroidGraphicsPipe.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsPipe) AndroidGraphicsPipe::
+pipe_constructor() {
+  return new AndroidGraphicsPipe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::get_preferred_window_thread
+//       Access: Public, Virtual
+//  Description: Returns an indication of the thread in which this
+//               GraphicsPipe requires its window processing to be
+//               performed: typically either the app thread (e.g. X)
+//               or the draw thread (Windows).
+////////////////////////////////////////////////////////////////////
+GraphicsPipe::PreferredWindowThread
+AndroidGraphicsPipe::get_preferred_window_thread() const {
+  // Most of the Android NDK window functions can be
+  // called from any thread.  Since we're creating the
+  // context at open_window time, let's choose "draw".
+  return PWT_app;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsPipe::make_output
+//       Access: Protected, Virtual
+//  Description: Creates a new window on the pipe, if possible.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsOutput) AndroidGraphicsPipe::
+make_output(const string &name,
+            const FrameBufferProperties &fb_prop,
+            const WindowProperties &win_prop,
+            int flags,
+            GraphicsEngine *engine,
+            GraphicsStateGuardian *gsg,
+            GraphicsOutput *host,
+            int retry,
+            bool &precertify) {
+
+  if (!_is_valid) {
+    return NULL;
+  }
+
+  AndroidGraphicsStateGuardian *androidgsg = 0;
+  if (gsg != 0) {
+    DCAST_INTO_R(androidgsg, gsg, NULL);
+  }
+
+  // First thing to try: an eglGraphicsWindow
+
+  if (retry == 0) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_refuse_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_rtt_cumulative)!=0)||
+        ((flags&BF_can_bind_color)!=0)||
+        ((flags&BF_can_bind_every)!=0)) {
+      return NULL;
+    }
+    return new AndroidGraphicsWindow(engine, this, name, fb_prop, win_prop,
+                                     flags, gsg, host);
+  }
+
+  // Second thing to try: a GLES(2)GraphicsBuffer
+  /*if (retry == 1) {
+    if ((host==0)||
+  //        (!gl_support_fbo)||
+        ((flags&BF_require_parasite)!=0)||
+        ((flags&BF_require_window)!=0)) {
+      return NULL;
+    }
+    // Early failure - if we are sure that this buffer WONT
+    // meet specs, we can bail out early.
+    if ((flags & BF_fb_props_optional)==0) {
+      if ((fb_prop.get_indexed_color() > 0)||
+          (fb_prop.get_back_buffers() > 0)||
+          (fb_prop.get_accum_bits() > 0)||
+          (fb_prop.get_multisamples() > 0)) {
+        return NULL;
+      }
+    }
+    // Early success - if we are sure that this buffer WILL
+    // meet specs, we can precertify it.
+    if ((eglgsg != 0) &&
+        (eglgsg->is_valid()) &&
+        (!eglgsg->needs_reset()) &&
+        (eglgsg->_supports_framebuffer_object) &&
+        (eglgsg->_glDrawBuffers != 0)&&
+        (fb_prop.is_basic())) {
+      precertify = true;
+    }
+#ifdef OPENGLES_2
+    return new GLES2GraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                  flags, gsg, host);
+#else
+    return new GLESGraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                  flags, gsg, host);
+#endif
+  }
+
+  // Third thing to try: a eglGraphicsBuffer
+  if (retry == 2) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_require_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)) {
+      return NULL;
+    }
+
+    if (!support_rtt) {
+      if (((flags&BF_rtt_cumulative)!=0)||
+          ((flags&BF_can_bind_every)!=0)) {
+        // If we require Render-to-Texture, but can't be sure we
+        // support it, bail.
+        return NULL;
+      }
+    }
+
+    return new eglGraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                 flags, gsg, host);
+  }
+
+  // Fourth thing to try: an eglGraphicsPixmap.
+  if (retry == 3) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_require_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)) {
+      return NULL;
+    }
+
+    if (((flags&BF_rtt_cumulative)!=0)||
+        ((flags&BF_can_bind_every)!=0)) {
+      return NULL;
+    }
+
+    return new eglGraphicsPixmap(engine, this, name, fb_prop, win_prop,
+                                 flags, gsg, host);
+  }*/
+
+  // Nothing else left to try.
+  return NULL;
+}

+ 94 - 0
panda/src/androiddisplay/androidGraphicsPipe.h

@@ -0,0 +1,94 @@
+// Filename: androidGraphicsPipe.h
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef ANDROIDGRAPHICSPIPE_H
+#define ANDROIDGRAPHICSPIPE_H
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+#include "graphicsPipe.h"
+
+#ifdef OPENGLES_2
+  #include "gles2gsg.h"
+//  #define NativeDisplayType EGLNativeDisplayType
+//  #define NativePixmapType EGLNativePixmapType
+//  #define NativeWindowType EGLNativeWindowType
+#else
+  #include "glesgsg.h"
+#endif
+#include <EGL/egl.h>
+
+class FrameBufferProperties;
+
+class AndroidGraphicsBuffer;
+class AndroidGraphicsPixmap;
+class AndroidGraphicsWindow;
+
+////////////////////////////////////////////////////////////////////
+//       Class : AndroidGraphicsPipe
+// Description : This graphics pipe represents the interface for
+//               creating OpenGL ES graphics windows on an X-based
+//               (e.g. Unix) client.
+////////////////////////////////////////////////////////////////////
+class AndroidGraphicsPipe : public GraphicsPipe {
+public:
+  AndroidGraphicsPipe();
+  virtual ~AndroidGraphicsPipe();
+
+  virtual string get_interface_name() const;
+  static PT(GraphicsPipe) pipe_constructor();
+
+public:
+  virtual PreferredWindowThread get_preferred_window_thread() const;
+
+protected:
+  virtual PT(GraphicsOutput) make_output(const string &name,
+                                         const FrameBufferProperties &fb_prop,
+                                         const WindowProperties &win_prop,
+                                         int flags,
+                                         GraphicsEngine *engine,
+                                         GraphicsStateGuardian *gsg,
+                                         GraphicsOutput *host,
+                                         int retry,
+                                         bool &precertify);
+
+private:
+  EGLDisplay _egl_display;
+  GraphicsWindow *_window;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsPipe::init_type();
+    register_type(_type_handle, "AndroidGraphicsPipe",
+                  GraphicsPipe::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class AndroidGraphicsBuffer;
+  friend class AndroidGraphicsPixmap;
+  friend class AndroidGraphicsWindow;
+};
+
+#include "androidGraphicsPipe.I"
+
+#endif

+ 25 - 0
panda/src/androiddisplay/androidGraphicsStateGuardian.I

@@ -0,0 +1,25 @@
+// Filename: androidGraphicsStateGuardian.I
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::get_fb_properties
+//       Access: Private
+//  Description: Gets the FrameBufferProperties for all windows and
+//               buffers that use this GSG.
+////////////////////////////////////////////////////////////////////
+INLINE const FrameBufferProperties &AndroidGraphicsStateGuardian::
+get_fb_properties() const {
+  return _fbprops;
+}

+ 403 - 0
panda/src/androiddisplay/androidGraphicsStateGuardian.cxx

@@ -0,0 +1,403 @@
+// Filename: androidGraphicsStateGuardian.cxx
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "androidGraphicsStateGuardian.h"
+#include "config_androiddisplay.h"
+#include "lightReMutexHolder.h"
+
+#include <dlfcn.h>
+
+TypeHandle AndroidGraphicsStateGuardian::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsStateGuardian::
+AndroidGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
+       AndroidGraphicsStateGuardian *share_with) :
+#ifdef OPENGLES_2
+  GLES2GraphicsStateGuardian(engine, pipe)
+#else
+  GLESGraphicsStateGuardian(engine, pipe)
+#endif
+{
+  _share_context = 0;
+  _context = 0;
+  _egl_display = 0;
+  _fbconfig = 0;
+  _format = 0;
+
+  if (share_with != (AndroidGraphicsStateGuardian *)NULL) {
+    _prepared_objects = share_with->get_prepared_objects();
+    _share_context = share_with->_context;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsStateGuardian::
+~AndroidGraphicsStateGuardian() {
+  if (_context != (EGLContext)NULL) {
+    if (!eglDestroyContext(_egl_display, _context)) {
+      androiddisplay_cat.error() << "Failed to destroy EGL context: "
+        << get_egl_error_string(eglGetError()) << "\n";
+    }
+    _context = (EGLContext)NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::get_properties
+//       Access: Private
+//  Description: Gets the FrameBufferProperties to match the
+//               indicated config.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+get_properties(FrameBufferProperties &properties,
+      bool &pbuffer_supported, bool &pixmap_supported,
+                        bool &slow, EGLConfig config) {
+
+  properties.clear();
+
+  // Now update our framebuffer_mode and bit depth appropriately.
+  EGLint red_size, green_size, blue_size,
+    alpha_size,
+    depth_size, stencil_size, samples, surface_type, caveat;
+
+  eglGetConfigAttrib(_egl_display, config, EGL_RED_SIZE, &red_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_GREEN_SIZE, &green_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_BLUE_SIZE, &blue_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_ALPHA_SIZE, &alpha_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_DEPTH_SIZE, &depth_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_STENCIL_SIZE, &stencil_size);
+  eglGetConfigAttrib(_egl_display, config, EGL_SAMPLES, &samples);
+  eglGetConfigAttrib(_egl_display, config, EGL_SURFACE_TYPE, &surface_type);
+  eglGetConfigAttrib(_egl_display, config, EGL_CONFIG_CAVEAT, &caveat);
+  int err = eglGetError();
+  if (err != EGL_SUCCESS) {
+    androiddisplay_cat.error() << "Failed to get EGL config attrib: "
+      << get_egl_error_string(err) << "\n";
+  }
+
+  pbuffer_supported = false;
+  if ((surface_type & EGL_PBUFFER_BIT)!=0) {
+    pbuffer_supported = true;
+  }
+
+  pixmap_supported = false;
+  if ((surface_type & EGL_PIXMAP_BIT)!=0) {
+    pixmap_supported = true;
+  }
+
+  slow = false;
+  if (caveat == EGL_SLOW_CONFIG) {
+    slow = true;
+  }
+
+  if ((surface_type & EGL_WINDOW_BIT)==0) {
+    // We insist on having a context that will support an onscreen window.
+    return;
+  }
+
+  properties.set_back_buffers(1);
+  properties.set_rgb_color(1);
+  properties.set_color_bits(red_size+green_size+blue_size);
+  properties.set_stencil_bits(stencil_size);
+  properties.set_depth_bits(depth_size);
+  properties.set_alpha_bits(alpha_size);
+  properties.set_multisamples(samples);
+
+  // Set both hardware and software bits, indicating not-yet-known.
+  properties.set_force_software(1);
+  properties.set_force_hardware(1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::choose_pixel_format
+//       Access: Private
+//  Description: Selects a visual or fbconfig for all the windows
+//               and buffers that use this gsg.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+choose_pixel_format(const FrameBufferProperties &properties,
+                    bool need_pbuffer, bool need_pixmap) {
+
+  _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  _fbconfig = 0;
+  _format = 0;
+
+  int attrib_list[] = {
+    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+#ifdef OPENGLES_1
+    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
+#endif
+#ifdef OPENGLES_2
+    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+    EGL_NONE
+  };
+
+  // First get the number of matching configurations, so we know how much memory to allocate.
+  int num_configs = 0, returned_configs;
+  if (!eglChooseConfig(_egl_display, attrib_list, NULL, num_configs, &returned_configs) || returned_configs <= 0) {
+    androiddisplay_cat.error() << "eglChooseConfig failed: "
+      << get_egl_error_string(eglGetError()) << "\n";
+    return;
+  }
+
+  num_configs = returned_configs;
+  EGLConfig *configs = new EGLConfig[num_configs];
+
+  if (!eglChooseConfig(_egl_display, attrib_list, configs, num_configs, &returned_configs) || returned_configs <= 0) {
+    androiddisplay_cat.error() << "eglChooseConfig failed: "
+      << get_egl_error_string(eglGetError()) << "\n";
+    delete[] configs;
+    return;
+  }
+
+  int best_quality = 0;
+  int best_result = 0;
+  FrameBufferProperties best_props;
+
+  for (int i = 0; i < num_configs; ++i) {
+    FrameBufferProperties fbprops;
+    bool pbuffer_supported, pixmap_supported, slow;
+    get_properties(fbprops, pbuffer_supported, pixmap_supported,
+                   slow, configs[i]);
+    // We're not protecting this code by an is_debug() check, because if we do,
+    // some weird compiler bug appears and somehow makes the quality always 0.
+    const char *pbuffertext = pbuffer_supported ? " (pbuffer)" : "";
+    const char *pixmaptext = pixmap_supported ? " (pixmap)" : "";
+    const char *slowtext = slow ? " (slow)" : "";
+    androiddisplay_cat.debug()
+      << i << ": " << fbprops << pbuffertext << pixmaptext << slowtext << "\n";
+    int quality = fbprops.get_quality(properties);
+    if ((quality > 0)&&(slow)) quality -= 10000000;
+
+    if (need_pbuffer && !pbuffer_supported) {
+      continue;
+    }
+    if (need_pixmap && !pixmap_supported) {
+      continue;
+    }
+
+    if (quality > best_quality) {
+      best_quality = quality;
+      best_result = i;
+      best_props = fbprops;
+    }
+  }
+
+  if (best_quality > 0) {
+    androiddisplay_cat.debug()
+      << "Chosen config " << best_result << ": " << best_props << "\n";
+    _fbconfig = configs[best_result];
+    eglGetConfigAttrib(_egl_display, _fbconfig, EGL_NATIVE_VISUAL_ID, &_format);
+
+    androiddisplay_cat.debug()
+      << "Window format: " << _format << "\n";
+
+    _fbprops = best_props;
+    return;
+  }
+
+  androiddisplay_cat.error() <<
+    "Could not find a usable pixel format.\n";
+
+  delete[] configs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::create_context
+//       Access: Private
+//  Description: Creates the context based on the config previously
+//               obtained in choose_pixel_format.
+////////////////////////////////////////////////////////////////////
+bool AndroidGraphicsStateGuardian::
+create_context() {
+  if (_context != EGL_NO_CONTEXT) {
+    destroy_context();
+  }
+
+#ifdef OPENGLES_2
+  EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+  _context = eglCreateContext(_egl_display, _fbconfig, _share_context, context_attribs);
+#else
+  _context = eglCreateContext(_egl_display, _fbconfig, _share_context, NULL);
+#endif
+
+  int err = eglGetError();
+  if (_context != EGL_NO_CONTEXT && err == EGL_SUCCESS) {
+    _needs_reset = true;
+    return true;
+  }
+
+  androiddisplay_cat.error()
+    << "Could not create EGL context!\n"
+    << get_egl_error_string(err) << "\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::destroy_context
+//       Access: Private
+//  Description: Destroys the context previously created by
+//               create_context.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+destroy_context() {
+  if (_context == EGL_NO_CONTEXT) {
+    return;
+  }
+
+  if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
+    androiddisplay_cat.error() << "Failed to call eglMakeCurrent: "
+      << get_egl_error_string(eglGetError()) << "\n";
+  }
+
+  release_all();
+
+  eglDestroyContext(_egl_display, _context);
+  _context = EGL_NO_CONTEXT;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::reset
+//       Access: Public, Virtual
+//  Description: Resets all internal state as if the gsg were newly
+//               created.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+reset() {
+#ifdef OPENGLES_2
+  GLES2GraphicsStateGuardian::reset();
+#else
+  GLESGraphicsStateGuardian::reset();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::egl_is_at_least_version
+//       Access: Public
+//  Description: Returns true if the runtime GLX version number is at
+//               least the indicated value, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool AndroidGraphicsStateGuardian::
+egl_is_at_least_version(int major_version, int minor_version) const {
+  if (_egl_version_major < major_version) {
+    return false;
+  }
+  if (_egl_version_minor < minor_version) {
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::gl_flush
+//       Access: Protected, Virtual
+//  Description: Calls glFlush().
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+gl_flush() const {
+#ifdef OPENGLES_2
+  GLES2GraphicsStateGuardian::gl_flush();
+#else
+  GLESGraphicsStateGuardian::gl_flush();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::gl_get_error
+//       Access: Protected, Virtual
+//  Description: Returns the result of glGetError().
+////////////////////////////////////////////////////////////////////
+GLenum AndroidGraphicsStateGuardian::
+gl_get_error() const {
+#ifdef OPENGLES_2
+  return GLES2GraphicsStateGuardian::gl_get_error();
+#else
+  return GLESGraphicsStateGuardian::gl_get_error();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::query_gl_version
+//       Access: Protected, Virtual
+//  Description: Queries the runtime version of OpenGL in use.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+query_gl_version() {
+#ifdef OPENGLES_2
+  GLES2GraphicsStateGuardian::query_gl_version();
+#else
+  GLESGraphicsStateGuardian::query_gl_version();
+#endif
+
+  // Calling eglInitialize on an already-initialized display will
+  // just provide us the version numbers.
+  if (!eglInitialize(_egl_display, &_egl_version_major, &_egl_version_minor)) {
+    androiddisplay_cat.error() << "Failed to get EGL version number: "
+      << get_egl_error_string(eglGetError()) << "\n";
+  }
+
+  // We output to glesgsg_cat instead of androiddisplay_cat, since this is
+  // where the GL version has been output, and it's nice to see the
+  // two of these together.
+#ifdef OPENGLES_2
+  if (gles2gsg_cat.is_debug()) {
+    gles2gsg_cat.debug()
+#else
+  if (glesgsg_cat.is_debug()) {
+    glesgsg_cat.debug()
+#endif
+      << "EGL_VERSION = " << _egl_version_major << "." << _egl_version_minor
+      << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::get_extra_extensions
+//       Access: Protected, Virtual
+//  Description: This may be redefined by a derived class (e.g. glx or
+//               wgl) to get whatever further extensions strings may
+//               be appropriate to that interface, in addition to the
+//               GL extension strings return by glGetString().
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsStateGuardian::
+get_extra_extensions() {
+  save_extensions(eglQueryString(_egl_display, EGL_EXTENSIONS));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsStateGuardian::do_get_extension_func
+//       Access: Public, Virtual
+//  Description: Returns the pointer to the GL extension function with
+//               the indicated name.  It is the responsibility of the
+//               caller to ensure that the required extension is
+//               defined in the OpenGL runtime prior to calling this;
+//               it is an error to call this for a function that is
+//               not defined.
+////////////////////////////////////////////////////////////////////
+void *AndroidGraphicsStateGuardian::
+do_get_extension_func(const char *prefix, const char *name) {
+  string fullname = string(prefix) + string(name);
+
+  return (void *)eglGetProcAddress(fullname.c_str());
+}

+ 97 - 0
panda/src/androiddisplay/androidGraphicsStateGuardian.h

@@ -0,0 +1,97 @@
+// Filename: androidGraphicsStateGuardian.h
+// Created by:  pro-rsoft (21May09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef ANDROIDGRAPHICSSTATEGUARDIAN_H
+#define ANDROIDGRAPHICSSTATEGUARDIAN_H
+
+#include "pandabase.h"
+#include "androidGraphicsPipe.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : AndroidGraphicsStateGuardian
+// Description : A tiny specialization on GLESGraphicsStateGuardian
+//               to add some egl-specific information.
+////////////////////////////////////////////////////////////////////
+#ifdef OPENGLES_2
+class AndroidGraphicsStateGuardian : public GLES2GraphicsStateGuardian {
+#else
+class AndroidGraphicsStateGuardian : public GLESGraphicsStateGuardian {
+#endif
+public:
+  INLINE const FrameBufferProperties &get_fb_properties() const;
+  void get_properties(FrameBufferProperties &properties,
+                      bool &pbuffer_supported, bool &pixmap_supported,
+                      bool &slow, EGLConfig config);
+  void choose_pixel_format(const FrameBufferProperties &properties,
+                           bool need_pbuffer, bool need_pixmap);
+  bool create_context();
+  void destroy_context();
+
+  AndroidGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
+         AndroidGraphicsStateGuardian *share_with);
+
+  virtual ~AndroidGraphicsStateGuardian();
+
+  virtual void reset();
+
+  bool egl_is_at_least_version(int major_version, int minor_version) const;
+
+protected:
+  EGLContext _share_context;
+  EGLContext _context;
+  EGLDisplay _egl_display;
+  EGLConfig _fbconfig;
+  EGLint _format;
+  FrameBufferProperties _fbprops;
+
+protected:
+  virtual void gl_flush() const;
+  virtual GLenum gl_get_error() const;
+
+  virtual void query_gl_version();
+  virtual void get_extra_extensions();
+  virtual void *do_get_extension_func(const char *prefix, const char *name);
+
+private:
+  int _egl_version_major, _egl_version_minor;
+
+  friend class AndroidGraphicsWindow;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+#ifdef OPENGLES_2
+    GLES2GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "AndroidGraphicsStateGuardian",
+                  GLES2GraphicsStateGuardian::get_class_type());
+#else
+    GLESGraphicsStateGuardian::init_type();
+    register_type(_type_handle, "AndroidGraphicsStateGuardian",
+                  GLESGraphicsStateGuardian::get_class_type());
+#endif
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "androidGraphicsStateGuardian.I"
+
+#endif

+ 14 - 0
panda/src/androiddisplay/androidGraphicsWindow.I

@@ -0,0 +1,14 @@
+// Filename: androidGraphicsWindow.I
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 771 - 0
panda/src/androiddisplay/androidGraphicsWindow.cxx

@@ -0,0 +1,771 @@
+// Filename: androidGraphicsWindow.cxx
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "androidGraphicsWindow.h"
+#include "androidGraphicsStateGuardian.h"
+#include "config_androiddisplay.h"
+#include "androidGraphicsPipe.h"
+
+#include "graphicsPipe.h"
+#include "keyboardButton.h"
+#include "mouseButton.h"
+#include "clockObject.h"
+#include "pStatTimer.h"
+#include "textEncoder.h"
+#include "throw_event.h"
+#include "nativeWindowHandle.h"
+
+#include "android_native_app_glue.h"
+#include <android/window.h>
+#include <android/log.h>
+
+extern struct android_app* panda_android_app;
+
+TypeHandle AndroidGraphicsWindow::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsWindow::
+AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                      const string &name,
+                      const FrameBufferProperties &fb_prop,
+                      const WindowProperties &win_prop,
+                      int flags,
+                      GraphicsStateGuardian *gsg,
+                      GraphicsOutput *host) :
+  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+{
+  AndroidGraphicsPipe *android_pipe;
+  DCAST_INTO_V(android_pipe, _pipe);
+
+  _egl_display = android_pipe->_egl_display;
+  _egl_surface = 0;
+
+  _app = panda_android_app;
+
+  GraphicsWindowInputDevice device =
+    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
+  add_input_device(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+AndroidGraphicsWindow::
+~AndroidGraphicsWindow() {
+  destroy_surface();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
+////////////////////////////////////////////////////////////////////
+bool AndroidGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  PStatTimer timer(_make_current_pcollector, current_thread);
+
+  begin_frame_spam(mode);
+  if (_gsg == (GraphicsStateGuardian *)NULL) {
+    return false;
+  }
+
+  //XXX not open yet.
+  if (_egl_surface == EGL_NO_SURFACE) {
+    return false;
+  }
+
+  AndroidGraphicsStateGuardian *androidgsg;
+  DCAST_INTO_R(androidgsg, _gsg, false);
+  {
+    if (eglGetCurrentDisplay() == _egl_display &&
+        eglGetCurrentSurface(EGL_READ) == _egl_surface &&
+        eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
+        eglGetCurrentContext() == androidgsg->_context) {
+      // No need to make the context current again.  Short-circuit
+      // this possibly-expensive call.
+    } else {
+      // Need to set the context.
+      if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
+        androiddisplay_cat.error() << "Failed to call eglMakeCurrent: "
+          << get_egl_error_string(eglGetError()) << "\n";
+      }
+    }
+  }
+
+  // Now that we have made the context current to a window, we can
+  // reset the GSG state if this is the first time it has been used.
+  // (We can't just call reset() when we construct the GSG, because
+  // reset() requires having a current context.)
+  androidgsg->reset_if_new();
+
+  if (mode == FM_render) {
+    // begin_render_texture();
+    clear_cube_map_selection();
+  }
+
+  _gsg->set_current_properties(&get_fb_properties());
+  return _gsg->begin_frame(current_thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::end_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after rendering is completed for a given frame.  It
+//               should do whatever finalization is required.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  end_frame_spam(mode);
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+
+  if (mode == FM_render) {
+    // end_render_texture();
+    copy_to_textures();
+  }
+
+  _gsg->end_frame(current_thread);
+
+  if (mode == FM_render) {
+    trigger_flip();
+    clear_cube_map_selection();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::end_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after begin_flip() has been called on all windows, to
+//               finish the exchange of the front and back buffers.
+//
+//               This should cause the window to wait for the flip, if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+end_flip() {
+  if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) {
+
+    // It doesn't appear to be necessary to ensure the graphics
+    // context is current before flipping the windows, and insisting
+    // on doing so can be a significant performance hit.
+
+    //make_current();
+
+    if (_egl_surface != EGL_NO_SURFACE) {
+      eglSwapBuffers(_egl_display, _egl_surface);
+    }
+  }
+  GraphicsWindow::end_flip();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::process_events
+//       Access: Public, Virtual
+//  Description: Do whatever processing is necessary to ensure that
+//               the window responds to user events.  Also, honor any
+//               requests recently made via request_properties()
+//
+//               This function is called only within the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+process_events() {
+  GraphicsWindow::process_events();
+
+  // Read all pending events.
+  int looper_id;
+  int events;
+  struct android_poll_source* source;
+
+  // Loop until all events are read.
+  while ((looper_id = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
+    // Process this event.
+    if (source != NULL) {
+      source->process(_app, source);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::set_properties_now
+//       Access: Public, Virtual
+//  Description: Applies the requested set of properties to the
+//               window, if possible, for instance to request a change
+//               in size or minimization status.
+//
+//               The window properties are applied immediately, rather
+//               than waiting until the next frame.  This implies that
+//               this method may *only* be called from within the
+//               window thread.
+//
+//               The return value is true if the properties are set,
+//               false if they are ignored.  This is mainly useful for
+//               derived classes to implement extensions to this
+//               function.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+set_properties_now(WindowProperties &properties) {
+  if (_pipe == (GraphicsPipe *)NULL) {
+    // If the pipe is null, we're probably closing down.
+    GraphicsWindow::set_properties_now(properties);
+    return;
+  }
+
+  GraphicsWindow::set_properties_now(properties);
+  if (!properties.is_any_specified()) {
+    // The base class has already handled this case.
+    return;
+  }
+
+  // There's not really much we can change on Android.
+  if (properties.has_fullscreen()) {
+    uint32_t add_flags = 0;
+    uint32_t del_flags = 0;
+    if (_properties.get_fullscreen()) {
+      add_flags |= AWINDOW_FLAG_FULLSCREEN;
+    } else {
+      del_flags |= AWINDOW_FLAG_FULLSCREEN;
+    }
+    ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
+
+    _properties.set_fullscreen(properties.get_fullscreen());
+    properties.clear_fullscreen();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::close_window
+//       Access: Protected, Virtual
+//  Description: Closes the window right now.  Called from the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+close_window() {
+  destroy_surface();
+
+  if (_gsg != (GraphicsStateGuardian *)NULL) {
+    _gsg.clear();
+  }
+
+  GraphicsWindow::close_window();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::open_window
+//       Access: Protected, Virtual
+//  Description: Opens the window right now.  Called from the window
+//               thread.  Returns true if the window is successfully
+//               opened, or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool AndroidGraphicsWindow::
+open_window() {
+  // GSG Creation/Initialization
+  AndroidGraphicsStateGuardian *androidgsg;
+  if (_gsg == 0) {
+    // There is no old gsg.  Create a new one.
+    androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, NULL);
+    androidgsg->choose_pixel_format(_fb_properties, false, false);
+    _gsg = androidgsg;
+  } else {
+    // If the old gsg has the wrong pixel format, create a
+    // new one that shares with the old gsg.
+    DCAST_INTO_R(androidgsg, _gsg, false);
+    if (!androidgsg->get_fb_properties().subsumes(_fb_properties)) {
+      androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, androidgsg);
+      androidgsg->choose_pixel_format(_fb_properties, false, false);
+      _gsg = androidgsg;
+    }
+  }
+
+  // Register the callbacks
+  assert(_app != NULL);
+  _app->userData = this;
+  _app->onAppCmd = handle_command;
+  _app->onInputEvent = handle_input_event;
+
+  // Wait until Android has opened the window.
+  while (_app->window == NULL) {
+    process_events();
+  }
+
+  // create_surface should have been called by now.
+  if (_egl_surface == EGL_NO_SURFACE) {
+    return false;
+  }
+
+  // Set some other properties.
+  _properties.set_origin(0, 0);
+  _properties.set_cursor_hidden(true);
+  _properties.set_undecorated(true);
+
+  if (!androidgsg->get_fb_properties().verify_hardware_software
+      (_fb_properties, androidgsg->get_gl_renderer())) {
+    close_window();
+    return false;
+  }
+
+  //_fb_properties = androidgsg->get_fb_properties();
+
+  androiddisplay_cat.error() << "open_window done\n";
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::destroy_surface
+//       Access: Protected, Virtual
+//  Description: Terminates the EGL surface.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+destroy_surface() {
+  if (_egl_surface != EGL_NO_SURFACE) {
+    if (!eglDestroySurface(_egl_display, _egl_surface)) {
+      androiddisplay_cat.error() << "Failed to destroy surface: "
+        << get_egl_error_string(eglGetError()) << "\n";
+    }
+    _egl_surface = EGL_NO_SURFACE;
+  }
+
+  // Destroy the current context.
+  if (_gsg != (GraphicsStateGuardian *)NULL) {
+    AndroidGraphicsStateGuardian *androidgsg;
+    DCAST_INTO_V(androidgsg, _gsg);
+    androidgsg->destroy_context();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::create_surface
+//       Access: Protected, Virtual
+//  Description: Creates the EGL surface.
+////////////////////////////////////////////////////////////////////
+bool AndroidGraphicsWindow::
+create_surface() {
+  AndroidGraphicsStateGuardian *androidgsg;
+  DCAST_INTO_R(androidgsg, _gsg, false);
+
+  // Reconfigure the window buffers to match that of our framebuffer config.
+  ANativeWindow_setBuffersGeometry(_app->window, 0, 0, androidgsg->_format);
+
+  // Set any window flags
+  uint32_t add_flags = 0;
+  uint32_t del_flags = 0;
+  if (_properties.get_fullscreen()) {
+    add_flags |= AWINDOW_FLAG_FULLSCREEN;
+  } else {
+    del_flags |= AWINDOW_FLAG_FULLSCREEN;
+  }
+  ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
+
+  // Create the EGL surface.
+  _egl_surface = eglCreateWindowSurface(_egl_display, androidgsg->_fbconfig, _app->window, NULL);
+  if (eglGetError() != EGL_SUCCESS) {
+    androiddisplay_cat.error()
+      << "Failed to create window surface.\n";
+    return false;
+  }
+
+  // Create a context.
+  if (androidgsg->_context == EGL_NO_CONTEXT) {
+    androiddisplay_cat.error() << "creating context\n";
+    if (!androidgsg->create_context()) {
+      return false;
+    }
+  }
+
+  // Switch to our newly created context.
+  if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
+    androiddisplay_cat.error() << "Failed to call eglMakeCurrent: "
+      << get_egl_error_string(eglGetError()) << "\n";
+  }
+
+  // Query the size of the surface.
+  //EGLint width, height;
+  //eglQuerySurface(_egl_display, _egl_surface, EGL_WIDTH, &width);
+  //eglQuerySurface(_egl_display, _egl_surface, EGL_HEIGHT, &height);
+
+  androidgsg->reset_if_new();
+  if (!androidgsg->is_valid()) {
+    close_window();
+    return false;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::handle_command
+//       Access: Private, Static
+//  Description: Android app sends a command from the main thread.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+handle_command(struct android_app *app, int32_t command) {
+  AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData;
+  window->ns_handle_command(command);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::ns_handle_command
+//       Access: Private
+//  Description: Android app sends a command from the main thread.
+////////////////////////////////////////////////////////////////////
+void AndroidGraphicsWindow::
+ns_handle_command(int32_t command) {
+  WindowProperties properties;
+  
+  switch (command) {
+    case APP_CMD_SAVE_STATE:
+      // The system has asked us to save our current state.  Do so.
+      //engine->app->savedState = malloc(sizeof(struct saved_state));
+      //*((struct saved_state*)engine->app->savedState) = engine->state;
+      //engine->app->savedStateSize = sizeof(struct saved_state);
+      break;
+    case APP_CMD_INIT_WINDOW:
+      // The window is being shown, get it ready.
+      if (_app->window != NULL) {
+        create_surface();
+        properties.set_minimized(false);
+        system_changed_properties(properties);
+      }
+      break;
+    case APP_CMD_TERM_WINDOW:
+      destroy_surface();
+      properties.set_minimized(true);
+      system_changed_properties(properties);
+      break;
+    case APP_CMD_WINDOW_RESIZED:
+      break;
+    case APP_CMD_WINDOW_REDRAW_NEEDED:
+      break;
+    case APP_CMD_CONTENT_RECT_CHANGED:
+      properties.set_origin(_app->contentRect.left, _app->contentRect.top);
+      properties.set_size(_app->contentRect.right - _app->contentRect.left,
+                          _app->contentRect.bottom - _app->contentRect.top);
+      system_changed_properties(properties);
+      break;
+    case APP_CMD_GAINED_FOCUS:
+      properties.set_foreground(true);
+      system_changed_properties(properties);
+      break;
+    case APP_CMD_LOST_FOCUS:
+      properties.set_foreground(false);
+      system_changed_properties(properties);
+      break;
+    case APP_CMD_DESTROY:
+      close_window();
+      properties.set_open(false);
+      system_changed_properties(properties);
+      break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::handle_input_event
+//       Access: Private, Static
+//  Description: Processes an input event.  Returns 1 if the event
+//               was handled, 0 otherwise.
+////////////////////////////////////////////////////////////////////
+int32_t AndroidGraphicsWindow::
+handle_input_event(struct android_app* app, AInputEvent *event) {
+  AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData;
+
+  int32_t event_type = AInputEvent_getType(event);
+  switch (event_type) {
+  case AINPUT_EVENT_TYPE_KEY:
+    return window->handle_key_event(event);
+  case AINPUT_EVENT_TYPE_MOTION:
+    return window->handle_motion_event(event);
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::handle_keystroke
+//       Access: Private
+//  Description: Processes a key event.
+////////////////////////////////////////////////////////////////////
+int32_t AndroidGraphicsWindow::
+handle_key_event(const AInputEvent *event) {
+  /*
+  int32_t meta = AKeyEvent_getMetaState(event);
+  if (meta | AMETA_ALT_ON) {
+    _input_devices[0].button_down(KeyboardButton.alt());
+  }
+  if (meta | AMETA_ALT_LEFT_ON) {
+    _input_devices[0].button_down(KeyboardButton.lalt());
+  }
+  if (meta | AMETA_ALT_RIGHT_ON) {
+    _input_devices[0].button_down(KeyboardButton.ralt());
+  }
+  if (meta | AMETA_SHIFT_ON) {
+    _input_devices[0].button_down(KeyboardButton.shift());
+  }
+  if (meta | AMETA_SHIFT_LEFT_ON) {
+    _input_devices[0].button_down(KeyboardButton.lshift());
+  }
+  if (meta | AMETA_SHIFT_RIGHT_ON) {
+    _input_devices[0].button_down(KeyboardButton.rshift());
+  }*/
+
+  int32_t keycode = AKeyEvent_getKeyCode(event);
+  ButtonHandle button = map_button(keycode);
+
+  if (button == ButtonHandle::none()) {
+    androiddisplay_cat.warning()
+      << "Unknown keycode: " << keycode << "\n";
+    return 0;
+  }
+
+  // Is it an up or down event?
+  int32_t action = AKeyEvent_getAction(event);
+  if (action == AKEY_EVENT_ACTION_DOWN) {
+    _input_devices[0].button_down(button);
+  } else if (action == AKEY_EVENT_ACTION_UP) {
+    _input_devices[0].button_up(button);
+  }
+  //TODO getRepeatCount, ACTION_MULTIPLE
+
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::handle_motion_event
+//       Access: Private
+//  Description: Processes a motion event.
+////////////////////////////////////////////////////////////////////
+int32_t AndroidGraphicsWindow::
+handle_motion_event(const AInputEvent *event) {
+  int32_t action = AMotionEvent_getAction(event);
+  action &= AMOTION_EVENT_ACTION_MASK;
+
+  if (action == AMOTION_EVENT_ACTION_DOWN) {
+    _input_devices[0].button_down(MouseButton::one());
+  } else if (action == AMOTION_EVENT_ACTION_UP) {
+    _input_devices[0].button_up(MouseButton::one());
+  }
+
+  float x = AMotionEvent_getX(event, 0);
+  float y = AMotionEvent_getY(event, 0);
+
+  _input_devices[0].set_pointer_in_window(x, y);
+
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AndroidGraphicsWindow::map_button
+//       Access: Private
+//  Description: Given an Android keycode, returns an appropriate
+//               ButtonHandle object, or ButtonHandle::none() if
+//               a matching ButtonHandle does not exist.
+////////////////////////////////////////////////////////////////////
+ButtonHandle AndroidGraphicsWindow::
+map_button(int32_t keycode) {
+  switch (keycode) {
+    case AKEYCODE_SOFT_LEFT:
+    case AKEYCODE_SOFT_RIGHT:
+    case AKEYCODE_HOME:
+    case AKEYCODE_BACK:
+    case AKEYCODE_CALL:
+    case AKEYCODE_ENDCALL:
+      break;
+    case AKEYCODE_0:
+      return KeyboardButton::ascii_key('0');
+    case AKEYCODE_1:
+      return KeyboardButton::ascii_key('1');
+    case AKEYCODE_2:
+      return KeyboardButton::ascii_key('2');
+    case AKEYCODE_3:
+      return KeyboardButton::ascii_key('3');
+    case AKEYCODE_4:
+      return KeyboardButton::ascii_key('4');
+    case AKEYCODE_5:
+      return KeyboardButton::ascii_key('5');
+    case AKEYCODE_6:
+      return KeyboardButton::ascii_key('6');
+    case AKEYCODE_7:
+      return KeyboardButton::ascii_key('7');
+    case AKEYCODE_8:
+      return KeyboardButton::ascii_key('8');
+    case AKEYCODE_9:
+      return KeyboardButton::ascii_key('9');
+    case AKEYCODE_STAR:
+      return KeyboardButton::ascii_key('*');
+    case AKEYCODE_POUND:
+      return KeyboardButton::ascii_key('#');
+    case AKEYCODE_DPAD_UP:
+      return KeyboardButton::up();
+    case AKEYCODE_DPAD_DOWN:
+      return KeyboardButton::down();
+    case AKEYCODE_DPAD_LEFT:
+      return KeyboardButton::left();
+    case AKEYCODE_DPAD_RIGHT:
+      return KeyboardButton::right();
+    case AKEYCODE_DPAD_CENTER:
+    case AKEYCODE_VOLUME_UP:
+    case AKEYCODE_VOLUME_DOWN:
+    case AKEYCODE_POWER:
+    case AKEYCODE_CAMERA:
+    case AKEYCODE_CLEAR:
+      break;
+    case AKEYCODE_A:
+      return KeyboardButton::ascii_key('a');
+    case AKEYCODE_B:
+      return KeyboardButton::ascii_key('b');
+    case AKEYCODE_C:
+      return KeyboardButton::ascii_key('c');
+    case AKEYCODE_D:
+      return KeyboardButton::ascii_key('d');
+    case AKEYCODE_E:
+      return KeyboardButton::ascii_key('e');
+    case AKEYCODE_F:
+      return KeyboardButton::ascii_key('f');
+    case AKEYCODE_G:
+      return KeyboardButton::ascii_key('g');
+    case AKEYCODE_H:
+      return KeyboardButton::ascii_key('h');
+    case AKEYCODE_I:
+      return KeyboardButton::ascii_key('i');
+    case AKEYCODE_J:
+      return KeyboardButton::ascii_key('j');
+    case AKEYCODE_K:
+      return KeyboardButton::ascii_key('k');
+    case AKEYCODE_L:
+      return KeyboardButton::ascii_key('l');
+    case AKEYCODE_M:
+      return KeyboardButton::ascii_key('m');
+    case AKEYCODE_N:
+      return KeyboardButton::ascii_key('n');
+    case AKEYCODE_O:
+      return KeyboardButton::ascii_key('o');
+    case AKEYCODE_P:
+      return KeyboardButton::ascii_key('p');
+    case AKEYCODE_Q:
+      return KeyboardButton::ascii_key('q');
+    case AKEYCODE_R:
+      return KeyboardButton::ascii_key('r');
+    case AKEYCODE_S:
+      return KeyboardButton::ascii_key('s');
+    case AKEYCODE_T:
+      return KeyboardButton::ascii_key('t');
+    case AKEYCODE_U:
+      return KeyboardButton::ascii_key('u');
+    case AKEYCODE_V:
+      return KeyboardButton::ascii_key('v');
+    case AKEYCODE_W:
+      return KeyboardButton::ascii_key('w');
+    case AKEYCODE_X:
+      return KeyboardButton::ascii_key('x');
+    case AKEYCODE_Y:
+      return KeyboardButton::ascii_key('y');
+    case AKEYCODE_Z:
+      return KeyboardButton::ascii_key('z');
+    case AKEYCODE_COMMA:
+      return KeyboardButton::ascii_key(',');
+    case AKEYCODE_PERIOD:
+      return KeyboardButton::ascii_key('.');
+    case AKEYCODE_ALT_LEFT:
+      return KeyboardButton::lalt();
+    case AKEYCODE_ALT_RIGHT:
+      return KeyboardButton::ralt();
+    case AKEYCODE_SHIFT_LEFT:
+      return KeyboardButton::lshift();
+    case AKEYCODE_SHIFT_RIGHT:
+      return KeyboardButton::rshift();
+    case AKEYCODE_TAB:
+      return KeyboardButton::tab();
+    case AKEYCODE_SPACE:
+      return KeyboardButton::space();
+    case AKEYCODE_SYM:
+    case AKEYCODE_EXPLORER:
+    case AKEYCODE_ENVELOPE:
+      break;
+    case AKEYCODE_ENTER:
+      return KeyboardButton::enter();
+    case AKEYCODE_DEL:
+      return KeyboardButton::del();
+    case AKEYCODE_GRAVE:
+      return KeyboardButton::ascii_key('`');
+    case AKEYCODE_MINUS:
+      return KeyboardButton::ascii_key('-');
+    case AKEYCODE_EQUALS:
+      return KeyboardButton::ascii_key('=');
+    case AKEYCODE_LEFT_BRACKET:
+      return KeyboardButton::ascii_key('[');
+    case AKEYCODE_RIGHT_BRACKET:
+      return KeyboardButton::ascii_key(']');
+    case AKEYCODE_BACKSLASH:
+      return KeyboardButton::ascii_key('\\');
+    case AKEYCODE_SEMICOLON:
+      return KeyboardButton::ascii_key(';');
+    case AKEYCODE_APOSTROPHE:
+      return KeyboardButton::ascii_key('\'');
+    case AKEYCODE_SLASH:
+      return KeyboardButton::ascii_key('/');
+    case AKEYCODE_AT:
+      return KeyboardButton::ascii_key('@');
+    case AKEYCODE_NUM:
+    case AKEYCODE_HEADSETHOOK:
+    case AKEYCODE_FOCUS:
+      break;
+    case AKEYCODE_PLUS:
+      return KeyboardButton::ascii_key('+');
+    case AKEYCODE_MENU:
+    case AKEYCODE_NOTIFICATION:
+    case AKEYCODE_SEARCH:
+    case AKEYCODE_MEDIA_PLAY_PAUSE:
+    case AKEYCODE_MEDIA_STOP:
+    case AKEYCODE_MEDIA_NEXT:
+    case AKEYCODE_MEDIA_PREVIOUS:
+    case AKEYCODE_MEDIA_REWIND:
+    case AKEYCODE_MEDIA_FAST_FORWARD:
+    case AKEYCODE_MUTE:
+      break;
+    case AKEYCODE_PAGE_UP:
+      return KeyboardButton::page_up();
+    case AKEYCODE_PAGE_DOWN:
+      return KeyboardButton::page_down();
+    case AKEYCODE_PICTSYMBOLS:
+    case AKEYCODE_SWITCH_CHARSET:
+    case AKEYCODE_BUTTON_A:
+    case AKEYCODE_BUTTON_B:
+    case AKEYCODE_BUTTON_C:
+    case AKEYCODE_BUTTON_X:
+    case AKEYCODE_BUTTON_Y:
+    case AKEYCODE_BUTTON_Z:
+    case AKEYCODE_BUTTON_L1:
+    case AKEYCODE_BUTTON_R1:
+    case AKEYCODE_BUTTON_L2:
+    case AKEYCODE_BUTTON_R2:
+    case AKEYCODE_BUTTON_THUMBL:
+    case AKEYCODE_BUTTON_THUMBR:
+    case AKEYCODE_BUTTON_START:
+    case AKEYCODE_BUTTON_SELECT:
+    case AKEYCODE_BUTTON_MODE:
+    default:
+      break;
+  }
+  return ButtonHandle::none();
+}

+ 99 - 0
panda/src/androiddisplay/androidGraphicsWindow.h

@@ -0,0 +1,99 @@
+// Filename: androidGraphicsWindow.h
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef ANDROIDGRAPHICSWINDOW_H
+#define ANDROIDGRAPHICSWINDOW_H
+
+#include "pandabase.h"
+
+#include "androidGraphicsPipe.h"
+#include "graphicsWindow.h"
+#include "buttonHandle.h"
+
+#include <android/native_window.h>
+#include <android/input.h>
+#include <android/native_activity.h>
+#include <android/rect.h>
+
+struct android_app;
+
+////////////////////////////////////////////////////////////////////
+//       Class : AndroidGraphicsWindow
+// Description : An interface to manage Android windows and their
+//               appropriate EGL surfaces.
+////////////////////////////////////////////////////////////////////
+class AndroidGraphicsWindow : public GraphicsWindow {
+public:
+  AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                        const string &name,
+                        const FrameBufferProperties &fb_prop,
+                        const WindowProperties &win_prop,
+                        int flags,
+                        GraphicsStateGuardian *gsg,
+                        GraphicsOutput *host);
+  virtual ~AndroidGraphicsWindow();
+
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_flip();
+
+  virtual void process_events();
+  virtual void set_properties_now(WindowProperties &properties);
+
+protected:
+  virtual void close_window();
+  virtual bool open_window();
+
+  virtual void destroy_surface();
+  virtual bool create_surface();
+
+private:
+  static void handle_command(struct android_app *app, int32_t command);
+  static int32_t handle_input_event(struct android_app *app, AInputEvent *event);
+
+  void ns_handle_command(int32_t command);
+  int32_t handle_key_event(const AInputEvent *event);
+  int32_t handle_motion_event(const AInputEvent *event);
+
+  ButtonHandle map_button(int32_t keycode);
+
+private:
+  struct android_app* _app;
+
+  EGLDisplay _egl_display;
+  EGLSurface _egl_surface;
+
+  const ARect *rect;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "AndroidGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "androidGraphicsWindow.I"
+
+#endif

+ 90 - 0
panda/src/androiddisplay/config_androiddisplay.cxx

@@ -0,0 +1,90 @@
+// Filename: config_androiddisplay.cxx
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_androiddisplay.h"
+#include "androidGraphicsPipe.h"
+#include "androidGraphicsWindow.h"
+#include "androidGraphicsStateGuardian.h"
+#include "graphicsPipeSelection.h"
+#include "dconfig.h"
+#include "pandaSystem.h"
+
+#include "config_display.h"
+
+Configure(config_androiddisplay);
+NotifyCategoryDef(androiddisplay, "display");
+
+ConfigureFn(config_androiddisplay) {
+  init_libandroiddisplay();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libandroiddisplay
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libandroiddisplay() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  init_libdisplay();
+  display_cat.get_safe_ptr();
+
+  AndroidGraphicsPipe::init_type();
+  AndroidGraphicsWindow::init_type();
+  AndroidGraphicsStateGuardian::init_type();
+
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  selection->add_pipe_type(AndroidGraphicsPipe::get_class_type(),
+                           AndroidGraphicsPipe::pipe_constructor);
+
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+#ifdef OPENGLES_2
+  ps->set_system_tag("OpenGL ES 2", "window_system", "Android");
+#else
+  ps->set_system_tag("OpenGL ES", "window_system", "Android");
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_egl_error_string
+//  Description: Returns the given EGL error as string.
+////////////////////////////////////////////////////////////////////
+const string get_egl_error_string(int error) {
+  switch (error) {
+    case 0x3000: return "EGL_SUCCESS"; break;
+    case 0x3001: return "EGL_NOT_INITIALIZED"; break;
+    case 0x3002: return "EGL_BAD_ACCESS"; break;
+    case 0x3003: return "EGL_BAD_ALLOC"; break;
+    case 0x3004: return "EGL_BAD_ATTRIBUTE"; break;
+    case 0x3005: return "EGL_BAD_CONFIG"; break;
+    case 0x3006: return "EGL_BAD_CONTEXT"; break;
+    case 0x3007: return "EGL_BAD_CURRENT_SURFACE"; break;
+    case 0x3008: return "EGL_BAD_DISPLAY"; break;
+    case 0x3009: return "EGL_BAD_MATCH"; break;
+    case 0x300A: return "EGL_BAD_NATIVE_PIXMAP"; break;
+    case 0x300B: return "EGL_BAD_NATIVE_WINDOW"; break;
+    case 0x300C: return "EGL_BAD_PARAMETER"; break;
+    case 0x300D: return "EGL_BAD_SURFACE"; break;
+    case 0x300E: return "EGL_CONTEXT_LOST"; break;
+    default: return "Unknown error";
+  }
+}

+ 43 - 0
panda/src/androiddisplay/config_androiddisplay.h

@@ -0,0 +1,43 @@
+// Filename: config_androiddisplay.h
+// Created by:  rdb (11Jan13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_ANDROIDDISPLAY_H
+#define CONFIG_ANDROIDDISPLAY_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
+
+#if defined(OPENGLES_1) && defined(OPENGLES_2)
+  #error OPENGLES_1 and OPENGLES_2 cannot be defined at the same time!
+#endif
+#if !defined(OPENGLES_1) && !defined(OPENGLES_2)
+  #error Either OPENGLES_1 or OPENGLES_2 must be defined when compiling androiddisplay!
+#endif
+
+#ifdef OPENGLES_2
+  NotifyCategoryDecl(androiddisplay, EXPCL_PANDAGLES2, EXPTP_PANDAGLES2);
+  
+  extern EXPCL_PANDAGLES2 void init_libandroiddisplay();
+  extern EXPCL_PANDAGLES2 const string get_egl_error_string(int error);
+#else
+  NotifyCategoryDecl(androiddisplay, EXPCL_PANDAGLES, EXPTP_PANDAGLES);
+  
+  extern EXPCL_PANDAGLES void init_libandroiddisplay();
+  extern EXPCL_PANDAGLES const string get_egl_error_string(int error);
+#endif
+
+#endif

+ 6 - 0
panda/src/androiddisplay/p3androiddisplay_composite1.cxx

@@ -0,0 +1,6 @@
+#include "config_androiddisplay.cxx"
+//#include "androidGraphicsBuffer.cxx"
+#include "androidGraphicsPipe.cxx"
+//#include "androidGraphicsPixmap.cxx"
+#include "androidGraphicsStateGuardian.cxx"
+#include "androidGraphicsWindow.cxx"