diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 44cbc65c5..95f8482c5 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -471,6 +471,13 @@ cogl_sources_c += \ $(srcdir)/winsys/cogl-winsys-sdl.c \ $(srcdir)/cogl-sdl.c endif +if SUPPORT_SDL2 +cogl_experimental_h += $(srcdir)/cogl-sdl.h +cogl_sources_c += \ + $(srcdir)/winsys/cogl-winsys-sdl-private.h \ + $(srcdir)/winsys/cogl-winsys-sdl2.c \ + $(srcdir)/cogl-sdl.c +endif EXTRA_DIST += stb_image.c diff --git a/cogl/cogl-sdl.c b/cogl/cogl-sdl.c index 76932c342..5de168386 100644 --- a/cogl/cogl-sdl.c +++ b/cogl/cogl-sdl.c @@ -31,13 +31,13 @@ #include "cogl-renderer-private.h" void -cogl_sdl_renderer_set_event_type (CoglRenderer *renderer, uint8_t type) +cogl_sdl_renderer_set_event_type (CoglRenderer *renderer, int type) { renderer->sdl_event_type_set = TRUE; renderer->sdl_event_type = type; } -uint8_t +int cogl_sdl_renderer_get_event_type (CoglRenderer *renderer) { _COGL_RETURN_VAL_IF_FAIL (renderer->sdl_event_type_set, SDL_USEREVENT); @@ -46,7 +46,7 @@ cogl_sdl_renderer_get_event_type (CoglRenderer *renderer) } CoglContext * -cogl_sdl_context_new (uint8_t type, GError **error) +cogl_sdl_context_new (int type, GError **error) { CoglRenderer *renderer = cogl_renderer_new (); CoglDisplay *display; diff --git a/cogl/cogl-sdl.h b/cogl/cogl-sdl.h index bf2eafaa0..0a3c4632e 100644 --- a/cogl/cogl-sdl.h +++ b/cogl/cogl-sdl.h @@ -123,7 +123,7 @@ G_BEGIN_DECLS * Stability: unstable */ CoglContext * -cogl_sdl_context_new (uint8_t type, GError **error); +cogl_sdl_context_new (int type, GError **error); /** * cogl_sdl_renderer_set_event_type: @@ -147,7 +147,7 @@ cogl_sdl_context_new (uint8_t type, GError **error); * Stability: unstable */ void -cogl_sdl_renderer_set_event_type (CoglRenderer *renderer, uint8_t type); +cogl_sdl_renderer_set_event_type (CoglRenderer *renderer, int type); /** * cogl_sdl_renderer_get_event_type: @@ -161,7 +161,7 @@ cogl_sdl_renderer_set_event_type (CoglRenderer *renderer, uint8_t type); * Since: 2.0 * Stability: unstable */ -uint8_t +int cogl_sdl_renderer_get_event_type (CoglRenderer *renderer); /** diff --git a/cogl/winsys/cogl-winsys-sdl2.c b/cogl/winsys/cogl-winsys-sdl2.c new file mode 100644 index 000000000..44843ade1 --- /dev/null +++ b/cogl/winsys/cogl-winsys-sdl2.c @@ -0,0 +1,415 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2011, 2012 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-renderer-private.h" +#include "cogl-display-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-swap-chain-private.h" +#include "cogl-onscreen-template-private.h" +#include "cogl-context-private.h" +#include "cogl-onscreen-private.h" +#include "cogl-winsys-sdl-private.h" + +typedef struct _CoglContextSdl2 +{ + SDL_Window *current_window; +} CoglContextSdl2; + +typedef struct _CoglRendererSdl2 +{ + int stub; +} CoglRendererSdl2; + +typedef struct _CoglDisplaySdl2 +{ + SDL_Window *dummy_window; + SDL_GLContext *context; +} CoglDisplaySdl2; + +typedef struct _CoglOnscreenSdl2 +{ + SDL_Window *window; +} CoglOnscreenSdl2; + +static CoglFuncPtr +_cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, + const char *name) +{ + return SDL_GL_GetProcAddress (name); +} + +static void +_cogl_winsys_renderer_disconnect (CoglRenderer *renderer) +{ + SDL_VideoQuit (); + + g_slice_free (CoglRendererSdl2, renderer->winsys); +} + +static CoglBool +_cogl_winsys_renderer_connect (CoglRenderer *renderer, + GError **error) +{ + if (SDL_VideoInit (NULL) < 0) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "SDL_Init failed: %s", + SDL_GetError ()); + return FALSE; + } + + renderer->winsys = g_slice_new0 (CoglRendererSdl2); + + return TRUE; +} + +static void +_cogl_winsys_display_destroy (CoglDisplay *display) +{ + CoglDisplaySdl2 *sdl_display = display->winsys; + + _COGL_RETURN_IF_FAIL (sdl_display != NULL); + + if (sdl_display->context) + SDL_GL_DeleteContext (sdl_display->context); + + if (sdl_display->dummy_window) + SDL_DestroyWindow (sdl_display->dummy_window); + + g_slice_free (CoglDisplaySdl2, display->winsys); + display->winsys = NULL; +} + +static void +set_gl_attribs_from_framebuffer_config (CoglFramebufferConfig *config) +{ + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 1); + + SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, + config->need_stencil ? 1 : 0); + + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, + config->swap_chain->length > 1 ? 1 : 0); + + SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, + config->swap_chain->has_alpha ? 1 : 0); +} + +static CoglBool +_cogl_winsys_display_setup (CoglDisplay *display, + GError **error) +{ + CoglDisplaySdl2 *sdl_display; + const char * (* get_string_func) (GLenum name); + const char *gl_version; + + _COGL_RETURN_VAL_IF_FAIL (display->winsys == NULL, FALSE); + + sdl_display = g_slice_new0 (CoglDisplaySdl2); + display->winsys = sdl_display; + + set_gl_attribs_from_framebuffer_config (&display->onscreen_template->config); + + if (display->renderer->driver == COGL_DRIVER_GLES1) + SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, 1); + else if (display->renderer->driver == COGL_DRIVER_GLES2) + SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, 2); + + /* Create a dummy 1x1 window that never gets display so that we can + * create a GL context */ + sdl_display->dummy_window = SDL_CreateWindow ("", + 0, 0, /* x/y */ + 1, 1, /* w/h */ + SDL_WINDOW_OPENGL | + SDL_WINDOW_HIDDEN); + if (sdl_display->dummy_window == NULL) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "SDL_CreateWindow failed: %s", + SDL_GetError ()); + goto error; + } + + sdl_display->context = SDL_GL_CreateContext (sdl_display->dummy_window); + + if (sdl_display->context == NULL) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "SDL_GL_CreateContext failed: %s", + SDL_GetError ()); + goto error; + } + + /* SDL doesn't seem to provide a way to select between GL and GLES + * and instead it will just pick one itself. We can at least try to + * verify that it picked the one we were expecting by looking at the + * GL version string */ + get_string_func = SDL_GL_GetProcAddress ("glGetString"); + gl_version = get_string_func (GL_VERSION); + + switch (display->renderer->driver) + { + case COGL_DRIVER_GL: + /* The first character of the version string will be a digit if + * it's normal GL */ + if (!g_ascii_isdigit (gl_version[0])) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "The GL driver was requested but SDL is using GLES"); + goto error; + } + break; + + case COGL_DRIVER_GLES2: + if (!g_str_has_prefix (gl_version, "OpenGL ES 2")) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "The GLES2 driver was requested but SDL is " + "not using GLES2"); + goto error; + } + break; + + case COGL_DRIVER_GLES1: + if (!g_str_has_prefix (gl_version, "OpenGL ES 1")) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_INIT, + "The GLES1 driver was requested but SDL is " + "not using GLES1"); + goto error; + } + break; + + default: + g_assert_not_reached (); + } + + return TRUE; + +error: + _cogl_winsys_display_destroy (display); + return FALSE; +} + +static CoglBool +_cogl_winsys_context_init (CoglContext *context, GError **error) +{ + CoglRenderer *renderer = context->display->renderer; + + context->winsys = g_new0 (CoglContextSdl2, 1); + + if (G_UNLIKELY (renderer->sdl_event_type_set == FALSE)) + g_error ("cogl_sdl_renderer_set_event_type() or cogl_sdl_context_new() " + "must be called during initialization"); + + if (!_cogl_context_update_features (context, error)) + return FALSE; + + if (SDL_GL_GetSwapInterval () != -1) + COGL_FLAGS_SET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, + TRUE); + + return TRUE; +} + +static void +_cogl_winsys_context_deinit (CoglContext *context) +{ + g_free (context->winsys); +} + +static void +_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) +{ + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + CoglContext *context = fb->context; + CoglContextSdl2 *sdl_context = context->winsys; + CoglDisplaySdl2 *sdl_display = context->display->winsys; + CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys; + + if (sdl_context->current_window == sdl_onscreen->window) + return; + + SDL_GL_MakeCurrent (sdl_onscreen->window, sdl_display->context); + + sdl_context->current_window = sdl_onscreen->window; + + /* It looks like SDL just directly calls a glXSwapInterval function + * when this is called. This may be provided by either the EXT + * extension, the SGI extension or the Mesa extension. The SGI + * extension is per context so we can't just do this once when the + * framebuffer is allocated. See the comments in the GLX winsys for + * more info. */ + if (COGL_FLAGS_GET (context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE)) + { + CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); + + SDL_GL_SetSwapInterval (fb->config.swap_throttled ? 1 : 0); + } +} + +static void +_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) +{ + CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys; + + if (sdl_onscreen->window != NULL) + { + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglContextSdl2 *sdl_context = context->winsys; + + if (sdl_context->current_window == sdl_onscreen->window) + sdl_context->current_window = NULL; + + SDL_DestroyWindow (sdl_onscreen->window); + sdl_onscreen->window = NULL; + } + + g_slice_free (CoglOnscreenSdl2, sdl_onscreen); + onscreen->winsys = NULL; +} + +static CoglBool +_cogl_winsys_onscreen_init (CoglOnscreen *onscreen, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenSdl2 *sdl_onscreen; + SDL_Window *window; + int width, height; + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + + window = SDL_CreateWindow ("" /* title */, + 0, 0, /* x/y */ + width, height, + SDL_WINDOW_OPENGL | + SDL_WINDOW_HIDDEN); + + if (window == NULL) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_ONSCREEN, + "SDL_CreateWindow failed: %s", + SDL_GetError ()); + return FALSE; + } + + onscreen->winsys = g_slice_new (CoglOnscreenSdl2); + sdl_onscreen = onscreen->winsys; + sdl_onscreen->window = window; + + return TRUE; +} + +static void +_cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen) +{ + CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys; + + SDL_GL_SwapWindow (sdl_onscreen->window); +} + +static void +_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen) +{ + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglContextSdl2 *sdl_context = context->winsys; + CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys; + + if (sdl_context->current_window != sdl_onscreen->window) + return; + + sdl_context->current_window = NULL; + _cogl_winsys_onscreen_bind (onscreen); +} + +static void +_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, + CoglBool visibility) +{ + CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys; + + if (visibility) + SDL_ShowWindow (sdl_onscreen->window); + else + SDL_HideWindow (sdl_onscreen->window); +} + +const CoglWinsysVtable * +_cogl_winsys_sdl_get_vtable (void) +{ + static CoglBool vtable_inited = FALSE; + static CoglWinsysVtable vtable; + + /* It would be nice if we could use C99 struct initializers here + like the GLX backend does. However this code is more likely to be + compiled using Visual Studio which (still!) doesn't support them + so we initialize it in code instead */ + + if (!vtable_inited) + { + memset (&vtable, 0, sizeof (vtable)); + + vtable.id = COGL_WINSYS_ID_SDL; + vtable.name = "SDL"; + vtable.renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address; + vtable.renderer_connect = _cogl_winsys_renderer_connect; + vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect; + vtable.display_setup = _cogl_winsys_display_setup; + vtable.display_destroy = _cogl_winsys_display_destroy; + vtable.context_init = _cogl_winsys_context_init; + vtable.context_deinit = _cogl_winsys_context_deinit; + vtable.onscreen_init = _cogl_winsys_onscreen_init; + vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit; + vtable.onscreen_bind = _cogl_winsys_onscreen_bind; + vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers; + vtable.onscreen_update_swap_throttled = + _cogl_winsys_onscreen_update_swap_throttled; + vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility; + + vtable_inited = TRUE; + } + + return &vtable; +} diff --git a/configure.ac b/configure.ac index f273dbd72..c18e1bf65 100644 --- a/configure.ac +++ b/configure.ac @@ -748,6 +748,30 @@ AS_IF([test "x$enable_sdl" = "xyes"], [SUPPORT_SDL=no]) AM_CONDITIONAL(SUPPORT_SDL, [test "x$SUPPORT_SDL" = "xyes"]) +AC_ARG_ENABLE( + [sdl2], + [AC_HELP_STRING([--enable-sdl2=@<:@no/yes@:>@], [Enable SDL2 support @<:@default=no@:>@])], + [], + [enable_sdl2=no]) +AS_IF([test "x$enable_sdl2" = "xyes"], + [ + PKG_CHECK_MODULES([SDL2], + [sdl2], + [], + [AC_MSG_ERROR([SDL2 support requested but SDL2 not found])]) + + SUPPORT_SDL2=yes + GL_WINSYS_APIS="$GL_WINSYS_APIS sdl2" + COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES sdl2" + + COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_SDL_SUPPORT" + ], + [SUPPORT_SDL2=no]) +AM_CONDITIONAL(SUPPORT_SDL2, [test "x$SUPPORT_SDL2" = "xyes"]) + +AS_IF([test "x$SUPPORT_SDL2" = "xyes" -a "x$SUPPORT_SDL" = "xyes"], + [AC_MSG_ERROR([The SDL1 and SDL2 winsyses are currently mutually exclusive])]) + EGL_PLATFORM_COUNT=0 AC_ARG_ENABLE( @@ -890,7 +914,7 @@ AC_ARG_ENABLE( [xlib-egl-platform], [AC_HELP_STRING([--enable-xlib-egl-platform=@<:@no/yes@:>@], [Enable support for the Xlib egl platform @<:@default=auto@:>@])], [], - AS_IF([test "x$enable_gles1" = "xyes" -o "x$enable_gles2" = "xyes" && test "x$SUPPORT_SDL_GLES" != "xyes" && test $EGL_PLATFORM_COUNT -eq 0], + AS_IF([test "x$enable_gles1" = "xyes" -o "x$enable_gles2" = "xyes" && test "x$SUPPORT_SDL_GLES" != "xyes" && test "x$SUPPORT_SDL2" != "xyes" && test $EGL_PLATFORM_COUNT -eq 0], [enable_xlib_egl_platform=yes], [enable_xlib_egl_platform=no]) ) AS_IF([test "x$enable_xlib_egl_platform" = "xyes"], diff --git a/examples/Makefile.am b/examples/Makefile.am index cb2964083..6145d2290 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -63,6 +63,12 @@ cogl_sdl_hello_SOURCES = cogl-sdl-hello.c cogl_sdl_hello_LDADD = $(common_ldadd) endif +if SUPPORT_SDL2 +programs += cogl-sdl2-hello +cogl_sdl2_hello_SOURCES = cogl-sdl2-hello.c +cogl_sdl2_hello_LDADD = $(common_ldadd) +endif + cogl_gles2_context_SOURCES = cogl-gles2-context.c cogl_gles2_context_LDADD = $(common_ldadd) diff --git a/examples/cogl-sdl2-hello.c b/examples/cogl-sdl2-hello.c new file mode 100644 index 000000000..f46577114 --- /dev/null +++ b/examples/cogl-sdl2-hello.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include + +/* This short example is just to demonstrate mixing SDL with Cogl as a + simple way to get portable support for events */ + +typedef struct Data +{ + CoglPrimitive *triangle; + CoglPipeline *pipeline; + float center_x, center_y; + CoglFramebuffer *fb; + CoglBool quit; + CoglBool redraw_queued; +} Data; + +static CoglBool +redraw (Data *data) +{ + CoglFramebuffer *fb = data->fb; + + cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + cogl_framebuffer_push_matrix (fb); + cogl_framebuffer_translate (fb, data->center_x, -data->center_y, 0.0f); + + cogl_framebuffer_draw_primitive (fb, data->pipeline, data->triangle); + cogl_framebuffer_pop_matrix (fb); + + cogl_onscreen_swap_buffers (COGL_ONSCREEN (fb)); + + return FALSE; +} + +static void +handle_event (Data *data, SDL_Event *event) +{ + switch (event->type) + { + case SDL_WINDOWEVENT: + switch (event->window.event) + { + case SDL_WINDOWEVENT_EXPOSED: + data->redraw_queued = TRUE; + break; + + case SDL_WINDOWEVENT_CLOSE: + data->quit = TRUE; + break; + } + break; + + case SDL_MOUSEMOTION: + { + int width = + cogl_framebuffer_get_width (COGL_FRAMEBUFFER (data->fb)); + int height = + cogl_framebuffer_get_height (COGL_FRAMEBUFFER (data->fb)); + + data->center_x = event->motion.x * 2.0f / width - 1.0f; + data->center_y = event->motion.y * 2.0f / height - 1.0f; + + data->redraw_queued = TRUE; + } + break; + + case SDL_QUIT: + data->quit = TRUE; + break; + } +} + +int +main (int argc, char **argv) +{ + CoglContext *ctx; + CoglOnscreen *onscreen; + GError *error = NULL; + CoglVertexP2C4 triangle_vertices[] = { + {0, 0.7, 0xff, 0x00, 0x00, 0x80}, + {-0.7, -0.7, 0x00, 0xff, 0x00, 0xff}, + {0.7, -0.7, 0x00, 0x00, 0xff, 0xff} + }; + Data data; + SDL_Event event; + + ctx = cogl_sdl_context_new (SDL_USEREVENT, &error); + if (!ctx) + { + fprintf (stderr, "Failed to create context: %s\n", error->message); + return 1; + } + + onscreen = cogl_onscreen_new (ctx, 800, 600); + data.fb = COGL_FRAMEBUFFER (onscreen); + + data.center_x = 0.0f; + data.center_y = 0.0f; + data.quit = FALSE; + + cogl_onscreen_show (onscreen); + + data.triangle = cogl_primitive_new_p2c4 (ctx, COGL_VERTICES_MODE_TRIANGLES, + 3, triangle_vertices); + data.pipeline = cogl_pipeline_new (ctx); + + data.redraw_queued = TRUE; + while (!data.quit) + { + while (!data.quit) + { + if (!SDL_PollEvent (&event)) + { + if (data.redraw_queued) + break; + + cogl_sdl_idle (ctx); + if (!SDL_WaitEvent (&event)) + { + fprintf (stderr, "Error waiting for SDL events"); + return 1; + } + } + + handle_event (&data, &event); + cogl_sdl_handle_event (ctx, &event); + } + + data.redraw_queued = redraw (&data); + } + + cogl_object_unref (ctx); + + return 0; +}