diff --git a/libvisual-plugins/configure.ac b/libvisual-plugins/configure.ac index 903dd5073..3447e48e6 100644 --- a/libvisual-plugins/configure.ac +++ b/libvisual-plugins/configure.ac @@ -8,7 +8,7 @@ m4_define([lv_plugins_version_micro], [1]) m4_define([lv_plugins_version], [lv_plugins_version_major.lv_plugins_version_minor.lv_plugins_version_micro]) m4_define([lv_plugins_version_suffix], [lv_plugins_version_major.lv_plugins_version_minor]) -AC_INIT(Libvisual plugins, lv_plugins_version, ds@nerds-incorporated.org) +AC_INIT([Libvisual plugins], [lv_plugins_version], [https://github.com/Libvisual/libvisual/issues], [libvisual-plugins], [https://github.com/Libvisual/libvisual]) AM_INIT_AUTOMAKE([1.7.0 dist-bzip2]) AC_CONFIG_SRCDIR([config.h.in]) diff --git a/libvisual/Makefile.am b/libvisual/Makefile.am index a3cf93bfd..c9177e86b 100644 --- a/libvisual/Makefile.am +++ b/libvisual/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to generate a Makefile.in -SUBDIRS = libvisual po examples +SUBDIRS = libvisual po examples tools/lv-tool PKG_CONFIG_FILE = libvisual-$(LIBVISUAL_VERSION_SUFFIX).pc diff --git a/libvisual/configure.ac b/libvisual/configure.ac index 0253fdbad..cdd09f9b9 100644 --- a/libvisual/configure.ac +++ b/libvisual/configure.ac @@ -8,7 +8,7 @@ m4_define([libvisual_version_micro], [1]) m4_define([libvisual_version], [libvisual_version_major.libvisual_version_minor.libvisual_version_micro]) -AC_INIT([Libvisual Library], [libvisual_version], [synap@nerds-incorporated.org], [libvisual]) +AC_INIT([Libvisual Library], [libvisual_version], [https://github.com/Libvisual/libvisual/issues], [libvisual], [https://github.com/Libvisual/libvisual]) AM_INIT_AUTOMAKE([1.7.0 dist-bzip2]) dnl TODO: Add these into lv_config.h (or maybe lv_version.h) @@ -374,6 +374,18 @@ AC_SUBST(VISUAL_LIBS) dnl ****************************** dnl Arguments to specify certain features. dnl ****************************** +AC_ARG_ENABLE([lv-tool], + AC_HELP_STRING([--disable-lv-tool], + [Do not build lv-tool @<:@default=enabled@:>@]), + [lv_tool_enabled=$enableval], + [lv_tool_enabled=yes]) +AC_MSG_CHECKING([wheter to build tool programs]) +if test x$lv_tool_enabled = xyes ; then + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + AC_ARG_ENABLE([examples], AC_HELP_STRING([--disable-examples], [Do not build example programs @<:@default=enabled@:>@]), @@ -382,19 +394,27 @@ AC_ARG_ENABLE([examples], AC_MSG_CHECKING([wheter to build example programs]) if test x$examples = xyes ; then AC_MSG_RESULT([yes]) - # Find SDL to build the examples. +else + AC_MSG_RESULT([no]) +fi + +if test x$lv_tool_enabled = xyes -o x$examples = xyes ; then + # Find SDL 1.x.x AC_PATH_TOOL(SDL_CONFIG, [sdl-config]) if test x$SDL_CONFIG = x ; then AC_MSG_ERROR([*** sdl-config not found. If you have installed from binaries, probably you have not - installed SDL development package. - See http://www.libsdl.org]) + installed SDL 1.x.x development package. + See https://www.libsdl.org/]) fi # On success, this will set and substitute SDL_CFLAGS and SDL_LIBS AM_PATH_SDL(1.2.0,,AC_MSG_ERROR([*** SDL >= 1.2.0 not installed - please install first])) - AC_SUBST([EXAMPLES], ['simplesdl morphsdl']) -else - AC_MSG_RESULT([no]) + if test x$examples = xyes ; then + AC_SUBST([EXAMPLES], ['simplesdl morphsdl']) + fi + if test x$lv_tool_enabled = xyes ; then + AC_SUBST([TOOLS_LV_TOOL], ['lv-tool']) + fi fi dnl ****************************** @@ -846,6 +866,7 @@ Makefile libvisual.pc libvisual/Makefile examples/Makefile +tools/lv-tool/Makefile ]) # # For now this are not included in the package diff --git a/libvisual/po/POTFILES.in b/libvisual/po/POTFILES.in index fed8b6dd5..baf8a9035 100644 --- a/libvisual/po/POTFILES.in +++ b/libvisual/po/POTFILES.in @@ -18,3 +18,4 @@ libvisual/lv_plugin.c libvisual/lv_thread.c libvisual/lv_transform.c libvisual/lv_video.c +tools/lv-tool/lv-tool.cpp diff --git a/libvisual/po/de.po b/libvisual/po/de.po index 878e98429..60f4acfce 100644 --- a/libvisual/po/de.po +++ b/libvisual/po/de.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: libvisual 0.4.0-2.2\n" "Report-Msgid-Bugs-To: https://github.com/Libvisual/libvisual/issues/\n" -"POT-Creation-Date: 2006-03-21 02:43+0800\n" +"POT-Creation-Date: 2023-01-15 01:44+0100\n" "PO-Revision-Date: 2017-08-10 18:46+GMT\n" "Last-Translator: Chris Leick \n" "Language-Team: German \n" @@ -900,5 +900,9 @@ msgstr "" msgid "Invalid depth passed to the scaler" msgstr "Ungültige Tiefe an Scaler übergeben" +#: tools/lv-tool/lv-tool.cpp:743 tools/lv-tool/lv-tool.cpp:840 +msgid "lv-tool" +msgstr "" + #~ msgid " bytes of memory" #~ msgstr " Bytes des Speichers" diff --git a/libvisual/po/es_AR.po b/libvisual/po/es_AR.po index ee86edcb8..587b5d6ed 100644 --- a/libvisual/po/es_AR.po +++ b/libvisual/po/es_AR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Libvisual Library 0.4.0\n" "Report-Msgid-Bugs-To: https://github.com/Libvisual/libvisual/issues/\n" -"POT-Creation-Date: 2006-03-21 02:43+0800\n" +"POT-Creation-Date: 2023-01-15 01:44+0100\n" "PO-Revision-Date: 2005-03-20 17:09-0300\n" "Last-Translator: \n" "Language-Team: Spanish\n" @@ -918,6 +918,10 @@ msgstr "" msgid "Invalid depth passed to the scaler" msgstr "Profundidad pasada al escalador inválida" +#: tools/lv-tool/lv-tool.cpp:743 tools/lv-tool/lv-tool.cpp:840 +msgid "lv-tool" +msgstr "" + #~ msgid " bytes of memory" #~ msgstr " bytes de memoria" diff --git a/libvisual/po/es_ES.po b/libvisual/po/es_ES.po index 45085de51..55e3f0f22 100644 --- a/libvisual/po/es_ES.po +++ b/libvisual/po/es_ES.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Libvisual Library 0.4.0\n" "Report-Msgid-Bugs-To: https://github.com/Libvisual/libvisual/issues/\n" -"POT-Creation-Date: 2006-03-21 02:43+0800\n" +"POT-Creation-Date: 2023-01-15 01:44+0100\n" "PO-Revision-Date: 2005-03-20 17:09-0300\n" "Last-Translator: \n" "Language-Team: Spanish\n" @@ -918,6 +918,10 @@ msgstr "" msgid "Invalid depth passed to the scaler" msgstr "Profundidad pasada al escalador inválida" +#: tools/lv-tool/lv-tool.cpp:743 tools/lv-tool/lv-tool.cpp:840 +msgid "lv-tool" +msgstr "" + #~ msgid " bytes of memory" #~ msgstr " bytes de memoria" diff --git a/libvisual/po/libvisual-0.4.pot b/libvisual/po/libvisual-0.4.pot index cf34dd540..2007894be 100644 --- a/libvisual/po/libvisual-0.4.pot +++ b/libvisual/po/libvisual-0.4.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: libvisual 0.4.1\n" "Report-Msgid-Bugs-To: https://github.com/Libvisual/libvisual/issues/\n" -"POT-Creation-Date: 2006-03-21 02:43+0800\n" +"POT-Creation-Date: 2023-01-15 01:44+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -870,3 +870,7 @@ msgstr "" #: libvisual/lv_video.c:2775 libvisual/lv_video.c:2922 msgid "Invalid depth passed to the scaler" msgstr "" + +#: tools/lv-tool/lv-tool.cpp:743 tools/lv-tool/lv-tool.cpp:840 +msgid "lv-tool" +msgstr "" diff --git a/libvisual/tools/lv-tool/Makefile.am b/libvisual/tools/lv-tool/Makefile.am new file mode 100644 index 000000000..5c5ab8320 --- /dev/null +++ b/libvisual/tools/lv-tool/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to generate a Makefile.in + +bin_PROGRAMS = $(TOOLS_LV_TOOL) + +EXTRA_PROGRAMS = lv-tool + +lv_tool_SOURCES = lv-tool.cpp +lv_tool_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libvisual +lv_tool_LDADD = $(SDL_LIBS) ../../libvisual/libvisual-@LIBVISUAL_VERSION_SUFFIX@.la diff --git a/libvisual/tools/lv-tool/lv-tool.cpp b/libvisual/tools/lv-tool/lv-tool.cpp new file mode 100644 index 000000000..030a590a4 --- /dev/null +++ b/libvisual/tools/lv-tool/lv-tool.cpp @@ -0,0 +1,938 @@ +// lv-tool - Libvisual commandline tool +// +// Copyright (C) 2012-2023 Libvisual team +// 2004-2006 Dennis Smit +// +// Authors: Daniel Hiepler +// Chong Kai Xiong +// Dennis Smit +// Scott Sibley +// Sebastian Pipping +// +// This file is part of lv-tool. +// +// lv-tool is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// lv-tool is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with lv-tool. If not, see . + +#include "config.h" +#include "gettext.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Defaults +#define DEFAULT_ACTOR_GL "lv_gltest" +#define DEFAULT_ACTOR_NONGL "lv_analyzer" +#define DEFAULT_INPUT "debug" +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 200 +#define DEFAULT_FPS 60 +#define DEFAULT_COLOR_DEPTH 0 + +// A thin C++ compatibility layer to reduce the diff to the master branch +namespace LV { + class Bin { + VisBin * m_bin; + + public: + Bin() : m_bin(visual_bin_new()) { + visual_log_return_if_fail( m_bin != nullptr ); + } + + bool actor_available(std::string const& actor_name) { + return visual_actor_valid_by_name(actor_name.c_str()); + } + + bool connect (std::string const& actor_name, std::string const& input_name) { + if (! visual_input_valid_by_name(input_name.c_str())) + return false; + + if (! actor_available(actor_name)) + return false; + + return visual_bin_connect_by_names( + m_bin, + const_cast(actor_name.c_str()), + const_cast(input_name.c_str())) >= 0; + } + + VisActor * get_actor () const { + return visual_bin_get_actor(m_bin); + } + + void set_supported_depth(VisVideoDepth depthflag) { + visual_bin_set_supported_depth(m_bin, depthflag); + }; + + int get_depth () const { + return visual_bin_get_depth(m_bin); + } + + void set_depth (VisVideoDepth depth) { + visual_bin_set_depth(m_bin, depth); + } + + void set_video(VisVideo * video) { + visual_bin_set_video(m_bin, video); + } + + void realize() { + visual_bin_realize(m_bin); + } + + void sync(bool noevent) { + visual_bin_sync(m_bin, noevent); + } + + bool depth_changed() { + return visual_bin_depth_changed(m_bin) == TRUE; + } + + void run() { + visual_bin_run(m_bin); + }; + + void switch_actor (std::string const& actname) { + visual_bin_switch_actor_by_name(m_bin, const_cast(actname.c_str())); + } + }; + + class Time { + Uint32 m_moment; + + Time(Uint32 moment) : m_moment(moment) { + } + + public: + Time() : m_moment(0) { + } + + static Time now() { + return Time(SDL_GetTicks()); + } + + Time operator-(Time & other) const { + return Time(this->m_moment - other.m_moment); + } + + uint64_t to_usecs() const { + return this->m_moment * 1000; + }; + }; +} + +namespace { + + std::string actor_name = DEFAULT_ACTOR_NONGL; + std::string input_name = DEFAULT_INPUT; + std::string exclude_actors; + + unsigned int width = DEFAULT_WIDTH; + unsigned int height = DEFAULT_HEIGHT; + unsigned int color_depth = DEFAULT_COLOR_DEPTH; + + unsigned int frame_rate = DEFAULT_FPS; + unsigned int frame_count = 0; + unsigned int actor_switch_after_frames = 0; + unsigned int actor_switch_framecount = 0; + + bool have_seed = 0; + uint32_t seed = 0; + + volatile std::sig_atomic_t terminate_process = false; + + enum class CycleDir + { + PREV, + NEXT + }; + + /** Class to manage LV's lifecycle */ + class Libvisual + { + public: + + Libvisual (int& argc, char**& argv) + { + visual_init (&argc, &argv); + } + + ~Libvisual () + { + visual_quit (); + } + }; + + class Display + { + SDL_Surface * m_screen; + VisVideoDepth m_requested_depth; + VisVideo * m_screen_video; + bool m_resizable; + + void destroy() { + if (m_screen_video) { + visual_object_unref(VISUAL_OBJECT(m_screen_video)); + m_screen_video = nullptr; + } + if (m_screen) { + // NOTE: The surface returned by SDL_SetVideoMode is either + // freed by the next call to SDL_SetVideoMode or + // when calling SDL_QUIT. So nothing to free here. + // https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsetvideomode.html + } + } + + public: + Display() : m_screen(nullptr), m_requested_depth(VISUAL_VIDEO_DEPTH_NONE), m_screen_video(nullptr), + m_resizable(false) { + } + + ~Display() { + destroy(); + + if (SDL_WasInit (SDL_INIT_VIDEO)) { + SDL_Quit(); + } + } + + VisVideo * create (VisVideoDepth depth, + VisVideoAttributeOptions const* vidoptions, + unsigned int width, + unsigned int height, + bool resizable) { + int videoflags = 0; + + if (resizable) + videoflags |= SDL_RESIZABLE; + + if (!SDL_WasInit (SDL_INIT_VIDEO)) { + if (SDL_Init (SDL_INIT_VIDEO) == -1) { + visual_log (VISUAL_LOG_ERROR, "Unable to init SDL VIDEO: %s", SDL_GetError ()); + return nullptr; + } + } + + m_resizable = resizable; + m_requested_depth = depth; + + destroy(); + + int bpp; + + if (depth == VISUAL_VIDEO_DEPTH_GL) { + SDL_VideoInfo const* videoinfo = SDL_GetVideoInfo (); + + if (!videoinfo) { + return nullptr; + } + + videoflags |= SDL_OPENGL; + + if (videoinfo->hw_available) + videoflags |= SDL_HWSURFACE; + + // TODO apply vidoptions here + + bpp = videoinfo->vfmt->BitsPerPixel; + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + } + else + { + bpp = 8 * visual_video_bpp_from_depth (depth); + } + + // Create surface + m_screen = SDL_SetVideoMode (width, height, bpp, videoflags); + visual_log_return_val_if_fail( m_screen != nullptr, nullptr ); + + // Recreate video object + m_screen_video = visual_video_new(); + visual_log_return_val_if_fail( m_screen_video != nullptr, nullptr ); + visual_video_set_attributes(m_screen_video, m_screen->w, m_screen->h, m_screen->pitch, depth); + visual_video_allocate_buffer(m_screen_video); + + SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + return m_screen_video; + } + + void drain_events(VisEventQueue & eventqueue) { + // NOTE: SDL_VIDEORESIZE calls create(..) here as well + SDL_Event event; + while (SDL_PollEvent (&event)) { + switch (event.type) { + case SDL_KEYUP: + visual_event_queue_add_keyboard (&eventqueue, + VisKey(event.key.keysym.sym), + VisKeyMod(event.key.keysym.mod), + VISUAL_KEY_UP); + break; + + case SDL_KEYDOWN: + visual_event_queue_add_keyboard (&eventqueue, VisKey(event.key.keysym.sym), + VisKeyMod(event.key.keysym.mod), + VISUAL_KEY_DOWN); + break; + + case SDL_VIDEORESIZE: + visual_event_queue_add_resize (&eventqueue, NULL, event.resize.w, event.resize.h); + // TODO non-NULL vidoptions here? + create (m_requested_depth, nullptr, event.resize.w, event.resize.h, m_resizable); + break; + + case SDL_MOUSEMOTION: + visual_event_queue_add_mousemotion (&eventqueue, event.motion.x, + event.motion.y); + break; + + case SDL_MOUSEBUTTONDOWN: + visual_event_queue_add_mousebutton (&eventqueue, event.button.button, + VISUAL_MOUSE_DOWN, + event.button.x, + event.button.y); + break; + + case SDL_MOUSEBUTTONUP: + visual_event_queue_add_mousebutton (&eventqueue, event.button.button, + VISUAL_MOUSE_UP, + event.button.x, + event.button.y); + break; + + case SDL_QUIT: + visual_event_queue_add_quit (&eventqueue, 0); + break; + + default: + break; + } + } + } + + VisVideo * get_video() const { + visual_log_return_val_if_fail( m_screen_video != nullptr, nullptr ); + return m_screen_video; + } + + static void set_title(std::string const& title) { + SDL_WM_SetCaption (title.c_str(), nullptr); + } + + void update_all() { + visual_log_return_if_fail( m_screen != nullptr ); + + if (m_screen->format->BitsPerPixel == 8) { + visual_log_return_if_fail( m_screen_video != nullptr ); + VisPalette * const pal = m_screen_video->pal; + + if (pal != nullptr && pal->ncolors <= 256) { + SDL_Color colors[256]; + + for (int i = 0; i < 256; i++) { + colors[i].r = pal->colors[i].r; + colors[i].g = pal->colors[i].g; + colors[i].b = pal->colors[i].b; + } + + SDL_SetColors (m_screen, colors, 0, 256); + } + } + + if (m_requested_depth == VISUAL_VIDEO_DEPTH_GL) { + SDL_GL_SwapBuffers(); + } else { + visual_mem_copy(m_screen->pixels, + visual_video_get_pixels(m_screen_video), + visual_video_get_size(m_screen_video)); + SDL_Flip(m_screen); + } + } + + void lock () { + visual_log_return_if_fail( m_screen != nullptr ); + if (SDL_MUSTLOCK (m_screen)) + SDL_LockSurface (m_screen); + } + + void unlock () { + visual_log_return_if_fail( m_screen != nullptr ); + if (SDL_MUSTLOCK (m_screen)) + SDL_UnlockSurface (m_screen); + } + }; + + class DisplayLock + { + Display * m_display; + + public: + DisplayLock(Display & display) : m_display(&display) { + visual_log_return_if_fail( m_display != nullptr ); + m_display->lock(); + } + + ~DisplayLock() { + m_display->unlock(); + } + }; + + /** print info about libvisual plugin */ + void print_plugin_info(VisPluginInfo const& info) + { + printf("Plugin: \"%s\" (%s)\n" + "\tAuthor: %s\n\tVersion: %s\t\n" + "\t%s - %s\n\n", + info.name, info.plugname, + info.author, info.version, + info.about, info.help); + } + + + /** print help for plugins */ + void print_plugin_help() + { + VisList * const all_plugins = visual_plugin_get_registry(); + + VisListEntry *le = NULL; + VisPluginRef *ref = NULL; + + printf("===== INPUTS =====\n"); + VisList * const inputs = visual_plugin_registry_filter(all_plugins, "Libvisual:core:input"); + ref = static_cast(visual_list_next (inputs, &le)); + + // print inputs + if(! ref) + { + std::cerr << "No input plugins found\n"; + } + else + { + do { + visual_plugin_load(ref); + print_plugin_info(*(ref->info)); + ref = static_cast(visual_list_next (inputs, &le)); + } while (ref != nullptr); + } + + printf("===== ACTORS =====\n"); + VisList * const actors = visual_plugin_registry_filter(all_plugins, "Libvisual:core:actor"); + ref = static_cast(visual_list_next (actors, &le)); + + // print actors + if(! ref) + { + std::cerr << "No actor plugins found\n"; + } + else + { + do { + visual_plugin_load(ref); + print_plugin_info(*(ref->info)); + ref = static_cast(visual_list_next (inputs, &le)); + } while (ref != nullptr); + } + } + + /** print commandline help */ + void print_help(std::string const& name) + { + printf("Usage: %s [options]\n\n" + "Valid options:\n" + "\t--help\t\t\t-h\t\tThis help text\n" + "\t--plugin-help\t\t-p\t\tList of installed plugins + information\n" + "\t--verbose\t\t-v\t\tIncrease log verbosity (may be used multiple times)\n" + "\t--dimensions \t-D \tRequest dimensions from display driver (no guarantee) [%dx%d]\n" + "\t--depth \t-c \tSet output colour depth (automatic by default)\n" + "\t--input \t\t-i \tUse this input plugin [%s]\n" + "\t--actor \t\t-a \tUse this actor plugin [%s/%s]\n" + "\t--seed \t\t-s \tSet random seed\n" + "\t--fps \t\t-f \t\tLimit output to n frames per second (if display driver supports it) [%d]\n" + "\t--framecount \t-F \t\tOutput n frames, then exit.\n" + "\t--switch \t\t-S \t\tSwitch actor after n frames.\n" + "\t--exclude \t-x \tProvide a list of actors to exclude.\n" + "\n", + name.c_str (), + width, height, + input_name.c_str (), + DEFAULT_ACTOR_GL, + DEFAULT_ACTOR_NONGL, + frame_rate); + } + + + /** + * parse commandline arguments + * + * @param argc from main() + * @param argv from main() + * @result 0 upon success, <0 upon failure, >0 if app should exit without error */ + int parse_args(int argc, char *argv[]) + { + static struct option loptions[] = { + {"help", no_argument, 0, 'h'}, + {"plugin-help", no_argument, 0, 'p'}, + {"verbose", no_argument, 0, 'v'}, + {"dimensions", required_argument, 0, 'D'}, + {"input", required_argument, 0, 'i'}, + {"actor", required_argument, 0, 'a'}, + {"fps", required_argument, 0, 'f'}, + {"seed", required_argument, 0, 's'}, + {"exclude", required_argument, 0, 'x'}, + {"framecount", required_argument, 0, 'F'}, + {"switch", required_argument, 0, 'S'}, + {"depth", required_argument, 0, 'c'}, + {0, 0, 0, 0 } + }; + + int index, argument; + + while ((argument = getopt_long(argc, argv, "hpvD:i:a:f:s:F:S:x:c:", loptions, &index)) >= 0) { + + switch(argument) { + // --help + case 'h': { + print_help(argv[0]); + return 1; + } + + // --plugin-help + case 'p': { + print_plugin_help(); + return 1; + } + + // --verbose + case 'v': { + VisLogVerboseness level = visual_log_get_verboseness (); + level = VisLogVerboseness (int (level) + 1); + if (int (level) > VISUAL_LOG_VERBOSENESS_HIGH) { + visual_log_set_verboseness (VISUAL_LOG_VERBOSENESS_HIGH); + } + else { + visual_log_set_verboseness (level); + } + break; + } + + // --dimensions + case 'D': { + if (std::sscanf (optarg, "%ux%u", &width, &height) != 2) + { + std::cerr << "Invalid dimensions: '" << optarg << "'. Use x (e.g. 320x200)\n"; + return -1; + } + break; + } + + // --depth + case 'c': { + if (std::sscanf (optarg, "%u", &color_depth) != 1 || + visual_video_depth_enum_from_value(color_depth) == VISUAL_VIDEO_DEPTH_ERROR) + { + std::cerr << "Invalid depth: '" << optarg << "'. Use integer value (e.g. 24)\n"; + return -1; + } + break; + } + + // --input + case 'i': { + // save name for later + input_name = optarg; + break; + } + + // --actor + case 'a': { + // save name for later + actor_name = optarg; + break; + } + + // --fps + case 'f': { + // set frame_rate + std::sscanf(optarg, "%u", &frame_rate); + break; + } + + // --seed + case 's': { + have_seed = true; + std::sscanf(optarg, "%d", &seed); + break; + } + + // --framecount + case 'F': { + // set framecount + std::sscanf(optarg, "%u", &frame_count); + break; + } + + // --switch + case 'S': { + // switch actor after n frames + std::sscanf(optarg, "%u", &actor_switch_after_frames); + break; + } + + // --exclude + case 'x': { + exclude_actors = optarg; + break; + } + + // invalid argument + case '?': { + print_help(argv[0]); + return -1; + } + + // unhandled arguments + default: + std::abort (); + } + } + + return 0; + } + + void handle_termination_signal (int signal) + { + (void)signal; + + terminate_process = true; + } + + /** + * Handle process termination signals. + */ + void setup_signal_handlers () + { + std::signal (SIGINT, handle_termination_signal); + std::signal (SIGTERM, handle_termination_signal); + } + + std::string cycle_actor_name (std::string const& name, CycleDir dir) + { + auto cycler = (dir == CycleDir::NEXT) ? visual_actor_get_next_by_name + : visual_actor_get_prev_by_name; + + auto new_name = cycler (name.c_str ()); + if (!new_name) { + new_name = cycler (nullptr); + } + + // FIXME: this won't work if an actor's name is used as part of + // another actor's name + if (exclude_actors.find (new_name) != std::string::npos) { + return cycle_actor_name (new_name, dir); + } + + return new_name; + } + +} // anonymous namespace + + +int main (int argc, char **argv) +{ + try { + // print warm welcome + std::cerr << "lv-tool" << " - " + << "Libvisual " << PACKAGE_VERSION + << " commandline tool - " + << PACKAGE_URL << "\n"; + + // setup signal handlers + setup_signal_handlers (); + + // default loglevel + visual_log_set_verboseness (VISUAL_LOG_VERBOSENESS_LOW); + + // initialize LV + Libvisual main {argc, argv}; + + // parse commandline arguments + int parse_result = parse_args (argc, argv); + if (parse_result < 0) { + throw std::runtime_error ("Failed to parse arguments"); + } + if (parse_result > 0) { + return EXIT_SUCCESS; + } + + // Set system-wide random seed + if (have_seed) { + visual_random_context_set_seed(&__lv_internal_random_context, seed); + } + + // create new VisBin for video output + LV::Bin bin; + bin.set_supported_depth(VISUAL_VIDEO_DEPTH_ALL); + + // Upgrade to a more appealing OpenGL actor if available + if (bin.actor_available(DEFAULT_ACTOR_GL)) { + actor_name = DEFAULT_ACTOR_GL; + } + + // Let the bin manage plugins. There's a bug otherwise. + if (!bin.connect(actor_name, input_name)) { + throw std::runtime_error ("Failed to start pipeline with actor '" + actor_name + "' and input '" + input_name + "'"); + } + + auto actor = bin.get_actor(); + + // Select output colour depth + + VisVideoDepth depth; + int depthflag = visual_actor_get_supported_depth(actor); + + // Pick the best display depth directly supported by non GL actor + if(depthflag != VISUAL_VIDEO_DEPTH_GL) + { + if (color_depth == 0) + { + depth = visual_video_depth_get_highest_nogl (depthflag); + } + // Pick user chosen colordepth + else + { + depth = visual_video_depth_enum_from_value (color_depth); + } + } + /* GL actor */ + else + { + depth = visual_video_depth_get_highest (depthflag); + } + + bin.set_depth (depth); + + auto vidoptions = visual_actor_get_video_attribute_options(actor); + + // initialize display + Display display; + + // create display + auto video = display.create(depth, vidoptions, width, height, true); + if(!video) { + throw std::runtime_error("Failed to setup display for rendering"); + } + + // Set the display title + display.set_title(_("lv-tool")); + + // put it all together + bin.set_video(video); + bin.realize(); + bin.sync(false); + bin.depth_changed(); + + // get a queue to handle events + VisEventQueue * const localqueue = visual_event_queue_new(); + visual_log_return_val_if_fail( localqueue != nullptr, EXIT_FAILURE ); + + // rendering statistics + uint64_t frames_drawn = 0; + + // frame rate control state + uint64_t const frame_period_us = frame_rate > 0 ? VISUAL_USEC_PER_SEC / frame_rate : 0; + LV::Time last_frame_time; + bool draw_frame = true; + + // main loop + bool running = true; + //bool visible = true; + + while (running) + { + // Check if process termination was signaled + if (terminate_process) { + std::cerr << "Received signal to terminate process, exiting..\n"; + return EXIT_SUCCESS; + } + + // Control frame rate + if (frame_rate > 0) { + if (frames_drawn > 0) { + draw_frame = (LV::Time::now () - last_frame_time).to_usecs () >= frame_period_us; + } + } + + if (draw_frame) { + DisplayLock lock {display}; + + // Draw audio data and render + bin.run(); + + // Display rendering + display.update_all (); + + // Record frame time + last_frame_time = LV::Time::now (); + + // All frames rendered? + frames_drawn++; + if (frame_count > 0 && frames_drawn >= frame_count) { + break; + } + + // switch actor? + if (actor_switch_after_frames > 0 && + frames_drawn >= actor_switch_after_frames + actor_switch_framecount) + { + actor_switch_framecount += actor_switch_after_frames; + + actor_name = cycle_actor_name (actor_name, CycleDir::NEXT); + std::cerr << "Switching to actor '" << actor_name << "'...\n"; + bin.switch_actor (actor_name); + } + } + + // Note: The static is to avoid stack-use-after-scope + static VisEvent ev; + + // Handle all events + display.drain_events(*localqueue); + + auto pluginqueue = visual_plugin_get_eventqueue (visual_actor_get_plugin(bin.get_actor())); + + while (visual_event_queue_poll(localqueue, &ev)) + { + if(ev.type != VISUAL_EVENT_RESIZE) + visual_event_queue_add(pluginqueue, &ev); + + switch (ev.type) + { + case VISUAL_EVENT_PARAM: + { + break; + } + + case VISUAL_EVENT_RESIZE: + { + DisplayLock lock {display}; + + width = ev.event.resize.width; + height = ev.event.resize.height; + + video = display.create(depth, vidoptions, width, height, true); + display.set_title(_("lv-tool")); + + bin.set_video (video); + bin.sync(false); + + break; + } + + case VISUAL_EVENT_MOUSEMOTION: + { + break; + } + + case VISUAL_EVENT_MOUSEBUTTONDOWN: + { + // switch to next actor + actor_name = cycle_actor_name (actor_name, CycleDir::NEXT); + + std::cerr << "Switching to actor '" << actor_name << "'...\n"; + bin.switch_actor (actor_name); + + break; + } + + case VISUAL_EVENT_MOUSEBUTTONUP: + { + break; + } + + case VISUAL_EVENT_KEYDOWN: + { + switch(ev.event.keyboard.keysym.sym) + { + case VKEY_ESCAPE: + { + running = false; + break; + } + + case VKEY_TAB: + { + break; + } + + default: + break; + } + + break; + } + + case VISUAL_EVENT_KEYUP: + { + break; + } + + case VISUAL_EVENT_QUIT: + { + running = false; + break; + } + + case VISUAL_EVENT_VISIBILITY: + { + //visible = ev.event.visibility.is_visible; + break; + } + + default: + { + break; + } + } + } + + if (bin.depth_changed()) + { + DisplayLock lock {display}; + + int depthflag = bin.get_depth(); + VisVideoDepth depth = visual_video_depth_get_highest(depthflag); + + display.create(depth, vidoptions, width, height, true); + + video = display.get_video(); + bin.set_video(video); + + bin.sync(true); + } + } + + return EXIT_SUCCESS; + } + catch (std::exception& error) { + std::cerr << error.what () << std::endl; + + return EXIT_FAILURE; + } +}