Une erreur est survenue de notre côté
Sélectionner une révision Git
-
Jonathan CROUZET a rédigéJonathan CROUZET a rédigé
module_sdl2.c 14,38 Kio
#define _POSIX_C_SOURCE 200809L
#define __LKT_MODULE_MAIN_SOURCE__
#include <lektor/lktmodule.h>
#include "mpv.h"
#include "thread.h"
#include <sqlite3.h>
#include <sched.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <SDL.h>
#include <mpv/render_gl.h>
#include <mpv/client.h>
#define WIDTH 400
#define HEIGHT 200
static volatile Uint32 wakeup_on_mpv_render_update, wakeup_on_mpv_events;
struct module_sdl2_window {
/* Related to SDL2 */
volatile SDL_Window *window;
volatile SDL_GLContext glcontext;
int is_fullscreen;
/* Mpv related */
volatile mpv_handle *mpv;
volatile mpv_render_context *mpv_gl;
volatile int mpv_time_pos; /* Don't write it in the database */
volatile int mpv_duration; /* Because don't need to be persistent */
volatile int state;
/* Thread related */
struct poller_thread self;
volatile int launched; /* SDL you sucks */
/* Things from the server */
struct queue *queue;
volatile sqlite3 *db;
};
/************************
* Function definitions *
************************/
static bool module_sdl2_get_elapsed(struct module_sdl2_window *, int *);
static bool module_sdl2_get_duration(struct module_sdl2_window *, int *);
static bool module_sdl2_set_volume(struct module_sdl2_window *, int);
static bool module_sdl2_load_file(struct module_sdl2_window *, const char *);
static bool module_sdl2_toggle_pause(struct module_sdl2_window *);
static void module_sdl2_free(struct module_sdl2_window *);
static void module_sdl2_close(struct module_sdl2_window *);
static bool module_sdl2_new(struct module_sdl2_window **, struct queue *, volatile sqlite3 *);
/*********************
* Private functions *
*********************/
static inline void *
get_proc_address_mpv(void UNUSED *fn_ctx, const char *name)
{
return SDL_GL_GetProcAddress(name);
}
static inline void
on_mpv_events(void UNUSED *ctx)
{
SDL_Event event = { .type = wakeup_on_mpv_events };
SDL_PushEvent(&event);
}
static inline void
on_mpv_render_update(void UNUSED *ctx)
{
SDL_Event event = { .type = wakeup_on_mpv_render_update };
SDL_PushEvent(&event);
}
static inline bool
init_mpv__(mpv_handle **ctx, volatile sqlite3 *db)
{
*ctx = lmpv_prepare(db);
int status;
RETURN_IF((status = mpv_initialize(*ctx)) < 0, mpv_error_string(status), 1);
RETURN_UNLESS(lmpv_observe_properties(*ctx), "Observe prop failed", 1);
return 0;
}
static inline bool
init_mpv_gl__(mpv_handle *mpv, mpv_render_context **mpv_gl,
mpv_render_param *params)
{
int status;
RETURN_IF((status = mpv_render_context_create(mpv_gl, mpv, params)) < 0,
mpv_error_string(status), 1);
wakeup_on_mpv_render_update = SDL_RegisterEvents(1);
wakeup_on_mpv_events = SDL_RegisterEvents(1);
if (wakeup_on_mpv_render_update == (Uint32) - 1 ||
wakeup_on_mpv_events == (Uint32) - 1) {
LOG_ERROR("WINDOW", "Failed to register event");
return 1;
}
mpv_set_wakeup_callback(mpv, on_mpv_events, NULL);
mpv_render_context_set_update_callback(*mpv_gl, on_mpv_render_update, NULL);
return 0;
}
/********************************
* va_list version of functions *
********************************/
static int
mod_new(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
struct queue *queue = va_arg(copy, struct queue *);
volatile sqlite3 *db = va_arg(copy, volatile sqlite3 *);
bool ret = module_sdl2_new(win, queue, db);
va_end(copy);
return ! ret;
}
static int
mod_close(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
module_sdl2_close(*win);
va_end(copy);
return 0;
}
static int
mod_free(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
module_sdl2_free(*win);
va_end(copy);
return 0;
}
static int
mod_toggle_pause(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
bool ret = module_sdl2_toggle_pause(*win);
va_end(copy);
return ! ret;
}
static int
mod_load_file(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
const char *file = va_arg(copy, const char *);
bool ret = module_sdl2_load_file(*win, file);
va_end(copy);
return ! ret;
}
static int
mod_set_volume(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
int volume = va_arg(copy, int);
bool ret = module_sdl2_set_volume(*win, volume);
va_end(copy);
return ! ret;
}
static int
mod_get_duration(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
int *duration = va_arg(copy, int *);
bool ret = module_sdl2_get_duration(*win, duration);
va_end(copy);
return ! ret;
}
static int
mod_get_elapsed(va_list *va)
{
va_list copy;
struct module_sdl2_window **win;
va_copy(copy, *va);
win = (struct module_sdl2_window **) va_arg(copy, void **);
int *elapsed = va_arg(copy, int *);
bool ret = module_sdl2_get_elapsed(*win, elapsed);
va_end(copy);
return ! ret;
}
/********************
* The module stuff *
********************/
REG_BEGIN(sdl2_reg)
REG_ADD_NAMED("new", mod_new)
REG_ADD_NAMED("free", mod_free)
REG_ADD_NAMED("close", mod_close)
REG_ADD_NAMED("toggle", mod_toggle_pause)
REG_ADD_NAMED("load", mod_load_file)
REG_ADD_NAMED("set_volume", mod_set_volume)
REG_ADD_NAMED("get_duration", mod_get_duration)
REG_ADD_NAMED("get_elapsed", mod_get_elapsed)
REG_END()
#if ! defined (LKT_STATIC_MODULE)
REG_EXPORT(sdl2_reg)
#endif
/***************************
* Function implementation *
***************************/
static void *
sdl_thread__(struct poller_thread_arg *arg)
{
struct module_sdl2_window *sdl2 = arg->args;
SDL_Event event;
uint64_t flags;
int w, h, redraw = 0;
bool ctrl = false;
const Uint8 *state;
free(arg);
struct lkt_module module = {
.handle = NULL,
.reg = sdl2_reg,
.data = sdl2,
};
/* Init the SDL window
Yeah, SDL you sucks */
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no"); /* It seems that sdl sucks. */
RETURN_IF(SDL_Init(SDL_INIT_VIDEO) < 0, "Failed to init SDL", NULL);
sdl2->window = SDL_CreateWindow("lektord", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
SDL_WINDOW_RESIZABLE);
RETURN_UNLESS(sdl2->window, "Failed to create the window", NULL);
sdl2->glcontext = SDL_GL_CreateContext((SDL_Window *) sdl2->window);
RETURN_UNLESS(sdl2->glcontext, "Failed to create the SDL context", NULL);
/* Init mpv here */
RETURN_IF(init_mpv__((mpv_handle **) & sdl2->mpv, sdl2->db),
"Failed to init mpv", false);
mpv_render_param params[] = {
{ MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL },
{
MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params)
{
.get_proc_address = get_proc_address_mpv,
}
},
{
MPV_RENDER_PARAM_ADVANCED_CONTROL, &(int)
{
1
}
},
{0}
};
RETURN_IF(init_mpv_gl__((mpv_handle *) sdl2->mpv, (mpv_render_context **)
&sdl2->mpv_gl, params),
"Failed to init mpv_gl", false);
while (!sdl2->launched)
sched_yield();
LOG_INFO("WINDOW", "Started SDL thread");
loop:
SDL_WaitEvent(&event);
if (sdl2->launched == 2)
goto end;
switch (event.type) {
case SDL_QUIT:
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_EXPOSED) {
lkt_queue_make_available(sdl2->queue, lkt_event_play);
lkt_queue_make_available(sdl2->queue, lkt_event_prop);
redraw = 1;
}
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_LEFT) {
const char *cmd_seek[] = { "seek", "-5", "relative", NULL };
mpv_command_async((mpv_handle *) sdl2->mpv, 0, cmd_seek);
} else if (event.key.keysym.sym == SDLK_RIGHT) {
const char *cmd_seek[] = { "seek", "+5", "relative", NULL };
mpv_command_async((mpv_handle *) sdl2->mpv, 0, cmd_seek);
}
break;
case SDL_KEYUP:
state = SDL_GetKeyboardState(NULL);
ctrl = state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL];
if (ctrl && event.key.keysym.sym == SDLK_SPACE)
lkt_queue_send(sdl2->queue, lkt_event_play_toggle, LKT_PLAY_TOGGLE);
else if (event.key.keysym.sym == SDLK_n && ctrl)
lkt_queue_send(sdl2->queue, lkt_event_play_next, NULL);
else if (event.key.keysym.sym == SDLK_p && ctrl)
lkt_queue_send(sdl2->queue, lkt_event_play_prev, NULL);
else if ((event.key.keysym.sym == SDLK_RETURN ||
event.key.keysym.sym == SDLK_RETURN2 ||
event.key.keysym.sym == SDLK_KP_ENTER) && ctrl)
lkt_queue_send(sdl2->queue, lkt_event_play_next, NULL);
else if (event.key.keysym.sym == SDLK_F11) {
if (sdl2->is_fullscreen)
SDL_SetWindowFullscreen((SDL_Window *) sdl2->window, 0);
else
SDL_SetWindowFullscreen((SDL_Window *) sdl2->window,
SDL_WINDOW_FULLSCREEN);
/* May use SDL_WINDOW_FULLSCREEN_DESKTOP, need to check. */
sdl2->is_fullscreen = 1 - sdl2->is_fullscreen;
}
break;
case SDL_TEXTINPUT:
break;
default:
if (event.type == wakeup_on_mpv_render_update) {
flags = mpv_render_context_update((mpv_render_context *)
sdl2->mpv_gl);
if (flags & MPV_RENDER_UPDATE_FRAME)
redraw = 1;
}
if (event.type == wakeup_on_mpv_events) {
lmpv_handle(&module, (mpv_handle *) sdl2->mpv, sdl2->queue,
&sdl2->mpv_time_pos, &sdl2->mpv_duration, &sdl2->state);
}
}
if (redraw) {
SDL_GetWindowSize((SDL_Window *) sdl2->window, &w, &h);
/* Specify the default framebuffer (0) as target. This will
render onto the entire screen. If you want to show the video
in a smaller rectangle or apply fancy transformations, you'll
need to render into a separate FBO and draw it manually. */
mpv_render_param params[] = { {
MPV_RENDER_PARAM_OPENGL_FBO, &(mpv_opengl_fbo)
{
.fbo = 0, .w = w, .h = h,
}
}, {
MPV_RENDER_PARAM_FLIP_Y, &(int)
{
1
}
}, {0}
};
mpv_render_context_render((mpv_render_context *) sdl2->mpv_gl, params);
SDL_GL_SwapWindow((SDL_Window *) sdl2->window);
redraw = 0;
}
sched_yield();
goto loop; /* A loop without indentation. */
end:
sdl2->launched = 0;
return NULL;
}
static bool
module_sdl2_new(struct module_sdl2_window **win, struct queue *queue,
volatile sqlite3 *db)
{
RETURN_UNLESS(win, "Invalid arguments", false);
struct poller_thread_arg *arg;
if (*win == NULL) {
*win = calloc(1, sizeof(struct module_sdl2_window));
RETURN_UNLESS(*win, "Out of memory", false);
memset(*win, 0, sizeof(struct module_sdl2_window));
(*win)->queue = queue;
(*win)->db = db;
/* Start the SDL thread */
arg = malloc(sizeof(struct poller_thread_arg));
RETURN_UNLESS(arg, "Out of memory", false);
arg->args = *win;
RETURN_IF(poller_new(&(*win)->self, sdl_thread__, arg),
"Failed to launch the SDL thread", false);
} else
LOG_DEBUG("WINDOW", "SDL window already created");
/* Finish */
SDL_SetWindowTitle((SDL_Window *) (*win)->window, "Lektord");
SDL_DisableScreenSaver();
(*win)->launched = 1;
return true;
}
static void
module_sdl2_close(struct module_sdl2_window *win)
{
RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING);
win->mpv_time_pos = win->mpv_duration = 0;
RETURN_UNLESS(win->mpv, "Missing mpv ctx", NOTHING);
static const char *cmd[] = { "stop", NULL };
mpv_command_async((mpv_handle *) win->mpv, 0, cmd);
LOG_INFO("SDL2", "Module closed");
/* TODO: make the SDL window background be black. */
}
static void
module_sdl2_free(struct module_sdl2_window *win)
{
RETURN_UNLESS(win, "Invalid arguments", NOTHING);
module_sdl2_close(win);
LOG_INFO("SDL2", "Waiting for window thread");
win->launched = 2;
while (win->launched)
sched_yield();
poller_join(&win->self, NULL);
LOG_INFO("SDL2", "Module terminated");
}
static bool
module_sdl2_toggle_pause(struct module_sdl2_window *win)
{
RETURN_UNLESS(win, "Invalid arguments", false);
return ! lmpv_toggle_pause((mpv_handle *) win->mpv);
}
static bool
module_sdl2_load_file(struct module_sdl2_window *win, const char *filepath)
{
RETURN_UNLESS(win, "Invalid arguments", false);
bool ret = ! lmpv_load_file((mpv_handle *) win->mpv, filepath);
win->mpv_duration = 0;
win->mpv_time_pos = 0;
return ret;
}
static bool
module_sdl2_set_volume(struct module_sdl2_window *win, int vol)
{
RETURN_UNLESS(win && win->window, "Invalid arguments", false);
return ! lmpv_set_volume((mpv_handle *) win->mpv, vol);
}
static bool
module_sdl2_get_duration(struct module_sdl2_window *win, int *dur_sec)
{
RETURN_UNLESS(win, "Invalid arguments", false);
*dur_sec = win->mpv_duration;
return true;
}
static bool
module_sdl2_get_elapsed(struct module_sdl2_window *win, int *elapsed_sec)
{
RETURN_UNLESS(win, "Invalid arguments", false);
*elapsed_sec = win->mpv_time_pos;
return true;
}