Linux: added SDL2 backend as replacement for GLFW.

Initially planned as alternative, made replacement after discussion with
minggo.

Summary:
GLFW v.3 released in June. New version breaks API without real need:
http://cocos2d-x.org/boards/6/topics/31277
Meanwhile SDL2 is very stable. SDL2 used in Steam Runtime and will be
part of Ubuntu SDK, it's supported by other distributions.

Completed:
- OpenGL initialization and window handling with SDL
- Keyboard, IME emulation, mouse and touches support with SDL
- Window title now contains app name on Linux
- Backspace correctly handled with new backend

Note:
- SDL on X11 have no proper multitouch, but backend handles multitouch
events to support future Linux graphics.
This commit is contained in:
Sergey Shambir 2013-07-08 06:26:53 +04:00
parent df33991a38
commit d23d486720
6 changed files with 237 additions and 271 deletions

View File

@ -106,7 +106,7 @@ Director::Director(void)
bool Director::init(void)
{
setDefaultValues();
setDefaultValues();
// scenes
_runningScene = NULL;
@ -253,6 +253,11 @@ void Director::drawScene(void)
// calculate "global" dt
calculateDeltaTime();
if (_openGLView)
{
_openGLView->pollInputEvents();
}
//tick before glClear: issue #533
if (! _paused)
{
@ -645,7 +650,7 @@ void Director::popToSceneStackLevel(int level)
// pop stack until reaching desired level
while (c > level)
{
Scene *current = (Scene*)_scenesStack->lastObject();
Scene *current = (Scene*)_scenesStack->lastObject();
if (current->isRunning())
{
@ -658,7 +663,7 @@ void Director::popToSceneStackLevel(int level)
c--;
}
_nextScene = (Scene*)_scenesStack->lastObject();
_nextScene = (Scene*)_scenesStack->lastObject();
_sendCleanupToScene = false;
}

View File

@ -55,6 +55,10 @@ EGLViewProtocol::~EGLViewProtocol()
}
void EGLViewProtocol::pollInputEvents()
{
}
void EGLViewProtocol::setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy)
{
CCASSERT(resolutionPolicy != ResolutionPolicy::UNKNOWN, "should set resolutionPolicy");

View File

@ -58,6 +58,12 @@ public:
/** Open or close IME keyboard , subclass must implement this method. */
virtual void setIMEKeyboardState(bool bOpen) = 0;
/**
* Polls input events. Subclass must implement methods if platform
* does not provide event callbacks.
*/
virtual void pollInputEvents();
/**
* Get the frame size of EGL view.
* In general, it returns the screen size since the EGL view is a fullscreen view.

View File

@ -7,78 +7,26 @@
#include "CCEGLView.h"
#include "CCGL.h"
#include "GL/glfw.h"
#include "ccMacros.h"
#include "CCDirector.h"
#include "touch_dispatcher/CCTouch.h"
#include "touch_dispatcher/CCTouchDispatcher.h"
#include "text_input_node/CCIMEDispatcher.h"
#include "keyboard_dispatcher/CCKeyboardDispatcher.h"
#include <unistd.h>
PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL;
PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL;
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL;
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL;
PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL;
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL;
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL;
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB = NULL;
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL;
bool initExtensions() {
#define LOAD_EXTENSION_FUNCTION(TYPE, FN) FN = (TYPE)glfwGetProcAddress(#FN);
bool bRet = false;
do {
// char* p = (char*) glGetString(GL_EXTENSIONS);
// printf(p);
/* Supports frame buffer? */
if (glfwExtensionSupported("GL_EXT_framebuffer_object") != GL_FALSE)
{
/* Loads frame buffer extension functions */
LOAD_EXTENSION_FUNCTION(PFNGLGENERATEMIPMAPEXTPROC,
glGenerateMipmapEXT);
LOAD_EXTENSION_FUNCTION(PFNGLGENFRAMEBUFFERSEXTPROC,
glGenFramebuffersEXT);
LOAD_EXTENSION_FUNCTION(PFNGLDELETEFRAMEBUFFERSEXTPROC,
glDeleteFramebuffersEXT);
LOAD_EXTENSION_FUNCTION(PFNGLBINDFRAMEBUFFEREXTPROC,
glBindFramebufferEXT);
LOAD_EXTENSION_FUNCTION(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC,
glCheckFramebufferStatusEXT);
LOAD_EXTENSION_FUNCTION(PFNGLFRAMEBUFFERTEXTURE2DEXTPROC,
glFramebufferTexture2DEXT);
} else {
break;
}
if (glfwExtensionSupported("GL_ARB_vertex_buffer_object") != GL_FALSE) {
LOAD_EXTENSION_FUNCTION(PFNGLGENBUFFERSARBPROC, glGenBuffersARB);
LOAD_EXTENSION_FUNCTION(PFNGLBINDBUFFERARBPROC, glBindBufferARB);
LOAD_EXTENSION_FUNCTION(PFNGLBUFFERDATAARBPROC, glBufferDataARB);
LOAD_EXTENSION_FUNCTION(PFNGLBUFFERSUBDATAARBPROC,
glBufferSubDataARB);
LOAD_EXTENSION_FUNCTION(PFNGLDELETEBUFFERSARBPROC,
glDeleteBuffersARB);
} else {
break;
}
bRet = true;
} while (0);
return bRet;
bool initExtensions()
{
// Do nothing, on Linux we use GLEW.
}
NS_CC_BEGIN
EGLView::EGLView()
: bIsInit(false)
: _wasInit(false)
, _frameZoomFactor(1.0f)
,_window(nullptr)
,_context(nullptr)
{
}
@ -86,185 +34,83 @@ EGLView::~EGLView()
{
}
void keyEventHandle(int iKeyID,int iKeyState) {
if (iKeyState ==GLFW_RELEASE) {
return;
}
if (iKeyID == GLFW_KEY_DEL) {
IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward();
} else if (iKeyID == GLFW_KEY_ENTER) {
IMEDispatcher::sharedDispatcher()->dispatchInsertText("\n", 1);
} else if (iKeyID == GLFW_KEY_TAB) {
}
}
void charEventHandle(int iCharID,int iCharState) {
if (iCharState ==GLFW_RELEASE) {
return;
}
// ascii char
IMEDispatcher::sharedDispatcher()->dispatchInsertText((const char *)&iCharID, 1);
}
void mouseButtonEventHandle(int iMouseID,int iMouseState) {
if (iMouseID == GLFW_MOUSE_BUTTON_LEFT) {
EGLView* pEGLView = EGLView::getInstance();
//get current mouse pos
int x,y;
glfwGetMousePos(&x, &y);
Point oPoint((float)x,(float)y);
/*
if (!Rect::RectContainsPoint(s_pMainWindow->_rcViewPort,oPoint))
{
CCLOG("not in the viewport");
return;
}
*/
oPoint.x /= pEGLView->_frameZoomFactor;
oPoint.y /= pEGLView->_frameZoomFactor;
int id = 0;
if (iMouseState == GLFW_PRESS) {
pEGLView->handleTouchesBegin(1, &id, &oPoint.x, &oPoint.y);
} else if (iMouseState == GLFW_RELEASE) {
pEGLView->handleTouchesEnd(1, &id, &oPoint.x, &oPoint.y);
}
}
}
void mousePosEventHandle(int iPosX,int iPosY) {
int iButtonState = glfwGetMouseButton(GLFW_MOUSE_BUTTON_LEFT);
//to test move
if (iButtonState == GLFW_PRESS) {
EGLView* pEGLView = EGLView::getInstance();
int id = 0;
float x = (float)iPosX;
float y = (float)iPosY;
x /= pEGLView->_frameZoomFactor;
y /= pEGLView->_frameZoomFactor;
pEGLView->handleTouchesMove(1, &id, &x, &y);
}
}
int closeEventHandle() {
Director::getInstance()->end();
return GL_TRUE;
}
void GLFWCALL keyboardEventHandle(int keyCode, int action)
static std::string getApplicationName()
{
KeyboardDispatcher *kbDisp = Director::getInstance()->getKeyboardDispatcher();
char fullpath[256] = {0};
ssize_t length = readlink("/proc/self/exe", fullpath, sizeof(fullpath)-1);
switch (action)
{
case GLFW_PRESS:
kbDisp->dispatchKeyboardEvent(keyCode, true);
break;
case GLFW_RELEASE:
kbDisp->dispatchKeyboardEvent(keyCode, false);
break;
if (length <= 0) {
return "Cocos2dx-Linux";
}
fullpath[length] = '\0';
const std::string appPath = fullpath;
return appPath.substr(appPath.find_last_of('/') + 1);
}
void checkSDLError(int line)
{
#if COCOS2D_DEBUG > 0
const char *error = SDL_GetError();
if (*error != '\0')
{
if (line != -1)
CCLOG("SDL Error: %s, line: %i", error, line);
else
CCLOG("SDL Error: %s", error);
SDL_ClearError();
}
#endif
}
void EGLView::setFrameSize(float width, float height)
{
bool eResult = false;
int u32GLFWFlags = GLFW_WINDOW;
//create the window by glfw.
bool eResult = false;
//create the window by SDL2.
//check
CCAssert(width!=0&&height!=0, "invalid window's size equal 0");
//check
CCAssert(width!=0&&height!=0, "invalid window's size equal 0");
//Inits GLFW
eResult = glfwInit() != GL_FALSE;
//Inits SDL2
eResult = SDL_Init(SDL_INIT_VIDEO) == 0;
if (!eResult) {
CCAssert(0, "fail to init the SDL");
}
if (!eResult) {
CCAssert(0, "fail to init the glfw");
}
const std::string appName = getApplicationName();
const int iDepth = 16; // set default value
/* Updates window hint */
glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, GL_TRUE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
int iDepth = 16; // set default value
/* Depending on video depth */
switch(iDepth)
{
/* 16-bit */
case 16:
{
/* Updates video mode */
eResult = (glfwOpenWindow(width, height, 5, 6, 5, 0, 16, 8, (int)u32GLFWFlags) != false) ? true : false;
_window = SDL_CreateWindow(appName.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, iDepth);
checkSDLError(__LINE__);
break;
}
_context = SDL_GL_CreateContext(_window);
/* 24-bit */
case 24:
{
/* Updates video mode */
eResult = (glfwOpenWindow(width, height, 8, 8, 8, 0, 16, 8, (int)u32GLFWFlags) != false) ? true : false;
if (!_window || !_context) {
CCAssert(0, "Failed to initialize OpenGL context");
} else {
SDL_GL_SetSwapInterval(1);
checkSDLError(__LINE__);
_wasInit = true;
break;
}
/* 32-bit */
default:
case 32:
{
/* Updates video mode */
eResult = (glfwOpenWindow(width, height, 8, 8, 8, 8, 16, 8, (int)u32GLFWFlags) != GL_FALSE) ? true :false;
break;
}
}
/* Success? */
if(eResult)
{
/* Updates actual size */
// glfwGetWindowSize(&width, &height);
EGLViewProtocol::setFrameSize(width, height);
/* Updates its title */
glfwSetWindowTitle("Cocos2dx-Linux");
//set the init flag
bIsInit = true;
//register the glfw key event
glfwSetKeyCallback(keyEventHandle);
//register the glfw char event
glfwSetCharCallback(charEventHandle);
//register the glfw mouse event
glfwSetMouseButtonCallback(mouseButtonEventHandle);
//register the glfw mouse pos event
glfwSetMousePosCallback(mousePosEventHandle);
#ifdef CC_KEYBOARD_SUPPORT
//register the glfw keyboard event
glfwSetKeyCallback(keyboardEventHandle);
#endif
glfwSetWindowCloseCallback(closeEventHandle);
//Inits extensions
eResult = initExtensions();
if (!eResult) {
CCAssert(0, "fail to init the extensions of opengl");
}
initGL();
}
EGLViewProtocol::setFrameSize(width, height);
initGL();
if (!GLEW_ARB_framebuffer_object)
CCAssert(0, "fail to init OpenGL extension ARB_framebuffer_object");
if (!GLEW_ARB_vertex_buffer_object)
CCAssert(0, "fail to init OpenGL extension ARB_vertex_buffer_object");
}
}
void EGLView::setFrameZoomFactor(float fZoomFactor)
{
_frameZoomFactor = fZoomFactor;
glfwSetWindowSize(_screenSize.width * fZoomFactor, _screenSize.height * fZoomFactor);
SDL_SetWindowSize(_window, _screenSize.width * fZoomFactor, _screenSize.height * fZoomFactor);
Director::getInstance()->setProjection(Director::getInstance()->getProjection());
}
@ -292,26 +138,130 @@ void EGLView::setScissorInPoints(float x , float y , float w , float h)
bool EGLView::isOpenGLReady()
{
return bIsInit;
return _wasInit;
}
void EGLView::end()
{
/* Exits from GLFW */
glfwTerminate();
delete this;
exit(0);
SDL_GL_DeleteContext(_context);
SDL_DestroyWindow(_window);
SDL_Quit();
delete this;
exit(0);
}
void EGLView::swapBuffers() {
if (bIsInit) {
/* Swap buffers */
glfwSwapBuffers();
}
if (_wasInit) {
/* Swap buffers */
SDL_GL_SwapWindow(_window);
}
}
void EGLView::setIMEKeyboardState(bool bOpen) {
// Polls events if SDL backend is on.
// Note that finger events allow multi-touch on Linux, but libsdl with X11
// backend does not support multitouch. However multitouch should work
// out of the box on upcoming Weston and Mir display servers.
// Also supports mouse touches emulation, keyboard events, text events.
void EGLView::pollInputEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
Director::sharedDirector()->end();
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
int id = event.button.which;
_pressedMouseInstances.insert(id);
Point oPoint((float)event.button.x, (float)event.button.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesBegin(1, &id, &oPoint.x, &oPoint.y);
}
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT) {
int id = event.button.which;
_pressedMouseInstances.erase(id);
Point oPoint((float)event.button.x, (float)event.button.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesEnd(1, &id, &oPoint.x, &oPoint.y);
}
break;
case SDL_MOUSEMOTION:
if (_pressedMouseInstances.count(event.motion.which)) {
int id = event.motion.which;
Point oPoint((float)event.motion.x, (float)event.motion.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesMove(1, &id, &oPoint.x, &oPoint.y);
}
break;
case SDL_FINGERDOWN: {
int fingerId = event.tfinger.fingerId;
Point oPoint((float)event.tfinger.x, (float)event.tfinger.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesBegin(1, &fingerId, &oPoint.x, &oPoint.y);
}
break;
case SDL_FINGERUP: {
int fingerId = event.tfinger.fingerId;
Point oPoint((float)event.tfinger.x, (float)event.tfinger.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesEnd(1, &fingerId, &oPoint.x, &oPoint.y);
break;
}
case SDL_FINGERMOTION: {
int fingerId = event.tfinger.fingerId;
Point oPoint((float)event.tfinger.x, (float)event.tfinger.y);
oPoint.x /= _frameZoomFactor;
oPoint.y /= _frameZoomFactor;
handleTouchesMove(1, &fingerId, &oPoint.x, &oPoint.y);
}
break;
case SDL_TEXTINPUT: {
const std::string text = event.text.text;
IMEDispatcher::sharedDispatcher()->dispatchInsertText(text.c_str(), text.size());
}
break;
case SDL_TEXTEDITING:
// TODO: not implemented.
// In SDL text editing is similar to patching. Each patch consists of
// selected text range and string that replaces selected text.
break;
case SDL_KEYDOWN: {
if (event.key.keysym.sym == SDLK_BACKSPACE)
IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward();
Director::sharedDirector()->getKeyboardDispatcher()->dispatchKeyboardEvent(event.key.keysym.sym, true);
break;
case SDL_KEYUP:
Director::sharedDirector()->getKeyboardDispatcher()->dispatchKeyboardEvent(event.key.keysym.sym, false);
}
break;
}
}
}
void EGLView::setIMEKeyboardState(bool bOpen)
{
_IMEKeyboardOpened = bOpen;
Rect zeroRect(0, 0, 0, 0);
IMEKeyboardNotificationInfo info;
info.begin = zeroRect;
info.end = zeroRect;
info.duration = 0;
if (bOpen) {
IMEDispatcher::sharedDispatcher()->dispatchKeyboardWillShow(info);
IMEDispatcher::sharedDispatcher()->dispatchKeyboardDidShow(info);
} else {
IMEDispatcher::sharedDispatcher()->dispatchKeyboardWillHide(info);
IMEDispatcher::sharedDispatcher()->dispatchKeyboardDidHide(info);
}
}
bool EGLView::initGL()
@ -349,14 +299,6 @@ bool EGLView::initGL()
void EGLView::destroyGL()
{
/*
if (_DC != NULL && _RC != NULL)
{
// deselect rendering context and delete it
wglMakeCurrent(_DC, NULL);
wglDeleteContext(_RC);
}
*/
}
EGLView* EGLView::getInstance()

View File

@ -11,6 +11,8 @@
#include "platform/CCCommon.h"
#include "cocoa/CCGeometry.h"
#include "platform/CCEGLViewProtocol.h"
#include <SDL2/SDL.h>
#include <set>
bool initExtensions();
@ -18,32 +20,33 @@ NS_CC_BEGIN
class EGLView : public EGLViewProtocol{
public:
EGLView();
virtual ~EGLView();
EGLView();
virtual ~EGLView();
friend void keyEventHandle(int,int);
friend void mouseButtonEventHandle(int,int);
friend void mousePosEventHandle(int,int);
friend void charEventHandle(int,int);
friend void keyEventHandle(int,int);
friend void mouseButtonEventHandle(int,int);
friend void mousePosEventHandle(int,int);
friend void charEventHandle(int,int);
/**
* iPixelWidth, height: the window's size
* iWidth ,height: the point size, which may scale.
* iDepth is not the buffer depth of opengl, it indicate how may bits for a pixel
*/
virtual void setFrameSize(float width, float height);
virtual void setViewPortInPoints(float x , float y , float w , float h);
virtual void setScissorInPoints(float x , float y , float w , float h);
/**
* iPixelWidth, height: the window's size
* iWidth ,height: the point size, which may scale.
* iDepth is not the buffer depth of opengl, it indicate how may bits for a pixel
*/
virtual void setFrameSize(float width, float height);
virtual void setViewPortInPoints(float x , float y , float w , float h);
virtual void setScissorInPoints(float x , float y , float w , float h);
/*
* Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop.
*/
void setFrameZoomFactor(float fZoomFactor);
float getFrameZoomFactor();
virtual bool isOpenGLReady();
virtual void end();
virtual void swapBuffers();
virtual void setIMEKeyboardState(bool bOpen);
/*
* Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop.
*/
void setFrameZoomFactor(float fZoomFactor);
float getFrameZoomFactor();
virtual bool isOpenGLReady();
virtual void end();
virtual void swapBuffers();
virtual void pollInputEvents();
virtual void setIMEKeyboardState(bool bOpen);
/**
@brief get the shared main open gl window
@ -54,13 +57,19 @@ public:
CC_DEPRECATED_ATTRIBUTE static EGLView* sharedOpenGLView();
private:
bool initGL();
void destroyGL();
private:
//store current mouse point for moving, valid if and only if the mouse pressed
Point _mousePoint;
bool bIsInit;
float _frameZoomFactor;
bool initGL();
void destroyGL();
//store current mouse point for moving, valid if and only if the mouse pressed
Point _mousePoint;
bool _wasInit;
float _frameZoomFactor;
SDL_Window *_window;
SDL_GLContext _context;
// Several mouse instances are possible.
std::set<int> _pressedMouseInstances;
bool _IMEKeyboardOpened;
};
NS_CC_END

View File

@ -100,7 +100,7 @@ SHAREDLIBS += -lfmodex
endif
endif
SHAREDLIBS += -lglfw -lGLEW -lfontconfig -lpthread -lGL
SHAREDLIBS += -lSDL2 -lGLEW -lfontconfig -lpthread -lGL
SHAREDLIBS += -L$(FMOD_LIBDIR) -Wl,-rpath,$(abspath $(FMOD_LIBDIR))
SHAREDLIBS += -L$(LIB_DIR) -Wl,-rpath,$(abspath $(LIB_DIR))