support multi-window in nf7core_sdl2

This commit is contained in:
falsycat 2023-11-30 22:01:32 +09:00
parent 3accae56b6
commit 2e48ea3c99
7 changed files with 259 additions and 97 deletions

View File

@ -3,6 +3,9 @@ target_sources(nf7core_sdl2
PRIVATE
mod.c
poll.h
win.c
PUBLIC
win.h
)
target_link_libraries(nf7core_sdl2
PRIVATE
@ -11,3 +14,6 @@ target_link_libraries(nf7core_sdl2
OpenGL
SDL2
)
target_tests(nf7core_sdl2
win.test.c
)

View File

@ -18,10 +18,8 @@
static atomic_uint_least32_t sdl_refcnt_ = 0;
static bool new_setup_gl_(void);
static bool new_init_win_(struct nf7_core_sdl2*);
static void del_(struct nf7_mod*);
static void finalize_(struct nf7_core_sdl2*);
NF7_UTIL_REFCNT_IMPL(, nf7_core_sdl2, {finalize_(this);});
struct nf7_mod* nf7_core_sdl2_new(const struct nf7* nf7) {
@ -47,89 +45,24 @@ struct nf7_mod* nf7_core_sdl2_new(const struct nf7* nf7) {
.nf7 = nf7,
.malloc = nf7->malloc,
.uv = nf7->uv,
.poll_interval = 30,
};
nf7_util_signal_init(&this->update, this->malloc);
if (!(new_setup_gl_() && new_init_win_(this))) {
nf7_util_log_error("failed to setup OpenGL");
if (!poll_setup_(this)) {
nf7_util_log_error("failed to setup polling");
goto ABORT;
}
nf7_util_log_debug("OpenGL is ready");
if (!poll_init_(this)) {
nf7_util_log_error("failed to setup event polling");
goto ABORT;
}
nf7_util_log_debug("polling is activated");
nf7_core_sdl2_ref(this);
return (struct nf7_mod*) this;
ABORT:
nf7_util_log_warn("initialization is aborted");
del_((struct nf7_mod*) this);
finalize_(this);
return nullptr;
}
static bool new_setup_gl_(void) {
# if defined(__APPLE__)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
# else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
# endif
return true;
}
static bool new_init_win_(struct nf7_core_sdl2* this) {
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
this->win = SDL_CreateWindow(
"helloworld",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (nullptr == this->win) {
nf7_util_log_error("failed to create SDL window: %s", SDL_GetError());
return false;
}
nf7_util_log_debug("GUI window is created");
this->gl = SDL_GL_CreateContext(this->win);
if (nullptr == this->gl) {
nf7_util_log_error("failed to create GL context: %s", SDL_GetError());
return false;
}
nf7_util_log_debug("OpenGL context is created");
if (0 != SDL_GL_SetSwapInterval(0)) {
nf7_util_log_warn(
"failed to set swap interval, this will cause a performance issue: %s",
SDL_GetError());
}
return true;
}
static void del_(struct nf7_mod* this_) {
struct nf7_core_sdl2* this = (struct nf7_core_sdl2*) this_;
static void finalize_(struct nf7_core_sdl2* this) {
if (nullptr != this) {
if (nullptr != this->gl) {
SDL_GL_DeleteContext(this->gl);
nf7_util_log_debug("OpenGL context is deleted");
}
if (nullptr != this->win) {
SDL_DestroyWindow(this->win);
nf7_util_log_debug("GUI window is destroyed");
}
nf7_util_signal_deinit(&this->update);
nf7_util_malloc_del(this->malloc, this);
}
if (1 == atomic_fetch_sub(&sdl_refcnt_, 1)) {
@ -139,11 +72,15 @@ static void del_(struct nf7_mod* this_) {
}
}
static void unref_(struct nf7_mod* this_) {
struct nf7_core_sdl2* this = (struct nf7_core_sdl2*) this_;
nf7_core_sdl2_unref(this);
}
const struct nf7_mod_meta nf7_core_sdl2 = {
.name = (const uint8_t*) "nf7core_sdl2",
.desc = (const uint8_t*) "provides SDL2 features",
.ver = 0,
.delete = del_,
.delete = unref_,
};

View File

@ -9,6 +9,7 @@
#include "nf7.h"
#include "util/malloc.h"
#include "util/refcnt.h"
#include "util/signal.h"
@ -24,12 +25,6 @@ struct nf7_core_sdl2 {
SDL_Window* win;
void* gl;
// uv handles (immutable)
uv_timer_t poll_timer;
// signals
struct nf7_util_signal update;
// mutable parameters
uint64_t poll_interval;
uint32_t refcnt;
};
NF7_UTIL_REFCNT_DECL(, nf7_core_sdl2);

View File

@ -7,49 +7,88 @@
#include "core/sdl2/mod.h"
static bool poll_init_(struct nf7_core_sdl2*);
static void poll_deinit_(struct nf7_core_sdl2*);
struct nf7_core_sdl2_poll {
struct nf7_core_sdl2* mod;
const struct nf7* nf7;
struct nf7_util_malloc* malloc;
uv_loop_t* uv;
uv_timer_t timer;
uint64_t interval;
};
static bool poll_setup_(struct nf7_core_sdl2*);
static void poll_cancel_(struct nf7_core_sdl2_poll*);
static void poll_proc_(uv_timer_t*);
static void poll_finalize_(uv_handle_t*);
static bool poll_init_(struct nf7_core_sdl2* this) {
assert(nullptr != this);
static bool poll_setup_(struct nf7_core_sdl2* mod) {
assert(nullptr != mod);
if (0 != uv_timer_init(this->uv, &this->poll_timer)) {
struct nf7_core_sdl2_poll* this =
nf7_util_malloc_new(mod->malloc, sizeof(*this));
if (nullptr == this) {
nf7_util_log_error("failed to allocate poll context");
return false;
}
*this = (struct nf7_core_sdl2_poll) {
.mod = mod,
.nf7 = mod->nf7,
.malloc = mod->malloc,
.uv = mod->uv,
.interval = 30,
};
if (0 != uv_timer_init(this->uv, &this->timer)) {
nf7_util_log_error("failed to init poll timer");
return false;
}
this->poll_timer.data = this;
this->timer.data = this;
if (0 != uv_timer_start(&this->poll_timer, poll_proc_, 0, 0)) {
if (0 != uv_timer_start(&this->timer, poll_proc_, 0, 0)) {
nf7_util_log_error("failed to start poll timer");
poll_deinit_(this);
poll_cancel_(this);
return false;
}
return true;
}
static void poll_deinit_(struct nf7_core_sdl2* this) {
static void poll_cancel_(struct nf7_core_sdl2_poll* this) {
assert(nullptr != this);
uv_close((uv_handle_t*) &this->poll_timer, nullptr);
if (this == this->timer.data) {
uv_close((uv_handle_t*) &this->timer, poll_finalize_);
}
}
static void poll_proc_(uv_timer_t* timer) {
assert(nullptr != timer);
struct nf7_core_sdl2* this = timer->data;
struct nf7_core_sdl2_poll* this = timer->data;
SDL_Event e;
while (0 != SDL_PollEvent(&e)) {
if (SDL_QUIT == e.type) {
nf7_util_log_info("SDL2 event poller is uninstalled");
poll_deinit_(this);
poll_cancel_(this);
return;
}
}
if (0 != uv_timer_start(&this->poll_timer, poll_proc_, this->poll_interval, 0)) {
if (0 != uv_timer_start(&this->timer, poll_proc_, this->interval, 0)) {
nf7_util_log_error("failed to restart poll timer");
poll_deinit_(this);
poll_cancel_(this);
}
}
static void poll_finalize_(uv_handle_t* handle) {
struct nf7_core_sdl2_poll* this = handle->data;
assert(nullptr != this);
nf7_util_malloc_del(this->malloc, this);
}

79
core/sdl2/win.c Normal file
View File

@ -0,0 +1,79 @@
// No copyright
#include "core/sdl2/win.h"
#include <assert.h>
#include <SDL.h>
#include "util/log.h"
static void setup_gl_(void);
bool nf7_core_sdl2_win_init(struct nf7_core_sdl2_win* this) {
assert(nullptr != this);
assert(nullptr != this->mod);
assert(nullptr != this->malloc);
setup_gl_();
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
this->win = SDL_CreateWindow(
"Nf7",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (nullptr == this->win) {
nf7_util_log_error("failed to create SDL window: %s", SDL_GetError());
return false;
}
nf7_util_log_debug("GUI window is created");
this->gl = SDL_GL_CreateContext(this->win);
if (nullptr == this->gl) {
nf7_util_log_error("failed to create GL context: %s", SDL_GetError());
return false;
}
nf7_util_log_debug("OpenGL context is created");
if (0 != SDL_GL_SetSwapInterval(0)) {
nf7_util_log_warn(
"failed to set swap interval, this will cause a performance issue: %s",
SDL_GetError());
}
nf7_core_sdl2_ref(this->mod);
return true;
}
void nf7_core_sdl2_win_deinit(struct nf7_core_sdl2_win* this) {
if (nullptr != this) {
if (nullptr != this->gl) {
SDL_GL_DeleteContext(this->gl);
nf7_util_log_debug("OpenGL context is deleted");
}
if (nullptr != this->win) {
SDL_DestroyWindow(this->win);
nf7_util_log_debug("GUI window is destroyed");
}
nf7_core_sdl2_unref(this->mod);
}
}
static void setup_gl_(void) {
# if defined(__APPLE__)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
# else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
# endif
}

22
core/sdl2/win.h Normal file
View File

@ -0,0 +1,22 @@
// No copyright
#pragma once
#include <SDL.h>
#include "nf7.h"
#include "util/malloc.h"
#include "core/sdl2/mod.h"
struct nf7_core_sdl2_win {
struct nf7_core_sdl2* mod;
struct nf7_util_malloc* malloc;
SDL_Window* win;
void* gl;
};
bool nf7_core_sdl2_win_init(struct nf7_core_sdl2_win*);
void nf7_core_sdl2_win_deinit(struct nf7_core_sdl2_win*);

84
core/sdl2/win.test.c Normal file
View File

@ -0,0 +1,84 @@
// No copyright
#include "core/sdl2/win.h"
#include <uv.h>
#include "test/common.h"
#include "util/log.h"
#include "core/sdl2/mod.h"
struct nf7_core_sdl2_win_test {
struct nf7_test* test;
struct nf7_util_malloc* malloc;
uv_loop_t* uv;
struct nf7_core_sdl2_win win;
uv_timer_t timer;
};
static void finalize_(struct nf7_core_sdl2_win_test*);
static void on_time_(uv_timer_t*);
static void on_close_(uv_handle_t*);
NF7_TEST(nf7_core_sdl2_win_test) {
struct nf7_core_sdl2* mod = (void*) nf7_get_mod_by_meta(
test_->nf7, (struct nf7_mod_meta*) &nf7_core_sdl2);
struct nf7_core_sdl2_win_test* this =
nf7_util_malloc_new(test_->malloc, sizeof(*this));
if (!nf7_test_expect(nullptr != this)) {
goto ABORT;
}
*this = (struct nf7_core_sdl2_win_test) {
.test = test_,
.malloc = test_->malloc,
.uv = test_->nf7->uv,
.win = {
.mod = mod,
.malloc = test_->malloc,
},
};
nf7_test_ref(this->test);
if (!nf7_test_expect(nf7_core_sdl2_win_init(&this->win))) {
goto ABORT;
}
if (0 != nf7_util_log_uv(uv_timer_init(this->uv, &this->timer))) {
goto ABORT;
}
this->timer.data = this;
if (0 != nf7_util_log_uv(uv_timer_start(&this->timer, on_time_, 3000, 0))) {
goto ABORT;
}
return true;
ABORT:
finalize_(this);
return false;
}
static void finalize_(struct nf7_core_sdl2_win_test* this) {
if (nullptr != this->timer.data) {
uv_close((uv_handle_t*) &this->timer, on_close_);
return;
}
nf7_core_sdl2_win_deinit(&this->win);
nf7_test_unref(this->test);
nf7_util_malloc_del(this->malloc, this);
}
static void on_time_(uv_timer_t* timer) {
struct nf7_core_sdl2_win_test* this = timer->data;
finalize_(this);
}
static void on_close_(uv_handle_t* handle) {
struct nf7_core_sdl2_win_test* this = handle->data;
handle->data = nullptr;
finalize_(this);
}