diff --git a/inc/lektor/defines.h b/inc/lektor/defines.h index 0b8fb58bd78c0ccc9f4ffd9a8d000955ae00e22a..8d71316b3d8d499888b1ccc9d1d31ce98e82b21c 100644 --- a/inc/lektor/defines.h +++ b/inc/lektor/defines.h @@ -15,15 +15,10 @@ #define LKT_DATABASE_KARA_COLUMNT_ANY "any_col" #define LKT_DATABASE_KARA_ALL "string" -#define LEKTOR_TAG_MAX 256 - -#ifndef LKT_MESSAGE_ARGS_MAX -#define LKT_MESSAGE_ARGS_MAX 32 -#endif - -#ifndef LKT_MESSAGE_MAX -#define LKT_MESSAGE_MAX 2048 -#endif +#define LEKTOR_TAG_MAX 256 +#define LKT_MESSAGE_ARGS_MAX 32 +#define LKT_MESSAGE_MAX 2048 +#define LKT_DEFAULT_LIST_SIZE 10 enum mpd_idle_flag { MPD_IDLE_NONE = 0, diff --git a/inc/lektor/module/module_sdl2.h b/inc/lektor/module/module_sdl2.h new file mode 100644 index 0000000000000000000000000000000000000000..8caa155f641e8196519d9d3ff5c415778ab99cba --- /dev/null +++ b/inc/lektor/module/module_sdl2.h @@ -0,0 +1,27 @@ +#pragma once + +#include <lektor/defines.h> +#include <stdbool.h> +#include <sqlite3.h> +#include <lektor/window.h> + +/* The only function with a setted filename */ +int module_set_function(void *mod, void *handle); + +/* Below there are needed functions for a window module. + * Names are not fixed but they follow a convention (to get a code easy + * to read). They should not be used in lektor! They will be loaded into + * the struct lkt_win by the module_set_function. + */ +bool module_sdl2_new(struct lkt_win *const win); +void module_sdl2_close(struct lkt_win *const win); +void module_sdl2_free(struct lkt_win *const win); + +bool module_sdl2_toggle_pause(struct lkt_win *const win); +bool module_sdl2_load_file(struct lkt_win *const win, const char *filepath); +bool module_sdl2_set_volume(struct lkt_win *const win, int vol); + +bool module_sdl2_get_duration(struct lkt_win *const win, int *dur_sec); +bool 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); diff --git a/inc/lektor/module/module_x11.h b/inc/lektor/module/module_x11.h index 5325fe48611662cd7155efdc2da013e23d3ef712..29f943a99fdc43eec72ff7fa91e3468665124700 100644 --- a/inc/lektor/module/module_x11.h +++ b/inc/lektor/module/module_x11.h @@ -17,11 +17,10 @@ bool module_x11_new(struct lkt_win *const win); void module_x11_close(struct lkt_win *const win); void module_x11_free(struct lkt_win *const win); -bool module_x11_toggle_pause(struct lkt_win *const win, bool *new_paused); +bool module_x11_toggle_pause(struct lkt_win *const win); bool module_x11_load_file(struct lkt_win *const win, const char *filepath); bool module_x11_set_volume(struct lkt_win *const win, int vol); -bool module_x11_is_paused(struct lkt_win *const win, bool *ret); bool module_x11_get_duration(struct lkt_win *const win, int *dur_sec); bool module_x11_get_elapsed(struct lkt_win *const win, int *elapsed_sec); diff --git a/inc/lektor/module/mpv.h b/inc/lektor/module/mpv.h new file mode 100644 index 0000000000000000000000000000000000000000..ce9c63e64d5ec03fc5fdb4ad9f00dcb3366b69de --- /dev/null +++ b/inc/lektor/module/mpv.h @@ -0,0 +1,16 @@ +#pragma once + +#include <sqlite3.h> +#include <mpv/client.h> +#include <lektor/net.h> + +void lmpv_free(mpv_handle **ctx); +mpv_handle *lmpv_new(unsigned long int wid); +int lmpv_observe_properties(mpv_handle *ctx); +mpv_handle *lmpv_prepare(void); + +int lmpv_set_volume(mpv_handle *ctx, int vol); +int lmpv_load_file(mpv_handle *ctx, const char *file); +int lmpv_toggle_pause(mpv_handle *ctx); +int lmpv_handle(struct lkt_win *win, mpv_handle *ctx, sqlite3 *db, enum mpd_idle_flag *mpd_idle_flag, + int *time_pos, int *time_duration); diff --git a/inc/lektor/window.h b/inc/lektor/window.h index f0f86b505b1ac39ac7b885a9f13d8fd9abc06107..87644c98f139d81ce4c35d8b36dd21019926d467 100644 --- a/inc/lektor/window.h +++ b/inc/lektor/window.h @@ -20,12 +20,12 @@ struct lkt_win { void (*free)(struct lkt_win *win); /* Entirelly liberate all the resources */ /* Playback control */ - bool (*toggle_pause)(struct lkt_win *win, bool *new_paused); + bool (*toggle_pause)(struct lkt_win *win); bool (*load_file)(struct lkt_win *win, const char *filepath); bool (*set_volume)(struct lkt_win *win, int vol); /* Get playback properties */ - bool (*is_paused)(struct lkt_win *win, bool *ret); + // bool (*is_paused)(struct lkt_win *win, bool *ret); bool (*get_duration)(struct lkt_win *win, int *dur_sec); bool (*get_elapsed)(struct lkt_win *win, int *elapsed_sec); diff --git a/meson.build b/meson.build index 99f26836c9a37a3050582269825d92f6d20a9134..6efa4f3825ea75c16910c5109784d5c2e33f67ef 100644 --- a/meson.build +++ b/meson.build @@ -19,6 +19,7 @@ cc = meson.get_compiler('c') libdl = cc.find_library('dl') dep_x11 = dependency('x11', required : false) dep_mpv = dependency('mpv', required : false) +dep_sdl = dependency('sdl2', required : false) # Sources @@ -88,9 +89,19 @@ lkt = executable( 'lkt' # X11 window module if dep_x11.found() and dep_mpv.found() lib_mod_x11 = shared_library ( '_module_x11' - , files(['src/module/module_x11.c']) + , files(['src/module/module_x11.c', 'src/module/mpv.c']) , include_directories : includes , dependencies : [ dep_x11, dep_mpv ] , link_with : lib ) endif + +# SQL2 window module +if dep_sdl.found() and dep_mpv.found() + lib_mod_sdl = shared_library ( '_module_sdl2' + , files(['src/module/module_sdl2.c', 'src/module/mpv.c']) + , include_directories : includes + , dependencies : [ dep_sdl, dep_mpv ] + , link_with : lib + ) +endif diff --git a/src/commands.c b/src/commands.c index 024532b0b98aec0a297f80f08099acf60e2e07f9..9c1184e14c4efec27df480bd244d273e0483bd66 100644 --- a/src/commands.c +++ b/src/commands.c @@ -148,7 +148,7 @@ bool command_pause(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr) { *watch_mask_ptr |= MPD_IDLE_PLAYER; - return database_queue_toggle_pause(db) && win->toggle_pause(win, NULL); + return database_queue_toggle_pause(db) && win->toggle_pause(win); } bool diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c new file mode 100644 index 0000000000000000000000000000000000000000..ab619b24ac90e66e988c28761c5af80f29a0a8d8 --- /dev/null +++ b/src/module/module_sdl2.c @@ -0,0 +1,339 @@ +#define _POSIX_C_SOURCE 200809L + +#include <lektor/module/module_sdl2.h> +#include <lektor/module/mpv.h> +#include <lektor/macro.h> +#include <lektor/defines.h> +#include <lektor/thread.h> + +#include <sched.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 + +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 */ + + /* Thread related */ + pthread_mutex_t mtx; + struct lkt_thread self; + volatile int launched; /* SDL you sucks */ +}; + +/* Private functions. */ + +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); +} + +static inline bool +init_mpv__(mpv_handle **ctx) +{ + *ctx = lmpv_prepare(); + int status; + RETURN_IF((status = mpv_initialize(*ctx)) < 0, mpv_error_string(status), 1); + RETURN_UNLESS(lmpv_observe_properties(*ctx), "Observe properties 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) { + 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; +} +/* Thread related functions */ + +static void * +sdl_thread__(struct lkt_thread_arg *arg) +{ + volatile struct lkt_win *const win = arg->args; + volatile struct module_sdl2_window *sdl2 = win->window; + SDL_Event event; + uint64_t flags; + int w, h, redraw = 0; + free(arg); + + /* 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), + "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, + } + }, + { + // Can't use mpv_command with that thing, should change to mpv_command_async + 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(); + + fprintf(stderr, " * Started SDL thread\n"); + +loop: + if (SDL_PollEvent(&event)) + sched_yield(); + + switch (event.type) { + case SDL_QUIT: + break; + + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_EXPOSED) { + fprintf(stderr, " . sdl_thread__: Window exposed\n"); + redraw = 1; + } + break; + + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_SPACE) { + const char *cmd_pause[] = { "cycle", "pause", NULL }; + mpv_command_async((mpv_handle *) sdl2->mpv, 0, cmd_pause); + fprintf(stderr, " . sdl_thread__: Toggle pause\n"); + } + break; + + case SDL_KEYUP: + 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; + fprintf(stderr, " . sdl_thread__: Toggle fullscreen\n"); + } + 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 (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; + } + goto loop; /* A loop without indentation. */ +} + +/* Exported functions */ + +extern int +module_set_function(void *arg__, void *handle__) +{ + RETURN_UNLESS(arg__ && handle__, "Invalid argument", 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->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) +{ + RETURN_UNLESS(win, "Invalid arguments", false); + + if (win->window == NULL) { + win->window = calloc(1, sizeof(struct module_sdl2_window)); + RETURN_UNLESS(win->window, "Out of memory", false); + memset(win->window, 0, sizeof(struct module_sdl2_window)); + + /* Yeah, this is how it is done. */ + pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + ((struct module_sdl2_window *) win->window)->mtx = mtx; + + /* Start the SDL thread */ + struct lkt_thread_arg *arg = calloc(1, sizeof(struct lkt_thread_arg)); + RETURN_UNLESS(arg, "Out of memory", false); + arg->args = win; + RETURN_IF(lkt_th_new(&((struct module_sdl2_window *) win->window)->self, + LKT_DEFAULT_LIST_SIZE, sdl_thread__, arg), + "Failed to launch the SDL thread", false); + } + + /* Finish */ + SDL_SetWindowTitle((SDL_Window *) ((struct module_sdl2_window *) win->window)->window, "Lektord"); + SDL_DisableScreenSaver(); + ((struct module_sdl2_window *) win->window)->launched = 1; + return true; +} + +void +module_sdl2_close(struct lkt_win *const win) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING); + struct module_sdl2_window *sdl2 = win->window; + RETURN_UNLESS(sdl2->mpv, "Missing mpv ctx", NOTHING); + static const char *cmd[] = { "stop", NULL }; + mpv_command_async((mpv_handle *) sdl2->mpv, 0, cmd); + /* TODO: make the SDL window background be black. */ +} + +void +module_sdl2_free(struct lkt_win *const win) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING); + struct module_sdl2_window *sdl2 = win->window; + mpv_render_context_free((mpv_render_context *) sdl2->mpv_gl); + sdl2->mpv_gl = NULL; + lmpv_free((mpv_handle **) &sdl2->mpv); +} + +bool +module_sdl2_toggle_pause(struct lkt_win *const win) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + return ! lmpv_toggle_pause((mpv_handle *) ((struct module_sdl2_window *) win->window)->mpv); +} + +bool +module_sdl2_load_file(struct lkt_win *const win, const char *filepath) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + struct module_sdl2_window *sdl2 = win->window; + bool ret = ! lmpv_load_file((mpv_handle *) 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) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + return ! lmpv_set_volume((mpv_handle *) ((struct module_sdl2_window *) win->window)->mpv, vol); +} + +bool +module_sdl2_get_duration(struct lkt_win *const win, int *dur_sec) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", 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) +{ + RETURN_UNLESS(win && win->window, "Invalid arguments", 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) +{ + struct module_sdl2_window *sdl2 = win->window; + RETURN_UNLESS(sdl2, "Can't handle events from a NULL window", false); + return ! lmpv_handle(win, (mpv_handle *) sdl2->mpv, db, mpd_idle_events, + (int *) &sdl2->mpv_time_pos, (int *) &sdl2->mpv_duration); +} diff --git a/src/module/module_x11.c b/src/module/module_x11.c index 49a2e8132527b9ea154975bfc6fecbd86dcc34b7..36787fd3c6726f4e8e68f26ba0cfa64ebfe5b323 100644 --- a/src/module/module_x11.c +++ b/src/module/module_x11.c @@ -1,4 +1,6 @@ #include <lektor/module/module_x11.h> +#include <lektor/macro.h> +#include <lektor/module/mpv.h> #include <lektor/database.h> #include <lektor/commands.h> @@ -41,9 +43,7 @@ struct module_x11_window { extern int module_set_function(void *arg__, void *handle__) { - if (NULL == arg__ || handle__ == NULL) - return 1; - + RETURN_UNLESS(arg__ && handle__, "Invalid arguments", 1); struct lkt_win *win = (struct lkt_win *) arg__; win->new = module_x11_new; @@ -52,7 +52,6 @@ module_set_function(void *arg__, void *handle__) win->toggle_pause = module_x11_toggle_pause; win->load_file = module_x11_load_file; win->set_volume = module_x11_set_volume; - win->is_paused = module_x11_is_paused; win->get_duration = module_x11_get_duration; win->get_elapsed = module_x11_get_elapsed; win->handle_events = module_x11_handle_events; @@ -71,11 +70,7 @@ static inline bool lx11_new(struct module_x11_window *ret) { ret->master_display = XOpenDisplay(NULL); - if (ret->master_display == NULL) { - fprintf(stderr, " ! lkt_x11: can't open display\n"); - return false; - } - + RETURN_UNLESS(ret->master_display, "Can't open display", false); ret->screen = DefaultScreen(ret->master_display); Colormap colormap = DefaultColormap(ret->master_display, ret->screen); XAllocNamedColor(ret->master_display, colormap, "black", &ret->black, &ret->black); @@ -112,48 +107,12 @@ lx11_new(struct module_x11_window *ret) return true; } -static inline void -lx11_fullscreen(struct module_x11_window *win, bool go_fullscreen) -{ - if (go_fullscreen) { - XSizeHints *size_hints = XAllocSizeHints(); - long hints = 0; - - if (NULL == size_hints) { - fprintf(stderr, "lx11_fullscreen: failed to allocate size hints\n"); - return; - } - - if (XGetWMSizeHints(win->master_display, win->master_win, size_hints, &hints, - XInternAtom(win->master_display, "WM_SIZE_HINTS", False))) { - fprintf(stderr, " ! lx11_fullscreen: failed to get size hints\n"); - goto error; - } - - XLowerWindow(win->master_display, win->master_win); - XUnmapWindow(win->master_display, win->master_win); - XSync(win->master_display, False); - - fprintf(stderr, " . lx11_fullscreen: size hints is: %ld\n", hints); -error: - XFree(size_hints); - } - - Atom atoms[2] = { XInternAtom(win->master_display, "_NET_WM_STATE_FULLSCREEN", go_fullscreen), None }; - XChangeProperty(win->master_display, win->master_win, - XInternAtom(win->master_display, "_NET_WM_STATE", False), - XA_ATOM, 32, PropModeReplace, (const unsigned char *) atoms, 1); - XMapWindow(win->master_display, win->master_win); - XRaiseWindow(win->master_display, win->master_win); -} - static inline bool lx11_handle(struct module_x11_window *win) { XEvent event; - if (!win || !win->mpv) - return true; + RETURN_UNLESS(win && win->mpv, "Invalid argument", true); for (;;) { @@ -184,9 +143,6 @@ lx11_handle(struct module_x11_window *win) // Key press if (event.type == KeyRelease) { fprintf(stderr, " . lx11_handle: released key with keycode: 0x%x\n", event.xkey.keycode); - // if (event.xkey.keycode == 0x5f) - // lx11_fullscreen(win, true); - // if (event.xkey.keycode == 0x47) XClearWindow(win->master_display, win->master_win); } @@ -205,186 +161,6 @@ lx11_handle(struct module_x11_window *win) return true; } -/* - * - * NOW: PRIVATE MPV FUNCTIONS :NOW - * - */ - -static inline void -lmpv_free(mpv_handle **ctx) -{ - if (!*ctx) { - fprintf(stderr, " ! lmpv_free: failed due to missing mpv ctx\n"); - return; - } - - static const char *cmd[] = {"quit", NULL}; - mpv_command(*ctx, cmd); - mpv_destroy(*ctx); - *ctx = NULL; - fprintf(stderr, " * lmpv_free: mpv context destroyed\n"); -} - -static inline bool -lmpv_handle(struct lkt_win *win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events) -{ - int ao_volume; - struct lkt_queue_state state; - bool sta = false; - struct module_x11_window *win_x11 = win->window; - mpv_event *event = NULL; - mpv_event_property *prop; - - if (!win || !win_x11 || !win_x11->mpv) - goto end; - - if (!database_queue_state(db, &state)) - goto end; - - for (;;) { - event = mpv_wait_event(win_x11->mpv, 0); - - switch (event->event_id) { - case MPV_EVENT_PAUSE: - case MPV_EVENT_UNPAUSE: - database_queue_toggle_pause(db); - break; - - case MPV_EVENT_SHUTDOWN: - database_queue_stop(db); - win->close(win); - goto end; - - case MPV_EVENT_NONE: - goto end; - - case MPV_EVENT_IDLE: - if (state.current > 0 && win_x11->mpv_time_pos > 0) - command_next(db, win, mpd_idle_events); - break; - - case MPV_EVENT_PROPERTY_CHANGE: - prop = (mpv_event_property *) event->data; - if (prop->format == MPV_FORMAT_NONE) - break; - // MPV volume (BUG: The flag is not MPV_FORMAT_NONE only at the end of the song...) // - if (!strcmp(prop->name, "ao-volume") - && prop->format == MPV_FORMAT_INT64) { - ao_volume = *(int *) prop->data; - database_config_queue(db, "volume", ao_volume); - } - // File duration // - if (!strcmp(prop->name, "duration") - && prop->format == MPV_FORMAT_INT64) { - win_x11->mpv_duration = *(int *) prop->data; - database_config_queue(db, "duration", *(int *) prop->data); - } - if (!strcmp(prop->name, "time-pos") - && prop->format == MPV_FORMAT_INT64) { - win_x11->mpv_time_pos = *(int *) prop->data; - database_config_queue(db, "elapsed", *(int *) prop->data); - } - // Pause state // - if (!strcmp(prop->name, "pause") - && prop->format == MPV_FORMAT_FLAG) - database_queue_set_paused(db, *(bool *) prop->data); - break; - default: - fprintf(stderr, " ! Unhandled mpv event: %s\n", mpv_event_name(event->event_id)); - break; - } - } - - sta = true; -end: - return sta; -} - -static mpv_handle * -lmpv_new(unsigned long int wid) -{ - mpv_handle *ctx = mpv_create(); - int status; - - if (!ctx) { - fprintf(stderr, " ! lmpv_new: failed creating context\n"); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "input-default-bindings", "yes")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set input-default-bindings: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "input-vo-keyboard", "yes")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set input-vo-keyboard: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "replaygain", "track")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set replaygain: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "gpu-context", "x11")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set gpu-context: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_property(ctx, "wid", MPV_FORMAT_INT64, &wid)) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set wid: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "demuxer-max-bytes", "10M")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set demuxer-max-bytes: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_option_string(ctx, "profile", "low-latency")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set demuxer-max-bytes: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_set_property_string(ctx, "hwdec", "yes")) < 0) { - fprintf(stderr, " ! lmpv_new: failed to set hwdec to no: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_initialize(ctx)) < 0) { - fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n", - mpv_error_string(status)); - return NULL; - } - - if ((status = mpv_observe_property(ctx, 0, "ao-volume", MPV_FORMAT_INT64)) < 0) - goto error; - - if ((status = mpv_observe_property(ctx, 0, "duration", MPV_FORMAT_INT64)) < 0) - goto error; - - if ((status = mpv_observe_property(ctx, 0, "time-pos", MPV_FORMAT_INT64)) < 0) - goto error; - - if ((status = mpv_observe_property(ctx, 0, "pause", MPV_FORMAT_FLAG)) < 0) - goto error; - - fprintf(stderr, " * lmpv_new: successfully created a mpv context\n"); - return ctx; -error: - lmpv_free(&ctx); - return NULL; -} - /* * * NOW: THE SETTED FUNCTIONS FOR THE WINDOW MODULE :NOW @@ -394,34 +170,20 @@ error: bool module_x11_new(struct lkt_win *const win) { - (void) lx11_fullscreen; - - if (win == NULL) - return false; - + RETURN_UNLESS(win, "Invalid argument", false); struct module_x11_window *x11_win = win->window; if (x11_win == NULL) { x11_win = calloc(1, sizeof(struct module_x11_window)); memset(x11_win, 0, sizeof(struct module_x11_window)); - - if (x11_win == NULL) { - fprintf(stderr, " ! module_x11_window: failed to allocate window, no memory\n"); - return false; - } - - if (!lx11_new(x11_win)) { - fprintf(stderr, " ! module_x11_window: failed to create the X11 window\n"); - return false; - } - + RETURN_UNLESS(x11_win, "Out of memory", false); + RETURN_UNLESS(lx11_new(x11_win), "Can't create X11 window", false); fprintf(stderr, " . module_x11_window: successfully created the X11 window\n"); } if (x11_win->mpv == NULL) { x11_win->mpv = lmpv_new(x11_win->master_win); - if (x11_win->mpv == NULL) - return false; + RETURN_UNLESS(x11_win->mpv, "Failed to create mpv", false); } win->window = x11_win; @@ -431,9 +193,7 @@ module_x11_new(struct lkt_win *const win) void module_x11_close(struct lkt_win *const win) { - if (win == NULL || win->window == NULL) - return; - + RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING); struct module_x11_window *const x11_win = win->window; lmpv_free(&x11_win->mpv); x11_win->child_display = NULL; @@ -443,164 +203,63 @@ module_x11_close(struct lkt_win *const win) void module_x11_free(struct lkt_win *const win) { - if (win == NULL || win->window == NULL) - return; - + RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING); struct module_x11_window *const x11_win = win->window; lmpv_free(&x11_win->mpv); - - if (win == NULL) - return; - XCloseDisplay(x11_win->master_display); free(x11_win); - win->window = NULL; } bool -module_x11_toggle_pause(struct lkt_win *const win, bool *new_paused) +module_x11_toggle_pause(struct lkt_win *const win) { - if (win == NULL || win->window == NULL) - return false; - - mpv_handle *ctx = ((struct module_x11_window *) win->window)->mpv; - - if (!ctx) { - fprintf(stderr, " ! module_x11_toggle_pause: failed due to missing mpv ctx\n"); - return false; - } - - const char *cmd[] = {"cycle", "pause", "up", NULL}; - int status; - - if ((status = mpv_command(ctx, cmd)) < 0) { - fprintf(stderr, "mpv_toggle_pause: Failed issue command: %s\n", - mpv_error_string(status)); - return false; - } - - return module_x11_is_paused(win, new_paused); + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + return ! lmpv_toggle_pause(((struct module_x11_window *) win->window)->mpv); } bool module_x11_load_file(struct lkt_win *const win, const char *filepath) { - if (win == NULL || win->window == NULL) - return false; - + RETURN_UNLESS(win && win->window, "Invalid arguments", false); struct module_x11_window *xwin = win->window; - mpv_handle *ctx = xwin->mpv; - - if (!ctx) { - fprintf(stderr, " ! module_x11_load_file: failed due to missing mpv ctx\n"); - return false; - } - - if (access(filepath, R_OK)) { - fprintf(stderr, " ! mpv_add_file: Faild to read file: %s\n", filepath); - return false; - } - - const char *cmd[] = {"loadfile", filepath, "replace", NULL}; - int status; - - if ((status = mpv_command(ctx, cmd)) < 0) { - fprintf(stderr, " ! mpv_add_file: Failed to add '%s': %s\n", - filepath, mpv_error_string(status)); - return false; - } - + bool ret = ! lmpv_load_file(xwin->mpv, filepath); xwin->mpv_duration = 0; xwin->mpv_time_pos = 0; - - fprintf(stderr, " . module_x11_load_file: added file %s\n", filepath); - return true; + return ret; } bool module_x11_set_volume(struct lkt_win *const win, int vol) { - if (win == NULL || win->window == NULL) - return false; - - mpv_handle *ctx = ((struct module_x11_window *) win->window)->mpv; - - if (!ctx) { - fprintf(stderr, " ! module_x11_set_volume: failed due to missing mpv ctx\n"); - return false; - } - - int status; - char str[5]; - memset(str, 0, 5); - snprintf(str, 4, "%d", vol); - const char *cmd[] = {"set", "ao-volume", str, NULL}; - - if ((status = mpv_command(ctx, cmd)) < 0) { - fprintf(stderr, " ! module_x11_set_volume: Failed to execute command: %s\n", - mpv_error_string(status)); - return false; - } - - return true; -} - -bool -module_x11_is_paused(struct lkt_win *const win, bool *ret) -{ - if (win == NULL || win->window == NULL) - return false; - - mpv_handle *ctx = ((struct module_x11_window *) win->window)->mpv; - int status; - - if (!ctx) { - fprintf(stderr, " ! module_x11_is_paused: failed due to missing mpv ctx\n"); - return false; - } - - if ((status = mpv_get_property(ctx, "pause", MPV_FORMAT_FLAG, &ret)) < 0) { - fprintf(stderr, "mpv_is_paused: Failed to get pause property: %s\n", - mpv_error_string(status)); - return false; - } - - return true; + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + return ! lmpv_set_volume(((struct module_x11_window *) win->window)->mpv, vol); } bool module_x11_get_duration(struct lkt_win *const win, int *dur_sec) { - if (win == NULL || win->window == NULL) - return false; - + RETURN_UNLESS(win && win->window, "Invalid arguments", false); struct module_x11_window *win_x11 = win->window; *dur_sec = win_x11->mpv_duration; - return true; } bool module_x11_get_elapsed(struct lkt_win *const win, int *elapsed_sec) { - if (win == NULL || win->window == NULL) - return false; - + RETURN_UNLESS(win && win->window, "Invalid arguments", false); struct module_x11_window *win_x11 = win->window; *elapsed_sec = win_x11->mpv_time_pos; - return true; } bool module_x11_handle_events(struct lkt_win *const win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events) { - if (win == NULL || win->window == NULL) - return false; - - bool sta = true; - sta &= lx11_handle(win->window); - sta &= lmpv_handle(win, db, mpd_idle_events); - return sta; + RETURN_UNLESS(win && win->window, "Invalid arguments", false); + struct module_x11_window *xwin = win->window; + return lx11_handle(xwin) && + ! lmpv_handle(win, xwin->mpv, db, mpd_idle_events, (int *) &xwin->mpv_time_pos, + (int *) &xwin->mpv_duration); } diff --git a/src/module/mpv.c b/src/module/mpv.c new file mode 100644 index 0000000000000000000000000000000000000000..c5d5626c4baab06492aa918ea7a8cc975dad6408 --- /dev/null +++ b/src/module/mpv.c @@ -0,0 +1,194 @@ +#include <lektor/module/mpv.h> +#include <lektor/commands.h> +#include <lektor/database.h> +#include <lektor/macro.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sqlite3.h> + +void +lmpv_free(mpv_handle **ctx) +{ + RETURN_UNLESS(ctx && *ctx, "Missing mpv ctx", NOTHING); + static const char *cmd[] = {"quit", NULL}; + mpv_command(*ctx, cmd); + mpv_destroy(*ctx); + *ctx = NULL; + fprintf(stderr, " * lmpv_free: mpv context destroyed\n"); +} + +mpv_handle * +lmpv_prepare(void) +{ + mpv_handle *ctx = mpv_create(); + int status; + RETURN_UNLESS(ctx, "Failed to create context", NULL); + +#define MPV_SET_OPTION(opt, value) \ + if ((status = mpv_set_option_string(ctx, opt, value)) < 0) { \ + fprintf(stderr, " . %s: Failed to set %s to %s: %s\n", \ + __func__, opt, value, mpv_error_string(status)); \ + return NULL; \ + } + + MPV_SET_OPTION("input-default-bindings", "yes"); + MPV_SET_OPTION("input-vo-keyboard", "yes"); + MPV_SET_OPTION("replaygain", "track"); + MPV_SET_OPTION("gpu-context", "x11"); + MPV_SET_OPTION("demuxer-max-bytes", "10M"); + MPV_SET_OPTION("profile", "low-latency"); + MPV_SET_OPTION("hwdec", "yes"); + return ctx; +} + +int +lmpv_observe_properties(mpv_handle *ctx) +{ + return (mpv_observe_property(ctx, 0, "ao-volume", MPV_FORMAT_INT64) >= 0) && + (mpv_observe_property(ctx, 0, "duration", MPV_FORMAT_INT64) >= 0) && + (mpv_observe_property(ctx, 0, "time-pos", MPV_FORMAT_INT64) >= 0) && + (mpv_observe_property(ctx, 0, "pause", MPV_FORMAT_FLAG) >= 0); +} + +mpv_handle * +lmpv_new(unsigned long int wid) +{ + mpv_handle *ctx = lmpv_prepare(); + int status; + + if ((status = mpv_set_property(ctx, "wid", MPV_FORMAT_INT64, &wid)) < 0) { + fprintf(stderr, " ! lmpv_new: failed to set wid: %s\n", + mpv_error_string(status)); + goto error; + } + + if ((status = mpv_initialize(ctx)) < 0) { + fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n", + mpv_error_string(status)); + goto error; + } + + if (!lmpv_observe_properties(ctx)) { + fprintf(stderr, " * lmpv_new: failed to observe properties\n"); + goto error; + } + + fprintf(stderr, " * lmpv_new: successfully created a mpv context\n"); + return ctx; +error: + lmpv_free(&ctx); + return NULL; +} + +int +lmpv_set_volume(mpv_handle *ctx, int vol) +{ + RETURN_UNLESS(ctx, "Missing mpv ctx", 1); + int status; + char str[5]; + memset(str, 0, 5); + snprintf(str, 4, "%d", vol); + const char *cmd[] = {"set", "ao-volume", str, NULL}; + if ((status = mpv_command_async(ctx, 0, cmd)) < 0) { + fprintf(stderr, " ! lmpv_set_volume: Failed to execute command: %s\n", + mpv_error_string(status)); + return 1; + } + return 0; +} + +int +lmpv_load_file(mpv_handle *ctx, const char *file) +{ + RETURN_UNLESS(ctx, "Missing mpv ctx", 1); + RETURN_IF(access(file, R_OK), "Failed to read file", 1); + const char *cmd[] = {"loadfile", file, "replace", NULL}; + int status; + if ((status = mpv_command_async(ctx, 0, cmd)) < 0) { + fprintf(stderr, " ! lmpv_load_file: Failed to add '%s': %s\n", + file, mpv_error_string(status)); + return 1; + } + return 0; +} + +int +lmpv_toggle_pause(mpv_handle *ctx) +{ + RETURN_UNLESS(ctx, "Missing mpv ctx", 1); + const char *cmd[] = {"cycle", "pause", "up", NULL}; + int status; + if ((status = mpv_command_async(ctx, 0, cmd)) < 0) { + fprintf(stderr, "lmpv_toggle_pause: Failed issue command: %s\n", + mpv_error_string(status)); + return 1; + } + return 0; +} + +int +lmpv_handle(struct lkt_win *win, mpv_handle *ctx, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events, + int *time_pos, int *time_duration) +{ + int ao_volume; + struct lkt_queue_state state; + mpv_event *event = NULL; + mpv_event_property *prop; + RETURN_UNLESS(ctx && win, "Invalid argument", 1); + RETURN_UNLESS(database_queue_state(db, &state), "Failed to get queue state", 1); + +loop: + event = mpv_wait_event(ctx, 0); + + switch (event->event_id) { + case MPV_EVENT_PAUSE: + case MPV_EVENT_UNPAUSE: + database_queue_toggle_pause(db); + break; + + case MPV_EVENT_SHUTDOWN: + database_queue_stop(db); + win->close(win); + return 1; + + case MPV_EVENT_NONE: + return 1; + + case MPV_EVENT_IDLE: + if (state.current > 0 && *time_pos > 0) + command_next(db, win, mpd_idle_events); + break; + + case MPV_EVENT_PROPERTY_CHANGE: + prop = (mpv_event_property *) event->data; + if (prop->format == MPV_FORMAT_NONE) + break; + // MPV volume (BUG: The flag is not MPV_FORMAT_NONE only at the end of the song...) // + if (!strcmp(prop->name, "ao-volume") + && prop->format == MPV_FORMAT_INT64) { + ao_volume = *(int *) prop->data; + database_config_queue(db, "volume", ao_volume); + } + // File duration // + if (!strcmp(prop->name, "duration") + && prop->format == MPV_FORMAT_INT64) { + *time_duration = *(int *) prop->data; + database_config_queue(db, "duration", *(int *) prop->data); + } + if (!strcmp(prop->name, "time-pos") + && prop->format == MPV_FORMAT_INT64) { + *time_pos = *(int *) prop->data; + database_config_queue(db, "elapsed", *(int *) prop->data); + } + // Pause state // + if (!strcmp(prop->name, "pause") + && prop->format == MPV_FORMAT_FLAG) + database_queue_set_paused(db, *(bool *) prop->data); + break; + default: + fprintf(stderr, " ! Unhandled mpv event: %s\n", mpv_event_name(event->event_id)); + break; + } + goto loop; /* A loop without indentation. */ +} diff --git a/src/repo/async.c b/src/repo/async.c index 8bd48e0fe58a79b7033ecb35b42dba2c685cbae9..52bc98d0aca2ce260c7e843f0a243642c2feb376 100644 --- a/src/repo/async.c +++ b/src/repo/async.c @@ -10,8 +10,6 @@ #include <lektor/thread.h> #include <limits.h> -#define LKT_DEFAULT_LIST_SIZE 10 - static struct lkt_thread repo_thread; static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;