Hello community, here is the log from the commit of package mpvpaper for openSUSE:Factory checked in at 2020-12-05 20:51:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mpvpaper (Old) and /work/SRC/openSUSE:Factory/.mpvpaper.new.5913 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mpvpaper" Sat Dec 5 20:51:25 2020 rev:2 rq:853315 version:1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/mpvpaper/mpvpaper.changes 2020-11-24 22:13:37.323559077 +0100 +++ /work/SRC/openSUSE:Factory/.mpvpaper.new.5913/mpvpaper.changes 2020-12-05 20:51:26.499569762 +0100 @@ -1,0 +2,13 @@ +Sat Dec 5 18:54:08 UTC 2020 - Michael Vetter <mvet...@suse.com> + +- Update to 1.1: + * 2 new options "--auto-pause/-p" and "--auto-stop/-s" + * 2 watch lists "pauselist" and "stoplist" + * New program called "mpvpaper-holder" to facilitate "stopping" mpvpaper + * User configs in "~/.config/mpv/" are now loaded + * mpv terminal mode is enabled by default for better controls + set "terminal=no" mpv option if you don't want that + * Various minor improvements + * See https://github.com/GhostNaN/mpvpaper/releases/tag/1.1 + +------------------------------------------------------------------- Old: ---- 1.0.tar.gz New: ---- 1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mpvpaper.spec ++++++ --- /var/tmp/diff_new_pack.7zvlVB/_old 2020-12-05 20:51:27.143570381 +0100 +++ /var/tmp/diff_new_pack.7zvlVB/_new 2020-12-05 20:51:27.147570384 +0100 @@ -17,7 +17,7 @@ Name: mpvpaper -Version: 1.0 +Version: 1.1 Release: 0 Summary: A video wallpaper program for wlroots based wayland compositors License: GPL-3.0-or-later @@ -48,5 +48,6 @@ %license LICENSE %doc README.md %{_bindir}/mpvpaper +%{_bindir}/mpvpaper-holder %changelog ++++++ 1.0.tar.gz -> 1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpvpaper-1.0/README.md new/mpvpaper-1.1/README.md --- old/mpvpaper-1.0/README.md 2020-08-20 02:54:55.000000000 +0200 +++ new/mpvpaper-1.1/README.md 2020-12-05 02:16:06.000000000 +0100 @@ -31,6 +31,9 @@ ### Arch Based: AUR package: https://aur.archlinux.org/packages/mpvpaper-git/ +### Gentoo: +GURU package: https://gpo.zugaina.org/Overlays/guru/gui-apps/mpvpaper/ + ## Usage ### Running Simple example: @@ -42,7 +45,11 @@ mpvpaper -o "no-audio --loop-playlist shuffle" HDMI-A-1 www.url/to/playlist ``` ### Controlling -If you would like to control MPVPaper while it's running, use a mpv input-ipc-server like this: +If MPVPaper is running in a terminal and is not forked: + +Simply enter your keyboard key bindings directly into the terminal. + +Else if you would like to control MPVPaper while it's forked, use a mpv input-ipc-server like this: ``` mpvpaper -o "input-ipc-server=/tmp/mpv-socket" DP-1 /path/to/video ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpvpaper-1.0/meson.build new/mpvpaper-1.1/meson.build --- old/mpvpaper-1.0/meson.build 2020-08-20 02:54:55.000000000 +0200 +++ new/mpvpaper-1.1/meson.build 2020-12-05 02:16:06.000000000 +0100 @@ -7,6 +7,7 @@ wl_egl=dependency('wayland-egl') egl=dependency('egl') mpv=dependency('mpv') +threads=dependency('threads') scanner=find_program('wayland-scanner') scanner_private_code=generator(scanner,output: '@BASENAME@-protocol.c',arguments: ['private-code','@INPUT@','@OUTPUT@']) @@ -28,4 +29,9 @@ executable(meson.project_name(), ['src/main.c', 'src/glad.c', 'src/cflogprinter.c'], include_directories : ['inc'], -dependencies: [dl_dep, wl_client, wl_egl, egl, mpv, protocols_dep], install: true) +dependencies: [dl_dep, wl_client, wl_egl, egl, mpv, threads, protocols_dep], install: true) + +shm_dep = cc.find_library('rt', required : false) +executable(meson.project_name() + '-holder', ['src/holder.c'], +include_directories : ['inc'], +dependencies: [dl_dep, wl_client, shm_dep, protocols_dep], install: true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpvpaper-1.0/src/holder.c new/mpvpaper-1.1/src/holder.c --- old/mpvpaper-1.0/src/holder.c 1970-01-01 01:00:00.000000000 +0100 +++ new/mpvpaper-1.1/src/holder.c 2020-12-05 02:16:06.000000000 +0100 @@ -0,0 +1,407 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <time.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <stdbool.h> + +#include <wayland-client.h> +#include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +struct wl_state { + struct wl_display *display; + struct wl_compositor *compositor; + struct wl_shm *shm; + struct zwlr_layer_shell_v1 *layer_shell; + struct zxdg_output_manager_v1 *xdg_output_manager; + struct wl_list outputs; // struct display_output::link + char* monitor; // User selected output + int run_display; +}; + +struct display_output { + uint32_t wl_name; + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + char *name; + + struct wl_state *state; + struct wl_buffer *buffer; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + + uint32_t width, height; + + struct wl_list link; +}; + +static struct { + char **argv_copy; + char **stoplist; + + bool auto_stop; + + int start_time; +} halt_info = {NULL, NULL, 0, 0}; + +static void nop() {} + +static void revive_mpvpaper() { + // Get the "real" cwd + char exe_dir[1024]; + int cut_point = readlink("/proc/self/exe", exe_dir, sizeof(exe_dir)); + for(uint i=cut_point; i > 1; i--) { + if (exe_dir[i] == '/') { + exe_dir[i+1] = '\0'; + break; + } + } + + execv(strcat(exe_dir, "mpvpaper"), halt_info.argv_copy); +} + +static void check_stoplist() { + for (uint i=0; halt_info.stoplist[i] != NULL; i++) { + char pid_name[512] = {0}; + strcpy(pid_name, "pidof "); + strcat(pid_name, halt_info.stoplist[i]); + strcat(pid_name, " > /dev/null"); + + while (!system(pid_name)) + usleep(100000); // 0.1 sec + } + if (!halt_info.auto_stop) + revive_mpvpaper(); +} + +static struct wl_buffer *create_dummy_buffer(struct display_output *output) { + const int WIDTH = 1, HEIGHT = 1; + + int stride = WIDTH * 4; // 4 bytes per pixel + int size = stride * HEIGHT; + + // Create shm + char name[] = "/wl_shm-dummy"; + shm_unlink(name); + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + shm_unlink(name); + ftruncate(fd, size); + + struct wl_shm_pool *pool = wl_shm_create_pool(output->state->shm, fd, size); + struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, WIDTH, HEIGHT, stride, WL_SHM_FORMAT_XRGB8888); + + wl_shm_pool_destroy(pool); + close(fd); + return buffer; +} + +const static struct wl_callback_listener wl_surface_frame_listener; + +static void create_surface_frame(struct display_output *output) { + output->buffer = create_dummy_buffer(output); + // Callback new frame + struct wl_callback *callback = wl_surface_frame(output->surface); + wl_callback_add_listener(callback, &wl_surface_frame_listener, output); + wl_surface_attach(output->surface, output->buffer, 0, 0); + wl_surface_damage(output->surface, 0, 0, output->width, output->height); + wl_surface_commit(output->surface); +} + +static void frame_handle_done(void *data, struct wl_callback *callback, uint32_t time) { + wl_callback_destroy(callback); + + if (halt_info.stoplist) { + check_stoplist(); // If checking stoplist took longer than a sec + if (time - halt_info.start_time < 1000) + revive_mpvpaper(); + } + else + revive_mpvpaper(); + + halt_info.start_time = time; + create_surface_frame(data); +} + +const static struct wl_callback_listener wl_surface_frame_listener = { + .done = frame_handle_done, +}; + +static void destroy_display_output(struct display_output *output) { + if (!output) { + return; + } + wl_list_remove(&output->link); + if (output->layer_surface != NULL) { + zwlr_layer_surface_v1_destroy(output->layer_surface); + } + if (output->surface != NULL) { + wl_surface_destroy(output->surface); + } + zxdg_output_v1_destroy(output->xdg_output); + wl_output_destroy(output->wl_output); + + free(output->name); + free(output); +} + +static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct display_output *output = data; + output->width = width; + output->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + + if (halt_info.stoplist) + check_stoplist(); + if (halt_info.auto_stop) + create_surface_frame(output); +} + +static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { + (void) surface; + destroy_display_output(data); +} + +static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void create_layer_surface(struct display_output *output) { + output->surface = wl_compositor_create_surface(output->state->compositor); + + // Empty input region + struct wl_region *input_region = wl_compositor_create_region(output->state->compositor); + wl_surface_set_input_region(output->surface, input_region); + wl_region_destroy(input_region); + + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + output->state->layer_shell, output->surface, output->wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "mpvpaper"); + + zwlr_layer_surface_v1_set_size(output->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); + zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); + wl_surface_commit(output->surface); + +} + +static void xdg_output_handle_name(void *data, + struct zxdg_output_v1 *xdg_output, const char *name) { + (void) xdg_output; + + struct display_output *output = data; + output->name = strdup(name); +} + +static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { + (void) xdg_output; + + struct display_output *output = data; + + if (strcmp(output->name, output->state->monitor) == 0 && !output->layer_surface) { + create_layer_surface(output); + } + else { + destroy_display_output(output); + } +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = nop, + .logical_size = nop, + .name = xdg_output_handle_name, + .description = nop, + .done = xdg_output_handle_done, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + (void) version; + + struct wl_state *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); + } + else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } + else if (strcmp(interface, wl_output_interface.name) == 0) { + struct display_output *output = calloc(1, sizeof(struct display_output)); + output->state = state; + output->wl_name = name; + output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3); + + wl_list_insert(&state->outputs, &output->link); + + if (state->run_display) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + state->layer_shell = wl_registry_bind(registry, name, + &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + state->xdg_output_manager = wl_registry_bind(registry, name, + &zxdg_output_manager_v1_interface, 2); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + (void) registry; + + struct wl_state *state = data; + struct display_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &state->outputs, link) { + if (output->wl_name == name) { + destroy_display_output(output); + break; + } + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +static void set_stop_list() { + + char *stop_path = calloc(strlen(getenv("HOME"))+1 + strlen("/.config/mpvpaper/stoplist")+1, sizeof(char)); + strcpy(stop_path, getenv("HOME")); + strcat(stop_path, "/.config/mpvpaper/stoplist"); + + FILE *file = fopen(stop_path, "r"); + if (file) { + // Get alloc size + fseek(file, 0L, SEEK_END); + halt_info.stoplist = calloc(ftell(file)+1, sizeof(char)); + rewind(file); + + // Read lines + char app[512]; + for (uint i=0; fscanf(file, "%s", app) == 1; i++) { + halt_info.stoplist[i] = strdup(app); + } + + free(stop_path); + fclose(file); + } +} + +static void parse_command_line(int argc, char **argv, struct wl_state *state) { + + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"fork", no_argument, NULL, 'f'}, + {"auto-pause", no_argument, NULL, 'p'}, + {"auto-stop", no_argument, NULL, 's'}, + {"layer", required_argument, NULL, 'l'}, + {"mpv-options", required_argument, NULL, 'o'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: mpvpaper-holder <mpvpaper options>\n" + "Descrition:\n" + "mpvpaper-holder acts as a lean gate keeper before mpvpaper can run\n" + "\n" + "It's sole purpose is to check if there is:\n" + "Any program is that running from the stoplist file\n" + "- Set in \"~/.config/mpvpaper/stoplist\"\n" + "If the wallpaper needs to be seen when drawn\n" + "- Set with \"-s\" or \"--auto-stop\" mpvpaper option\n"; + + if(argc > 2) { + char opt; + while((opt = getopt_long(argc, argv, "hvfpsl:o:Z:", long_options, NULL)) != -1) { + + switch (opt) { + case 'h': + fprintf(stdout, "%s", usage); + exit(EXIT_SUCCESS); + case 's': + halt_info.auto_stop = 1; + break; + } + } + if(optind + 1 >= argc) { + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } + state->monitor = strdup(argv[optind]); + } + else { + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) { + struct wl_state state = {0}; + wl_list_init(&state.outputs); + + parse_command_line(argc, argv, &state); + set_stop_list(); + + // Copy argv + int argv_alloc_size = 0; + for(int i=0; argv[i] != NULL; i++) { + argv_alloc_size += strlen(argv[i])+1; + } + halt_info.argv_copy = calloc(argv_alloc_size+1, sizeof(char)); + for(int i=0; i < argc; i++) { + halt_info.argv_copy[i] = strdup(argv[i]); + } + + state.display = wl_display_connect(NULL); + if (!state.display) { + return EXIT_FAILURE; + } + + struct wl_registry *registry = wl_display_get_registry(state.display); + wl_registry_add_listener(registry, ®istry_listener, &state); + wl_display_roundtrip(state.display); + if (state.compositor == NULL || state.layer_shell == NULL || + state.xdg_output_manager == NULL) { + + return EXIT_FAILURE; + } + + struct display_output *output; + wl_list_for_each(output, &state.outputs, link) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state.xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, + &xdg_output_listener, output); + } + + // Check outputs + wl_display_roundtrip(state.display); + if (wl_list_empty(&state.outputs)) { + return EXIT_FAILURE; + } + + state.run_display = 1; + while (wl_display_dispatch(state.display) != -1) { + // NOP + } + + struct display_output *tmp_output; + wl_list_for_each_safe(output, tmp_output, &state.outputs, link) { + destroy_display_output(output); + } + + return EXIT_SUCCESS; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpvpaper-1.0/src/main.c new/mpvpaper-1.1/src/main.c --- old/mpvpaper-1.0/src/main.c 2020-08-20 02:54:55.000000000 +0200 +++ new/mpvpaper-1.1/src/main.c 2020-12-05 02:16:06.000000000 +0100 @@ -4,6 +4,9 @@ #include <getopt.h> #include <unistd.h> #include <pthread.h> +#include <time.h> +#include <stdbool.h> +#include <signal.h> #include <wayland-client.h> #include <wayland-egl.h> @@ -27,7 +30,7 @@ struct wl_list outputs; // struct display_output::link char* monitor; // User selected output int surface_layer; - int run_display; + bool run_display; }; struct display_output { @@ -51,22 +54,77 @@ static EGLContext *egl_context; static EGLSurface *egl_surface; -static mpv_handle* mpv; +static mpv_handle *mpv; static mpv_render_context *mpv_glcontext; -static char* video_path; +static char *video_path; -static int VERBOSE = 0; +static struct { + char **pauselist; + char **stoplist; + + char **argv_copy; + char *save_info; + + bool auto_pause; + bool auto_stop; + + int is_paused; + bool frame_ready; + bool kill_render_loop; + +} halt_info = {NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0}; + +static pthread_t threads[5]; + +static bool VERBOSE = 0; static void nop() {} -const static struct wl_callback_listener wl_surface_frame_listener; +static void exit_cleanup() { + // Cancel all threads + for(uint i=0; threads[i] != 0; i++) { + if (pthread_self() != threads[i]) + pthread_cancel(threads[i]); + } -static void render(struct display_output *output) { - mpv_event* event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_SHUTDOWN || event->event_id == MPV_EVENT_IDLE) { - exit(EXIT_SUCCESS); + // Give mpv a chance to finish + halt_info.kill_render_loop = 1; + for (int trys=10; halt_info.kill_render_loop && trys > 0; trys--) { + usleep(10000); } + if (mpv_glcontext) + mpv_render_context_free(mpv_glcontext); + if (mpv) + mpv_terminate_destroy(mpv); + + if (egl_surface) + eglDestroySurface(egl_display, egl_surface); + if (egl_context) + eglDestroyContext(egl_display, egl_context); + if (egl_window) + wl_egl_window_destroy(egl_window); +} + +static void exit_mpvpaper(int reason) { + if (VERBOSE) + cflp_info("Exiting mpvpaper"); + exit_cleanup(); + exit(reason); +} + +static void *exit_by_pthread() { exit_mpvpaper(1); pthread_exit(NULL);} + +static void handle_signal(int signum) { + (void) signum; + // Separate thread to avoid crash + pthread_t thread; + pthread_create(&thread, NULL, exit_by_pthread, NULL); +} + +const static struct wl_callback_listener wl_surface_frame_listener; + +static void render(struct display_output *output) { mpv_render_param render_params[] = { {MPV_RENDER_PARAM_OPENGL_FBO, &(mpv_opengl_fbo){ .fbo = 0, @@ -84,21 +142,257 @@ wl_callback_add_listener(callback, &wl_surface_frame_listener, output); // Display frame - if (!eglSwapBuffers(egl_display, egl_surface)) { + if (!eglSwapBuffers(egl_display, egl_surface)) cflp_error("Failed to swap egl buffers 0x%X", eglGetError()); - } } -static void frame_handle_done(void *data, struct wl_callback *callback, uint32_t time) { - (void) time; +static void frame_handle_done(void *data, struct wl_callback *callback, uint32_t frame_time) { + (void) frame_time; wl_callback_destroy(callback); - render(data); + + // Reset deadman switch timer + halt_info.frame_ready = 1; + + // Sleep more while paused + if (halt_info.is_paused) { + int start_time = time(NULL); + while (halt_info.is_paused) { + if (time(NULL) - start_time >= 1) + break; + usleep(1000); + } + } + + // Render next frame + if (!halt_info.kill_render_loop) + render(data); + else + halt_info.kill_render_loop = 0; } const static struct wl_callback_listener wl_surface_frame_listener = { .done = frame_handle_done, }; +static void stop_mpvpaper() { + + // Save video position to arg -Z + const char* time_pos = mpv_get_property_string(mpv, "time-pos"); + const char* playlist_pos = mpv_get_property_string(mpv, "playlist-pos"); + + char save_info[30]; + sprintf(save_info, "%s %s", time_pos, playlist_pos); + + int argv_alloc_size = strlen("-Z")+1 + strlen(save_info)+1; + for(uint i=0; halt_info.argv_copy[i] != NULL; i++) { + argv_alloc_size += strlen(halt_info.argv_copy[i])+1; + } + char **argv = calloc(argv_alloc_size+1, sizeof(char)); + + uint i = 0; + for(i=0; halt_info.argv_copy[i] != NULL; i++) { + argv[i] = strdup(halt_info.argv_copy[i]); + } + argv[i] = "-Z"; + argv[i+1] = save_info; + argv[i+2] = NULL; + + // Get the "real" cwd + char exe_dir[1024]; + int cut_point = readlink("/proc/self/exe", exe_dir, sizeof(exe_dir)); + for(uint i=cut_point; i > 1; i--) { + if (exe_dir[i] == '/') { + exe_dir[i+1] = '\0'; + break; + } + } + + exit_cleanup(); + // Start holder script + execv(strcat(exe_dir, "mpvpaper-holder"), argv); + + cflp_error("Failed to stop mpvpaper"); + exit(EXIT_FAILURE); +} + +// Allow pthread_cancel while sleeping +static void pthread_sleep(uint time) { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + sleep(time); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); +} +static void pthread_usleep(uint time) { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + usleep(time); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); +} + +static char *check_watch_list(char **list) { + + char pid_name[512] = {0}; + + for (uint i=0; list[i] != NULL; i++) { + strcpy(pid_name, "pidof "); + strcat(pid_name, list[i]); + strcat(pid_name, " > /dev/null"); + + // Stop if program is open + if (!system(pid_name)) { + return list[i]; + } + } + return NULL; +} + +static void *monitor_pauselist() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + bool is_paused = 0; + + while (halt_info.pauselist) { + if (!halt_info.is_paused) { + char *app; + while ((app = check_watch_list(halt_info.pauselist))) { + if (app && !is_paused) { + if (VERBOSE) + cflp_info("Pausing for %s", app); + mpv_command_async(mpv, 0, (const char*[]) {"set", "pause", "yes", NULL}); + is_paused = 1; + halt_info.is_paused += 1; + } + pthread_sleep(1); + } + if (is_paused) { + is_paused = 0; + halt_info.is_paused -= 1; + } + } + pthread_sleep(1); + } + pthread_exit(NULL); +} + +static void *monitor_stoplist() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (halt_info.stoplist) { + char *app = check_watch_list(halt_info.stoplist); + if (app) { + if (VERBOSE) + cflp_info("Stopping for %s", app); + stop_mpvpaper(); + } + pthread_sleep(1); + } + pthread_exit(NULL); +} + +static void *handle_auto_pause() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (halt_info.auto_pause) { + if (!halt_info.is_paused) { + time_t start_time = time(NULL); + bool is_paused = 0; + + // Set deadman switch timer + halt_info.frame_ready = 0; + while(!halt_info.frame_ready) { + if ((time(NULL) - start_time) >= 2 && !is_paused) { + if (VERBOSE) + cflp_info("Pausing because mpvpaper is hidden"); + mpv_command_async(mpv, 0, (const char*[]) {"set", "pause", "yes", NULL}); + is_paused = 1; + halt_info.is_paused += 1; + } + pthread_usleep(10000); + } + if (is_paused) { + is_paused = 0; + halt_info.is_paused -= 1; + } + } + pthread_sleep(1); + } + pthread_exit(NULL); +} + +static void *handle_auto_stop() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (halt_info.auto_stop) { + time_t start_time = time(NULL); + + // Set deadman switch timer + halt_info.frame_ready = 0; + while(!halt_info.frame_ready) { + if ((time(NULL) - start_time) >= 2) { + if (VERBOSE) + cflp_info("Stopping because mpvpaper is hidden"); + stop_mpvpaper(); + break; + } + pthread_usleep(10000); + } + pthread_sleep(1); + } + pthread_exit(NULL); +} + +static void *handle_mpv_events() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + bool mpv_paused = 0; + + while (!halt_info.kill_render_loop) { + mpv_event* event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_SHUTDOWN || event->event_id == MPV_EVENT_IDLE) + exit_mpvpaper(0); + else if (event->event_id == MPV_EVENT_PAUSE) { + mpv_paused = 1; + // User paused + if (!halt_info.is_paused) + halt_info.is_paused += 1; + } + else if (event->event_id == MPV_EVENT_UNPAUSE) { + mpv_paused = 0; + halt_info.is_paused = 0; + } + + if (!halt_info.is_paused && mpv_paused) { + mpv_command_async(mpv, 0, (const char*[]) {"set", "pause", "no", NULL}); + } + + pthread_usleep(10000); + } + pthread_exit(NULL); +} + +static void init_threads() { + uint id = 0; + + pthread_create(&threads[id], NULL, handle_mpv_events, NULL); + id++; + + // Thread for monitoring if mpvpaper is hidden + if (halt_info.auto_pause) { + pthread_create(&threads[id], NULL, handle_auto_pause, NULL); + id++; + } + else if (halt_info.auto_stop) { + pthread_create(&threads[id], NULL, handle_auto_stop, NULL); + id++; + } + + // Threads for monitoring watch lists + if (halt_info.pauselist) { + pthread_create(&threads[id], NULL, monitor_pauselist, NULL); + id++; + } + if (halt_info.stoplist) { + pthread_create(&threads[id], NULL, monitor_stoplist, NULL); + id++; + } +} + static void *get_proc_address_mpv(void *ctx, const char *name){ (void) ctx; return eglGetProcAddress(name); @@ -109,16 +403,42 @@ mpv = mpv_create(); if (!mpv) { cflp_error("Failed creating mpv context"); - exit(EXIT_FAILURE); + exit_mpvpaper(1); } + // Enable user control through terminal by default + mpv_set_option_string(mpv, "input-default-bindings", "yes"); + mpv_set_option_string(mpv, "input-terminal", "yes"); + mpv_set_option_string(mpv, "terminal", "yes"); + + // Load user configs + const char *home_dir = getenv("HOME"); + char *config_path = calloc(strlen(home_dir)+1 + 30, sizeof(char)); + strcpy(config_path, home_dir); + + char loaded_configs[50] = ""; + + strcpy(config_path+strlen(home_dir), "/.config/mpv/mpv.conf"); + if (mpv_load_config_file(mpv, config_path) == 0) + strcat(loaded_configs, "mpv.conf "); + strcpy(config_path+strlen(home_dir), "/.config/mpv/input.conf"); + if (mpv_load_config_file(mpv, config_path) == 0) + strcat(loaded_configs, "input.conf "); + strcpy(config_path+strlen(home_dir), "/.config/mpv/fonts.conf"); + if(mpv_load_config_file(mpv, config_path) == 0) + strcat(loaded_configs, "fonts.conf "); + free(config_path); + + if (VERBOSE && strcmp(loaded_configs, "")) + cflp_info("Loaded [ %s] user configs from \"~/.config/mpv/\"", loaded_configs); + // Set mpv_options passed - mpv_set_option_string(mpv, "include", "/tmp/mpvpaper.conf"); + mpv_load_config_file(mpv, "/tmp/mpvpaper.conf"); remove("/tmp/mpvpaper.conf"); if (mpv_initialize(mpv) < 0) { cflp_error("mpv init failed"); - exit(EXIT_FAILURE); + exit_mpvpaper(1); } // Have mpv render onto egl context mpv_render_param params[] = { @@ -131,16 +451,33 @@ if (mpv_render_context_create(&mpv_glcontext, mpv, params) < 0) cflp_error("Failed to initialize mpv GL context"); - const char* cmd[] = {"loadfile", video_path, NULL}; - mpv_command_async(mpv, 0, cmd); + // Restore video position after auto stop event + char* default_start = NULL; + if (halt_info.save_info) { + char time_pos[10]; + char playlist_pos[10]; + sscanf(halt_info.save_info, "%s %s", time_pos, playlist_pos); + + // Save default start pos + default_start = mpv_get_property_string(mpv, "start"); + // Restore video position + mpv_command_async(mpv, 0, (const char*[]) {"set", "start", time_pos, NULL}); + // Recover playlist pos, that is if it's not shuffled... + mpv_command_async(mpv, 0, (const char*[]) {"set", "playlist-start", playlist_pos, NULL}); + } + + mpv_command_async(mpv, 0, (const char*[]) {"loadfile", video_path, NULL}); mpv_event* event = mpv_wait_event(mpv, 1); while (event->event_id != MPV_EVENT_FILE_LOADED){ event = mpv_wait_event(mpv, 1); } - if (VERBOSE) { + if (VERBOSE) cflp_info("Loaded %s", video_path); - } + + // Return start pos to default + if (default_start) + mpv_command_async(mpv, 0, (const char*[]) {"set", "start", default_start, NULL}); } static void init_egl(struct display_output *output) { @@ -172,7 +509,7 @@ {0, 0} }; egl_context = NULL; - for (int i = 0; gl_versions[i].major > 0; i++) { + for (uint i = 0; gl_versions[i].major > 0; i++) { const EGLint ctx_attrib[] = { EGL_CONTEXT_MAJOR_VERSION, gl_versions[i].major, EGL_CONTEXT_MINOR_VERSION, gl_versions[i].major, @@ -188,7 +525,7 @@ } if (!egl_context) { cflp_error("Failed to create EGL context"); - exit(EXIT_FAILURE); + exit_mpvpaper(1); } } @@ -236,7 +573,9 @@ init_egl(output); if (!mpv) { init_mpv(output); + init_threads(); } + if (egl_display && mpv_glcontext) { // Start render loop render(output); @@ -262,7 +601,6 @@ // Empty input region struct wl_region *input_region = wl_compositor_create_region(output->state->compositor); - wl_surface_set_input_region(output->surface, input_region); wl_region_destroy(input_region); @@ -279,7 +617,6 @@ zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); wl_surface_commit(output->surface); - } static void xdg_output_handle_name(void *data, @@ -301,7 +638,7 @@ char *paren = strrchr(description, '('); if (paren) { size_t length = paren - description; - output->identifier = malloc(length); + output->identifier = calloc(length, sizeof(char)); if (!output->identifier) { cflp_warning("Failed to allocate output identifier"); return; @@ -384,12 +721,51 @@ .global_remove = handle_global_remove, }; +static char **get_watch_list(char *path_name) { + + FILE *file = fopen(path_name, "r"); + if (file) { + // Get alloc size + fseek(file, 0L, SEEK_END); + char **list = calloc(ftell(file) + 1, sizeof(char)); + rewind(file); + + // Read lines + char app[512]; + for (uint i=0; fscanf(file, "%s", app) == 1; i++) { + list[i] = strdup(app); + } + + fclose(file); + return list; + } + return NULL; +} + +static void set_watch_lists() { + const char *home_dir = getenv("HOME"); + + char *pause_path = calloc(strlen(home_dir)+1 + strlen("/.config/mpvpaper/pauselist")+1, sizeof(char)); + strcpy(pause_path, home_dir); + strcat(pause_path, "/.config/mpvpaper/pauselist"); + halt_info.pauselist = get_watch_list(pause_path); + free(pause_path); + + char *stop_path = calloc(strlen(home_dir)+1 + strlen("/.config/mpvpaper/stoplist")+1, sizeof(char)); + strcpy(stop_path, home_dir); + strcat(stop_path, "/.config/mpvpaper/stoplist"); + halt_info.stoplist = get_watch_list(stop_path); + free(stop_path); +} + static void parse_command_line(int argc, char **argv, struct wl_state *state) { static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {"fork", no_argument, NULL, 'f'}, + {"auto-pause", no_argument, NULL, 'p'}, + {"auto-stop", no_argument, NULL, 's'}, {"layer", required_argument, NULL, 'l'}, {"mpv-options", required_argument, NULL, 'o'}, {0, 0, 0, 0} @@ -397,20 +773,25 @@ const char *usage = "Usage: mpvpaper [options] <output> <url|path filename>\n" + "Example: mpvpaper -o \"no-audio loop\" DP-2 /path/to/video\n" "Options:\n" - "--help\t\t-h\tDisplays this help message\n" - "--verbose\t-v\tBe more verbose\n" - "--fork\t\t-f\tForks mpvpaper so you can close the terminal\n" - "--layer\t\t-l\tSpecifies shell surface layer to run on (background by default)\n" - "--mpv-options\t-o\tForwards mpv options (Must be within quotes\"\")\n"; + "--help -h Displays this help message\n" + "--verbose -v Be more verbose\n" + "--fork -f Forks mpvpaper so you can close the terminal\n" + "--auto-pause -p Automagically pause mpv when the wallpaper is hidden\n" + " This saves CPU usage, more or less, seamlessly\n" + "--auto-stop -s Automagically stop mpv when the wallpaper is hidden\n" + " This saves CPU/RAM usage, although more abruptly\n" + "--layer -l Specifies shell surface layer to run on (background by default)\n" + "--mpv-options -o Forwards mpv options (Must be within quotes\"\")\n"; if(argc > 2) { - char* layer_name; - char* mpv_options; + char *layer_name; + char *mpv_options; char opt; - while((opt = getopt_long(argc, argv, "hvfl:o:", long_options, NULL)) != -1) { + while((opt = getopt_long(argc, argv, "hvfpsl:o:Z:", long_options, NULL)) != -1) { switch (opt) { case 'h': @@ -427,6 +808,14 @@ fclose(stderr); fclose(stdin); break; + case 'p': + halt_info.auto_pause = 1; + halt_info.auto_stop = 0; + break; + case 's': + halt_info.auto_stop = 1; + halt_info.auto_pause = 0; + break; case 'l': layer_name = strdup(optarg); if(layer_name == NULL) { @@ -440,20 +829,26 @@ } else if(strcasecmp(layer_name, "overlay") == 0) { state->surface_layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; } else { - cflp_error("%s is not a shell surface layer", layer_name); + cflp_error("%s is not a shell surface layer\n" + "Your options are: top, bottom, background and overlay", layer_name); exit(EXIT_FAILURE); } break; case 'o': - mpv_options = optarg; + mpv_options = strdup(optarg); // Forward to a tmp file so mpv can parse options for (int i = 0; i < (int)strlen(mpv_options); i++) { - if (mpv_options[i] == ' ') mpv_options[i] = '\n'; + if (mpv_options[i] == ' ') + mpv_options[i] = '\n'; } FILE* file = fopen("/tmp/mpvpaper.conf", "w"); fputs(mpv_options, file); fclose(file); break; + + case 'Z': // Hidden option to recover video pos after stopping + halt_info.save_info = strdup(optarg); + break; } } if(optind + 1 >= argc) { @@ -472,14 +867,34 @@ fprintf(stderr, "%s", usage); exit(EXIT_FAILURE); } - } + int main(int argc, char **argv) { + signal(SIGINT, handle_signal); struct wl_state state = {0}; wl_list_init(&state.outputs); parse_command_line(argc, argv, &state); + set_watch_lists(); + + // Copy argv + int argv_alloc_size = 0; + for(int i=0; argv[i] != NULL; i++) { + argv_alloc_size += strlen(argv[i])+1; + } + halt_info.argv_copy = calloc(argv_alloc_size, sizeof(char)); + + int j = 0; + for(int i=0; i < argc; i++) { + if (strcmp(argv[i], "-Z") == 0) { // Remove hidden opt + i++; // Skip optind + } + else { + halt_info.argv_copy[j] = strdup(argv[i]); + j++; + } + } state.display = wl_display_connect(NULL); if (!state.display) { @@ -514,7 +929,7 @@ } state.run_display = 1; - while (wl_display_dispatch(state.display) != -1 && state.run_display) { + while (wl_display_dispatch(state.display) != -1) { // NOP } _______________________________________________ openSUSE Commits mailing list -- commit@lists.opensuse.org To unsubscribe, email commit-le...@lists.opensuse.org List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette List Archives: https://lists.opensuse.org/archives/list/commit@lists.opensuse.org