c28dfdf6b47c728abfcda574e80d99c01f53a30d
[sdlhildon] / sdlgles / src / SDL_gles.c
1 /* This file is part of SDL_gles - SDL addon for OpenGL|ES
2  * Copyright (C) 2010 Javier S. Pedro
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 3 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA or see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <dlfcn.h>
24 #include <assert.h>
25
26 #include <EGL/egl.h>
27
28 #include <SDL.h>
29 #include <SDL_syswm.h>
30
31 #include "SDL_gles.h"
32
33 typedef struct SDL_GLES_ContextPriv
34 {
35         SDL_GLES_Context p;
36
37         EGLConfig egl_config;
38         EGLContext egl_context;
39 } SDL_GLES_ContextPriv;
40
41 static const char * default_libgl[] = {
42         [SDL_GLES_VERSION_1_1] = "/usr/lib/libGLES_CM.so",
43         [SDL_GLES_VERSION_2_0] = "/usr/lib/libGLESv2.so"
44 };
45
46 static SDL_GLES_Version gl_version = SDL_GLES_VERSION_NONE;
47 static void* gl_handle = NULL;
48 static EGLint egl_major, egl_minor;
49
50 static Display *display = NULL;
51 static EGLDisplay *egl_display = EGL_NO_DISPLAY;
52 static EGLSurface egl_surface = EGL_NO_SURFACE;
53 static EGLint attrib_list[] = {
54         EGL_BUFFER_SIZE,                        0,
55         EGL_RED_SIZE,                           0,
56         EGL_GREEN_SIZE,                         0,
57         EGL_BLUE_SIZE,                          0,
58         EGL_ALPHA_SIZE,                         0,
59         EGL_CONFIG_CAVEAT,                      EGL_DONT_CARE,
60         EGL_CONFIG_ID,                          EGL_DONT_CARE,
61         EGL_DEPTH_SIZE,                         0,
62         EGL_LEVEL,                                      0,
63         EGL_NATIVE_RENDERABLE,          EGL_DONT_CARE,
64         EGL_NATIVE_VISUAL_TYPE,         EGL_DONT_CARE,
65         EGL_SAMPLE_BUFFERS,                     0,
66         EGL_SAMPLES,                            0,
67         EGL_STENCIL_SIZE,                       0,
68         EGL_SURFACE_TYPE,                       EGL_WINDOW_BIT,
69         EGL_TRANSPARENT_TYPE,           EGL_NONE,
70         EGL_TRANSPARENT_RED_VALUE,      EGL_DONT_CARE,
71         EGL_TRANSPARENT_GREEN_VALUE,EGL_DONT_CARE,
72         EGL_TRANSPARENT_BLUE_VALUE,     EGL_DONT_CARE,
73         EGL_NONE
74 };
75 static SDL_GLES_ContextPriv *cur_context = NULL;
76
77 static const char * get_error_string(int error) {
78         switch (error) {
79                 case EGL_SUCCESS:
80                         return "EGL_SUCCESS";
81                 case EGL_NOT_INITIALIZED:
82                         return "EGL_NOT_INITIALIZED";
83                 case EGL_BAD_ACCESS:
84                         return "EGL_BAD_ACCESS";
85                 case EGL_BAD_ALLOC:
86                         return "EGL_BAD_ALLOC";
87                 case EGL_BAD_ATTRIBUTE:
88                         return "EGL_BAD_ATTRIBUTE";
89                 case EGL_BAD_CONFIG:
90                         return "EGL_BAD_CONFIG";
91                 case EGL_BAD_CONTEXT:
92                         return "EGL_BAD_CONTEXT";
93                 case EGL_BAD_CURRENT_SURFACE:
94                         return "EGL_BAD_CURRENT_SURFACE";
95                 case EGL_BAD_DISPLAY:
96                         return "EGL_BAD_DISPLAY";
97                 case EGL_BAD_MATCH:
98                         return "EGL_BAD_MATCH";
99                 case EGL_BAD_NATIVE_PIXMAP:
100                         return "EGL_BAD_NATIVE_PIXMAP";
101                 case EGL_BAD_NATIVE_WINDOW:
102                         return "EGL_BAD_NATIVE_WINDOW";
103                 case EGL_BAD_PARAMETER:
104                         return "EGL_BAD_PARAMETER";
105                 case EGL_BAD_SURFACE:
106                         return "EGL_BAD_SURFACE";
107                 case EGL_CONTEXT_LOST:
108                         return "EGL_CONTEXT_LOST";
109                 default:
110                         return "EGL_UNKNOWN_ERROR";
111     }
112 }
113
114 int SDL_GLES_LoadLibrary(const char *path)
115 {
116         if (!path) {
117                 path = getenv("SDL_VIDEO_GL_DRIVER");
118                 if (!path) {
119                         switch (gl_version) {
120                                 case SDL_GLES_VERSION_1_1:
121                                 case SDL_GLES_VERSION_2_0:
122                                         path = default_libgl[gl_version];
123                                 break;
124                                 default:
125                                         SDL_SetError("No GL version specific and SDL_VIDEO_GL_DRIVER set");
126                                         return -1;
127                         }
128                 }
129         }
130
131         /* Dynamically load the desired GL library */
132         gl_handle = dlopen(path, RTLD_LAZY|RTLD_GLOBAL);
133         if (!gl_handle) {
134                 SDL_SetError("Failed to open GL library: %s (%s)", path, dlerror());
135                 return -2;
136         }
137
138         return 0;
139 }
140
141 void* SDL_GLES_GetProcAddress(const char *proc)
142 {
143         if (!gl_handle) return NULL;
144         return dlsym(gl_handle, proc);
145 }
146
147 int SDL_GLES_Init(SDL_GLES_Version version)
148 {
149         SDL_SysWMinfo info;
150         EGLBoolean res;
151
152         SDL_VERSION(&info.version);
153         if (SDL_GetWMInfo(&info) != 1) {
154                 SDL_SetError("SDL_gles is incompatible with this SDL version");
155                 return -1;
156         }
157
158         display = info.info.x11.gfxdisplay;
159
160         egl_display = eglGetDisplay((EGLNativeDisplayType)display);
161         if (egl_display == EGL_NO_DISPLAY) {
162                 SDL_SetError("EGL found no available displays");
163                 return -2;
164         }
165
166         res = eglInitialize(egl_display, &egl_major, &egl_minor);
167         if (!res) {
168                 SDL_SetError("EGL failed to initialize: %s",
169                         get_error_string(eglGetError()));
170                 return -2;
171         }
172
173         gl_version = version;
174         return 0;
175 }
176
177 void SDL_GLES_Quit()
178 {
179         if (gl_handle) {
180                 dlclose(gl_handle);
181                 gl_handle = NULL;
182         }
183         if (egl_display != EGL_NO_DISPLAY) {
184                 eglTerminate(egl_display);
185                 egl_display = EGL_NO_DISPLAY;
186         }
187 }
188
189 int SDL_GLES_SetVideoMode()
190 {
191         SDL_SysWMinfo info;
192         EGLBoolean res;
193
194         SDL_VERSION(&info.version);
195         if (SDL_GetWMInfo(&info) != 1) {
196                 SDL_SetError("SDL_gles is incompatible with this SDL version");
197                 return -1;
198         }
199
200         /* Destroy previous surface, if any. */
201         if (egl_surface != EGL_NO_SURFACE) {
202                 /* Ensure the surface is not the current one,
203                  * thus freeing memory earlier. */
204                 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
205                          EGL_NO_CONTEXT);
206                 eglDestroySurface(egl_display, egl_surface);
207                 egl_surface = EGL_NO_SURFACE;
208         }
209
210         /* No current context? Quietly defer surface creation.
211          * Surface will be created on the call to MakeCurrent. */
212         if (!cur_context) {
213                 return 0;
214         }
215
216         /* Create the new window surface. */
217         egl_surface = eglCreateWindowSurface(egl_display, cur_context->egl_config,
218                 (EGLNativeWindowType)info.info.x11.window, NULL);
219         if (egl_surface == EGL_NO_SURFACE) {
220                 SDL_SetError("EGL failed to create a window surface: %s",
221                         get_error_string(eglGetError()));
222                 return -2;
223         }
224
225         /* New surface created. Make it active. */
226         assert(cur_context && cur_context->egl_context != EGL_NO_CONTEXT);
227         res = eglMakeCurrent(egl_display, egl_surface, egl_surface,
228                 cur_context->egl_context);
229
230         if (!res) {
231                 SDL_SetError("EGL failed to change current surface: %s",
232                         get_error_string(eglGetError()));
233                 cur_context = NULL;
234                 return -2;
235         }
236
237         return 0;
238 }
239
240 SDL_GLES_Context* SDL_GLES_CreateContext(void)
241 {
242         SDL_GLES_ContextPriv *context = malloc(sizeof(SDL_GLES_ContextPriv));
243         if (!context) {
244                 SDL_Error(SDL_ENOMEM);
245                 return NULL;
246         }
247
248         EGLBoolean res;
249         EGLConfig configs[1];
250         EGLint num_config;
251
252         res = eglChooseConfig(egl_display, attrib_list, configs, 1, &num_config);
253         if (!res || num_config < 1) {
254                 SDL_SetError("EGL failed to find any valid config with required attributes: %s",
255                         get_error_string(eglGetError()));
256                 return NULL;
257         }
258
259         context->egl_config = configs[0];
260         context->egl_context = eglCreateContext(egl_display, configs[0],
261                 EGL_NO_CONTEXT, NULL);
262
263         return (SDL_GLES_Context*) context;
264 }
265
266 void SDL_GLES_DeleteContext(SDL_GLES_Context* c)
267 {
268         SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
269         if (!context) return;
270
271         if (cur_context == context) {
272                 /* Deleting the active context */
273                 SDL_GLES_MakeCurrent(NULL);
274         }
275
276         eglDestroyContext(egl_display, context->egl_context);
277         free(context);
278 }
279
280 int SDL_GLES_MakeCurrent(SDL_GLES_Context* c)
281 {
282         SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
283         int res;
284
285         cur_context = context;
286
287         /* SDL_GLES_SetVideoMode() will appropiately clear the current context
288          * (and surface), then create a new surface matching the selected context
289          * config and make both the surface and the context the active ones. */
290         res = SDL_GLES_SetVideoMode();
291         if (res != 0) return res; /* Surface (re-)creation failed. */
292
293         /* TODO Update attrib_list. Make SDL_GLES_GetAttribute work. */
294
295         switch (gl_version) {
296                 case SDL_GLES_VERSION_1_1:
297                 case SDL_GLES_VERSION_2_0:
298                         res = eglBindAPI(EGL_OPENGL_ES_API);
299                         if (!res) {
300                                 SDL_SetError("EGL failed to bind the required API: %s",
301                                         get_error_string(eglGetError()));
302                                 return -2;
303                         }
304                         break;
305                 default:
306                         break;
307         }
308
309         return 0;
310 }
311
312 void SDL_GLES_SwapBuffers()
313 {
314         eglSwapBuffers(egl_display, egl_surface);
315 }
316