From 46e7baff745d35a2079d714ffb4284e85b74d1f7 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Fri, 20 Feb 2015 22:04:22 +0100 Subject: [PATCH] bash-completion: Implement in a different way. + Gets installed + Uses a helper tool, gst-completion-helper, installed in bash-completions/helpers. + Adds a common script that other tools can source. https://bugzilla.gnome.org/show_bug.cgi?id=744877 --- Makefile.am | 3 +- configure.ac | 24 ++++ data/Makefile.am | 4 + data/completions/gst-inspect-1.0 | 105 ++++++++++++++ data/completions/gst-launch-1.0 | 143 +++++++++++++++++++ libs/gst/helpers/.gitignore | 1 + libs/gst/helpers/Makefile.am | 16 +++ libs/gst/helpers/gst | 35 +++++ libs/gst/helpers/gst-completion-helper.c | 226 +++++++++++++++++++++++++++++++ pkgconfig/gstreamer-uninstalled.pc.in | 2 + pkgconfig/gstreamer.pc.in | 2 + 11 files changed, 560 insertions(+), 1 deletion(-) create mode 100644 data/Makefile.am create mode 100644 data/completions/gst-inspect-1.0 create mode 100644 data/completions/gst-launch-1.0 create mode 100644 libs/gst/helpers/gst create mode 100644 libs/gst/helpers/gst-completion-helper.c diff --git a/Makefile.am b/Makefile.am index ca6071d..a002b96 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,7 +11,8 @@ SUBDIRS = pkgconfig \ docs \ po \ m4 \ - common + common \ + data if BUILD_TOOLS SUBDIRS += tools diff --git a/configure.ac b/configure.ac index 6f765ef..e637239 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,29 @@ AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO([$PACKAGE_VERSION_NANO], ["${srcdir}/gstreamer.doap"], [$PACKAGE_VERSION_MAJOR.$PACKAGE_VERSION_MINOR.$PACKAGE_VERSION_MICRO]) +dnl check for bash completion +AC_ARG_WITH([bash-completion-dir], + AS_HELP_STRING([--with-bash-completion-dir[=PATH]], + [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]), + [], + [with_bash_completion_dir=yes]) + +if test "x$with_bash_completion_dir" = "xyes"; then + PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], + [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], + [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"]) + PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], + [BASH_HELPERS_DIR="`pkg-config --variable=helpersdir bash-completion`"], + [BASH_HELPERS_DIR="$datadir/bash-completion/helpers"]) +else + BASH_COMPLETION_DIR="$with_bash_completion_dir/completions" + BASH_HELPERS_DIR="$with_bash_completion_dir/helpers" +fi + +AC_SUBST([BASH_COMPLETION_DIR]) +AC_SUBST([BASH_HELPERS_DIR]) +AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) + dnl build static plugins or not AC_MSG_CHECKING([whether to build static plugins or not]) AC_ARG_ENABLE( @@ -783,6 +806,7 @@ AG_GST_CHECK_CHECKS() AC_CONFIG_FILES( Makefile +data/Makefile gst/Makefile gst/gstconfig.h gst/gstversion.h diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..7098492 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,4 @@ +if ENABLE_BASH_COMPLETION +bashcompletiondir = $(BASH_COMPLETION_DIR) +dist_bashcompletion_DATA = completions/gst-inspect-1.0 completions/gst-launch-1.0 +endif diff --git a/data/completions/gst-inspect-1.0 b/data/completions/gst-inspect-1.0 new file mode 100644 index 0000000..130b963 --- /dev/null +++ b/data/completions/gst-inspect-1.0 @@ -0,0 +1,105 @@ +# GStreamer +# Copyright (C) 2015 Mathieu Duponchelle +# +# bash/zsh completion support for gst-inspect +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +HELPERDIR="${BASH_SOURCE[0]%/*}/../helpers" + +if [[ ! -d "$HELPERDIR" ]]; then + HELPERDIR="$(pkg-config --variable=helpersdir gstreamer-1.0)" +else + HELPERDIR=`cd "$HELPERDIR"; pwd` +fi + +# Common definitions +. "$HELPERDIR"/gst + +HELPER="$HELPERDIR/gst-completion-helper-1.0" + +_inspect_all_arguments () +{ + COMPREPLY=( $(compgen -W "$(gst-inspect-1.0 --help-all | grep -oh '[[:graph:]]*--[[:graph:]]*' | cut -d'=' -f1)" -- $cur) ) +} + +_inspect_all_elements () +{ + COMPREPLY=( $(compgen -W "$($HELPER -l)" -- $cur) ) +} + +_gstinspect___atleast_version () { _mandatory__argument; } + +_gstinspect___exists () +{ + _inspect_all_elements +} + +__inspect_main () +{ + local i=1 command function_exists completion_func + + while [[ $i -ne $COMP_CWORD ]]; + do + local var + var="${COMP_WORDS[i]}" + if [[ "$var" == "--"* ]] + then + command="$var" + fi + i=$[$i+1] + done + + if [[ "$command" == "--gst"* ]]; then + completion_func="_${command//-/_}" + else + completion_func="_gstinspect_${command//-/_}" + fi + + declare -f $completion_func >/dev/null 2>&1 + + function_exists=$? + + if [[ "$cur" == "--"* ]]; then + _inspect_all_arguments + elif [ $function_exists -eq 0 ] + then + $completion_func + else + _inspect_all_elements + fi +} + +__inspect_func_wrap () +{ + local cur prev + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + $1 +} + +# Setup completion for certain functions defined above by setting common +# variables and workarounds. +# This is NOT a public function; use at your own risk. +__inspect_complete () +{ + local wrapper="__inspect_wrap${2}" + eval "$wrapper () { __inspect_func_wrap $2 ; }" + complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \ + || complete -o default -o nospace -F $wrapper $1 +} + +__inspect_complete gst-inspect-1.0 __inspect_main diff --git a/data/completions/gst-launch-1.0 b/data/completions/gst-launch-1.0 new file mode 100644 index 0000000..a12e06e --- /dev/null +++ b/data/completions/gst-launch-1.0 @@ -0,0 +1,143 @@ +# GStreamer +# Copyright (C) 2015 Mathieu Duponchelle +# +# bash/zsh completion support for gst-launch +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +HELPERDIR="${BASH_SOURCE[0]%/*}/../helpers" + +if [[ ! -d "$HELPERDIR" ]]; then + HELPERDIR="$(pkg-config --variable=helpersdir gstreamer-1.0)" +else + HELPERDIR=`cd "$HELPERDIR"; pwd` +fi + +# Common definitions +. "$HELPERDIR"/gst + +HELPER="$HELPERDIR/gst-completion-helper-1.0" + +_launch_all_arguments () +{ + COMPREPLY=( $(compgen -W "$(gst-launch-1.0 --help-all | grep -oh '[[:graph:]]*--[[:graph:]]*' | cut -d'=' -f1)" -- $cur) ) +} + +_complete_compatible_elements () +{ + COMPREPLY=( $(compgen -W "$($HELPER --compatible-with $previous_element)" -- $cur) ) +} + +_complete_all_elements () +{ + COMPREPLY=( $(compgen -W "$($HELPER -l)" -- $cur) ) +} + +_complete_element_properties () +{ + COMPREPLY=( $(compgen -W "$($HELPER --element-properties $previous_element)" -- $cur) ) +} + +_gstlaunch___exclude_ () { _mandatory__argument; } + +__launch_main () +{ + local i=1 command function_exists previous_element have_previous_element=0 completion_func + + while [[ $i -ne $COMP_CWORD ]]; + do + local var + var="${COMP_WORDS[i]}" + if [[ "$var" == "--"* ]] + then + command="$var" + fi + i=$[$i+1] + done + + i=1 + while [[ $i -ne $COMP_CWORD ]]; + do + local var + var="${COMP_WORDS[i]}" + + if [[ "$var" == "--"* ]] + then + i=$[$i+1] + continue + fi + + $(gst-inspect-1.0 --exists $var) + if [ $? -eq 0 ] + then + previous_element="$var" + have_previous_element=1 + fi + i=$[$i+1] + done + + if [[ "$command" == "--gst"* ]]; then + completion_func="_${command//-/_}" + else + completion_func="_gstlaunch_${command//-/_}" + fi + + # Seems like bash doesn't like "exclude" in function names + if [[ "$completion_func" == "_gstlaunch___exclude" ]] + then + completion_func="_gstlaunch___exclude_" + fi + + declare -f $completion_func >/dev/null 2>&1 + + function_exists=$? + + if [[ "$cur" == "--"* ]]; then + _launch_all_arguments + elif [ $function_exists -eq 0 ] + then + $completion_func + elif [ $have_previous_element -ne 0 ] && [[ "$prev" == "!" ]] + then + _complete_compatible_elements + elif [ $have_previous_element -ne 0 ] + then + _complete_element_properties + else + _complete_all_elements + fi +} + +__launch_func_wrap () +{ + local cur prev + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + $1 +} + +# Setup completion for certain functions defined above by setting common +# variables and workarounds. +# This is NOT a public function; use at your own risk. +__launch_complete () +{ + local wrapper="__launch_wrap${2}" + eval "$wrapper () { __launch_func_wrap $2 ; }" + complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \ + || complete -o default -o nospace -F $wrapper $1 +} + +__launch_complete gst-launch-1.0 __launch_main diff --git a/libs/gst/helpers/.gitignore b/libs/gst/helpers/.gitignore index faf8158..bca7cd8 100644 --- a/libs/gst/helpers/.gitignore +++ b/libs/gst/helpers/.gitignore @@ -1,2 +1,3 @@ gst-plugin-scanner +gst-completion-helper-?.?* *.o diff --git a/libs/gst/helpers/Makefile.am b/libs/gst/helpers/Makefile.am index 2816bc2..25e9553 100644 --- a/libs/gst/helpers/Makefile.am +++ b/libs/gst/helpers/Makefile.am @@ -1,3 +1,19 @@ +if ENABLE_BASH_COMPLETION +bin_PROGRAMS = gst-completion-helper-@GST_API_VERSION@ + +gst_completion_helper_@GST_API_VERSION@_SOURCES = gst-completion-helper.c +gst_completion_helper_@GST_API_VERSION@_CFLAGS = $(GST_OBJ_CFLAGS) +gst_completion_helper_@GST_API_VERSION@_LDADD = $(GST_OBJ_LIBS) + +bashhelpersdir = $(BASH_HELPERS_DIR) +dist_bashhelpers_DATA = gst + +install-data-hook: + cd $(DESTDIR)$(bindir) && \ + mv gst-completion-helper-@GST_API_VERSION@ $(BASH_HELPERS_DIR)/gst-completion-helper-@GST_API_VERSION@ + chmod 755 $(BASH_HELPERS_DIR)/gst-completion-helper-@GST_API_VERSION@ +endif + helpers_PROGRAMS = gst-plugin-scanner helpersdir=$(libexecdir)/gstreamer-$(GST_API_VERSION) diff --git a/libs/gst/helpers/gst b/libs/gst/helpers/gst new file mode 100644 index 0000000..76275e6 --- /dev/null +++ b/libs/gst/helpers/gst @@ -0,0 +1,35 @@ +# GStreamer +# Copyright (C) 2015 Mathieu Duponchelle +# +# bash/zsh completion support for common gstreamer options +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +___gst_debug_level () { _mandatory__argument; } +___gst_debug () { _mandatory__argument; } +___gst_debug_color_mode () { _mandatory__argument; } +___gst_plugin_path () { _mandatory__argument; } +___gst_plugin_load () { _mandatory__argument; } + +_mandatory__argument () +{ + if [[ "$prev" != "$command" ]] + then + COMPREPLY=( $(compgen -W "$(ges-launch-1.0 --help-all | grep -oh '[[:graph:]]*--[[:graph:]]*' | cut -d'=' -f1)" -- $cur) ) + else + COMPREPLY=("$cur") + fi +} diff --git a/libs/gst/helpers/gst-completion-helper.c b/libs/gst/helpers/gst-completion-helper.c new file mode 100644 index 0000000..2e83dd5 --- /dev/null +++ b/libs/gst/helpers/gst-completion-helper.c @@ -0,0 +1,226 @@ +/* GStreamer + * Copyright (C) 2015 Mathieu Duponchelle + * + * gst-completion-helper.c: tool to let other tools enjoy fast and powerful + * gstreamer-aware completion + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +static GList * +get_pad_templates_info (GstElement * element, GstElementFactory * factory, + GstPadDirection direction) +{ + const GList *pads; + GstStaticPadTemplate *padtemplate; + GList *caps_list = NULL; + + if (gst_element_factory_get_num_pad_templates (factory) == 0) { + g_print (" none\n"); + return NULL; + } + + pads = gst_element_factory_get_static_pad_templates (factory); + while (pads) { + padtemplate = (GstStaticPadTemplate *) (pads->data); + pads = g_list_next (pads); + + if (padtemplate->direction != direction) + continue; + + if (padtemplate->static_caps.string) { + caps_list = + g_list_append (caps_list, + gst_static_caps_get (&padtemplate->static_caps)); + } + + } + + return caps_list; +} + +static GList * +_get_pad_caps (const gchar * factory_name, GstPadDirection direction) +{ + GstElementFactory *factory = gst_element_factory_find (factory_name); + GstElement *element = gst_element_factory_make (factory_name, NULL); + + if (!element) + return NULL; + if (!factory) + return NULL; + factory = + GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE + (factory))); + if (!factory) + return NULL; + return get_pad_templates_info (element, factory, direction); +} + +static gboolean +_are_linkable (GstPluginFeature * feature, GList * caps_list) +{ + gboolean print = FALSE; + GstElementFactory *factory = GST_ELEMENT_FACTORY (feature); + + GList *tmp; + print = FALSE; + for (tmp = caps_list; tmp; tmp = tmp->next) { + if (gst_element_factory_can_sink_any_caps (factory, tmp->data)) { + print = TRUE; + break; + } + } + + return print; +} + +static void +_list_features (const gchar * compatible_with) +{ + GList *plugins, *orig_plugins; + GList *caps_list = NULL; + + if (compatible_with) { + caps_list = _get_pad_caps (compatible_with, GST_PAD_SRC); + } + + orig_plugins = plugins = gst_registry_get_plugin_list (gst_registry_get ()); + while (plugins) { + GList *features, *orig_features; + GstPlugin *plugin; + + plugin = (GstPlugin *) (plugins->data); + plugins = g_list_next (plugins); + + if (GST_OBJECT_FLAG_IS_SET (plugin, GST_PLUGIN_FLAG_BLACKLISTED)) { + continue; + } + + orig_features = features = + gst_registry_get_feature_list_by_plugin (gst_registry_get (), + gst_plugin_get_name (plugin)); + while (features) { + GstPluginFeature *feature; + + if (G_UNLIKELY (features->data == NULL)) + goto next; + feature = GST_PLUGIN_FEATURE (features->data); + + if (GST_IS_ELEMENT_FACTORY (feature)) { + gboolean print = TRUE; + if (caps_list) + print = _are_linkable (feature, caps_list); + if (print) + g_print ("%s ", gst_plugin_feature_get_name (feature)); + } + + next: + features = g_list_next (features); + } + + gst_plugin_feature_list_free (orig_features); + } + + g_list_free (caps_list); + g_print ("\n"); + gst_plugin_list_free (orig_plugins); +} + +static void +_print_element_properties_info (GstElement * element) +{ + GParamSpec **property_specs; + guint num_properties, i; + + property_specs = g_object_class_list_properties + (G_OBJECT_GET_CLASS (element), &num_properties); + + for (i = 0; i < num_properties; i++) { + GParamSpec *param = property_specs[i]; + + if (param->flags & G_PARAM_WRITABLE) { + g_print ("%s= ", g_param_spec_get_name (param)); + } + } + + g_free (property_specs); +} + +static void +_list_element_properties (const gchar * factory_name) +{ + GstElement *element = gst_element_factory_make (factory_name, NULL); + + _print_element_properties_info (element); +} + +int +main (int argc, char *argv[]) +{ + gboolean list_features = FALSE; + gchar *compatible_with = NULL; + gchar *element = NULL; + + GOptionEntry options[] = { + {"list-features", 'l', 0, G_OPTION_ARG_NONE, &list_features, + "list all the available features", NULL}, + {"compatible-with", '\0', 0, G_OPTION_ARG_STRING, &compatible_with, + "Only print the elements that could be queued after this feature name", + NULL}, + {"element-properties", '\0', 0, G_OPTION_ARG_STRING, &element, + "The element to list properties on", NULL}, + {NULL} + }; + + GOptionContext *ctx; + GError *err = NULL; + + ctx = g_option_context_new ("PIPELINE-DESCRIPTION"); + g_option_context_add_main_entries (ctx, options, NULL); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + if (err) + g_printerr ("Error initializing: %s\n", GST_STR_NULL (err->message)); + else + g_printerr ("Error initializing: Unknown error!\n"); + exit (1); + } + g_option_context_free (ctx); + + if (compatible_with) { + _list_features (compatible_with); + exit (EXIT_SUCCESS); + } + + if (element) { + _list_element_properties (element); + exit (EXIT_SUCCESS); + } + + if (list_features) { + _list_features (NULL); + exit (EXIT_SUCCESS); + } +} diff --git a/pkgconfig/gstreamer-uninstalled.pc.in b/pkgconfig/gstreamer-uninstalled.pc.in index ac1eeef..bf8bf42 100644 --- a/pkgconfig/gstreamer-uninstalled.pc.in +++ b/pkgconfig/gstreamer-uninstalled.pc.in @@ -7,6 +7,8 @@ includedir=@abs_top_builddir@ toolsdir=@abs_top_builddir@/tools pluginsdir=@abs_top_builddir@ girdir=@abs_top_builddir@/gst +completionsdir=@abs_top_builddir@/data/completions +helpersdir=@abs_top_builddir@/libs/gst/helpers typelibdir=@abs_top_builddir@/gst Name: GStreamer Uninstalled diff --git a/pkgconfig/gstreamer.pc.in b/pkgconfig/gstreamer.pc.in index 96649ef..381c96d 100644 --- a/pkgconfig/gstreamer.pc.in +++ b/pkgconfig/gstreamer.pc.in @@ -7,6 +7,8 @@ pluginsdir=@libdir@/gstreamer-@GST_API_VERSION@ datarootdir=${prefix}/share datadir=${datarootdir} girdir=${datadir}/gir-1.0 +completionsdir=@BASH_COMPLETION_DIR@ +helpersdir=@BASH_HELPERS_DIR@ typelibdir=${libdir}/girepository-1.0 Name: GStreamer -- 2.7.4