bash-completion: Implement in a different way.
authorMathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Fri, 20 Feb 2015 21:04:22 +0000 (22:04 +0100)
committerMathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Mon, 23 Feb 2015 16:57:57 +0000 (17:57 +0100)
+ 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
configure.ac
data/Makefile.am [new file with mode: 0644]
data/completions/gst-inspect-1.0 [new file with mode: 0644]
data/completions/gst-launch-1.0 [new file with mode: 0644]
libs/gst/helpers/.gitignore
libs/gst/helpers/Makefile.am
libs/gst/helpers/gst [new file with mode: 0644]
libs/gst/helpers/gst-completion-helper.c [new file with mode: 0644]
pkgconfig/gstreamer-uninstalled.pc.in
pkgconfig/gstreamer.pc.in

index ca6071d..a002b96 100644 (file)
@@ -11,7 +11,8 @@ SUBDIRS = pkgconfig \
        docs \
        po \
        m4 \
-       common
+       common \
+       data
 
 if BUILD_TOOLS
 SUBDIRS += tools
index 6f765ef..e637239 100644 (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
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644 (file)
index 0000000..7098492
--- /dev/null
@@ -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 (file)
index 0000000..130b963
--- /dev/null
@@ -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
diff --git a/data/completions/gst-launch-1.0 b/data/completions/gst-launch-1.0
new file mode 100644 (file)
index 0000000..a12e06e
--- /dev/null
@@ -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
index faf8158..bca7cd8 100644 (file)
@@ -1,2 +1,3 @@
 gst-plugin-scanner
+gst-completion-helper-?.?*
 *.o
index 2816bc2..25e9553 100644 (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)
 
diff --git a/libs/gst/helpers/gst b/libs/gst/helpers/gst
new file mode 100644 (file)
index 0000000..76275e6
--- /dev/null
@@ -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
+}
diff --git a/libs/gst/helpers/gst-completion-helper.c b/libs/gst/helpers/gst-completion-helper.c
new file mode 100644 (file)
index 0000000..2e83dd5
--- /dev/null
@@ -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);
+  }
+}
index ac1eeef..bf8bf42 100644 (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
index 96649ef..381c96d 100644 (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