Commit 602fc904 authored by Chad Versace's avatar Chad Versace

all: Replace tagged unions with a more traditional object model

This rewrites the bulk of Waffle's code.

When I first began writing Waffle, I wanted to experiment with
a non-traditional object model that used tagged unions. Very soon I began
to abhor the "innovative" decision. This patch replaces the tagged-union
model with a more traditional object model (as found in the Linux kernel
[1], Google's NaCl, libdrm, and many other places) that uses vtables and
embedded structures.

[1] Neil Brown. LWN, 2011 June 1. Object-oriented design patterns in the kernel.
    (Part 1: http://lwn.net/Articles/444910/).
    (Part 2: http://lwn.net/Articles/446317/).

As an example of the new object model, below is an outline of how
waffle_window_swap_buffers() is now implemeneted.

// file: waffle_window.c

bool
waffle_window_swap_buffers(struct waffle_window *self)
{
    struct wcore_window *wc_self = wcore_window(self); // safe cast
    // Check preconditions ...
    return wc_self->vtbl->swap_buffers(wc_self);
}

// file: wcore_window.h

struct wcore_window_vtbl {
    bool
    (*swap_buffers)(struct wcore_window *self);
    // More member functions ...
};

struct wcore_window {
    const struct wcore_window_vtbl *vtbl;
    struct waffle_window {} wfl;
    // More members ...
};

// file: glx_window.h

struct glx_window {
    struct wcore_window wcore;
    // More members ...
};

// file: glx_window.c

static bool
glx_window_swap_buffers(struct wcore_window *wc_self)
{
    struct glx_window *self = glx_window(wc_self); // safe cast
    // Call glXSwapBuffers ...
    return true;
}

static const struct wcore_window_vtbl glx_window_wcore_vtbl = {
    .swap_buffers = glx_window_swap_buffers,
    // More members ...
};
Signed-off-by: default avatarChad Versace <chad.versace@linux.intel.com>
parent 50b0bca2
......@@ -90,13 +90,6 @@ extern "C" {
WAFFLE_API bool
waffle_init(const int32_t *attrib_list);
/// @brief Get the platform given to waffle_init().
///
/// This may be called before waffle_init(), in which `WAFFLE_NONE`
/// is returned. Otherwise, one of `WAFFLE_PLATFORM_*` is returned.
WAFFLE_API int32_t
waffle_get_platform(void);
#ifdef __cplusplus
} // end extern "C"
#endif
......
......@@ -15,8 +15,8 @@ set(waffle_sources
api/waffle_init.c
api/waffle_window.c
core/wcore_config_attrs.c
core/wcore_display.c
core/wcore_error.c
core/wcore_platform.c
core/wcore_tinfo.c
)
......@@ -27,7 +27,6 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
cgl/cgl_display.m
cgl/cgl_dl.m
cgl/cgl_error.m
cgl/cgl_gl_misc.m
cgl/cgl_platform.m
cgl/cgl_window.m
......@@ -54,8 +53,6 @@ if(waffle_has_glx)
glx/glx_config.c
glx/glx_context.c
glx/glx_display.c
glx/glx_dl.c
glx/glx_gl_misc.c
glx/glx_platform.c
glx/glx_window.c
)
......@@ -80,8 +77,6 @@ if(waffle_has_wayland)
wayland/wayland_config.c
wayland/wayland_context.c
wayland/wayland_display.c
wayland/wayland_dl.c
wayland/wayland_gl_misc.c
wayland/wayland_platform.c
wayland/wayland_priv_egl.c
wayland/wayland_window.c
......@@ -94,7 +89,8 @@ endif()
if(waffle_has_x11)
list(APPEND waffle_sources
x11/x11.c
x11/x11_display.c
x11/x11_window.c
)
list(APPEND waffle_libdeps
${waffle_X11-xcb_library}
......@@ -108,8 +104,6 @@ if(waffle_has_x11_egl)
x11_egl/xegl_config.c
x11_egl/xegl_context.c
x11_egl/xegl_display.c
x11_egl/xegl_dl.c
x11_egl/xegl_gl_misc.c
x11_egl/xegl_platform.c
x11_egl/xegl_priv_egl.c
x11_egl/xegl_window.c
......
......@@ -23,27 +23,16 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// @defgroup xegl_dl xegl_dl
/// @ingroup xegl
/// @{
/// @file
#pragma once
#include <stdbool.h>
#include <stdint.h>
union native_platform;
#include <stddef.h>
bool
xegl_dl_can_open(
union native_platform *native,
int32_t waffle_dl);
void*
xegl_dl_sym(
union native_platform *native,
int32_t waffle_dl,
const char *name);
// This header is so sad and lonely... but there is no other appropriate place
// to define this struct.
/// @}
struct api_object {
/// @brief Display to which object belongs.
///
/// For consistency, a `waffle_display` belongs to itself.
size_t display_id;
};
......@@ -28,15 +28,17 @@
/// @file
#include "api_priv.h"
#include <stdio.h>
#include <stdlib.h>
#include "api_priv.h"
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_platform.h>
struct wcore_platform *api_current_platform = 0;
#include "api_object.h"
struct wcore_platform *api_platform = 0;
bool
api_check_entry(const struct api_object *obj_list[], int length)
......@@ -45,7 +47,7 @@ api_check_entry(const struct api_object *obj_list[], int length)
wcore_error_reset();
if (!api_current_platform) {
if (!api_platform) {
wcore_error(WAFFLE_NOT_INITIALIZED);
return false;
}
......@@ -65,17 +67,4 @@ api_check_entry(const struct api_object *obj_list[], int length)
return true;
}
size_t
api_new_object_id(void)
{
static size_t counter = 1;
if (counter == 0) {
fprintf(stderr, "waffle: error: internal counter wrapped to 0\n");
abort();
}
return counter++;
}
/// @}
......@@ -38,13 +38,12 @@
struct api_object;
struct wcore_platform;
union native_config;
union native_context;
union native_display;
union native_window;
/// @brief Managed by waffle_init().
extern struct wcore_platform *api_current_platform;
///
/// This is null if and only if waffle has not been initialized with
/// waffle_init().
extern struct wcore_platform *api_platform;
/// @brief Used to validate most API entry points.
///
......@@ -54,67 +53,8 @@ extern struct wcore_platform *api_current_platform;
/// Emit an error and return false if any of the following:
/// - waffle is not initialized
/// - an object pointer is null
/// - an object has an old platform id
/// - two objects belong to different displays
bool
api_check_entry(const struct api_object *obj_list[], int length);
/// @brief Create a unique id.
size_t
api_new_object_id(void);
struct api_object {
/// @brief Display to which object belongs.
///
/// This is identical to object_id for waffle_display.
size_t display_id;
};
struct waffle_config {
struct api_object api;
union native_config *native;
};
struct waffle_context {
struct api_object api;
union native_context *native;
};
struct waffle_display {
struct api_object api;
union native_display *native;
};
struct waffle_window {
struct api_object api;
union native_window *native;
};
/// Return null if @a config is null.
static inline struct api_object*
waffle_config_get_api_obj(struct waffle_config *config)
{
return config ? &config->api : NULL;
}
/// Return null if @a ctx is null.
static inline struct api_object*
waffle_context_get_api_obj(struct waffle_context *ctx)
{
return ctx ? &ctx->api : NULL;
}
/// Return null if @a display is null.
static inline struct api_object*
waffle_display_get_api_obj(struct waffle_display *display)
{
return display ? &display->api : NULL;
}
/// Return null if @a window is null.
static inline struct api_object*
waffle_window_get_api_obj(struct waffle_window *window)
{
return window ? &window->api : NULL;
}
/// @}
......@@ -32,9 +32,9 @@
#include <stdlib.h>
#include <waffle/native.h>
#include <waffle/core/wcore_config_attrs.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_config.h>
#include <waffle/core/wcore_display.h>
#include <waffle/core/wcore_platform.h>
#include "api_priv.h"
......@@ -44,56 +44,44 @@ waffle_config_choose(
struct waffle_display *dpy,
const int32_t attrib_list[])
{
struct waffle_config *self = NULL;
struct wcore_config *wc_self;
struct wcore_display *wc_dpy = wcore_display(dpy);
struct wcore_config_attrs attrs;
bool ok = true;
const struct api_object *obj_list[] = {
waffle_display_get_api_obj(dpy),
wc_dpy ? &wc_dpy->api : NULL,
};
if (!api_check_entry(obj_list, 1))
return NULL;
self = malloc(sizeof(*self));
if (!self) {
wcore_error(WAFFLE_OUT_OF_MEMORY);
return NULL;
}
ok = wcore_config_attrs_parse(attrib_list, &attrs);
if (!ok)
goto error;
self->api.display_id = dpy->api.display_id;
self->native = api_current_platform->dispatch->
config_choose(dpy->native, &attrs);
if (!self->native)
goto error;
return NULL;
return self;
wc_self = api_platform->vtbl->choose_config(api_platform,
wc_dpy,
&attrs);
if (!wc_self)
return NULL;
error:
free(self);
return NULL;
return &wc_self->wfl;
}
bool
waffle_config_destroy(struct waffle_config *self)
{
bool ok = true;
struct wcore_config *wc_self = wcore_config(self);
const struct api_object *obj_list[] = {
waffle_config_get_api_obj(self),
wc_self ? &wc_self->api : NULL,
};
if (!api_check_entry(obj_list, 1))
return false;
ok &= api_current_platform->dispatch->config_destroy(self->native);
free(self);
return ok;
return wc_self->vtbl->destroy(wc_self);
}
/// @}
......@@ -32,8 +32,7 @@
#include <stdlib.h>
#include <waffle/native.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_context.h>
#include <waffle/core/wcore_platform.h>
#include "api_priv.h"
......@@ -43,56 +42,42 @@ waffle_context_create(
struct waffle_config *config,
struct waffle_context *shared_ctx)
{
struct waffle_context *self;
int obj_list_length;
struct wcore_context *wc_self;
struct wcore_config *wc_config = wcore_config(config);
struct wcore_context *wc_shared_ctx = wcore_context(shared_ctx);
const struct api_object *obj_list[] = {
waffle_config_get_api_obj(config),
waffle_context_get_api_obj(shared_ctx),
};
const struct api_object *obj_list[2];
int len = 0;
if (shared_ctx)
obj_list_length = 2;
else
obj_list_length = 1;
obj_list[len++] = wc_config ? &wc_config->api : NULL;
if (wc_shared_ctx)
obj_list[len++] = wc_shared_ctx ? &wc_shared_ctx->api : NULL;
if (!api_check_entry(obj_list, obj_list_length))
if (!api_check_entry(obj_list, len))
return false;
self = malloc(sizeof(*self));
if (!self) {
wcore_error(WAFFLE_OUT_OF_MEMORY);
return NULL;
}
self->api.display_id = config->api.display_id;
self->native = api_current_platform->dispatch->
context_create(config->native,
shared_ctx ? shared_ctx->native : NULL);
if (!self->native) {
free(self);
wc_self = api_platform->vtbl->create_context(api_platform,
wc_config,
wc_shared_ctx);
if (!wc_self)
return NULL;
}
return self;
return &wc_self->wfl;
}
bool
waffle_context_destroy(struct waffle_context *self)
{
bool ok = true;
struct wcore_context *wc_self = wcore_context(self);
const struct api_object *obj_list[] = {
waffle_context_get_api_obj(self),
wc_self ? &wc_self->api : NULL,
};
if (!api_check_entry(obj_list, 1))
return false;
ok &= api_current_platform->dispatch->context_destroy(self->native);
free(self);
return ok;
return wc_self->vtbl->destroy(wc_self);
}
/// @}
......@@ -32,55 +32,42 @@
#include <stdlib.h>
#include <waffle/native.h>
#include <waffle/waffle_enum.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_display.h>
#include <waffle/core/wcore_platform.h>
#include <waffle/core/wcore_util.h>
#include "api_priv.h"
struct waffle_display*
waffle_display_connect(const char *name)
{
struct waffle_display *self;
struct wcore_display *wc_self;
if (!api_check_entry(NULL, 0))
return NULL;
self = malloc(sizeof(*self));
if (!self) {
wcore_error(WAFFLE_OUT_OF_MEMORY);
wc_self = api_platform->vtbl->connect_to_display(api_platform, name);
if (!wc_self)
return NULL;
}
self->api.display_id = api_new_object_id();
self->native = api_current_platform->dispatch->
display_connect(api_current_platform->native, name);
if (!self->native) {
free(self);
return NULL;
}
return self;
return &wc_self->wfl;
}
bool
waffle_display_disconnect(struct waffle_display *self)
{
bool ok = true;
struct wcore_display *wc_self = wcore_display(self);
const struct api_object *obj_list[] = {
waffle_display_get_api_obj(self),
wc_self ? &wc_self->api : NULL,
};
if (!api_check_entry(obj_list, 1))
return false;
ok &= api_current_platform->dispatch->display_disconnect(self->native);
free(self);
return ok;
return wc_self->vtbl->destroy(wc_self);
}
bool
......@@ -88,8 +75,10 @@ waffle_display_supports_context_api(
struct waffle_display *self,
int32_t context_api)
{
struct wcore_display *wc_self = wcore_display(self);
const struct api_object *obj_list[] = {
waffle_display_get_api_obj(self),
wc_self ? &wc_self->api : NULL,
};
if (!api_check_entry(obj_list, 1))
......@@ -106,8 +95,7 @@ waffle_display_supports_context_api(
return false;
}
return api_current_platform->dispatch->
display_supports_context_api(self->native, context_api);
return wc_self->vtbl->supports_context_api(wc_self, context_api);
}
/// @}
......@@ -30,7 +30,6 @@
#include <waffle/waffle_dl.h>
#include <waffle/native.h>
#include <waffle/waffle_enum.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_platform.h>
......@@ -60,8 +59,7 @@ waffle_dl_can_open(int32_t dl)
if (!waffle_dl_check_enum(dl))
return false;
return api_current_platform->dispatch->
dl_can_open(api_current_platform->native, dl);
return api_platform->vtbl->dl_can_open(api_platform, dl);
}
void*
......@@ -73,8 +71,7 @@ waffle_dl_sym(int32_t dl, const char *name)
if (!waffle_dl_check_enum(dl))
return false;
return api_current_platform->dispatch->
dl_sym(api_current_platform->native, dl, name);
return api_platform->vtbl->dl_sym(api_platform, dl, name);
}
/// @}
......@@ -33,10 +33,12 @@
#include <stddef.h>
#include <string.h>
#include <waffle/native.h>
#include <waffle/waffle_enum.h>
#include <waffle/core/wcore_context.h>
#include <waffle/core/wcore_display.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_platform.h>
#include <waffle/core/wcore_window.h>
#include "api_priv.h"
......@@ -83,26 +85,26 @@ waffle_make_current(
struct waffle_window *window,
struct waffle_context *ctx)
{
int obj_list_length = 1;
struct wcore_display *wc_dpy = wcore_display(dpy);
struct wcore_window *wc_window = wcore_window(window);
struct wcore_context *wc_ctx = wcore_context(ctx);
const struct api_object *obj_list[] = {
waffle_display_get_api_obj(dpy),
0,
0,
};
const struct api_object *obj_list[3];
int len = 0;
if (window)
obj_list[obj_list_length++] = waffle_window_get_api_obj(window);
if (ctx)
obj_list[obj_list_length++] = waffle_context_get_api_obj(ctx);
obj_list[len++] = wc_dpy ? &wc_dpy->api : NULL;
if (wc_window)
obj_list[len++] = wc_window ? &wc_window->api : NULL;
if (wc_ctx)
obj_list[len++] = wc_ctx ? &wc_ctx->api : NULL;
if (!api_check_entry(obj_list, obj_list_length))
if (!api_check_entry(obj_list, len))
return false;
return api_current_platform->dispatch->
make_current(dpy->native,
window ? window->native :NULL,
ctx ? ctx->native : NULL);
return api_platform->vtbl->make_current(api_platform,
wc_dpy,
wc_window,
wc_ctx);
}
void*
......@@ -111,8 +113,7 @@ waffle_get_proc_address(const char *name)
if (!api_check_entry(NULL, 0))
return NULL;
return api_current_platform->dispatch->
get_proc_address(api_current_platform->native, name);
return api_platform->vtbl->get_proc_address(api_platform, name);
}
/// @}
......@@ -36,6 +36,12 @@
#include "api_priv.h"
struct wcore_platform* droid_platform_create(void);
struct wcore_platform* cgl_platform_create(void);
struct wcore_platform* glx_platform_create(void);
struct wcore_platform* wayland_platform_create(void);
struct wcore_platform* xegl_platform_create(void);
static bool
waffle_init_parse_attrib_list(
const int32_t attrib_list[],
......@@ -93,6 +99,36 @@ waffle_init_parse_attrib_list(
return true;
}
static struct wcore_platform*
waffle_init_create_platform(int32_t waffle_platform)
{
switch (waffle_platform) {
#ifdef WAFFLE_HAS_ANDROID
case WAFFLE_PLATFORM_ANDROID:
return droid_platform_create();
#endif
#ifdef WAFFLE_HAS_CGL
case WAFFLE_PLATFORM_CGL:
return cgl_platform_create();
#endif
#ifdef WAFFLE_HAS_GLX
case WAFFLE_PLATFORM_GLX:
return glx_platform_create();
#endif
#ifdef WAFFLE_HAS_WAYLAND
case WAFFLE_PLATFORM_WAYLAND:
return wayland_platform_create();
#endif
#ifdef WAFFLE_HAS_X11_EGL
case WAFFLE_PLATFORM_X11_EGL:
return xegl_platform_create();
#endif
default:
assert(false);
return NULL;
}
}
bool
waffle_init(const int32_t *attrib_list)
{
......@@ -101,7 +137,7 @@ waffle_init(const int32_t *attrib_list)
wcore_error_reset();
if (api_current_platform) {
if (api_platform) {
wcore_error(WAFFLE_ALREADY_INITIALIZED);
return false;
}
......@@ -110,22 +146,11 @@ waffle_init(const int32_t *attrib_list)
if (!ok)
return false;
api_current_platform = wcore_platform_create(platform);
if (!api_current_platform)
api_platform = waffle_init_create_platform(platform);
if (!api_platform)
return false;
return true;
}
int32_t
waffle_get_platform(void)
{
wcore_error_reset();
if (api_current_platform)
return api_current_platform->native_tag;
else
return WAFFLE_NONE;
}
/// @}
......@@ -32,9 +32,9 @@
#include <stdlib.h>
#include <waffle/native.h>
#include <waffle/core/wcore_error.h>
#include <waffle/core/wcore_config.h>
#include <waffle/core/wcore_platform.h>
#include <waffle/core/wcore_window.h>
#include "api_priv.h"
......@@ -43,78 +43,69 @@ waffle_window_create(
struct waffle_config *config,
int width, int height)
{
struct wcore_window *wc_self;
struct wcore_config *wc_config = wcore_config(config);
const struct api_object *obj_list[] = {
waffle_config_get_api_obj(config),
wc_config ? &wc_config->api : NULL,
};
struct waffle_window *self;
if (!api_check_entry(obj_list, 1))
return false;
self = malloc(sizeof(*self));
if (!self) {
wcore_error(WAFFLE_OUT_OF_MEMORY);
return NULL;
}
self->api.display_id = config->api.display_id;
self->native = api_current_platform->dispatch->
window_create(config->native, width, height);
if (!self->native) {
free(self);
wc_self = api_platform->vtbl->create_window(api_platform,
wc_config,
width,
height);
if (!wc_self)
return NULL;
}