diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c index 7fa151f13d53d8d9cc581b0fd71679a21060b302..2c12527420e27992f413c99a0077282a3041d47d 100644 --- a/src/module/module_sdl2.c +++ b/src/module/module_sdl2.c @@ -29,13 +29,6 @@ struct module_sdl2_window { static Uint32 wakeup_on_mpv_render_update, wakeup_on_mpv_events; -static void -die(const char *msg) -{ - fprintf(stderr, "%s\n", msg); - exit(1); -} - static inline void * get_proc_address_mpv(void *fn_ctx, const char *name) { @@ -59,181 +52,6 @@ on_mpv_render_update(void *ctx) SDL_PushEvent(&event); } -int -main(int argc, char *argv[]) -{ - if (argc != 2) - die("pass a single media file as argument"); - - mpv_handle *mpv = mpv_create(); - if (!mpv) - die("context init failed"); - - // Some minor options can only be set before mpv_initialize(). - if (mpv_initialize(mpv) < 0) - die("mpv init failed"); - - // Jesus Christ SDL, you suck! - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no"); - - if (SDL_Init(SDL_INIT_VIDEO) < 0) - die("SDL init failed"); - - SDL_Window *window = - SDL_CreateWindow("hi", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - 1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | - SDL_WINDOW_RESIZABLE); - if (!window) - die("failed to create SDL window"); - - SDL_GLContext glcontext = SDL_GL_CreateContext(window); - if (!glcontext) - die("failed to create SDL GL context"); - - 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, - } - }, - // Tell libmpv that you will call mpv_render_context_update() on render - // context update callbacks, and that you will _not_ block on the core - // ever (see <libmpv/render.h> "Threading" section for what libmpv - // functions you can call at all when this is active). - // In particular, this means you must call e.g. mpv_command_async() - // instead of mpv_command(). - // If you want to use synchronous calls, either make them on a separate - // thread, or remove the option below (this will disable features like - // DR and is not recommended anyway). - { - MPV_RENDER_PARAM_ADVANCED_CONTROL, &(int) - { - 1 - } - }, - {0} - }; - - // This makes mpv use the currently set GL context. It will use the callback - // (passed via params) to resolve GL builtin functions, as well as extensions. - mpv_render_context *mpv_gl; - if (mpv_render_context_create(&mpv_gl, mpv, params) < 0) - die("failed to initialize mpv GL context"); - - // We use events for thread-safe notification of the SDL main loop. - // Generally, the wakeup callbacks (set further below) should do as least - // work as possible, and merely wake up another thread to do actual work. - // On SDL, waking up the mainloop is the ideal course of action. SDL's - // SDL_PushEvent() is thread-safe, so we use that. - 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) - die("could not register events"); - - // When normal mpv events are available. - mpv_set_wakeup_callback(mpv, on_mpv_events, NULL); - - // When there is a need to call mpv_render_context_update(), which can - // request a new frame to be rendered. - // (Separate from the normal event handling mechanism for the sake of - // users which run OpenGL on a different thread.) - mpv_render_context_set_update_callback(mpv_gl, on_mpv_render_update, NULL); - - // Play this file. - const char *cmd[] = {"loadfile", argv[1], NULL}; - mpv_command_async(mpv, 0, cmd); - - while (1) { - SDL_Event event; - if (SDL_WaitEvent(&event) != 1) - die("event loop error"); - int redraw = 0; - switch (event.type) { - case SDL_QUIT: - goto done; - case SDL_WINDOWEVENT: - if (event.window.event == SDL_WINDOWEVENT_EXPOSED) - redraw = 1; - break; - case SDL_KEYDOWN: - if (event.key.keysym.sym == SDLK_SPACE) { - const char *cmd_pause[] = {"cycle", "pause", NULL}; - mpv_command_async(mpv, 0, cmd_pause); - } - if (event.key.keysym.sym == SDLK_s) { - // Also requires MPV_RENDER_PARAM_ADVANCED_CONTROL if you want - // screenshots to be rendered on GPU (like --vo=gpu would do). - const char *cmd_scr[] = {"screenshot-to-file", - "screenshot.png", - "window", - NULL - }; - printf("attempting to save screenshot to %s\n", cmd_scr[1]); - mpv_command_async(mpv, 0, cmd_scr); - } - break; - default: - // Happens when there is new work for the render thread (such as - // rendering a new video frame or redrawing it). - if (event.type == wakeup_on_mpv_render_update) { - uint64_t flags = mpv_render_context_update(mpv_gl); - if (flags & MPV_RENDER_UPDATE_FRAME) - redraw = 1; - } - // Happens when at least 1 new event is in the mpv event queue. - if (event.type == wakeup_on_mpv_events) { - // Handle all remaining mpv events. - while (1) { - mpv_event *mp_event = mpv_wait_event(mpv, 0); - if (mp_event->event_id == MPV_EVENT_NONE) - break; - printf("event: %s\n", mpv_event_name(mp_event->event_id)); - } - } - } - if (redraw) { - int w, h; - SDL_GetWindowSize(window, &w, &h); - mpv_render_param params[] = { - // 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_OPENGL_FBO, &(mpv_opengl_fbo) - { - .fbo = 0, - .w = w, - .h = h, - } - }, - { - MPV_RENDER_PARAM_FLIP_Y, &(int) - { - 1 - } - }, - {0} - }; - mpv_render_context_render(mpv_gl, params); - SDL_GL_SwapWindow(window); - } - } -done: - - // Destroy the GL renderer and all of the GL objects it allocated. If video - // is still running, the video track will be deselected. - mpv_render_context_free(mpv_gl); - - mpv_detach_destroy(mpv); - - printf("properly terminated\n"); - return 0; -} - /* Exported functions */ extern int @@ -259,6 +77,50 @@ module_set_function(void *arg__, void *handle__) return 0; } +/* Private init functions */ + +static inline bool +init_mpv__(mpv_handle **ctx) +{ + *ctx = lmpv_prepare(); + int status; + if ((status = mpv_initialize(*ctx)) < 0) { + fprintf(stderr, " ! init_mpv__: failed to initialize mpv: %s\n", + mpv_error_string(status)); + return 1; + } + if (!lmpv_observe_properties(*ctx)) { + fprintf(stderr, " ! init_mpv__: failed to observe properties\n"); + return 1; + } + return 0; +} + +static inline bool +init_mpv_gl__(mpv_handle *mpv, mpv_render_context **mpv_gl, mpv_render_param *params) +{ + int status; + if ((status = mpv_render_context_create(mpv_gl, mpv, params)) < 0) { + fprintf(stderr, " ! init_mpv_gl__: Failed to create render context: %s\n", + mpv_error_string(status)); + return 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) { + fprintf(stderr, " . init_mpv_gl__: Failed to register events\n"); + 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; +} + +/* Exported functions */ + bool module_sdl2_new(struct lkt_win *const win) { @@ -266,7 +128,22 @@ module_sdl2_new(struct lkt_win *const win) return false; struct module_sdl2_window *sdl2 = win->window; - int status; + 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} + }; if (sdl2 == NULL) { sdl2 = calloc(1, sizeof(struct module_sdl2_window)); @@ -292,43 +169,14 @@ module_sdl2_new(struct lkt_win *const win) goto error; /* Init mpv here */ - sdl2->mpv = lmpv_prepare(); - - if ((status = mpv_initialize(sdl2->mpv)) < 0) { - fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n", - mpv_error_string(status)); + if (init_mpv__(&sdl2->mpv)) goto error; - } - - if (!lmpv_observe_properties(sdl2->mpv)) { - fprintf(stderr, " * lmpv_new: failed to observe properties\n"); - goto error; - } /* Init opengl with mpv and sdl2 */ - - 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} - }; - if (mpv_render_context_create(&sdl2->mpv_gl, sdl2->mpv, params) < 0) goto error; - fprintf(stderr, " . module_sdl2_new: successfully created the X11 window\n"); - + fprintf(stderr, " * Successfully created the SDL2 window\n"); 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) @@ -336,28 +184,17 @@ module_sdl2_new(struct lkt_win *const win) mpv_set_wakeup_callback(sdl2->mpv, on_mpv_events, NULL); mpv_render_context_set_update_callback(sdl2->mpv_gl, on_mpv_render_update, NULL); - } - - if (sdl2->mpv == NULL) { - sdl2->mpv = lmpv_prepare(); - if ((status = mpv_initialize(sdl2->mpv)) < 0) { - fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n", - mpv_error_string(status)); + /* Init mpv_gl here */ + if (init_mpv_gl__(sdl2->mpv, &sdl2->mpv_gl, params)) goto error; - } - - if (!lmpv_observe_properties(sdl2->mpv)) { - fprintf(stderr, " * lmpv_new: failed to observe properties\n"); - goto error; - } - - if (sdl2->mpv == NULL) - return false; } - if (sdl2->mpv_gl == NULL) { - } + if (sdl2->mpv == NULL && init_mpv__(&sdl2->mpv)) + goto error; + + if (sdl2->mpv_gl == NULL && init_mpv_gl__(sdl2->mpv, &sdl2->mpv_gl, params)) + goto error; win->window = sdl2; return true; @@ -370,7 +207,10 @@ module_sdl2_close(struct lkt_win *const win) { if (win == NULL || win->window == NULL) return; - lmpv_free(&((struct module_sdl2_window *)win->window)->mpv); + struct module_sdl2_window *sdl2 = win->window; + mpv_render_context_free(sdl2->mpv_gl); + sdl2->mpv_gl = NULL; + lmpv_free(&sdl2->mpv); } void @@ -440,4 +280,71 @@ module_sdl2_get_elapsed(struct lkt_win *const win, int *elapsed_sec) bool module_sdl2_handle_events(struct lkt_win *const win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events) { + struct module_sdl2_window *sdl2 = win->window; + if (sdl2 == NULL) + return false; + + for (;;) { + SDL_Event event; + int redraw = 0; + if (SDL_WaitEvent(&event) != 1) + return false; + + switch (event.type) { + case SDL_QUIT: + return true; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_EXPOSED) + redraw = 1; + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_SPACE) { + const char *cmd_pause[] = { "cycle", "pause", NULL }; + mpv_command_async(sdl2->mpv, 0, cmd_pause); + } + break; + default: + if (event.type == wakeup_on_mpv_render_update) { + uint64_t flags = mpv_render_context_update(sdl2->mpv_gl); + if (flags & MPV_RENDER_UPDATE_FRAME) + redraw = 1; + } + // Happens when at least 1 new event is in the mpv event queue. + if (event.type == wakeup_on_mpv_events) { + // Handle all remaining mpv events. + for (;;) { + mpv_event *mp_event = mpv_wait_event(sdl2->mpv, 0); + if (mp_event->event_id == MPV_EVENT_NONE) + break; + printf("event: %s\n", mpv_event_name(mp_event->event_id)); + } + } + } + + if (redraw) { + int w, h; + SDL_GetWindowSize(sdl2->window, &w, &h); + mpv_render_param params[] = { + // 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_OPENGL_FBO, &(mpv_opengl_fbo) + { + .fbo = 0, .w = w, .h = h, + } + }, + { + MPV_RENDER_PARAM_FLIP_Y, &(int) + { + 1 + } + }, + {0} + }; + mpv_render_context_render(sdl2->mpv_gl, params); + SDL_GL_SwapWindow(sdl2->window); + } + } }