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
This commit is contained in:
Mathieu Duponchelle 2015-02-20 22:04:22 +01:00
parent bcce3fbcb8
commit 46e7baff74
11 changed files with 560 additions and 1 deletions

View file

@ -11,7 +11,8 @@ SUBDIRS = pkgconfig \
docs \
po \
m4 \
common
common \
data
if BUILD_TOOLS
SUBDIRS += tools

View file

@ -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

4
data/Makefile.am Normal file
View file

@ -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

View file

@ -0,0 +1,105 @@
# GStreamer
# Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
#
# 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

View file

@ -0,0 +1,143 @@
# GStreamer
# Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
#
# 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

View file

@ -1,2 +1,3 @@
gst-plugin-scanner
gst-completion-helper-?.?*
*.o

View file

@ -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)

35
libs/gst/helpers/gst Normal file
View file

@ -0,0 +1,35 @@
# GStreamer
# Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
#
# 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
}

View file

@ -0,0 +1,226 @@
/* GStreamer
* Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
*
* 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 <gst/gst.h>
#include <glib.h>
#include <stdlib.h>
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);
}
}

View file

@ -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

View file

@ -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