Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • bed922f425f507e1ca3b661c1cf3dc997832291e
  • master par défaut protégée
  • rust-playlist-sync
  • rust
  • fix-qt-deprecated-qvariant-type
  • fix-mpris-qtwindow-race-condition
  • rust-appimage-wayland
  • windows-build-rebased
  • v2.5 protégée
  • v2.4 protégée
  • v2.3-1 protégée
  • v2.3 protégée
  • v2.2 protégée
  • v2.1 protégée
  • v2.0 protégée
  • v1.8-3 protégée
  • v1.8-2 protégée
  • v1.8-1 protégée
  • v1.8 protégée
  • v1.7 protégée
  • v1.6 protégée
  • v1.5 protégée
  • v1.4 protégée
  • v1.3 protégée
  • v1.2 protégée
  • v1.1 protégée
  • v1.0 protégée
27 résultats

module_sdl2.c

Blame
  • module_sdl2.c 13,54 Kio
    #include <lektor/module/module_sdl2.h>
    #include <lektor/module/mpv.h>
    #include <lektor/macro.h>
    #include <lektor/defines.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL.h>
    #include <mpv/render_gl.h>
    #include <mpv/client.h>
    
    #define WIDTH   400
    #define HEIGHT  200
    
    struct module_sdl2_window {
        /* Related to SDL2 */
        SDL_Window *window;
        SDL_GLContext glcontext;
    
        /* Mpv related */
        mpv_handle *mpv;
        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  //
    };
    
    /* Private functions. */
    
    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)
    {
        (void) fn_ctx;
        return SDL_GL_GetProcAddress(name);
    }
    
    static inline void
    on_mpv_events(void *ctx)
    {
        (void) ctx;
        SDL_Event event = { .type = wakeup_on_mpv_events };
        SDL_PushEvent(&event);
    }
    
    static inline void
    on_mpv_render_update(void *ctx)
    {
        (void) ctx;
        SDL_Event event = { .type = wakeup_on_mpv_render_update };
        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
    module_set_function(void *arg__, void *handle__)
    {
        if (NULL == arg__ || handle__ == NULL)
            return 1;
    
        struct lkt_win *win = (struct lkt_win *) arg__;
    
        win->new = module_sdl2_new;
        win->close = module_sdl2_close;
        win->free = module_sdl2_free;
        win->toggle_pause = module_sdl2_toggle_pause;
        win->load_file = module_sdl2_load_file;
        win->set_volume = module_sdl2_set_volume;
        win->is_paused = module_sdl2_is_paused;
        win->get_duration = module_sdl2_get_duration;
        win->get_elapsed = module_sdl2_get_elapsed;
        win->handle_events = module_sdl2_handle_events;
        win->handle = handle__;
    
        return 0;
    }
    
    bool
    module_sdl2_new(struct lkt_win *const win)
    {
        if (win == NULL)
            return false;
    
        struct module_sdl2_window *sdl2 = win->window;
        int status;
    
        if (sdl2 == NULL) {
            sdl2 = calloc(1, sizeof(struct module_sdl2_window));
            memset(sdl2, 0, sizeof(struct module_sdl2_window));
    
            if (sdl2 == NULL) {
                fprintf(stderr, " ! module_sdl2_new: failed to allocate window, no memory\n");
                return false;
            }
    
            /* CREATE THE SDL2 WINDOW TODO */
            SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no"); /* It seems that sdl sucks. */
            if (SDL_Init(SDL_INIT_VIDEO) < 0)
                goto error;
    
            sdl2->window = SDL_CreateWindow("lektord", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                                            WIDTH, HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
            if (!sdl2->window)
                goto error;
    
            sdl2->glcontext = SDL_GL_CreateContext(sdl2->window);
            if (!sdl2->glcontext)
                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));
                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");
    
            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)
                goto error;
    
            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));
                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) {
        }
    
        win->window = sdl2;
        return true;
    error:
        return false;
    }
    
    void
    module_sdl2_close(struct lkt_win *const win)
    {
        if (win == NULL || win->window == NULL)
            return;
        lmpv_free(&((struct module_sdl2_window *)win->window)->mpv);
    }
    
    void
    module_sdl2_free(struct lkt_win *const win)
    {
        module_sdl2_close(win);
    }
    
    bool
    module_sdl2_toggle_pause(struct lkt_win *const win, bool *new_paused)
    {
        if (win == NULL || win->window == NULL)
            return false;
        int pause, ret = ! lmpv_toggle_pause(((struct module_sdl2_window *) win->window)->mpv, &pause);
        *new_paused = pause;
        return ret;
    }
    
    bool
    module_sdl2_load_file(struct lkt_win *const win, const char *filepath)
    {
        if (win == NULL || win->window == NULL)
            return false;
        struct module_sdl2_window *sdl2 = win->window;
        bool ret = ! lmpv_load_file(sdl2->mpv, filepath);
        sdl2->mpv_duration = 0;
        sdl2->mpv_time_pos = 0;
        return ret;
    }
    
    bool
    module_sdl2_set_volume(struct lkt_win *const win, int vol)
    {
        if (win == NULL || win->window == NULL)
            return false;
        return ! lmpv_set_volume(((struct module_sdl2_window *) win->window)->mpv, vol);
    }
    
    bool
    module_sdl2_is_paused(struct lkt_win *const win, bool *ret)
    {
        if (win == NULL || win->window == NULL)
            return false;
        int ret_, sta = ! lmpv_is_paused(((struct module_sdl2_window *) win->window)->mpv, &ret_);
        *ret = ret_;
        return sta;
    }
    
    bool
    module_sdl2_get_duration(struct lkt_win *const win, int *dur_sec)
    {
        if (win == NULL || win->window == NULL)
            return false;
        *dur_sec = ((struct module_sdl2_window *) win->window)->mpv_duration;
        return true;
    }
    
    bool
    module_sdl2_get_elapsed(struct lkt_win *const win, int *elapsed_sec)
    {
        if (win == NULL || win->window == NULL)
            return false;
        *elapsed_sec = ((struct module_sdl2_window *) win->window)->mpv_time_pos;
        return true;
    }
    
    bool
    module_sdl2_handle_events(struct lkt_win *const win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events)
    {
    }