summaryrefslogtreecommitdiffstats
path: root/demos/window_sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'demos/window_sdl.c')
-rw-r--r--demos/window_sdl.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/demos/window_sdl.c b/demos/window_sdl.c
new file mode 100644
index 0000000..1fd22ce
--- /dev/null
+++ b/demos/window_sdl.c
@@ -0,0 +1,404 @@
+// License: CC0 / Public Domain
+
+#if !defined(USE_GL) && !defined(USE_VK) || defined(USE_GL) && defined(USE_VK)
+#error Specify exactly one of -DUSE_GL or -DUSE_VK when compiling!
+#endif
+
+#include <SDL.h>
+
+#include "common.h"
+#include "window.h"
+
+#ifdef USE_VK
+#define VK_NO_PROTOTYPES
+#include <libplacebo/vulkan.h>
+#include <SDL_vulkan.h>
+#define WINFLAG_API SDL_WINDOW_VULKAN
+#define IMPL win_impl_sdl_vk
+#define IMPL_NAME "SDL2 (vulkan)"
+#define IMPL_TAG "sdl2-vk"
+#endif
+
+#ifdef USE_GL
+#include <libplacebo/opengl.h>
+#define WINFLAG_API SDL_WINDOW_OPENGL
+#define IMPL win_impl_sdl_gl
+#define IMPL_NAME "SDL2 (opengl)"
+#define IMPL_TAG "sdl2-gl"
+#endif
+
+#ifdef NDEBUG
+#define DEBUG false
+#else
+#define DEBUG true
+#endif
+
+const struct window_impl IMPL;
+
+struct priv {
+ struct window w;
+ SDL_Window *win;
+
+#ifdef USE_VK
+ VkSurfaceKHR surf;
+ pl_vulkan vk;
+ pl_vk_inst vk_inst;
+#endif
+
+#ifdef USE_GL
+ SDL_GLContext gl_ctx;
+ pl_opengl gl;
+#endif
+
+ int scroll_dx, scroll_dy;
+ char **files;
+ size_t files_num;
+ size_t files_size;
+ bool file_seen;
+ char *clip_text;
+};
+
+#ifdef USE_GL
+static bool make_current(void *priv)
+{
+ struct priv *p = priv;
+ return SDL_GL_MakeCurrent(p->win, p->gl_ctx) == 0;
+}
+
+static void release_current(void *priv)
+{
+ struct priv *p = priv;
+ SDL_GL_MakeCurrent(p->win, NULL);
+}
+#endif
+
+static struct window *sdl_create(pl_log log, const struct window_params *params)
+{
+ struct priv *p = calloc(1, sizeof(struct priv));
+ if (!p)
+ return NULL;
+
+ p->w.impl = &IMPL;
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "SDL2: Failed initializing: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ uint32_t sdl_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | WINFLAG_API;
+ p->win = SDL_CreateWindow(params->title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ params->width, params->height, sdl_flags);
+ if (!p->win) {
+ fprintf(stderr, "SDL2: Failed creating window: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ int w, h;
+
+#ifdef USE_VK
+
+ unsigned int num = 0;
+ if (!SDL_Vulkan_GetInstanceExtensions(p->win, &num, NULL)) {
+ fprintf(stderr, "SDL2: Failed enumerating vulkan extensions: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ const char **exts = malloc(num * sizeof(const char *));
+ SDL_Vulkan_GetInstanceExtensions(p->win, &num, exts);
+
+ p->vk_inst = pl_vk_inst_create(log, pl_vk_inst_params(
+ .get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr(),
+ .debug = DEBUG,
+ .extensions = exts,
+ .num_extensions = num,
+ ));
+ free(exts);
+ if (!p->vk_inst) {
+ fprintf(stderr, "libplacebo: Failed creating vulkan instance!\n");
+ goto error;
+ }
+
+ if (!SDL_Vulkan_CreateSurface(p->win, p->vk_inst->instance, &p->surf)) {
+ fprintf(stderr, "SDL2: Failed creating surface: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ p->vk = pl_vulkan_create(log, pl_vulkan_params(
+ .instance = p->vk_inst->instance,
+ .get_proc_addr = p->vk_inst->get_proc_addr,
+ .surface = p->surf,
+ .allow_software = true,
+ ));
+ if (!p->vk) {
+ fprintf(stderr, "libplacebo: Failed creating vulkan device\n");
+ goto error;
+ }
+
+ p->w.swapchain = pl_vulkan_create_swapchain(p->vk, pl_vulkan_swapchain_params(
+ .surface = p->surf,
+ .present_mode = VK_PRESENT_MODE_FIFO_KHR,
+ ));
+
+ if (!p->w.swapchain) {
+ fprintf(stderr, "libplacebo: Failed creating vulkan swapchain\n");
+ goto error;
+ }
+
+ p->w.gpu = p->vk->gpu;
+
+ SDL_Vulkan_GetDrawableSize(p->win, &w, &h);
+#endif // USE_VK
+
+#ifdef USE_GL
+ p->gl_ctx = SDL_GL_CreateContext(p->win);
+ if (!p->gl_ctx) {
+ fprintf(stderr, "SDL2: Failed creating GL context: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ p->gl = pl_opengl_create(log, pl_opengl_params(
+ .allow_software = true,
+ .debug = DEBUG,
+ .make_current = make_current,
+ .release_current = release_current,
+ .get_proc_addr = (void *) SDL_GL_GetProcAddress,
+ .priv = p,
+ ));
+ if (!p->gl) {
+ fprintf(stderr, "libplacebo: Failed creating opengl device\n");
+ goto error;
+ }
+
+ p->w.swapchain = pl_opengl_create_swapchain(p->gl, pl_opengl_swapchain_params(
+ .swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow,
+ .priv = p->win,
+ ));
+
+ if (!p->w.swapchain) {
+ fprintf(stderr, "libplacebo: Failed creating opengl swapchain\n");
+ goto error;
+ }
+
+ p->w.gpu = p->gl->gpu;
+
+ SDL_GL_GetDrawableSize(p->win, &w, &h);
+#endif // USE_GL
+
+ pl_swapchain_colorspace_hint(p->w.swapchain, &params->colors);
+ if (!pl_swapchain_resize(p->w.swapchain, &w, &h)) {
+ fprintf(stderr, "libplacebo: Failed initializing swapchain\n");
+ goto error;
+ }
+
+ return &p->w;
+
+error:
+ window_destroy((struct window **) &p);
+ return NULL;
+}
+
+static void sdl_destroy(struct window **window)
+{
+ struct priv *p = (struct priv *) *window;
+ if (!p)
+ return;
+
+ pl_swapchain_destroy(&p->w.swapchain);
+
+#ifdef USE_VK
+ pl_vulkan_destroy(&p->vk);
+ if (p->surf) {
+ PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
+ p->vk_inst->get_proc_addr(p->vk_inst->instance, "vkDestroySurfaceKHR");
+ vkDestroySurfaceKHR(p->vk_inst->instance, p->surf, NULL);
+ }
+ pl_vk_inst_destroy(&p->vk_inst);
+#endif
+
+#ifdef USE_GL
+ pl_opengl_destroy(&p->gl);
+ SDL_GL_DeleteContext(p->gl_ctx);
+#endif
+
+ for (int i = 0; i < p->files_num; i++)
+ SDL_free(p->files[i]);
+ free(p->files);
+
+ SDL_free(p->clip_text);
+ SDL_DestroyWindow(p->win);
+ SDL_Quit();
+ free(p);
+ *window = NULL;
+}
+
+static inline void handle_event(struct priv *p, SDL_Event *event)
+{
+ switch (event->type) {
+ case SDL_QUIT:
+ p->w.window_lost = true;
+ return;
+
+ case SDL_WINDOWEVENT:
+ if (event->window.windowID != SDL_GetWindowID(p->win))
+ return;
+
+ if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
+ int width = event->window.data1, height = event->window.data2;
+ if (!pl_swapchain_resize(p->w.swapchain, &width, &height)) {
+ fprintf(stderr, "libplacebo: Failed resizing swapchain? Exiting...\n");
+ p->w.window_lost = true;
+ }
+ }
+ return;
+
+ case SDL_MOUSEWHEEL:
+ p->scroll_dx += event->wheel.x;
+ p->scroll_dy += event->wheel.y;
+ return;
+
+ case SDL_DROPFILE:
+ if (p->files_num == p->files_size) {
+ size_t new_size = p->files_size ? p->files_size * 2 : 16;
+ char **new_files = realloc(p->files, new_size * sizeof(char *));
+ if (!new_files)
+ return;
+ p->files = new_files;
+ p->files_size = new_size;
+ }
+
+ p->files[p->files_num++] = event->drop.file;
+ return;
+ }
+}
+
+static void sdl_poll(struct window *window, bool block)
+{
+ struct priv *p = (struct priv *) window;
+ SDL_Event event;
+ int ret;
+
+ do {
+ ret = block ? SDL_WaitEvent(&event) : SDL_PollEvent(&event);
+ if (ret)
+ handle_event(p, &event);
+
+ // Only block on the first iteration
+ block = false;
+ } while (ret);
+}
+
+static void sdl_get_cursor(const struct window *window, int *x, int *y)
+{
+ SDL_GetMouseState(x, y);
+}
+
+static bool sdl_get_button(const struct window *window, enum button btn)
+{
+ static const uint32_t button_mask[] = {
+ [BTN_LEFT] = SDL_BUTTON_LMASK,
+ [BTN_RIGHT] = SDL_BUTTON_RMASK,
+ [BTN_MIDDLE] = SDL_BUTTON_MMASK,
+ };
+
+ return SDL_GetMouseState(NULL, NULL) & button_mask[btn];
+}
+
+static bool sdl_get_key(const struct window *window, enum key key)
+{
+ static const size_t key_map[] = {
+ [KEY_ESC] = SDL_SCANCODE_ESCAPE,
+ };
+
+ return SDL_GetKeyboardState(NULL)[key_map[key]];
+}
+
+static void sdl_get_scroll(const struct window *window, float *dx, float *dy)
+{
+ struct priv *p = (struct priv *) window;
+ *dx = p->scroll_dx;
+ *dy = p->scroll_dy;
+ p->scroll_dx = p->scroll_dy = 0;
+}
+
+static char *sdl_get_file(const struct window *window)
+{
+ struct priv *p = (struct priv *) window;
+ if (p->file_seen) {
+ assert(p->files_num);
+ SDL_free(p->files[0]);
+ memmove(&p->files[0], &p->files[1], --p->files_num * sizeof(char *));
+ p->file_seen = false;
+ }
+
+ if (!p->files_num)
+ return NULL;
+
+ p->file_seen = true;
+ return p->files[0];
+}
+
+static bool sdl_is_fullscreen(const struct window *window)
+{
+ const struct priv *p = (const struct priv *) window;
+ return SDL_GetWindowFlags(p->win) & SDL_WINDOW_FULLSCREEN;
+}
+
+static bool sdl_toggle_fullscreen(const struct window *window, bool fullscreen)
+{
+ struct priv *p = (struct priv *) window;
+ bool window_fullscreen = sdl_is_fullscreen(window);
+
+ if (window_fullscreen == fullscreen)
+ return true;
+
+ SDL_DisplayMode mode;
+ if (SDL_GetDesktopDisplayMode(0, &mode))
+ {
+ fprintf(stderr, "SDL2: Failed to get display mode: %s\n", SDL_GetError());
+ SDL_ClearError();
+ return false;
+ }
+
+ if (SDL_SetWindowDisplayMode(p->win, &mode))
+ {
+ fprintf(stderr, "SDL2: Failed to set window display mode: %s\n", SDL_GetError());
+ SDL_ClearError();
+ return false;
+ }
+
+ if (SDL_SetWindowFullscreen(p->win, fullscreen ? SDL_WINDOW_FULLSCREEN : 0)) {
+ fprintf(stderr, "SDL2: SetWindowFullscreen failed: %s\n", SDL_GetError());
+ SDL_ClearError();
+ return false;
+ }
+
+ return true;
+}
+
+static const char *sdl_get_clipboard(const struct window *window)
+{
+ struct priv *p = (struct priv *) window;
+ SDL_free(p->clip_text);
+ return p->clip_text = SDL_GetClipboardText();
+}
+
+static void sdl_set_clipboard(const struct window *window, const char *text)
+{
+ SDL_SetClipboardText(text);
+}
+
+const struct window_impl IMPL = {
+ .name = IMPL_NAME,
+ .tag = IMPL_TAG,
+ .create = sdl_create,
+ .destroy = sdl_destroy,
+ .poll = sdl_poll,
+ .get_cursor = sdl_get_cursor,
+ .get_button = sdl_get_button,
+ .get_key = sdl_get_key,
+ .get_scroll = sdl_get_scroll,
+ .get_file = sdl_get_file,
+ .toggle_fullscreen = sdl_toggle_fullscreen,
+ .is_fullscreen = sdl_is_fullscreen,
+ .get_clipboard = sdl_get_clipboard,
+ .set_clipboard = sdl_set_clipboard,
+};