diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp index 79fb420ab7..19029d4482 100644 --- a/dtool/LocalSetup.pp +++ b/dtool/LocalSetup.pp @@ -118,6 +118,10 @@ #endif #if $[HAVE_GL] #print + OpenGL +#elif $[HAVE_GLES2] +#print + OpenGL ES 2 +#elif $[HAVE_GLES] +#print + OpenGL ES #else #print - Did not find OpenGL #endif @@ -326,6 +330,12 @@ $[cdefine HAVE_GLU] # define MIN_GL_VERSION_MINOR $[word 2,$[MIN_GL_VERSION]] #endif +/* Define if we have OpenGL ES installed and want to build for GLES. */ +$[cdefine HAVE_GLES] + +/* Define if we have OpenGL ES installed and want to build for GLES2. */ +$[cdefine HAVE_GLES2] + /* Define if we have OpenCV installed and want to build for OpenCV. */ $[cdefine HAVE_OPENCV] @@ -346,6 +356,9 @@ $[cdefine MESA_MGL] /* Define if we have GLX installed and want to build for GLX. */ $[cdefine HAVE_GLX] +/* Define if we have EGL installed and want to build for EGL. */ +$[cdefine HAVE_EGL] + /* Define if we have Windows-GL installed and want to build for Wgl. */ $[cdefine HAVE_WGL] @@ -413,7 +426,7 @@ $[cdefine SUPPORT_IMMEDIATE_MODE] /* Define if we want to compile in support for pipelining. */ $[cdefine DO_PIPELINING] -/* Define if we want to keep Notify debug messages around, or undefine +/* Define if we want to keep Notify debug messages around, or undefine to compile them out. */ $[cdefine NOTIFY_DEBUG] diff --git a/dtool/Package.pp b/dtool/Package.pp index 170eef975a..a57bb32322 100644 --- a/dtool/Package.pp +++ b/dtool/Package.pp @@ -204,6 +204,16 @@ #set HAVE_GL $[HAVE_GL] #set HAVE_GLU $[HAVE_GLU] +#set GLES_IPATH $[unixfilename $[GLES_IPATH]] +#set GLES_LPATH $[unixfilename $[GLES_LPATH]] +#set GLES_LIBS $[GLES_LIBS] +#set HAVE_GLES $[HAVE_GLES] + +#set GLES2_IPATH $[unixfilename $[GLES2_IPATH]] +#set GLES2_LPATH $[unixfilename $[GLES2_LPATH]] +#set GLES2_LIBS $[GLES2_LIBS] +#set HAVE_GLES2 $[HAVE_GLES2] + #set MESA_IPATH $[unixfilename $[MESA_IPATH]] #set MESA_LPATH $[unixfilename $[MESA_LPATH]] #set MESA_LIBS $[MESA_LIBS] @@ -214,6 +224,10 @@ #set GLX_LPATH $[unixfilename $[GLX_LPATH]] #set HAVE_GLX $[HAVE_GLX] +#set EGL_IPATH $[unixfilename $[EGL_IPATH]] +#set EGL_LPATH $[unixfilename $[EGL_LPATH]] +#set HAVE_EGL $[HAVE_EGL] + #set HAVE_WGL $[HAVE_WGL] #set DX8_IPATH $[unixfilename $[DX8_IPATH]] diff --git a/dtool/pptempl/Global.pp b/dtool/pptempl/Global.pp index 998c43d8ca..7470f6186c 100644 --- a/dtool/pptempl/Global.pp +++ b/dtool/pptempl/Global.pp @@ -113,6 +113,20 @@ #define gl_framework $[GL_FRAMEWORK] #endif +#if $[HAVE_GLES] + #define gles_ipath $[wildcard $[GLES_IPATH]] + #define gles_lpath $[wildcard $[GLES_LPATH]] + #define gles_cflags $[GLES_CFLAGS] + #define gles_libs $[GLES_LIBS] +#endif + +#if $[HAVE_GLES2] + #define gles2_ipath $[wildcard $[GLES2_IPATH]] + #define gles2_lpath $[wildcard $[GLES2_LPATH]] + #define gles2_cflags $[GLES2_CFLAGS] + #define gles2_libs $[GLES2_LIBS] +#endif + #if $[HAVE_SDL] #define sdl_ipath $[wildcard $[SDL_IPATH]] #define sdl_lpath $[wildcard $[SDL_LPATH]] @@ -150,6 +164,13 @@ #define glx_libs $[GLX_LIBS] #endif +#if $[HAVE_EGL] + #define egl_ipath $[wildcard $[EGL_IPATH]] + #define egl_lpath $[wildcard $[EGL_LPATH]] + #define egl_cflags $[EGL_CFLAGS] + #define egl_libs $[EGL_LIBS] +#endif + #if $[HAVE_GLUT] #define glut_ipath $[wildcard $[GLUT_IPATH]] #define glut_lpath $[wildcard $[GLUT_LPATH]] diff --git a/panda/metalibs/pandagles/Sources.pp b/panda/metalibs/pandagles/Sources.pp new file mode 100644 index 0000000000..6424bdfb3d --- /dev/null +++ b/panda/metalibs/pandagles/Sources.pp @@ -0,0 +1,22 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAGLES +#define BUILD_DIRECTORY $[HAVE_GLES] + +#define COMPONENT_LIBS \ + glesgsg egldisplay + +#define LOCAL_LIBS gsgbase display express +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m prc:c + +#begin metalib_target + #define TARGET pandagles + #define SOURCES pandagles.cxx pandagles.h + #define INSTALL_HEADERS pandagles.h +#end metalib_target diff --git a/panda/metalibs/pandagles/pandagles.cxx b/panda/metalibs/pandagles/pandagles.cxx new file mode 100644 index 0000000000..9e68de49c6 --- /dev/null +++ b/panda/metalibs/pandagles/pandagles.cxx @@ -0,0 +1,51 @@ +// Filename: pandagles.cxx +// Created by: pro-rsoft (8Jun09) +// +//////////////////////////////////////////////////////////////////// + +#include "pandagles.h" + +#define OPENGLES_1 +#include "config_glesgsg.h" + +#ifdef HAVE_EGL +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" +#endif + +// By including checkPandaVersion.h, we guarantee that runtime +// attempts to load libpandagles.so/.dll will fail if they inadvertently +// link with the wrong version of libdtool.so/.dll. + +#include "checkPandaVersion.h" + +//////////////////////////////////////////////////////////////////// +// Function: init_libpandagles +// 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_libpandagles() { + init_libglesgsg(); + +#ifdef HAVE_EGL + init_libegldisplay(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: get_pipe_type_pandagles +// Description: Returns the TypeHandle index of the recommended +// graphics pipe type defined by this module. +//////////////////////////////////////////////////////////////////// +int +get_pipe_type_pandagles() { +#ifdef HAVE_EGL + return eglGraphicsPipe::get_class_type().get_index(); +#endif + + return 0; +} diff --git a/panda/metalibs/pandagles/pandagles.h b/panda/metalibs/pandagles/pandagles.h new file mode 100644 index 0000000000..c4d5df921b --- /dev/null +++ b/panda/metalibs/pandagles/pandagles.h @@ -0,0 +1,15 @@ +// Filename: pandagles.h +// Created by: pro-rsoft (8Jun09) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDAGLES_H +#define PANDAGLES_H + +#include "pandabase.h" + +EXPCL_PANDAGLES void init_libpandagles(); +extern "C" EXPCL_PANDAGLES int get_pipe_type_pandagles(); + +#endif + diff --git a/panda/metalibs/pandagles2/Sources.pp b/panda/metalibs/pandagles2/Sources.pp new file mode 100644 index 0000000000..238a116808 --- /dev/null +++ b/panda/metalibs/pandagles2/Sources.pp @@ -0,0 +1,22 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAGLES +#define BUILD_DIRECTORY $[HAVE_GLES2] + +#define COMPONENT_LIBS \ + gles2gsg egl2display + +#define LOCAL_LIBS gsgbase display express +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m prc:c + +#begin metalib_target + #define TARGET pandagles2 + #define SOURCES pandagles2.cxx pandagles2.h + #define INSTALL_HEADERS pandagles2.h +#end metalib_target diff --git a/panda/metalibs/pandagles2/pandagles2.cxx b/panda/metalibs/pandagles2/pandagles2.cxx new file mode 100644 index 0000000000..ff890ecd8e --- /dev/null +++ b/panda/metalibs/pandagles2/pandagles2.cxx @@ -0,0 +1,51 @@ +// Filename: pandagles2.cxx +// Created by: pro-rsoft (8Jun09) +// +//////////////////////////////////////////////////////////////////// + +#include "pandagles2.h" + +#define OPENGLES_2 +#include "config_gles2gsg.h" + +#ifdef HAVE_EGL +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" +#endif + +// By including checkPandaVersion.h, we guarantee that runtime +// attempts to load libpandagles2.so/.dll will fail if they inadvertently +// link with the wrong version of libdtool.so/.dll. + +#include "checkPandaVersion.h" + +//////////////////////////////////////////////////////////////////// +// Function: init_libpandagles2 +// 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_libpandagles2() { + init_libgles2gsg(); + +#ifdef HAVE_EGL + init_libegldisplay(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: get_pipe_type_pandagles2 +// Description: Returns the TypeHandle index of the recommended +// graphics pipe type defined by this module. +//////////////////////////////////////////////////////////////////// +int +get_pipe_type_pandagles2() { +#ifdef HAVE_EGL + return eglGraphicsPipe::get_class_type().get_index(); +#endif + + return 0; +} diff --git a/panda/metalibs/pandagles2/pandagles2.h b/panda/metalibs/pandagles2/pandagles2.h new file mode 100644 index 0000000000..dbac54b9ed --- /dev/null +++ b/panda/metalibs/pandagles2/pandagles2.h @@ -0,0 +1,15 @@ +// Filename: pandagles.h +// Created by: pro-rsoft (16Jun09) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDAGLES2_H +#define PANDAGLES2_H + +#include "pandabase.h" + +EXPCL_PANDAGLES2 void init_libpandagles2(); +extern "C" EXPCL_PANDAGLES2 int get_pipe_type_pandagles2(); + +#endif + diff --git a/panda/src/egldisplay/Sources.pp b/panda/src/egldisplay/Sources.pp new file mode 100644 index 0000000000..30e0fcc627 --- /dev/null +++ b/panda/src/egldisplay/Sources.pp @@ -0,0 +1,50 @@ +#define BUILD_DIRECTORY $[HAVE_EGL] + +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m + +#begin lib_target + #define TARGET egldisplay + #define BUILD_TARGET $[HAVE_GLES] + #define USE_PACKAGES gles egl x11 + #define C++FLAGS -DOPENGLES_1 + #define LOCAL_LIBS \ + glesgsg + + #define SOURCES \ + config_egldisplay.cxx config_egldisplay.h \ + eglGraphicsBuffer.h eglGraphicsBuffer.cxx \ + eglGraphicsPipe.I eglGraphicsPipe.cxx eglGraphicsPipe.h \ + eglGraphicsPixmap.h eglGraphicsPixmap.cxx \ + eglGraphicsWindow.h eglGraphicsWindow.cxx \ + eglGraphicsStateGuardian.h eglGraphicsStateGuardian.cxx + + #define INSTALL_HEADERS \ + eglGraphicsBuffer.h eglGraphicsPixmap.h \ + eglGraphicsPipe.I eglGraphicsPipe.h \ + eglGraphicsWindow.I eglGraphicsWindow.h + +#end lib_target + +#begin lib_target + #define TARGET egl2display + #define BUILD_TARGET $[HAVE_GLES2] + #define USE_PACKAGES gles2 egl x11 + #define C++FLAGS -DOPENGLES_2 + #define LOCAL_LIBS \ + gles2gsg + + #define SOURCES \ + config_egldisplay.cxx config_egldisplay.h \ + eglGraphicsBuffer.h eglGraphicsBuffer.cxx \ + eglGraphicsPipe.I eglGraphicsPipe.cxx eglGraphicsPipe.h \ + eglGraphicsPixmap.h eglGraphicsPixmap.cxx \ + eglGraphicsWindow.h eglGraphicsWindow.cxx \ + eglGraphicsStateGuardian.h eglGraphicsStateGuardian.cxx + + #define INSTALL_HEADERS \ + eglGraphicsBuffer.h eglGraphicsPixmap.h \ + eglGraphicsPipe.I eglGraphicsPipe.h \ + eglGraphicsWindow.I eglGraphicsWindow.h + +#end lib_target diff --git a/panda/src/egldisplay/config_egldisplay.cxx b/panda/src/egldisplay/config_egldisplay.cxx new file mode 100644 index 0000000000..a289ae7bf1 --- /dev/null +++ b/panda/src/egldisplay/config_egldisplay.cxx @@ -0,0 +1,120 @@ +// Filename: config_egldisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// +// +// 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_egldisplay.h" +#include "eglGraphicsPipe.h" +#include "eglGraphicsWindow.h" +#include "eglGraphicsStateGuardian.h" +#include "graphicsPipeSelection.h" +#include "dconfig.h" +#include "pandaSystem.h" + +Configure(config_egldisplay); +NotifyCategoryDef(egldisplay, "display"); + +ConfigureFn(config_egldisplay) { + init_libegldisplay(); +} + +ConfigVariableString display_cfg +("display", "", + PRC_DESC("Specify the X display string for the default display. If this " + "is not specified, $DISPLAY is used.")); + +ConfigVariableBool x_error_abort +("x-error-abort", false, + PRC_DESC("Set this true to trigger and abort (and a stack trace) on receipt " + "of an error from the X window system. This can make it easier " + "to discover where these errors are generated.")); + +ConfigVariableInt x_wheel_up_button +("x-wheel-up-button", 4, + PRC_DESC("This is the mouse button index of the wheel_up event: which " + "mouse button number does the system report when the mouse wheel " + "is rolled one notch up?")); + +ConfigVariableInt x_wheel_down_button +("x-wheel-down-button", 5, + PRC_DESC("This is the mouse button index of the wheel_down event: which " + "mouse button number does the system report when the mouse wheel " + "is rolled one notch down?")); + +ConfigVariableInt x_wheel_left_button +("x-wheel-left-button", 6, + PRC_DESC("This is the mouse button index of the wheel_left event: which " + "mouse button number does the system report when one scrolls " + "to the left?")); + +ConfigVariableInt x_wheel_right_button +("x-wheel-right-button", 7, + PRC_DESC("This is the mouse button index of the wheel_right event: which " + "mouse button number does the system report when one scrolls " + "to the right?")); + +//////////////////////////////////////////////////////////////////// +// Function: init_libegldisplay +// 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_libegldisplay() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + eglGraphicsPipe::init_type(); + eglGraphicsWindow::init_type(); + eglGraphicsStateGuardian::init_type(); + + GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr(); + selection->add_pipe_type(eglGraphicsPipe::get_class_type(), + eglGraphicsPipe::pipe_constructor); + + PandaSystem *ps = PandaSystem::get_global_ptr(); +#ifdef OPENGLES_2 + ps->set_system_tag("OpenGL ES 2", "window_system", "EGL"); +#else + ps->set_system_tag("OpenGL ES", "window_system", "EGL"); +#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"; + } +} diff --git a/panda/src/egldisplay/config_egldisplay.h b/panda/src/egldisplay/config_egldisplay.h new file mode 100644 index 0000000000..e8601dd806 --- /dev/null +++ b/panda/src/egldisplay/config_egldisplay.h @@ -0,0 +1,51 @@ +// Filename: config_egldisplay.h +// Created by: cary (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 CONFIG_EGLDISPLAY_H +#define CONFIG_EGLDISPLAY_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 egldisplay! +#endif + +#ifdef OPENGLES_2 + NotifyCategoryDecl(egldisplay, EXPCL_PANDAGLES2, EXPTP_PANDAGLES2); + + extern EXPCL_PANDAGLES2 void init_libegldisplay(); + extern EXPCL_PANDAGLES2 const string get_egl_error_string(int error); +#else + NotifyCategoryDecl(egldisplay, EXPCL_PANDAGLES, EXPTP_PANDAGLES); + + extern EXPCL_PANDAGLES void init_libegldisplay(); + extern EXPCL_PANDAGLES const string get_egl_error_string(int error); +#endif + +extern ConfigVariableString display_cfg; +extern ConfigVariableBool x_error_abort; + +extern ConfigVariableInt x_wheel_up_button; +extern ConfigVariableInt x_wheel_down_button; +extern ConfigVariableInt x_wheel_left_button; +extern ConfigVariableInt x_wheel_right_button; + +#endif diff --git a/panda/src/egldisplay/eglGraphicsBuffer.cxx b/panda/src/egldisplay/eglGraphicsBuffer.cxx new file mode 100644 index 0000000000..fe28b57841 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsBuffer.cxx @@ -0,0 +1,229 @@ +// Filename: eglGraphicsBuffer.cxx +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "eglGraphicsBuffer.h" +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "pStatTimer.h" + +TypeHandle eglGraphicsBuffer::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsBuffer:: +eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) +{ + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + _pbuffer = EGL_NO_SURFACE; + + // Since the pbuffer never gets flipped, we get screenshots from the + // same buffer we draw into. + _screenshot_buffer_type = _draw_buffer_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsBuffer:: +~eglGraphicsBuffer() { + nassertv(_pbuffer == EGL_NO_SURFACE); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::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 eglGraphicsBuffer:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglMakeCurrent(eglgsg->_egl_display, _pbuffer, _pbuffer, eglgsg->_context)) { + egldisplay_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.) + eglgsg->reset_if_new(); + + if (mode == FM_render) { + for (int i=0; iset_current_properties(&get_fb_properties()); + return _gsg->begin_frame(current_thread); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::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 eglGraphicsBuffer:: +end_frame(FrameMode mode, Thread *current_thread) { + end_frame_spam(mode); + nassertv(_gsg != (GraphicsStateGuardian *)NULL); + + if (mode == FM_render) { + copy_to_textures(); + } + + _gsg->end_frame(current_thread); + + if (mode == FM_render) { + trigger_flip(); + if (_one_shot) { + prepare_for_deletion(); + } + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::close_buffer +// Access: Protected, Virtual +// Description: Closes the buffer right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void eglGraphicsBuffer:: +close_buffer() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_V(eglgsg, _gsg); + if (!eglMakeCurrent(eglgsg->_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _gsg.clear(); + _active = false; + + if (_pbuffer != EGL_NO_SURFACE) { + if (!eglDestroySurface(_egl_display, _pbuffer)) { + egldisplay_cat.error() << "Failed to destroy surface: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _pbuffer = EGL_NO_SURFACE; + } + } + + _is_valid = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::open_buffer +// Access: Protected, Virtual +// Description: Opens the buffer right now. Called from the window +// thread. Returns true if the buffer is successfully +// opened, or false if there was a problem. +//////////////////////////////////////////////////////////////////// +bool eglGraphicsBuffer:: +open_buffer() { + eglGraphicsPipe *egl_pipe; + DCAST_INTO_R(egl_pipe, _pipe, false); + + // GSG Creation/Initialization + eglGraphicsStateGuardian *eglgsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), true, false); + _gsg = eglgsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) { + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), true, false); + _gsg = eglgsg; + } + } + + if (eglgsg->_fbconfig == None) { + // If we didn't use an fbconfig to create the GSG, we can't create + // a PBuffer. + return false; + } + + int attrib_list[] = { + EGL_WIDTH, _x_size, + EGL_HEIGHT, _y_size, + EGL_NONE + }; + + _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list); + + if (_pbuffer == EGL_NO_SURFACE) { + egldisplay_cat.error() + << "Failed to create EGL pbuffer surface: " + << get_egl_error_string(eglGetError()) << "\n"; + return false; + } + + if (!eglMakeCurrent(eglgsg->_egl_display, _pbuffer, _pbuffer, eglgsg->_context)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + eglgsg->reset_if_new(); + if (!eglgsg->is_valid()) { + close_buffer(); + return false; + } + if (!eglgsg->get_fb_properties().verify_hardware_software + (_fb_properties, eglgsg->get_gl_renderer())) { + close_buffer(); + return false; + } + _fb_properties = eglgsg->get_fb_properties(); + + _is_valid = true; + return true; +} diff --git a/panda/src/egldisplay/eglGraphicsBuffer.h b/panda/src/egldisplay/eglGraphicsBuffer.h new file mode 100644 index 0000000000..b1a5fcd1df --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsBuffer.h @@ -0,0 +1,69 @@ +// Filename: eglGraphicsBuffer.h +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 EGLGRAPHICSBUFFER_H +#define EGLGRAPHICSBUFFER_H + +#include "pandabase.h" + +#include "eglGraphicsPipe.h" +#include "graphicsBuffer.h" + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsBuffer +// Description : An offscreen buffer in the EGL environment. This +// creates an EGL pbuffer. +//////////////////////////////////////////////////////////////////// +class eglGraphicsBuffer : public GraphicsBuffer { +public: + eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~eglGraphicsBuffer(); + + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + +protected: + virtual void close_buffer(); + virtual bool open_buffer(); + +private: + Display *_display; + EGLSurface _pbuffer; + EGLDisplay _egl_display; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsBuffer::init_type(); + register_type(_type_handle, "eglGraphicsBuffer", + GraphicsBuffer::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; +}; + +#endif diff --git a/panda/src/egldisplay/eglGraphicsPipe.I b/panda/src/egldisplay/eglGraphicsPipe.I new file mode 100644 index 0000000000..eacf1fe8fd --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsPipe.I @@ -0,0 +1,72 @@ +// Filename: eglGraphicsPipe.I +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::get_display +// Access: Public +// Description: Returns a pointer to the X display associated with +// the pipe: the display on which to create the windows. +//////////////////////////////////////////////////////////////////// +INLINE Display *eglGraphicsPipe:: +get_display() const { + return _display; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::get_screen +// Access: Public +// Description: Returns the X screen number associated with the pipe. +//////////////////////////////////////////////////////////////////// +INLINE int eglGraphicsPipe:: +get_screen() const { + return _screen; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::get_root +// Access: Public +// Description: Returns the handle to the root window on the pipe's +// display. +//////////////////////////////////////////////////////////////////// +INLINE Window eglGraphicsPipe:: +get_root() const { + return _root; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::get_im +// Access: Public +// Description: Returns the input method opened for the pipe, or NULL +// if the input method could not be opened for some +// reason. +//////////////////////////////////////////////////////////////////// +INLINE XIM eglGraphicsPipe:: +get_im() const { + return _im; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::get_hidden_cursor +// Access: Public +// Description: Returns an invisible Cursor suitable for assigning to +// windows that have the cursor_hidden property set. +//////////////////////////////////////////////////////////////////// +INLINE Cursor eglGraphicsPipe:: +get_hidden_cursor() { + if (_hidden_cursor == None) { + make_hidden_cursor(); + } + return _hidden_cursor; +} diff --git a/panda/src/egldisplay/eglGraphicsPipe.cxx b/panda/src/egldisplay/eglGraphicsPipe.cxx new file mode 100644 index 0000000000..dbc8e89bb6 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsPipe.cxx @@ -0,0 +1,429 @@ +// Filename: eglGraphicsPipe.cxx +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "eglGraphicsBuffer.h" +#include "eglGraphicsPipe.h" +#include "eglGraphicsPixmap.h" +#include "eglGraphicsWindow.h" +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "frameBufferProperties.h" + +TypeHandle eglGraphicsPipe::_type_handle; + +bool eglGraphicsPipe::_error_handlers_installed = false; +eglGraphicsPipe::ErrorHandlerFunc *eglGraphicsPipe::_prev_error_handler; +eglGraphicsPipe::IOErrorHandlerFunc *eglGraphicsPipe::_prev_io_error_handler; + +LightReMutex eglGraphicsPipe::_x_mutex; + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsPipe:: +eglGraphicsPipe(const string &display) { + string display_spec = display; + if (display_spec.empty()) { + display_spec = display_cfg; + } + if (display_spec.empty()) { + display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY"); + } + if (display_spec.empty()) { + display_spec = ":0.0"; + } + + // The X docs say we should do this to get international character + // support from the keyboard. + setlocale(LC_ALL, ""); + + // But it's important that we use the "C" locale for numeric + // formatting, since all of the internal Panda code assumes this--we + // need a decimal point to mean a decimal point. + setlocale(LC_NUMERIC, "C"); + + _is_valid = false; + _supported_types = OT_window | OT_buffer | OT_texture_buffer; + _display = NULL; + _screen = 0; + _root = (Window)NULL; + _im = (XIM)NULL; + _hidden_cursor = None; + _egl_display = NULL; + + install_error_handlers(); + + _display = XOpenDisplay(display_spec.c_str()); + if (!_display) { + egldisplay_cat.error() + << "Could not open display \"" << display_spec << "\".\n"; + return; + } + + if (!XSupportsLocale()) { + egldisplay_cat.warning() + << "X does not support locale " << setlocale(LC_ALL, NULL) << "\n"; + } + XSetLocaleModifiers(""); + + _screen = DefaultScreen(_display); + _root = RootWindow(_display, _screen); + _display_width = DisplayWidth(_display, _screen); + _display_height = DisplayHeight(_display, _screen); + _is_valid = true; + + _egl_display = eglGetDisplay((NativeDisplayType) _display); + if (!eglInitialize(_egl_display, NULL, NULL)) { + egldisplay_cat.error() + << "Couldn't initialize the EGL display: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + // Connect to an input method for supporting international text + // entry. + _im = XOpenIM(_display, NULL, NULL, NULL); + if (_im == (XIM)NULL) { + egldisplay_cat.warning() + << "Couldn't open input method.\n"; + } + + // What styles does the current input method support? + /* + XIMStyles *im_supported_styles; + XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL); + + for (int i = 0; i < im_supported_styles->count_styles; i++) { + XIMStyle style = im_supported_styles->supported_styles[i]; + cerr << "style " << i << ". " << hex << style << dec << "\n"; + } + + XFree(im_supported_styles); + */ + + // Get some X atom numbers. + _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false); + _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false); + _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false); + _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false); + _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false); + _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false); + _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false); + _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false); + _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false); + _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsPipe:: +~eglGraphicsPipe() { + release_hidden_cursor(); + if (_im) { + XCloseIM(_im); + } + if (_display) { + XCloseDisplay(_display); + } + if (_egl_display) { + if (!eglTerminate(_egl_display)) { + egldisplay_cat.error() << "Failed to terminate EGL display: " + << get_egl_error_string(eglGetError()) << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::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 eglGraphicsPipe:: +get_interface_name() const { + return "OpenGL ES"; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::pipe_constructor +// Access: Public, Static +// Description: This function is passed to the GraphicsPipeSelection +// object to allow the user to make a default +// eglGraphicsPipe. +//////////////////////////////////////////////////////////////////// +PT(GraphicsPipe) eglGraphicsPipe:: +pipe_constructor() { + return new eglGraphicsPipe; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::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 +eglGraphicsPipe::get_preferred_window_thread() const { + // Actually, since we're creating the graphics context in + // open_window() now, it appears we need to ensure the open_window() + // call is performed in the draw thread for now, even though X wants + // all of its calls to be single-threaded. + + // This means that all X windows may have to be handled by the same + // draw thread, which we didn't intend (though the global _x_mutex + // may allow them to be technically served by different threads, + // even though the actual X calls will be serialized). There might + // be a better way. + + return PWT_draw; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::make_output +// Access: Protected, Virtual +// Description: Creates a new window on the pipe, if possible. +//////////////////////////////////////////////////////////////////// +PT(GraphicsOutput) eglGraphicsPipe:: +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; + } + + eglGraphicsStateGuardian *eglgsg = 0; + if (gsg != 0) { + DCAST_INTO_R(eglgsg, gsg, NULL); + } + + bool support_rtt; + support_rtt = false; + if (eglgsg) { + support_rtt = + eglgsg -> get_supports_render_texture() && + support_render_texture; + } + // 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 eglGraphicsWindow(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); + } + + // Third 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; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::make_hidden_cursor +// Access: Private +// Description: Called once to make an invisible Cursor for return +// from get_hidden_cursor(). +//////////////////////////////////////////////////////////////////// +void eglGraphicsPipe:: +make_hidden_cursor() { + nassertv(_hidden_cursor == None); + + unsigned int x_size, y_size; + XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size); + + Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1); + + XColor black; + memset(&black, 0, sizeof(black)); + + _hidden_cursor = XCreatePixmapCursor(_display, empty, empty, + &black, &black, x_size, y_size); + XFreePixmap(_display, empty); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::release_hidden_cursor +// Access: Private +// Description: Called once to release the invisible cursor created +// by make_hidden_cursor(). +//////////////////////////////////////////////////////////////////// +void eglGraphicsPipe:: +release_hidden_cursor() { + if (_hidden_cursor != None) { + XFreeCursor(_display, _hidden_cursor); + _hidden_cursor = None; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::install_error_handlers +// Access: Private, Static +// Description: Installs new Xlib error handler functions if this is +// the first time this function has been called. These +// error handler functions will attempt to reduce Xlib's +// annoying tendency to shut down the client at the +// first error. Unfortunately, it is difficult to play +// nice with the client if it has already installed its +// own error handlers. +//////////////////////////////////////////////////////////////////// +void eglGraphicsPipe:: +install_error_handlers() { + if (_error_handlers_installed) { + return; + } + + _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler); + _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler); + _error_handlers_installed = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::error_handler +// Access: Private, Static +// Description: This function is installed as the error handler for a +// non-fatal Xlib error. +//////////////////////////////////////////////////////////////////// +int eglGraphicsPipe:: +error_handler(Display *display, XErrorEvent *error) { + static const int msg_len = 80; + char msg[msg_len]; + XGetErrorText(display, error->error_code, msg, msg_len); + egldisplay_cat.error() + << msg << "\n"; + + if (x_error_abort) { + abort(); + } + + // We return to allow the application to continue running, unlike + // the default X error handler which exits. + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPipe::io_error_handler +// Access: Private, Static +// Description: This function is installed as the error handler for a +// fatal Xlib error. +//////////////////////////////////////////////////////////////////// +int eglGraphicsPipe:: +io_error_handler(Display *display) { + egldisplay_cat.fatal() + << "X fatal error on display " << (void *)display << "\n"; + + // Unfortunately, we can't continue from this function, even if we + // promise never to use X again. We're supposed to terminate + // without returning, and if we do return, the caller will exit + // anyway. Sigh. Very poor design on X's part. + return 0; +} diff --git a/panda/src/egldisplay/eglGraphicsPipe.h b/panda/src/egldisplay/eglGraphicsPipe.h new file mode 100644 index 0000000000..75aa01c084 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsPipe.h @@ -0,0 +1,155 @@ +// Filename: eglGraphicsPipe.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 EGLGRAPHICSPIPE_H +#define EGLGRAPHICSPIPE_H + +#include "pandabase.h" +#include "graphicsWindow.h" +#include "graphicsPipe.h" +#include "lightMutex.h" +#include "lightReMutex.h" + +#ifdef OPENGLES_2 + #include "gles2gsg.h" + #include + #define NativeDisplayType EGLNativeDisplayType + #define NativePixmapType EGLNativePixmapType + #define NativeWindowType EGLNativeWindowType +#else + #include "glesgsg.h" + #include +#endif + +class FrameBufferProperties; + +#ifdef CPPPARSER +// A simple hack so interrogate can parse this file. +typedef int Display; +typedef int Window; +typedef int XErrorEvent; +typedef int XVisualInfo; +typedef int Atom; +typedef int Cursor; +typedef int XIM; +typedef int XIC; +#else +#include + +#endif // CPPPARSER + +class eglGraphicsBuffer; +class eglGraphicsPixmap; +class eglGraphicsWindow; + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsPipe +// Description : This graphics pipe represents the interface for +// creating OpenGL ES graphics windows on an X-based +// (e.g. Unix) client. +//////////////////////////////////////////////////////////////////// +class eglGraphicsPipe : public GraphicsPipe { +public: + eglGraphicsPipe(const string &display = string()); + virtual ~eglGraphicsPipe(); + + virtual string get_interface_name() const; + static PT(GraphicsPipe) pipe_constructor(); + + INLINE Display *get_display() const; + INLINE int get_screen() const; + INLINE Window get_root() const; + INLINE XIM get_im() const; + + INLINE Cursor get_hidden_cursor(); + +public: + virtual PreferredWindowThread get_preferred_window_thread() const; + +public: + // Atom specifications. + Atom _wm_delete_window; + Atom _net_wm_window_type; + Atom _net_wm_window_type_splash; + Atom _net_wm_window_type_fullscreen; + Atom _net_wm_state; + Atom _net_wm_state_fullscreen; + Atom _net_wm_state_above; + Atom _net_wm_state_below; + Atom _net_wm_state_add; + Atom _net_wm_state_remove; + +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: + void make_hidden_cursor(); + void release_hidden_cursor(); + + static void install_error_handlers(); + static int error_handler(Display *display, XErrorEvent *error); + static int io_error_handler(Display *display); + + Display *_display; + int _screen; + Window _root; + XIM _im; + EGLDisplay _egl_display; + + Cursor _hidden_cursor; + + typedef int ErrorHandlerFunc(Display *, XErrorEvent *); + typedef int IOErrorHandlerFunc(Display *); + static bool _error_handlers_installed; + static ErrorHandlerFunc *_prev_error_handler; + static IOErrorHandlerFunc *_prev_io_error_handler; + +public: + // This Mutex protects any X library calls, which all have to be + // single-threaded. In particular, it protects eglMakeCurrent(). + static LightReMutex _x_mutex; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsPipe::init_type(); + register_type(_type_handle, "eglGraphicsPipe", + 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 eglGraphicsBuffer; + friend class eglGraphicsPixmap; + friend class eglGraphicsWindow; +}; + +#include "eglGraphicsPipe.I" + +#endif diff --git a/panda/src/egldisplay/eglGraphicsPixmap.cxx b/panda/src/egldisplay/eglGraphicsPixmap.cxx new file mode 100644 index 0000000000..e45b867a62 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsPixmap.cxx @@ -0,0 +1,258 @@ +// Filename: eglGraphicsPixmap.cxx +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "eglGraphicsPixmap.h" +#include "eglGraphicsWindow.h" +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "pStatTimer.h" + +TypeHandle eglGraphicsPixmap::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsPixmap:: +eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) +{ + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + _display = egl_pipe->get_display(); + _egl_display = egl_pipe->_egl_display; + _drawable = None; + _x_pixmap = None; + _egl_surface = EGL_NO_SURFACE; + + // Since the pixmap never gets flipped, we get screenshots from the + // same pixmap we draw into. + _screenshot_buffer_type = _draw_buffer_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsPixmap:: +~eglGraphicsPixmap() { + nassertv(_x_pixmap == None && _egl_surface == EGL_NO_SURFACE); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::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 eglGraphicsPixmap:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) { + egldisplay_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.) + eglgsg->reset_if_new(); + + if (mode == FM_render) { + for (int i=0; iset_current_properties(&get_fb_properties()); + return _gsg->begin_frame(current_thread); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::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 eglGraphicsPixmap:: +end_frame(FrameMode mode, Thread *current_thread) { + end_frame_spam(mode); + nassertv(_gsg != (GraphicsStateGuardian *)NULL); + + if (mode == FM_render) { + copy_to_textures(); + } + + _gsg->end_frame(current_thread); + + if (mode == FM_render) { + trigger_flip(); + if (_one_shot) { + prepare_for_deletion(); + } + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::close_buffer +// Access: Protected, Virtual +// Description: Closes the pixmap right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void eglGraphicsPixmap:: +close_buffer() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _gsg.clear(); + _active = false; + } + + if (_egl_surface != EGL_NO_SURFACE) { + if (!eglDestroySurface(_egl_display, _egl_surface)) { + egldisplay_cat.error() << "Failed to destroy surface: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _egl_surface = EGL_NO_SURFACE; + } + + if (_x_pixmap != None) { + XFreePixmap(_display, _x_pixmap); + _x_pixmap = None; + } + + _is_valid = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsPixmap::open_buffer +// Access: Protected, Virtual +// Description: Opens the pixmap right now. Called from the window +// thread. Returns true if the pixmap is successfully +// opened, or false if there was a problem. +//////////////////////////////////////////////////////////////////// +bool eglGraphicsPixmap:: +open_buffer() { + eglGraphicsPipe *egl_pipe; + DCAST_INTO_R(egl_pipe, _pipe, false); + + // GSG Creation/Initialization + eglGraphicsStateGuardian *eglgsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL); + eglgsg->choose_pixel_format(_fb_properties, _display, egl_pipe->get_screen(), false, true); + _gsg = eglgsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) { + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg); + eglgsg->choose_pixel_format(_fb_properties, _display, egl_pipe->get_screen(), false, true); + _gsg = eglgsg; + } + } + + if (eglgsg->_fbconfig == None) { + // If we didn't use an fbconfig to create the GSG, we can't create + // a PBuffer. + return false; + } + + XVisualInfo *visual_info = eglgsg->_visual; + if (visual_info == NULL) { + // No X visual for this fbconfig; how can we create the pixmap? + egldisplay_cat.error() + << "No X visual: cannot create pixmap.\n"; + return false; + } + + _drawable = egl_pipe->get_root(); + if (_host != NULL) { + if (_host->is_of_type(eglGraphicsWindow::get_class_type())) { + eglGraphicsWindow *win = DCAST(eglGraphicsWindow, _host); + _drawable = win->get_xwindow(); + } else if (_host->is_of_type(eglGraphicsPixmap::get_class_type())) { + eglGraphicsPixmap *pix = DCAST(eglGraphicsPixmap, _host); + _drawable = pix->_drawable; + } + } + + _x_pixmap = XCreatePixmap(_display, _drawable, + _x_size, _y_size, visual_info->depth); + if (_x_pixmap == None) { + egldisplay_cat.error() + << "Failed to create X pixmap.\n"; + close_buffer(); + return false; + } + + nassertr(eglgsg->_fbconfig, false); + _egl_surface = eglCreatePixmapSurface(_egl_display, eglgsg->_fbconfig, (NativePixmapType) _x_pixmap, NULL); + + if (_egl_surface == EGL_NO_SURFACE) { + egldisplay_cat.error() + << "Failed to create EGL pixmap surface:" + << get_egl_error_string(eglGetError()) << "\n"; + close_buffer(); + return false; + } + + eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context); + eglgsg->reset_if_new(); + if (!eglgsg->is_valid()) { + close_buffer(); + return false; + } + if (!eglgsg->get_fb_properties().verify_hardware_software + (_fb_properties, eglgsg->get_gl_renderer())) { + close_buffer(); + return false; + } + _fb_properties = eglgsg->get_fb_properties(); + + _is_valid = true; + return true; +} diff --git a/panda/src/egldisplay/eglGraphicsPixmap.h b/panda/src/egldisplay/eglGraphicsPixmap.h new file mode 100644 index 0000000000..d0bed223e8 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsPixmap.h @@ -0,0 +1,73 @@ +// Filename: eglGraphicsPixmap.h +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 EGLGRAPHICSPIXMAP_H +#define EGLGRAPHICSPIXMAP_H + +#include "pandabase.h" + +#include "eglGraphicsPipe.h" +#include "graphicsBuffer.h" + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsPixmap +// Description : Another offscreen buffer in the EGL environment. This +// creates a Pixmap object, which is probably less +// efficient than an EGLPBuffer, so this class is a +// second choice to eglGraphicsBuffer. +//////////////////////////////////////////////////////////////////// +class eglGraphicsPixmap : public GraphicsBuffer { +public: + eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~eglGraphicsPixmap(); + + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + +protected: + virtual void close_buffer(); + virtual bool open_buffer(); + +private: + Display *_display; + Window _drawable; + Pixmap _x_pixmap; + EGLSurface _egl_surface; + EGLDisplay _egl_display; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsBuffer::init_type(); + register_type(_type_handle, "eglGraphicsPixmap", + GraphicsBuffer::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; +}; + +#endif diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.I b/panda/src/egldisplay/eglGraphicsStateGuardian.I new file mode 100644 index 0000000000..6af06e2715 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.I @@ -0,0 +1,26 @@ +// Filename: eglGraphicsStateGuardian.I +// 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." +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::get_fb_properties +// Access: Private +// Description: Gets the FrameBufferProperties for all windows and +// buffers that use this GSG. +//////////////////////////////////////////////////////////////////// +INLINE const FrameBufferProperties &eglGraphicsStateGuardian:: +get_fb_properties() const { + return _fbprops; +} + + diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.cxx b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx new file mode 100644 index 0000000000..e962d82d32 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx @@ -0,0 +1,376 @@ +// Filename: eglGraphicsStateGuardian.cxx +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "lightReMutexHolder.h" + +#include + +TypeHandle eglGraphicsStateGuardian::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsStateGuardian:: +eglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + eglGraphicsStateGuardian *share_with) : +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian(engine, pipe) +#else + GLESGraphicsStateGuardian(engine, pipe) +#endif +{ + _share_context=0; + _context=0; + _display=0; + _egl_display=0; + _screen=0; + _visual=0; + _visuals=0; + _fbconfig=0; + + if (share_with != (eglGraphicsStateGuardian *)NULL) { + _prepared_objects = share_with->get_prepared_objects(); + _share_context = share_with->_context; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsStateGuardian:: +~eglGraphicsStateGuardian() { + if (_visuals != (XVisualInfo *)NULL) { + XFree(_visuals); + } + if (_context != (EGLContext)NULL) { + if (!eglDestroyContext(_egl_display, _context)) { + egldisplay_cat.error() << "Failed to destroy EGL context: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _context = (EGLContext)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::get_properties +// Access: Private +// Description: Gets the FrameBufferProperties to match the +// indicated config. +//////////////////////////////////////////////////////////////////// +void eglGraphicsStateGuardian:: +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. + int 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) { + egldisplay_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: eglGraphicsStateGuardian::choose_pixel_format +// Access: Private +// Description: Selects a visual or fbconfig for all the windows +// and buffers that use this gsg. Also creates the GL +// context and obtains the visual. +//////////////////////////////////////////////////////////////////// +void eglGraphicsStateGuardian:: +choose_pixel_format(const FrameBufferProperties &properties, + Display *display, + int screen, bool need_pbuffer, bool need_pixmap) { + + _display = display; + _egl_display = eglGetDisplay((NativeDisplayType) display); + _screen = screen; + _context = 0; + _fbconfig = 0; + _visual = 0; + _visuals = 0; + _fbprops.clear(); + + int attrib_list[] = { + EGL_RENDERABLE_TYPE, EGL_DONT_CARE, + EGL_SURFACE_TYPE, EGL_DONT_CARE, + EGL_NONE + }; + + int num_configs = 0; + EGLConfig configs[32]; + if (!eglChooseConfig(_egl_display, attrib_list, configs, 32, &num_configs) || num_configs <= 0) { + egldisplay_cat.error() << "eglChooseConfig failed: " + << get_egl_error_string(eglGetError()) << "\n"; + return; + } + + int best_quality = 0; + int best_result = 0; + FrameBufferProperties best_props; + + if (configs != 0) { + 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)" : ""; + egldisplay_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; + } + } + } + int depth = DefaultDepth(_display, _screen); + _visual = new XVisualInfo; + XMatchVisualInfo(_display, _screen, depth, TrueColor, _visual); + + if (best_quality > 0) { + egldisplay_cat.debug() + << "Chosen config " << best_result << ": " << best_props << "\n"; + _fbconfig = configs[best_result]; + _context = eglCreateContext(_egl_display, _fbconfig, _share_context, NULL); + int err = eglGetError(); + if (_context && err == EGL_SUCCESS) { + if (_visual) { + _fbprops = best_props; + return; + } + } + // This really shouldn't happen, so I'm not too careful about cleanup. + egldisplay_cat.error() + << "Could not create EGL context!\n" + << get_egl_error_string(err) << "\n"; + _fbconfig = 0; + _context = 0; + _visual = 0; + _visuals = 0; + } + + egldisplay_cat.error() << + "Could not find a usable pixel format.\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state as if the gsg were newly +// created. +//////////////////////////////////////////////////////////////////// +void eglGraphicsStateGuardian:: +reset() { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::reset(); +#else + GLESGraphicsStateGuardian::reset(); +#endif + + // If "Mesa" is present, assume software. However, if "Mesa DRI" is + // found, it's actually a Mesa-based OpenGL layer running over a + // hardware driver. + if (_gl_renderer.find("Mesa") != string::npos && + _gl_renderer.find("Mesa DRI") == string::npos) { + // It's Mesa, therefore probably a software context. + _fbprops.set_force_software(1); + _fbprops.set_force_hardware(0); + } else { + _fbprops.set_force_hardware(1); + _fbprops.set_force_software(0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::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 eglGraphicsStateGuardian:: +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: eglGraphicsStateGuardian::gl_flush +// Access: Protected, Virtual +// Description: Calls glFlush(). +//////////////////////////////////////////////////////////////////// +void eglGraphicsStateGuardian:: +gl_flush() const { + // This call requires synchronization with X. + LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::gl_flush(); +#else + GLESGraphicsStateGuardian::gl_flush(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::gl_get_error +// Access: Protected, Virtual +// Description: Returns the result of glGetError(). +//////////////////////////////////////////////////////////////////// +GLenum eglGraphicsStateGuardian:: +gl_get_error() const { + // This call requires synchronization with X. + LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); +#ifdef OPENGLES_2 + return GLES2GraphicsStateGuardian::gl_get_error(); +#else + return GLESGraphicsStateGuardian::gl_get_error(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::query_gl_version +// Access: Protected, Virtual +// Description: Queries the runtime version of OpenGL in use. +//////////////////////////////////////////////////////////////////// +void eglGraphicsStateGuardian:: +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)) { + egldisplay_cat.error() << "Failed to get EGL version number: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + // We output to glesgsg_cat instead of egldisplay_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: eglGraphicsStateGuardian::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 eglGraphicsStateGuardian:: +get_extra_extensions() { + save_extensions(eglQueryString(_egl_display, EGL_EXTENSIONS)); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsStateGuardian::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 *eglGraphicsStateGuardian:: +do_get_extension_func(const char *prefix, const char *name) { + string fullname = string(prefix) + string(name); + + return (void *)eglGetProcAddress(fullname.c_str()); +} diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.h b/panda/src/egldisplay/eglGraphicsStateGuardian.h new file mode 100644 index 0000000000..745e63109e --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.h @@ -0,0 +1,99 @@ +// Filename: eglGraphicsStateGuardian.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 EGLGRAPHICSSTATEGUARDIAN_H +#define EGLGRAPHICSSTATEGUARDIAN_H + +#include "pandabase.h" +#include "eglGraphicsPipe.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsStateGuardian +// Description : A tiny specialization on GLESGraphicsStateGuardian +// to add some egl-specific information. +//////////////////////////////////////////////////////////////////// +#ifdef OPENGLES_2 +class eglGraphicsStateGuardian : public GLES2GraphicsStateGuardian { +#else +class eglGraphicsStateGuardian : 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, + Display *_display, + int _screen, + bool need_pbuffer, bool need_pixmap); + + eglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + eglGraphicsStateGuardian *share_with); + + virtual ~eglGraphicsStateGuardian(); + + virtual void reset(); + + bool egl_is_at_least_version(int major_version, int minor_version) const; + + EGLContext _share_context; + EGLContext _context; + EGLDisplay _egl_display; + Display *_display; + int _screen; + XVisualInfo *_visual; + XVisualInfo *_visuals; + EGLConfig _fbconfig; + 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; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::init_type(); + register_type(_type_handle, "eglGraphicsStateGuardian", + GLES2GraphicsStateGuardian::get_class_type()); +#else + GLESGraphicsStateGuardian::init_type(); + register_type(_type_handle, "eglGraphicsStateGuardian", + 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 "eglGraphicsStateGuardian.I" + +#endif diff --git a/panda/src/egldisplay/eglGraphicsWindow.I b/panda/src/egldisplay/eglGraphicsWindow.I new file mode 100644 index 0000000000..eba52cc6a6 --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsWindow.I @@ -0,0 +1,24 @@ +// Filename: eglGraphicsWindow.I +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::get_xwindow +// Access: Public +// Description: Returns the X11 Window handle. +//////////////////////////////////////////////////////////////////// +INLINE Window eglGraphicsWindow:: +get_xwindow() const { + return _xwindow; +} diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx new file mode 100644 index 0000000000..ff3f62f1dc --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -0,0 +1,1684 @@ +// Filename: eglGraphicsWindow.cxx +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "eglGraphicsWindow.h" +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "keyboardButton.h" +#include "mouseButton.h" +#include "clockObject.h" +#include "pStatTimer.h" +#include "textEncoder.h" +#include "throw_event.h" +#include "lightReMutexHolder.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_LINUX_INPUT_H +#include +#endif + +TypeHandle eglGraphicsWindow::_type_handle; + +#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7))) + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsWindow:: +eglGraphicsWindow(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) +{ + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + _display = egl_pipe->get_display(); + _screen = egl_pipe->get_screen(); + _xwindow = (Window)NULL; + _ic = (XIC)NULL; + _egl_display = egl_pipe->_egl_display; + _egl_surface = 0; + _awaiting_configure = false; + _wm_delete_window = egl_pipe->_wm_delete_window; + _net_wm_window_type = egl_pipe->_net_wm_window_type; + _net_wm_window_type_splash = egl_pipe->_net_wm_window_type_splash; + _net_wm_window_type_fullscreen = egl_pipe->_net_wm_window_type_fullscreen; + _net_wm_state = egl_pipe->_net_wm_state; + _net_wm_state_fullscreen = egl_pipe->_net_wm_state_fullscreen; + _net_wm_state_above = egl_pipe->_net_wm_state_above; + _net_wm_state_below = egl_pipe->_net_wm_state_below; + _net_wm_state_add = egl_pipe->_net_wm_state_add; + _net_wm_state_remove = egl_pipe->_net_wm_state_remove; + + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); + add_input_device(device); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsWindow:: +~eglGraphicsWindow() { +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::move_pointer +// Access: Published, Virtual +// Description: Forces the pointer to the indicated position within +// the window, if possible. +// +// Returns true if successful, false on failure. This +// may fail if the mouse is not currently within the +// window, or if the API doesn't support this operation. +//////////////////////////////////////////////////////////////////// +bool eglGraphicsWindow:: +move_pointer(int device, int x, int y) { + // Note: this is not thread-safe; it should be called only from App. + // Probably not an issue. + if (device == 0) { + // Move the system mouse pointer. + if (!_properties.get_foreground() || + !_input_devices[0].get_pointer().get_in_window()) { + // If the window doesn't have input focus, or the mouse isn't + // currently within the window, forget it. + return false; + } + + const MouseData &md = _input_devices[0].get_pointer(); + if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) { + XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y); + _input_devices[0].set_pointer_in_window(x, y); + } + return true; + } else { + // Move a raw mouse. + if ((device < 1)||(device >= _input_devices.size())) { + return false; + } + _input_devices[device].set_pointer_in_window(x, y); + return true; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::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 eglGraphicsWindow:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + if (_awaiting_configure) { + // Don't attempt to draw while we have just reconfigured the + // window and we haven't got the notification back yet. + return false; + } + + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_R(eglgsg, _gsg, false); + { + LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); + + if (eglGetCurrentDisplay() == _egl_display && + eglGetCurrentSurface(EGL_READ) == _egl_surface && + eglGetCurrentSurface(EGL_DRAW) == _egl_surface && + eglGetCurrentContext() == eglgsg->_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, eglgsg->_context)) { + egldisplay_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.) + eglgsg->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: eglGraphicsWindow::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 eglGraphicsWindow:: +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(); + if (_one_shot) { + prepare_for_deletion(); + } + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::begin_flip +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after end_frame() has been called on all windows, to +// initiate the exchange of the front and back buffers. +// +// This should instruct the window to prepare for the +// flip at the next video sync, but it should not wait. +// +// We have the two separate functions, begin_flip() and +// end_flip(), to make it easier to flip all of the +// windows at the same time. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +begin_flip() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + + // 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(); + + LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); + eglSwapBuffers(_egl_display, _egl_surface); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::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 eglGraphicsWindow:: +process_events() { + LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); + + GraphicsWindow::process_events(); + + if (_xwindow == (Window)0) { + return; + } + + poll_raw_mice(); + + XEvent event; + XKeyEvent keyrelease_event; + bool got_keyrelease_event = false; + + while (XCheckIfEvent(_display, &event, check_event, (char *)this)) { + if (XFilterEvent(&event, None)) { + continue; + } + + if (got_keyrelease_event) { + // If a keyrelease event is immediately followed by a matching + // keypress event, that's just key repeat and we should treat + // the two events accordingly. It would be nice if X provided a + // way to differentiate between keyrepeat and explicit + // keypresses more generally. + got_keyrelease_event = false; + + if (event.type == KeyPress && + event.xkey.keycode == keyrelease_event.keycode && + (event.xkey.time - keyrelease_event.time <= 1)) { + // In particular, we only generate down messages for the + // repeated keys, not down-and-up messages. + handle_keystroke(event.xkey); + + // We thought about not generating the keypress event, but we + // need that repeat for backspace. Rethink later. + handle_keypress(event.xkey); + continue; + + } else { + // This keyrelease event is not immediately followed by a + // matching keypress event, so it's a genuine release. + handle_keyrelease(keyrelease_event); + } + } + + WindowProperties properties; + ButtonHandle button; + + switch (event.type) { + case ReparentNotify: + break; + + case ConfigureNotify: + _awaiting_configure = false; + if (_properties.get_fixed_size()) { + // If the window properties indicate a fixed size only, undo + // any attempt by the user to change them. In X, there + // doesn't appear to be a way to universally disallow this + // directly (although we do set the min_size and max_size to + // the same value, which seems to work for most window + // managers.) + WindowProperties current_props = get_properties(); + if (event.xconfigure.width != current_props.get_x_size() || + event.xconfigure.height != current_props.get_y_size()) { + XWindowChanges changes; + changes.width = current_props.get_x_size(); + changes.height = current_props.get_y_size(); + int value_mask = (CWWidth | CWHeight); + XConfigureWindow(_display, _xwindow, value_mask, &changes); + } + + } else { + // A normal window may be resized by the user at will. + properties.set_size(event.xconfigure.width, event.xconfigure.height); + system_changed_properties(properties); + } + break; + + case ButtonPress: + // This refers to the mouse buttons. + button = get_mouse_button(event.xbutton); + _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input_devices[0].button_down(button); + break; + + case ButtonRelease: + button = get_mouse_button(event.xbutton); + _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input_devices[0].button_up(button); + break; + + case MotionNotify: + _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y); + break; + + case KeyPress: + handle_keystroke(event.xkey); + handle_keypress(event.xkey); + break; + + case KeyRelease: + // The KeyRelease can't be processed immediately, because we + // have to check first if it's immediately followed by a + // matching KeyPress event. + keyrelease_event = event.xkey; + got_keyrelease_event = true; + break; + + case EnterNotify: + _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); + break; + + case LeaveNotify: + _input_devices[0].set_pointer_out_of_window(); + break; + + case FocusIn: + properties.set_foreground(true); + system_changed_properties(properties); + break; + + case FocusOut: + properties.set_foreground(false); + system_changed_properties(properties); + break; + + case UnmapNotify: + properties.set_minimized(true); + system_changed_properties(properties); + break; + + case MapNotify: + properties.set_minimized(false); + system_changed_properties(properties); + + // Auto-focus the window when it is mapped. + XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime); + break; + + case ClientMessage: + if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) { + // This is a message from the window manager indicating that + // the user has requested to close the window. + string close_request_event = get_close_request_event(); + if (!close_request_event.empty()) { + // In this case, the app has indicated a desire to intercept + // the request and process it directly. + throw_event(close_request_event); + + } else { + // In this case, the default case, the app does not intend + // to service the request, so we do by closing the window. + + // TODO: don't release the gsg in the window thread. + close_window(); + properties.set_open(false); + system_changed_properties(properties); + } + } + break; + + case DestroyNotify: + // Apparently, we never get a DestroyNotify on a toplevel + // window. Instead, we rely on hints from the window manager + // (see above). + egldisplay_cat.info() + << "DestroyNotify\n"; + break; + + default: + egldisplay_cat.error() + << "unhandled X event type " << event.type << "\n"; + } + } + + if (got_keyrelease_event) { + // This keyrelease event is not immediately followed by a + // matching keypress event, so it's a genuine release. + handle_keyrelease(keyrelease_event); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::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 eglGraphicsWindow:: +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; + } + + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + + // Fullscreen mode is implemented with a hint to the window manager. + // However, we also implicitly set the origin to (0, 0) and the size + // to the desktop size, and request undecorated mode, in case the + // user has a less-capable window manager (or no window manager at + // all). + if (properties.get_fullscreen()) { + properties.set_undecorated(true); + properties.set_origin(0, 0); + properties.set_size(egl_pipe->get_display_width(), + egl_pipe->get_display_height()); + } + + GraphicsWindow::set_properties_now(properties); + if (!properties.is_any_specified()) { + // The base class has already handled this case. + return; + } + + // The window is already open; we are limited to what we can change + // on the fly. + + // We'll pass some property requests on as a window manager hint. + WindowProperties wm_properties = _properties; + wm_properties.add_properties(properties); + + // The window title may be changed by issuing another hint request. + // Assume this will be honored. + if (properties.has_title()) { + _properties.set_title(properties.get_title()); + properties.clear_title(); + } + + // Ditto for fullscreen mode. + if (properties.has_fullscreen()) { + _properties.set_fullscreen(properties.get_fullscreen()); + properties.clear_fullscreen(); + } + + // The size and position of an already-open window are changed via + // explicit X calls. These may still get intercepted by the window + // manager. Rather than changing _properties immediately, we'll + // wait for the ConfigureNotify message to come back. + XWindowChanges changes; + int value_mask = 0; + + if (properties.has_origin()) { + changes.x = properties.get_x_origin(); + changes.y = properties.get_y_origin(); + value_mask |= (CWX | CWY); + properties.clear_origin(); + } + if (properties.has_size()) { + changes.width = properties.get_x_size(); + changes.height = properties.get_y_size(); + value_mask |= (CWWidth | CWHeight); + properties.clear_size(); + } + if (properties.has_z_order()) { + // We'll send the classic stacking request through the standard + // interface, for users of primitive window managers; but we'll + // also send it as a window manager hint, for users of modern + // window managers. + _properties.set_z_order(properties.get_z_order()); + switch (properties.get_z_order()) { + case WindowProperties::Z_bottom: + changes.stack_mode = Below; + break; + + case WindowProperties::Z_normal: + changes.stack_mode = TopIf; + break; + + case WindowProperties::Z_top: + changes.stack_mode = Above; + break; + } + + value_mask |= (CWStackMode); + properties.clear_z_order(); + } + + if (value_mask != 0) { + XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes); + + // Don't draw anything until this is done reconfiguring. + _awaiting_configure = true; + } + + // We hide the cursor by setting it to an invisible pixmap. + if (properties.has_cursor_hidden()) { + _properties.set_cursor_hidden(properties.get_cursor_hidden()); + if (properties.get_cursor_hidden()) { + XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor()); + } else { + XDefineCursor(_display, _xwindow, None); + } + properties.clear_cursor_hidden(); + } + + if (properties.has_foreground()) { + if (properties.get_foreground()) { + XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime); + } else { + XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime); + } + properties.clear_foreground(); + } + + set_wm_properties(wm_properties, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::close_window +// Access: Protected, Virtual +// Description: Closes the window right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +close_window() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _gsg.clear(); + _active = false; + } + + if (_ic != (XIC)NULL) { + XDestroyIC(_ic); + _ic = (XIC)NULL; + } + + if (_egl_surface != 0) { + if (!eglDestroySurface(_egl_display, _egl_surface)) { + egldisplay_cat.error() << "Failed to destroy surface: " + << get_egl_error_string(eglGetError()) << "\n"; + } + } + + if (_xwindow != (Window)NULL) { + XDestroyWindow(_display, _xwindow); + _xwindow = (Window)NULL; + + // This may be necessary if we just closed the last X window in an + // application, so the server hears the close request. + XFlush(_display); + } + GraphicsWindow::close_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::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 eglGraphicsWindow:: +open_window() { + eglGraphicsPipe *egl_pipe; + DCAST_INTO_R(egl_pipe, _pipe, false); + + // GSG Creation/Initialization + eglGraphicsStateGuardian *eglgsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false); + _gsg = eglgsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) { + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false); + _gsg = eglgsg; + } + } + + XVisualInfo *visual_info = eglgsg->_visual; + if (visual_info == NULL) { + // No X visual for this fbconfig; how can we open the window? + egldisplay_cat.error() + << "No X visual: cannot open window.\n"; + return false; + } + Visual *visual = visual_info->visual; + int depth = visual_info->depth; + + if (!_properties.has_origin()) { + _properties.set_origin(0, 0); + } + if (!_properties.has_size()) { + _properties.set_size(100, 100); + } + + Window root_window; + if (!_properties.has_parent_window()) { + root_window = egl_pipe->get_root(); + } else { + root_window = (Window) (int) _properties.get_parent_window(); + } + + setup_colormap(visual_info); + + _event_mask = + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | + FocusChangeMask | + StructureNotifyMask; + + // Initialize window attributes + XSetWindowAttributes wa; + wa.background_pixel = XBlackPixel(_display, _screen); + wa.border_pixel = 0; + wa.colormap = _colormap; + wa.event_mask = _event_mask; + + unsigned long attrib_mask = + CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + _xwindow = XCreateWindow + (_display, root_window, + _properties.get_x_origin(), _properties.get_y_origin(), + _properties.get_x_size(), _properties.get_y_size(), + 0, depth, InputOutput, visual, attrib_mask, &wa); + + if (_xwindow == (Window)0) { + egldisplay_cat.error() + << "failed to create X window.\n"; + return false; + } + set_wm_properties(_properties, false); + + // We don't specify any fancy properties of the XIC. It would be + // nicer if we could support fancy IM's that want preedit callbacks, + // etc., but that can wait until we have an X server that actually + // supports these to test it on. + XIM im = egl_pipe->get_im(); + _ic = NULL; + if (im) { + _ic = XCreateIC + (im, + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + (void*)NULL); + if (_ic == (XIC)NULL) { + egldisplay_cat.warning() + << "Couldn't create input context.\n"; + } + } + + if (_properties.get_cursor_hidden()) { + XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor()); + } + + _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL); + if (eglGetError() != EGL_SUCCESS) { + egldisplay_cat.error() + << "Failed to create window surface.\n"; + return false; + } + + if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + eglgsg->reset_if_new(); + if (!eglgsg->is_valid()) { + close_window(); + return false; + } + if (!eglgsg->get_fb_properties().verify_hardware_software + (_fb_properties, eglgsg->get_gl_renderer())) { + close_window(); + return false; + } + _fb_properties = eglgsg->get_fb_properties(); + + XMapWindow(_display, _xwindow); + + if (_properties.get_raw_mice()) { + open_raw_mice(); + } else { + if (egldisplay_cat.is_debug()) { + egldisplay_cat.debug() + << "Raw mice not requested.\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::set_wm_properties +// Access: Private +// Description: Asks the window manager to set the appropriate +// properties. In X, these properties cannot be +// specified directly by the application; they must be +// requested via the window manager, which may or may +// not choose to honor the request. +// +// If already_mapped is true, the window has already +// been mapped (manifested) on the display. This means +// we may need to use a different action in some cases. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +set_wm_properties(const WindowProperties &properties, bool already_mapped) { + // Name the window if there is a name + XTextProperty window_name; + XTextProperty *window_name_p = (XTextProperty *)NULL; + if (properties.has_title()) { + char *name = (char *)properties.get_title().c_str(); + if (XStringListToTextProperty(&name, 1, &window_name) != 0) { + window_name_p = &window_name; + } + } + + // The size hints request a window of a particular size and/or a + // particular placement onscreen. + XSizeHints *size_hints_p = NULL; + if (properties.has_origin() || properties.has_size()) { + size_hints_p = XAllocSizeHints(); + if (size_hints_p != (XSizeHints *)NULL) { + if (properties.has_origin()) { + size_hints_p->x = properties.get_x_origin(); + size_hints_p->y = properties.get_y_origin(); + size_hints_p->flags |= USPosition; + } + if (properties.has_size()) { + size_hints_p->width = properties.get_x_size(); + size_hints_p->height = properties.get_y_size(); + size_hints_p->flags |= USSize; + + if (properties.has_fixed_size()) { + size_hints_p->min_width = properties.get_x_size(); + size_hints_p->min_height = properties.get_y_size(); + size_hints_p->max_width = properties.get_x_size(); + size_hints_p->max_height = properties.get_y_size(); + size_hints_p->flags |= (PMinSize | PMaxSize); + } + } + } + } + + // The window manager hints include requests to the window manager + // other than those specific to window geometry. + XWMHints *wm_hints_p = NULL; + wm_hints_p = XAllocWMHints(); + if (wm_hints_p != (XWMHints *)NULL) { + if (properties.has_minimized() && properties.get_minimized()) { + wm_hints_p->initial_state = IconicState; + } else { + wm_hints_p->initial_state = NormalState; + } + wm_hints_p->flags = StateHint; + } + + // Two competing window manager interfaces have evolved. One of + // them allows to set certain properties as a "type"; the other one + // as a "state". We'll try to honor both. + static const int max_type_data = 32; + PN_int32 type_data[max_type_data]; + int next_type_data = 0; + + static const int max_state_data = 32; + PN_int32 state_data[max_state_data]; + int next_state_data = 0; + + static const int max_set_data = 32; + class SetAction { + public: + inline SetAction() { } + inline SetAction(Atom state, Atom action) : _state(state), _action(action) { } + Atom _state; + Atom _action; + }; + SetAction set_data[max_set_data]; + int next_set_data = 0; + + if (properties.get_fullscreen()) { + // For a "fullscreen" request, we pass this through, hoping the + // window manager will support EWMH. + type_data[next_type_data++] = _net_wm_window_type_fullscreen; + + // We also request it as a state. + state_data[next_state_data++] = _net_wm_state_fullscreen; + set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add); + } else { + set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove); + } + + // If we asked for a window without a border, there's no excellent + // way to arrange that. For users whose window managers follow the + // EWMH specification, we can ask for a "splash" screen, which is + // usually undecorated. It's not exactly right, but the spec + // doesn't give us an exactly-right option. + + // For other users, we'll totally punt and just set the window's + // Class to "Undecorated", and let the user configure his/her window + // manager not to put a border around windows of this class. + XClassHint *class_hints_p = NULL; + if (properties.get_undecorated()) { + class_hints_p = XAllocClassHint(); + class_hints_p->res_class = (char*) "Undecorated"; + + if (!properties.get_fullscreen()) { + type_data[next_type_data++] = _net_wm_window_type_splash; + } + } + + if (properties.has_z_order()) { + switch (properties.get_z_order()) { + case WindowProperties::Z_bottom: + state_data[next_state_data++] = _net_wm_state_below; + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); + break; + + case WindowProperties::Z_normal: + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); + break; + + case WindowProperties::Z_top: + state_data[next_state_data++] = _net_wm_state_above; + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add); + break; + } + } + + nassertv(next_type_data < max_type_data); + nassertv(next_state_data < max_state_data); + nassertv(next_set_data < max_set_data); + + XChangeProperty(_display, _xwindow, _net_wm_window_type, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)type_data, next_type_data); + + // Request the state properties all at once. + XChangeProperty(_display, _xwindow, _net_wm_state, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)state_data, next_state_data); + + if (already_mapped) { + // We have to request state changes differently when the window + // has been mapped. To do this, we need to send a client message + // to the root window for each change. + + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + + for (int i = 0; i < next_set_data; ++i) { + XClientMessageEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.send_event = True; + event.display = _display; + event.window = _xwindow; + event.message_type = _net_wm_state; + event.format = 32; + event.data.l[0] = set_data[i]._action; + event.data.l[1] = set_data[i]._state; + event.data.l[2] = 0; + event.data.l[3] = 1; + + XSendEvent(_display, egl_pipe->get_root(), True, 0, (XEvent *)&event); + } + } + + XSetWMProperties(_display, _xwindow, window_name_p, window_name_p, + NULL, 0, size_hints_p, wm_hints_p, class_hints_p); + + if (size_hints_p != (XSizeHints *)NULL) { + XFree(size_hints_p); + } + if (wm_hints_p != (XWMHints *)NULL) { + XFree(wm_hints_p); + } + if (class_hints_p != (XClassHint *)NULL) { + XFree(class_hints_p); + } + + // Also, indicate to the window manager that we'd like to get a + // chance to close our windows cleanly, rather than being rudely + // disconnected from the X server if the user requests a window + // close. + Atom protocols[] = { + _wm_delete_window, + }; + + XSetWMProtocols(_display, _xwindow, protocols, + sizeof(protocols) / sizeof(Atom)); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::setup_colormap +// Access: Private +// Description: Allocates a colormap appropriate to the visual and +// stores in in the _colormap method. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +setup_colormap(XVisualInfo *visual) { + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + Window root_window = egl_pipe->get_root(); + + int visual_class = visual->c_class; + int rc, is_rgb; + + switch (visual_class) { + case PseudoColor: + _colormap = XCreateColormap(_display, root_window, + visual->visual, AllocAll); + break; + case TrueColor: + case DirectColor: + _colormap = XCreateColormap(_display, root_window, + visual->visual, AllocNone); + break; + case StaticColor: + case StaticGray: + case GrayScale: + _colormap = XCreateColormap(_display, root_window, + visual->visual, AllocNone); + break; + default: + egldisplay_cat.error() + << "Could not allocate a colormap for visual class " + << visual_class << ".\n"; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::open_raw_mice +// Access: Private +// Description: Adds raw mice to the _input_devices list. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +open_raw_mice() +{ +#ifdef HAVE_LINUX_INPUT_H + bool any_present = false; + bool any_mice = false; + + for (int i=0; i<64; i++) { + uint8_t evtypes[EV_MAX/8 + 1]; + ostringstream fnb; + fnb << "/dev/input/event" << i; + string fn = fnb.str(); + int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0); + if (fd >= 0) { + any_present = true; + char name[256]; + char phys[256]; + char uniq[256]; + if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)|| + (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)|| + (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)|| + (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) { + close(fd); + egldisplay_cat.error() << + "Opening raw mice: ioctl failed on " << fn << "\n"; + } else { + if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) { + for (char *p=name; *p; p++) { + if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { + *p = '_'; + } + } + for (char *p=uniq; *p; p++) { + if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { + *p = '_'; + } + } + string full_id = ((string)name) + "." + uniq; + MouseDeviceInfo inf; + inf._fd = fd; + inf._input_device_index = _input_devices.size(); + inf._io_buffer = ""; + _mouse_device_info.push_back(inf); + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_only(this, full_id); + add_input_device(device); + egldisplay_cat.info() << "Raw mouse " << + inf._input_device_index << " detected: " << full_id << "\n"; + any_mice = true; + } else { + close(fd); + } + } + } else { + if ((errno == ENOENT)||(errno == ENOTDIR)) { + break; + } else { + any_present = true; + egldisplay_cat.error() << + "Opening raw mice: " << strerror(errno) << " " << fn << "\n"; + } + } + } + + if (!any_present) { + egldisplay_cat.error() << + "Opening raw mice: files not found: /dev/input/event*\n"; + } else if (!any_mice) { + egldisplay_cat.error() << + "Opening raw mice: no mouse devices detected in /dev/input/event*\n"; + } +#else + egldisplay_cat.error() << + "Opening raw mice: panda not compiled with raw mouse support.\n"; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::poll_raw_mice +// Access: Private +// Description: Reads events from the raw mouse device files. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +poll_raw_mice() +{ +#ifdef HAVE_LINUX_INPUT_H + for (int dev=0; dev<_mouse_device_info.size(); dev++) { + MouseDeviceInfo &inf = _mouse_device_info[dev]; + + // Read all bytes into buffer. + if (inf._fd >= 0) { + while (1) { + char tbuf[1024]; + int nread = read(inf._fd, tbuf, sizeof(tbuf)); + if (nread > 0) { + inf._io_buffer += string(tbuf, nread); + } else { + if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) { + break; + } + close(inf._fd); + inf._fd = -1; + break; + } + } + } + + // Process events. + int nevents = inf._io_buffer.size() / sizeof(struct input_event); + if (nevents == 0) { + continue; + } + const input_event *events = (const input_event *)(inf._io_buffer.c_str()); + GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index]; + int x = dev.get_raw_pointer().get_x(); + int y = dev.get_raw_pointer().get_y(); + for (int i=0; i= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) { + int btn = events[i].code - BTN_MOUSE; + dev.set_pointer_in_window(x,y); + if (events[i].value) { + dev.button_down(MouseButton::button(btn)); + } else { + dev.button_up(MouseButton::button(btn)); + } + } + } + } + inf._io_buffer.erase(0,nevents*sizeof(struct input_event)); + dev.set_pointer_in_window(x,y); + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::handle_keystroke +// Access: Private +// Description: Generates a keystroke corresponding to the indicated +// X KeyPress event. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +handle_keystroke(XKeyEvent &event) { + _input_devices[0].set_pointer_in_window(event.x, event.y); + + if (_ic) { + // First, get the keystroke as a wide-character sequence. + static const int buffer_size = 256; + wchar_t buffer[buffer_size]; + Status status; + int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL, + &status); + if (status == XBufferOverflow) { + egldisplay_cat.error() + << "Overflowed input buffer.\n"; + } + + // Now each of the returned wide characters represents a + // keystroke. + for (int i = 0; i < len; i++) { + _input_devices[0].keystroke(buffer[i]); + } + + } else { + // Without an input context, just get the ascii keypress. + ButtonHandle button = get_button(event, true); + if (button.has_ascii_equivalent()) { + _input_devices[0].keystroke(button.get_ascii_equivalent()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::handle_keypress +// Access: Private +// Description: Generates a keypress corresponding to the indicated +// X KeyPress event. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +handle_keypress(XKeyEvent &event) { + _input_devices[0].set_pointer_in_window(event.x, event.y); + + // Now get the raw unshifted button. + ButtonHandle button = get_button(event, false); + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_down(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_down(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_down(KeyboardButton::alt()); + } + if (button != ButtonHandle::none()) { + _input_devices[0].button_down(button); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::handle_keyrelease +// Access: Private +// Description: Generates a keyrelease corresponding to the indicated +// X KeyRelease event. +//////////////////////////////////////////////////////////////////// +void eglGraphicsWindow:: +handle_keyrelease(XKeyEvent &event) { + _input_devices[0].set_pointer_in_window(event.x, event.y); + + // Now get the raw unshifted button. + ButtonHandle button = get_button(event, false); + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_up(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_up(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_up(KeyboardButton::alt()); + } + if (button != ButtonHandle::none()) { + _input_devices[0].button_up(button); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::get_button +// Access: Private +// Description: Returns the Panda ButtonHandle corresponding to the +// keyboard button indicated by the given key event. +//////////////////////////////////////////////////////////////////// +ButtonHandle eglGraphicsWindow:: +get_button(XKeyEvent &key_event, bool allow_shift) { + KeySym key = XLookupKeysym(&key_event, 0); + + if ((key_event.state & Mod2Mask) != 0) { + // Mod2Mask corresponds to NumLock being in effect. In this case, + // we want to get the alternate keysym associated with any keypad + // keys. Weird system. + KeySym k2; + ButtonHandle button; + switch (key) { + case XK_KP_Space: + case XK_KP_Tab: + case XK_KP_Enter: + case XK_KP_F1: + case XK_KP_F2: + case XK_KP_F3: + case XK_KP_F4: + case XK_KP_Equal: + case XK_KP_Multiply: + case XK_KP_Add: + case XK_KP_Separator: + case XK_KP_Subtract: + case XK_KP_Divide: + case XK_KP_Left: + case XK_KP_Up: + case XK_KP_Right: + case XK_KP_Down: + case XK_KP_Begin: + case XK_KP_Prior: + case XK_KP_Next: + case XK_KP_Home: + case XK_KP_End: + case XK_KP_Insert: + case XK_KP_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + k2 = XLookupKeysym(&key_event, 1); + button = map_button(k2); + if (button != ButtonHandle::none()) { + return button; + } + // If that didn't produce a button we know, just fall through + // and handle the normal, un-numlocked key. + break; + + default: + break; + } + } + + if (allow_shift) { + // If shift is held down, get the shifted keysym. + if ((key_event.state & ShiftMask) != 0) { + KeySym k2 = XLookupKeysym(&key_event, 1); + ButtonHandle button = map_button(k2); + if (button != ButtonHandle::none()) { + return button; + } + } + + // If caps lock is down, shift lowercase letters to uppercase. We + // can do this in just the ASCII set, because we handle + // international keyboards elsewhere (via an input context). + if ((key_event.state & (ShiftMask | LockMask)) != 0) { + if (key >= XK_a and key <= XK_z) { + key += (XK_A - XK_a); + } + } + } + + return map_button(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::map_button +// Access: Private +// Description: Maps from a single X keysym to Panda's ButtonHandle. +// Called by get_button(), above. +//////////////////////////////////////////////////////////////////// +ButtonHandle eglGraphicsWindow:: +map_button(KeySym key) { + switch (key) { + case XK_BackSpace: + return KeyboardButton::backspace(); + case XK_Tab: + case XK_KP_Tab: + return KeyboardButton::tab(); + case XK_Return: + case XK_KP_Enter: + return KeyboardButton::enter(); + case XK_Escape: + return KeyboardButton::escape(); + case XK_KP_Space: + case XK_space: + return KeyboardButton::space(); + case XK_exclam: + return KeyboardButton::ascii_key('!'); + case XK_quotedbl: + return KeyboardButton::ascii_key('"'); + case XK_numbersign: + return KeyboardButton::ascii_key('#'); + case XK_dollar: + return KeyboardButton::ascii_key('$'); + case XK_percent: + return KeyboardButton::ascii_key('%'); + case XK_ampersand: + return KeyboardButton::ascii_key('&'); + case XK_apostrophe: // == XK_quoteright + return KeyboardButton::ascii_key('\''); + case XK_parenleft: + return KeyboardButton::ascii_key('('); + case XK_parenright: + return KeyboardButton::ascii_key(')'); + case XK_asterisk: + case XK_KP_Multiply: + return KeyboardButton::ascii_key('*'); + case XK_plus: + case XK_KP_Add: + return KeyboardButton::ascii_key('+'); + case XK_comma: + case XK_KP_Separator: + return KeyboardButton::ascii_key(','); + case XK_minus: + case XK_KP_Subtract: + return KeyboardButton::ascii_key('-'); + case XK_period: + case XK_KP_Decimal: + return KeyboardButton::ascii_key('.'); + case XK_slash: + case XK_KP_Divide: + return KeyboardButton::ascii_key('/'); + case XK_0: + case XK_KP_0: + return KeyboardButton::ascii_key('0'); + case XK_1: + case XK_KP_1: + return KeyboardButton::ascii_key('1'); + case XK_2: + case XK_KP_2: + return KeyboardButton::ascii_key('2'); + case XK_3: + case XK_KP_3: + return KeyboardButton::ascii_key('3'); + case XK_4: + case XK_KP_4: + return KeyboardButton::ascii_key('4'); + case XK_5: + case XK_KP_5: + return KeyboardButton::ascii_key('5'); + case XK_6: + case XK_KP_6: + return KeyboardButton::ascii_key('6'); + case XK_7: + case XK_KP_7: + return KeyboardButton::ascii_key('7'); + case XK_8: + case XK_KP_8: + return KeyboardButton::ascii_key('8'); + case XK_9: + case XK_KP_9: + return KeyboardButton::ascii_key('9'); + case XK_colon: + return KeyboardButton::ascii_key(':'); + case XK_semicolon: + return KeyboardButton::ascii_key(';'); + case XK_less: + return KeyboardButton::ascii_key('<'); + case XK_equal: + case XK_KP_Equal: + return KeyboardButton::ascii_key('='); + case XK_greater: + return KeyboardButton::ascii_key('>'); + case XK_question: + return KeyboardButton::ascii_key('?'); + case XK_at: + return KeyboardButton::ascii_key('@'); + case XK_A: + return KeyboardButton::ascii_key('A'); + case XK_B: + return KeyboardButton::ascii_key('B'); + case XK_C: + return KeyboardButton::ascii_key('C'); + case XK_D: + return KeyboardButton::ascii_key('D'); + case XK_E: + return KeyboardButton::ascii_key('E'); + case XK_F: + return KeyboardButton::ascii_key('F'); + case XK_G: + return KeyboardButton::ascii_key('G'); + case XK_H: + return KeyboardButton::ascii_key('H'); + case XK_I: + return KeyboardButton::ascii_key('I'); + case XK_J: + return KeyboardButton::ascii_key('J'); + case XK_K: + return KeyboardButton::ascii_key('K'); + case XK_L: + return KeyboardButton::ascii_key('L'); + case XK_M: + return KeyboardButton::ascii_key('M'); + case XK_N: + return KeyboardButton::ascii_key('N'); + case XK_O: + return KeyboardButton::ascii_key('O'); + case XK_P: + return KeyboardButton::ascii_key('P'); + case XK_Q: + return KeyboardButton::ascii_key('Q'); + case XK_R: + return KeyboardButton::ascii_key('R'); + case XK_S: + return KeyboardButton::ascii_key('S'); + case XK_T: + return KeyboardButton::ascii_key('T'); + case XK_U: + return KeyboardButton::ascii_key('U'); + case XK_V: + return KeyboardButton::ascii_key('V'); + case XK_W: + return KeyboardButton::ascii_key('W'); + case XK_X: + return KeyboardButton::ascii_key('X'); + case XK_Y: + return KeyboardButton::ascii_key('Y'); + case XK_Z: + return KeyboardButton::ascii_key('Z'); + case XK_bracketleft: + return KeyboardButton::ascii_key('['); + case XK_backslash: + return KeyboardButton::ascii_key('\\'); + case XK_bracketright: + return KeyboardButton::ascii_key(']'); + case XK_asciicircum: + return KeyboardButton::ascii_key('^'); + case XK_underscore: + return KeyboardButton::ascii_key('_'); + case XK_grave: // == XK_quoteleft + return KeyboardButton::ascii_key('`'); + case XK_a: + return KeyboardButton::ascii_key('a'); + case XK_b: + return KeyboardButton::ascii_key('b'); + case XK_c: + return KeyboardButton::ascii_key('c'); + case XK_d: + return KeyboardButton::ascii_key('d'); + case XK_e: + return KeyboardButton::ascii_key('e'); + case XK_f: + return KeyboardButton::ascii_key('f'); + case XK_g: + return KeyboardButton::ascii_key('g'); + case XK_h: + return KeyboardButton::ascii_key('h'); + case XK_i: + return KeyboardButton::ascii_key('i'); + case XK_j: + return KeyboardButton::ascii_key('j'); + case XK_k: + return KeyboardButton::ascii_key('k'); + case XK_l: + return KeyboardButton::ascii_key('l'); + case XK_m: + return KeyboardButton::ascii_key('m'); + case XK_n: + return KeyboardButton::ascii_key('n'); + case XK_o: + return KeyboardButton::ascii_key('o'); + case XK_p: + return KeyboardButton::ascii_key('p'); + case XK_q: + return KeyboardButton::ascii_key('q'); + case XK_r: + return KeyboardButton::ascii_key('r'); + case XK_s: + return KeyboardButton::ascii_key('s'); + case XK_t: + return KeyboardButton::ascii_key('t'); + case XK_u: + return KeyboardButton::ascii_key('u'); + case XK_v: + return KeyboardButton::ascii_key('v'); + case XK_w: + return KeyboardButton::ascii_key('w'); + case XK_x: + return KeyboardButton::ascii_key('x'); + case XK_y: + return KeyboardButton::ascii_key('y'); + case XK_z: + return KeyboardButton::ascii_key('z'); + case XK_braceleft: + return KeyboardButton::ascii_key('{'); + case XK_bar: + return KeyboardButton::ascii_key('|'); + case XK_braceright: + return KeyboardButton::ascii_key('}'); + case XK_asciitilde: + return KeyboardButton::ascii_key('~'); + case XK_F1: + case XK_KP_F1: + return KeyboardButton::f1(); + case XK_F2: + case XK_KP_F2: + return KeyboardButton::f2(); + case XK_F3: + case XK_KP_F3: + return KeyboardButton::f3(); + case XK_F4: + case XK_KP_F4: + return KeyboardButton::f4(); + case XK_F5: + return KeyboardButton::f5(); + case XK_F6: + return KeyboardButton::f6(); + case XK_F7: + return KeyboardButton::f7(); + case XK_F8: + return KeyboardButton::f8(); + case XK_F9: + return KeyboardButton::f9(); + case XK_F10: + return KeyboardButton::f10(); + case XK_F11: + return KeyboardButton::f11(); + case XK_F12: + return KeyboardButton::f12(); + case XK_KP_Left: + case XK_Left: + return KeyboardButton::left(); + case XK_KP_Up: + case XK_Up: + return KeyboardButton::up(); + case XK_KP_Right: + case XK_Right: + return KeyboardButton::right(); + case XK_KP_Down: + case XK_Down: + return KeyboardButton::down(); + case XK_KP_Prior: + case XK_Prior: + return KeyboardButton::page_up(); + case XK_KP_Next: + case XK_Next: + return KeyboardButton::page_down(); + case XK_KP_Home: + case XK_Home: + return KeyboardButton::home(); + case XK_KP_End: + case XK_End: + return KeyboardButton::end(); + case XK_KP_Insert: + case XK_Insert: + return KeyboardButton::insert(); + case XK_KP_Delete: + case XK_Delete: + return KeyboardButton::del(); + case XK_Num_Lock: + return KeyboardButton::num_lock(); + case XK_Scroll_Lock: + return KeyboardButton::scroll_lock(); + case XK_Print: + return KeyboardButton::print_screen(); + case XK_Pause: + return KeyboardButton::pause(); + case XK_Shift_L: + return KeyboardButton::lshift(); + case XK_Shift_R: + return KeyboardButton::rshift(); + case XK_Control_L: + return KeyboardButton::lcontrol(); + case XK_Control_R: + return KeyboardButton::rcontrol(); + case XK_Alt_L: + return KeyboardButton::lalt(); + case XK_Alt_R: + return KeyboardButton::ralt(); + case XK_Meta_L: + case XK_Meta_R: + return KeyboardButton::meta(); + case XK_Caps_Lock: + return KeyboardButton::caps_lock(); + case XK_Shift_Lock: + return KeyboardButton::shift_lock(); + } + + return ButtonHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::get_mouse_button +// Access: Private +// Description: Returns the Panda ButtonHandle corresponding to the +// mouse button indicated by the given button event. +//////////////////////////////////////////////////////////////////// +ButtonHandle eglGraphicsWindow:: +get_mouse_button(XButtonEvent &button_event) { + int index = button_event.button; + if (index == x_wheel_up_button) { + return MouseButton::wheel_up(); + } else if (index == x_wheel_down_button) { + return MouseButton::wheel_down(); + } else if (index == x_wheel_left_button) { + return MouseButton::wheel_left(); + } else if (index == x_wheel_right_button) { + return MouseButton::wheel_right(); + } else { + return MouseButton::button(index - 1); + } +} +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsWindow::check_event +// Access: Private, Static +// Description: This function is used as a predicate to +// XCheckIfEvent() to determine if the indicated queued +// X event is relevant and should be returned to this +// window. +//////////////////////////////////////////////////////////////////// +Bool eglGraphicsWindow:: +check_event(Display *display, XEvent *event, char *arg) { + const eglGraphicsWindow *self = (eglGraphicsWindow *)arg; + + // We accept any event that is sent to our window. + return (event->xany.window == self->_xwindow); +} diff --git a/panda/src/egldisplay/eglGraphicsWindow.h b/panda/src/egldisplay/eglGraphicsWindow.h new file mode 100644 index 0000000000..9b31afce9d --- /dev/null +++ b/panda/src/egldisplay/eglGraphicsWindow.h @@ -0,0 +1,123 @@ +// Filename: eglGraphicsWindow.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 EGLGRAPHICSWINDOW_H +#define EGLGRAPHICSWINDOW_H + +#include "pandabase.h" + +#include "eglGraphicsPipe.h" +#include "graphicsWindow.h" +#include "buttonHandle.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsWindow +// Description : An interface to the egl system for managing GLES +// windows under X. +//////////////////////////////////////////////////////////////////// +class eglGraphicsWindow : public GraphicsWindow { +public: + eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~eglGraphicsWindow(); + + virtual bool move_pointer(int device, int x, int y); + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + virtual void begin_flip(); + + virtual void process_events(); + virtual void set_properties_now(WindowProperties &properties); + + INLINE Window get_xwindow() const; + +protected: + virtual void close_window(); + virtual bool open_window(); + +private: + void set_wm_properties(const WindowProperties &properties, + bool already_mapped); + + void setup_colormap(XVisualInfo *visual); + void handle_keystroke(XKeyEvent &event); + void handle_keypress(XKeyEvent &event); + void handle_keyrelease(XKeyEvent &event); + + ButtonHandle get_button(XKeyEvent &key_event, bool allow_shift); + ButtonHandle map_button(KeySym key); + ButtonHandle get_mouse_button(XButtonEvent &button_event); + + static Bool check_event(Display *display, XEvent *event, char *arg); + + void open_raw_mice(); + void poll_raw_mice(); + +private: + Display *_display; + int _screen; + Window _xwindow; + Colormap _colormap; + XIC _ic; + EGLDisplay _egl_display; + EGLSurface _egl_surface; + + long _event_mask; + bool _awaiting_configure; + Atom _wm_delete_window; + Atom _net_wm_window_type; + Atom _net_wm_window_type_splash; + Atom _net_wm_window_type_fullscreen; + Atom _net_wm_state; + Atom _net_wm_state_fullscreen; + Atom _net_wm_state_above; + Atom _net_wm_state_below; + Atom _net_wm_state_add; + Atom _net_wm_state_remove; + + struct MouseDeviceInfo { + int _fd; + int _input_device_index; + string _io_buffer; + }; + pvector _mouse_device_info; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsWindow::init_type(); + register_type(_type_handle, "eglGraphicsWindow", + 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 "eglGraphicsWindow.I" + +#endif diff --git a/panda/src/gles2gsg/Sources.pp b/panda/src/gles2gsg/Sources.pp new file mode 100644 index 0000000000..7b4cdaae44 --- /dev/null +++ b/panda/src/gles2gsg/Sources.pp @@ -0,0 +1,20 @@ +#define BUILD_DIRECTORY $[HAVE_GLES2] +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m prc:c +#define USE_PACKAGES gles2 +#begin lib_target + #define TARGET gles2gsg + #define LOCAL_LIBS \ + glstuff gsgbase gobj display \ + putil linmath mathutil pnmimage + + #define SOURCES \ + config_gles2gsg.h config_gles2gsg.cxx \ + gles2ext_shadow.h \ + gles2gsg.h gles2gsg.cxx + + #define INSTALL_HEADERS \ + config_gles2gsg.h gles2gsg.h + +#end lib_target + diff --git a/panda/src/gles2gsg/config_gles2gsg.cxx b/panda/src/gles2gsg/config_gles2gsg.cxx new file mode 100644 index 0000000000..1c7f56a999 --- /dev/null +++ b/panda/src/gles2gsg/config_gles2gsg.cxx @@ -0,0 +1,44 @@ +// Filename: config_gles2gsg.cxx +// Created by: pro-rsoft (14Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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_gles2gsg.h" +#include "gles2gsg.h" + +#include "dconfig.h" + +ConfigureDef(config_gles2gsg); +NotifyCategoryDef(gles2gsg, ":display:gsg"); + +ConfigureFn(config_gles2gsg) { + init_libgles2gsg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libgles2gsg +// 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_libgles2gsg() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + GLES2init_classes(); +} diff --git a/panda/src/gles2gsg/config_gles2gsg.h b/panda/src/gles2gsg/config_gles2gsg.h new file mode 100644 index 0000000000..82594e23e7 --- /dev/null +++ b/panda/src/gles2gsg/config_gles2gsg.h @@ -0,0 +1,27 @@ +// Filename: config_gles2gsg.h +// Created by: pro-rsoft (14Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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_GLES2GSG_H +#define CONFIG_GLES2GSG_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "dconfig.h" + +ConfigureDecl(config_gles2gsg, EXPCL_PANDAGLES2, EXPTP_PANDAGLES2); +NotifyCategoryDecl(gles2gsg, EXPCL_PANDAGLES2, EXPTP_PANDAGLES2); + +extern EXPCL_PANDAGLES2 void init_libgles2gsg(); + +#endif diff --git a/panda/src/gles2gsg/gles2gsg.cxx b/panda/src/gles2gsg/gles2gsg.cxx new file mode 100644 index 0000000000..de92358b2f --- /dev/null +++ b/panda/src/gles2gsg/gles2gsg.cxx @@ -0,0 +1,20 @@ +// Filename: gles2gsg.cxx +// Created by: pro-rsoft (14Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +// This is the actual .cxx file to include if you want to pick up +// any or all of the header files in this directory as compiled to use +// the "true" GLES2 library. + +#include "gles2gsg.h" +#include "glstuff_src.cxx" diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h new file mode 100644 index 0000000000..c72bac20b2 --- /dev/null +++ b/panda/src/gles2gsg/gles2gsg.h @@ -0,0 +1,74 @@ +// Filename: gles2gsg.h +// Created by: pro-rsoft (14Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 GLES2GSG_H +#define GLES2GSG_H + +// This header file compiles a GSG for the limited subset of OpenGL +// that is OpenGL ES 2. + +#include "pandabase.h" +#include "config_gles2gsg.h" + +#define GLP(name) gl##name +#define GLUP(name) glu##name +#define CLP(name) GLES2##name +#define GLPREFIX_QUOTED "gl" +#define CLASSPREFIX_QUOTED "GLES2" +#define GLSYSTEM_NAME "OpenGL ES 2" +#define CONFIGOBJ config_gles2gsg +#define GLCAT gles2gsg_cat +#define EXPCL_GL EXPCL_PANDAGLES2 +#define EXPTP_GL EXPTP_PANDAGLES2 +#ifdef OPENGLES_1 + #error OPENGLES_1 should not be defined! +#endif +#ifndef OPENGLES + #define OPENGLES +#endif +#ifndef OPENGLES_2 + #define OPENGLES_2 +#endif + +#include +#include + +// This helps to keep the source clean of hundreds of #ifdefs. +#ifdef OPENGLES_2 + #define GL_RENDERBUFFER_EXT GL_RENDERBUFFER + #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER_EXT GL_FRAMEBUFFER + #define GL_READ_FRAMEBUFFER_EXT GL_FRAMEBUFFER + #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE + #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT + #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS + #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT GL_FRAMEBUFFER_INCOMPLETE_FORMATS + #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT + #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0 + #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT + #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES + #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES + #define GL_TEXTURE_3D GL_TEXTURE_3D_OES + #define GL_MAX_3D_TEXTURE_SIZE GL_MAX_3D_TEXTURE_SIZE_OES +#endif + +#undef SUPPORT_IMMEDIATE_MODE +#define APIENTRY +#define APIENTRYP * + +#include "glstuff_src.h" + +#endif // GLES2GSG_H diff --git a/panda/src/gles2gsg/gles2gsg_composite.cxx b/panda/src/gles2gsg/gles2gsg_composite.cxx new file mode 100644 index 0000000000..4d1c8de928 --- /dev/null +++ b/panda/src/gles2gsg/gles2gsg_composite.cxx @@ -0,0 +1 @@ +#include "gles2gsg_composite1.cxx" diff --git a/panda/src/gles2gsg/gles2gsg_composite1.cxx b/panda/src/gles2gsg/gles2gsg_composite1.cxx new file mode 100644 index 0000000000..5ab633363c --- /dev/null +++ b/panda/src/gles2gsg/gles2gsg_composite1.cxx @@ -0,0 +1,3 @@ + +#include "config_gles2gsg.cxx" +#include "gles2gsg.cxx" diff --git a/panda/src/glesgsg/Sources.pp b/panda/src/glesgsg/Sources.pp new file mode 100644 index 0000000000..c0067a52c8 --- /dev/null +++ b/panda/src/glesgsg/Sources.pp @@ -0,0 +1,20 @@ +#define BUILD_DIRECTORY $[HAVE_GLES] +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m prc:c +#define USE_PACKAGES gles +#begin lib_target + #define TARGET glesgsg + #define LOCAL_LIBS \ + glstuff gsgbase gobj display \ + putil linmath mathutil pnmimage + + #define SOURCES \ + config_glesgsg.h config_glesgsg.cxx \ + glesext_shadow.h \ + glesgsg.h glesgsg.cxx + + #define INSTALL_HEADERS \ + config_glesgsg.h glesgsg.h + +#end lib_target + diff --git a/panda/src/glesgsg/config_glesgsg.cxx b/panda/src/glesgsg/config_glesgsg.cxx new file mode 100644 index 0000000000..329da03281 --- /dev/null +++ b/panda/src/glesgsg/config_glesgsg.cxx @@ -0,0 +1,44 @@ +// Filename: config_glesgsg.cxx +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "config_glesgsg.h" +#include "glesgsg.h" + +#include "dconfig.h" + +ConfigureDef(config_glesgsg); +NotifyCategoryDef(glesgsg, ":display:gsg"); + +ConfigureFn(config_glesgsg) { + init_libglesgsg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libglesgsg +// 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_libglesgsg() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + GLESinit_classes(); +} diff --git a/panda/src/glesgsg/config_glesgsg.h b/panda/src/glesgsg/config_glesgsg.h new file mode 100644 index 0000000000..d20e48c242 --- /dev/null +++ b/panda/src/glesgsg/config_glesgsg.h @@ -0,0 +1,27 @@ +// Filename: config_glesgsg.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 CONFIG_GLESGSG_H +#define CONFIG_GLESGSG_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "dconfig.h" + +ConfigureDecl(config_glesgsg, EXPCL_PANDAGLES, EXPTP_PANDAGLES); +NotifyCategoryDecl(glesgsg, EXPCL_PANDAGLES, EXPTP_PANDAGLES); + +extern EXPCL_PANDAGLES void init_libglesgsg(); + +#endif diff --git a/panda/src/iphonedisplay/glesgsg.mm b/panda/src/glesgsg/glesgsg.cxx similarity index 71% rename from panda/src/iphonedisplay/glesgsg.mm rename to panda/src/glesgsg/glesgsg.cxx index c1ba480fdd..a1ae407512 100644 --- a/panda/src/iphonedisplay/glesgsg.mm +++ b/panda/src/glesgsg/glesgsg.cxx @@ -1,5 +1,5 @@ // Filename: glesgsg.cxx -// Created by: drose (09Apr09) +// Created by: pro-rsoft (21May09) // //////////////////////////////////////////////////////////////////// // @@ -12,5 +12,9 @@ // //////////////////////////////////////////////////////////////////// +// This is the actual .cxx file to include if you want to pick up +// any or all of the header files in this directory as compiled to use +// the "true" GLES library. + #include "glesgsg.h" #include "glstuff_src.cxx" diff --git a/panda/src/glesgsg/glesgsg.h b/panda/src/glesgsg/glesgsg.h new file mode 100644 index 0000000000..42d58841a1 --- /dev/null +++ b/panda/src/glesgsg/glesgsg.h @@ -0,0 +1,100 @@ +// Filename: glesgsg.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 GLESGSG_H +#define GLESGSG_H + +// This header file compiles a GSG for the limited subset of OpenGL +// that is OpenGL ES. + +#include "pandabase.h" +#include "config_glesgsg.h" + +#define GLP(name) gl##name +#define GLUP(name) glu##name +#define CLP(name) GLES##name +#define GLPREFIX_QUOTED "gl" +#define CLASSPREFIX_QUOTED "GLES" +#define GLSYSTEM_NAME "OpenGL ES" +#define CONFIGOBJ config_glesgsg +#define GLCAT glesgsg_cat +#define EXPCL_GL EXPCL_PANDAGLES +#define EXPTP_GL EXPTP_PANDAGLES +#ifndef OPENGLES + #define OPENGLES +#endif +#ifndef OPENGLES_1 + #define OPENGLES_1 +#endif +#ifdef OPENGLES_2 + #error OPENGLES_2 should not be defined! +#endif + +#ifdef IS_OSX + #include + #include +#else + #include + #include +#endif + +// This helps to keep the source clean of hundreds of #ifdefs. +#ifdef OPENGLES_1 + #define GL_NONE GL_NONE_OES + #define GL_RENDERBUFFER_EXT GL_RENDERBUFFER_OES + #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES + #define GL_DRAW_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES + #define GL_READ_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES + #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES + #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT_OES + #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT_OES + #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE_OES + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES + #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES + #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES + #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED_OES + #define GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT24_OES + #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES + #define GL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_OES + #define GL_UNSIGNED_INT_24_8_EXT GL_UNSIGNED_INT_24_8_OES + #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES + #define GL_DEPTH_COMPONENT16 GL_DEPTH_COMPONENT16_OES + #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES + #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES + #define GL_TEXTURE_CUBE_MAP GL_TEXTURE_CUBE_MAP_OES + #define GL_TEXTURE_CUBE_MAP_POSITIVE_X GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES + #define GL_MAX_CUBE_MAP_TEXTURE_SIZE GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES + #define GL_MIRRORED_REPEAT GL_MIRRORED_REPEAT_OES + #define GL_RGB5_A1 GL_RGB5_A1_OES + #define GL_RGBA4 GL_RGBA4_OES + #define GL_RGB8 GL_RGB8_OES + #define GL_RGBA8 GL_RGBA8_OES + #define GL_FUNC_ADD GL_FUNC_ADD_OES + #define GL_FUNC_SUBTRACT GL_FUNC_SUBTRACT_OES + #define GL_FUNC_REVERSE_SUBTRACT GL_FUNC_REVERSE_SUBTRACT_OES +#endif + +#undef SUPPORT_IMMEDIATE_MODE +#define APIENTRY +#define APIENTRYP * + +#include "glstuff_src.h" + +#endif // GLESGSG_H diff --git a/panda/src/glesgsg/glesgsg_composite.cxx b/panda/src/glesgsg/glesgsg_composite.cxx new file mode 100644 index 0000000000..78b5c0d691 --- /dev/null +++ b/panda/src/glesgsg/glesgsg_composite.cxx @@ -0,0 +1 @@ +#include "glesgsg_composite1.cxx" diff --git a/panda/src/glesgsg/glesgsg_composite1.cxx b/panda/src/glesgsg/glesgsg_composite1.cxx new file mode 100644 index 0000000000..ad31da0abc --- /dev/null +++ b/panda/src/glesgsg/glesgsg_composite1.cxx @@ -0,0 +1,3 @@ + +#include "config_glesgsg.cxx" +#include "glesgsg.cxx" diff --git a/panda/src/iphonedisplay/Sources.pp b/panda/src/iphonedisplay/Sources.pp index ddd85ccd10..f4bf264cd5 100644 --- a/panda/src/iphonedisplay/Sources.pp +++ b/panda/src/iphonedisplay/Sources.pp @@ -12,14 +12,12 @@ framework putil collide pgraph chan text \ pnmimage pnmimagetypes event effects gobj display \ mathutil putil express dgraph device tform \ - linmath pstatclient panda glstuff + linmath pstatclient panda glstuff glesgsg #define SOURCES \ config_iphonedisplay.h config_iphonedisplay.mm \ viewController.h viewController.mm \ eaglView.h eaglView.mm \ - glesext_shadow.h \ - glesgsg.h glesgsg.mm \ iPhoneGraphicsPipe.h iPhoneGraphicsPipe.mm \ iPhoneGraphicsStateGuardian.h iPhoneGraphicsStateGuardian.mm \ iPhoneGraphicsWindow.h iPhoneGraphicsWindow.I iPhoneGraphicsWindow.mm diff --git a/panda/src/iphonedisplay/glesext_shadow.h b/panda/src/iphonedisplay/glesext_shadow.h deleted file mode 100644 index cffb6df526..0000000000 --- a/panda/src/iphonedisplay/glesext_shadow.h +++ /dev/null @@ -1,174 +0,0 @@ -// Filename: glesext_shadow.h -// Created by: drose (09Apr09) -// -//////////////////////////////////////////////////////////////////// -// -// 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." -// -//////////////////////////////////////////////////////////////////// - -// This file is designed to #define the extension symbols that aren't -// used for OpenGL ES, just so we can compile the glstuff module. -// These symbols have to be defined, even if they are never actually -// used at runtime; and it doesn't particularly matter what their -// defined value is. - -#define GL_ACCUM_ALPHA_BITS 0 -#define GL_ACCUM_BLUE_BITS 1 -#define GL_ACCUM_BUFFER_BIT 2 -#define GL_ACCUM_GREEN_BITS 3 -#define GL_ACCUM_RED_BITS 4 -#define GL_AUX_BUFFERS 5 -#define GL_BACK_LEFT 6 -#define GL_BACK_RIGHT 7 -#define GL_BGR 8 -#define GL_BLUE 9 -#define GL_CLAMP 10 -#define GL_CLAMP_TO_BORDER 11 -#define GL_COLOR_ATTACHMENT0_EXT 12 -#define GL_COLOR_ATTACHMENT1_EXT 13 -#define GL_COLOR_INDEX 14 -#define GL_COMPARE_R_TO_TEXTURE_ARB 15 -#define GL_COMPILE 16 -#define GL_COMPILE_AND_EXECUTE 17 -#define GL_COMPRESSED_ALPHA 18 -#define GL_COMPRESSED_LUMINANCE 19 -#define GL_COMPRESSED_LUMINANCE_ALPHA 20 -#define GL_COMPRESSED_RGB 21 -#define GL_COMPRESSED_RGBA 22 -#define GL_COMPRESSED_RGBA_FXT1_3DFX 23 -#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 24 -#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 25 -#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 26 -#define GL_COMPRESSED_RGB_FXT1_3DFX 27 -#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 28 -#define GL_CONSTANT_ALPHA 29 -#define GL_CONSTANT_COLOR 30 -#define GL_COORD_REPLACE_ARB 31 -#define GL_DECR_WRAP 32 -#define GL_DEPTH_ATTACHMENT_EXT 33 -#define GL_DEPTH_COMPONENT 34 -#define GL_DEPTH_STENCIL_EXT 35 -#define GL_DEPTH_TEXTURE_MODE_ARB 36 -#define GL_DOUBLEBUFFER 37 -#define GL_EYE_LINEAR 38 -#define GL_EYE_PLANE 39 -#define GL_FILL 40 -#define GL_FRAMEBUFFER_COMPLETE_EXT 41 -#define GL_FRAMEBUFFER_EXT 42 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 43 -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 44 -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 45 -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 46 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 47 -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 48 -#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 49 -#define GL_FRONT_LEFT 50 -#define GL_FRONT_RIGHT 51 -#define GL_FUNC_ADD 52 -#define GL_FUNC_REVERSE_SUBTRACT 53 -#define GL_FUNC_SUBTRACT 54 -#define GL_GREEN 55 -#define GL_INCR_WRAP 56 -#define GL_INDEX_BITS 57 -#define GL_INTENSITY 58 -#define GL_LEFT 59 -#define GL_LIGHT_MODEL_LOCAL_VIEWER 60 -#define GL_LINE 61 -#define GL_MATRIX_INDEX_ARRAY_ARB 62 -#define GL_MATRIX_PALETTE_ARB 63 -#define GL_MAX 64 -#define GL_MAX_3D_TEXTURE_SIZE 65 -#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 66 -#define GL_MAX_DRAW_BUFFERS 67 -#define GL_MAX_ELEMENTS_INDICES 68 -#define GL_MAX_ELEMENTS_VERTICES 69 -#define GL_MAX_PALETTE_MATRICES_ARB 70 -#define GL_MAX_VERTEX_UNITS_ARB 71 -#define GL_MIN 72 -#define GL_MIRRORED_REPEAT 73 -#define GL_MIRROR_CLAMP_EXT 74 -#define GL_MIRROR_CLAMP_TO_BORDER_EXT 75 -#define GL_MIRROR_CLAMP_TO_EDGE_EXT 76 -#define GL_MODELVIEW0_ARB 77 -#define GL_MODELVIEW1_ARB 78 -#define GL_MODELVIEW2_ARB 79 -#define GL_NONE 80 -#define GL_NORMAL_MAP 81 -#define GL_OBJECT_LINEAR 82 -#define GL_OBJECT_PLANE 83 -#define GL_ONE_MINUS_CONSTANT_ALPHA 84 -#define GL_ONE_MINUS_CONSTANT_COLOR 85 -#define GL_POINT 86 -#define GL_POINT_SPRITE_ARB 87 -#define GL_POLYGON_SMOOTH 88 -#define GL_POLYGON_SMOOTH_HINT 89 -#define GL_Q 90 -#define GL_QUERY_COUNTER_BITS 91 -#define GL_R 92 -#define GL_R3_G3_B2 93 -#define GL_RED 94 -#define GL_REFLECTION_MAP 95 -#define GL_RENDERBUFFER_EXT 96 -#define GL_RGB12 97 -#define GL_RGB5 98 -#define GL_RGB5_A1 99 -#define GL_RGB8 100 -#define GL_RGBA12 101 -#define GL_RGBA16F_ARB 102 -#define GL_RGBA32F_ARB 103 -#define GL_RGBA4 104 -#define GL_RGBA8 105 -#define GL_RGBA8_EXT 106 -#define GL_RIGHT 107 -#define GL_S 108 -#define GL_SAMPLES_PASSED 109 -#define GL_SPHERE_MAP 110 -#define GL_STENCIL_ATTACHMENT_EXT 111 -#define GL_STENCIL_TEST_TWO_SIDE_EXT 112 -#define GL_STEREO 113 -#define GL_STREAM_DRAW 114 -#define GL_T 115 -#define GL_TEXTURE_1D 116 -#define GL_TEXTURE_3D 117 -#define GL_TEXTURE_ALPHA_SIZE 118 -#define GL_TEXTURE_BLUE_SIZE 119 -#define GL_TEXTURE_BORDER_COLOR 120 -#define GL_TEXTURE_COMPARE_FUNC_ARB 121 -#define GL_TEXTURE_COMPARE_MODE_ARB 122 -#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 123 -#define GL_TEXTURE_COMPRESSION_HINT 124 -#define GL_TEXTURE_CUBE_MAP 125 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 126 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 127 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 128 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 129 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 130 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 131 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 132 -#define GL_TEXTURE_DEPTH 133 -#define GL_TEXTURE_GEN_MODE 134 -#define GL_TEXTURE_GEN_Q 135 -#define GL_TEXTURE_GEN_R 136 -#define GL_TEXTURE_GEN_S 137 -#define GL_TEXTURE_GEN_T 138 -#define GL_TEXTURE_GREEN_SIZE 139 -#define GL_TEXTURE_HEIGHT 140 -#define GL_TEXTURE_INTENSITY_SIZE 141 -#define GL_TEXTURE_INTERNAL_FORMAT 142 -#define GL_TEXTURE_LUMINANCE_SIZE 143 -#define GL_TEXTURE_MAX_LEVEL 144 -#define GL_TEXTURE_RED_SIZE 145 -#define GL_TEXTURE_WIDTH 146 -#define GL_TEXTURE_WRAP_R 147 -#define GL_UNSIGNED_INT 148 -#define GL_UNSIGNED_INT_24_8_EXT 149 -#define GL_VERTEX_BLEND_ARB 150 -#define GL_WEIGHT_ARRAY_ARB 151 -#define GL_WEIGHT_SUM_UNITY_ARB 152 -#define GL_DEPTH24_STENCIL8_EXT 153 diff --git a/panda/src/iphonedisplay/glesgsg.h b/panda/src/iphonedisplay/glesgsg.h deleted file mode 100644 index a9a8ef1d79..0000000000 --- a/panda/src/iphonedisplay/glesgsg.h +++ /dev/null @@ -1,49 +0,0 @@ -// Filename: glesgsg.h -// Created by: drose (09Apr09) -// -//////////////////////////////////////////////////////////////////// -// -// 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 GLESGSG_H -#define GLESGSG_H - -// This header file compiles a GSG for the limited subset of OpenGL -// that is OpenGL ES. - -#include "pandabase.h" -#include "config_iphonedisplay.h" - -#define GLP(name) gl##name -#define GLUP(name) glu##name -#define CLP(name) GLES##name -#define GLPREFIX_QUOTED "gl" -#define CLASSPREFIX_QUOTED "GLES" -#define GLSYSTEM_NAME "OpenGL ES" -#define CONFIGOBJ config_iphonedisplay -#define GLCAT iphonedisplay_cat -#define EXPCL_GL EXPCL_MISC -#define EXPTP_GL EXPTP_MISC -#define OPENGLES -#define OPENGLES_1 -#undef HAVE_GLU - -#include -#include - -#include "glesext_shadow.h" - -#undef SUPPORT_IMMEDIATE_MODE -#define APIENTRY -#define APIENTRYP * - -#include "glstuff_src.h" - -#endif // GLESGSG_H diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index 4a651971da..cda282fa33 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -120,6 +120,22 @@ #define EXPTP_PANDAGL extern #endif +#ifdef BUILDING_PANDAGLES + #define EXPCL_PANDAGLES __declspec(dllexport) + #define EXPTP_PANDAGLES +#else + #define EXPCL_PANDAGLES __declspec(dllimport) + #define EXPTP_PANDAGLES extern +#endif + +#ifdef BUILDING_PANDAGLES2 + #define EXPCL_PANDAGLES2 __declspec(dllexport) + #define EXPTP_PANDAGLES2 +#else + #define EXPCL_PANDAGLES2 __declspec(dllimport) + #define EXPTP_PANDAGLES2 extern +#endif + #ifdef BUILDING_PANDAGLUT #define EXPCL_PANDAGLUT __declspec(dllexport) #define EXPTP_PANDAGLUT @@ -246,6 +262,12 @@ #define EXPCL_PANDAGL #define EXPTP_PANDAGL +#define EXPCL_PANDAGLES +#define EXPTP_PANDAGLES + +#define EXPCL_PANDAGLES2 +#define EXPTP_PANDAGLES2 + #define EXPCL_PANDAGLUT #define EXPTP_PANDAGLUT