Browse Source

Generate documentation with Sphinx and add an overview

add_opts_to_lv2apply
David Robillard 8 months ago
parent
commit
eb0f335d49
  1. 26
      doc/c/Doxyfile
  2. 10
      doc/c/index.rst
  3. 18
      doc/c/overview.rst
  4. 90
      doc/c/plugins.rst
  5. 21
      doc/c/uis.rst
  6. 153
      doc/c/world.rst
  7. 43
      doc/c/wscript
  8. 79
      doc/conf.py.in
  9. 20
      doc/footer.html
  10. 37
      doc/header.html
  11. 226
      doc/layout.xml
  12. 6
      doc/mainpage.md
  13. 2462
      doc/reference.doxygen.in
  14. 739
      doc/style.css
  15. 9
      doc/summary.rst
  16. 655
      scripts/dox_to_sphinx.py
  17. 6
      wscript

26
doc/c/Doxyfile

@ -0,0 +1,26 @@
PROJECT_NAME = Lilv
PROJECT_BRIEF = "A library for simple use of LV2 plugins"
QUIET = YES
WARN_AS_ERROR = NO
WARN_IF_UNDOCUMENTED = NO
WARN_NO_PARAMDOC = NO
JAVADOC_AUTOBRIEF = YES
CASE_SENSE_NAMES = YES
HIDE_IN_BODY_DOCS = YES
REFERENCES_LINK_SOURCE = NO
GENERATE_HTML = NO
GENERATE_LATEX = NO
GENERATE_XML = YES
XML_PROGRAMLISTING = NO
SHOW_FILES = NO
MACRO_EXPANSION = YES
PREDEFINED = LILV_API LILV_DEPRECATED
INPUT = ../../include/lilv/lilv.h
OUTPUT_DIRECTORY = .

10
doc/c/index.rst

@ -0,0 +1,10 @@
####
Lilv
####
.. include:: summary.rst
.. toctree::
overview
api/lilv

18
doc/c/overview.rst

@ -0,0 +1,18 @@
.. default-domain:: c
.. highlight:: c
########
Overview
########
The complete API is declared in ``lilv.h``:
.. code-block:: c
#include <lilv/lilv.h>
.. toctree::
world
plugins
uis

90
doc/c/plugins.rst

@ -0,0 +1,90 @@
.. default-domain:: c
.. highlight:: c
#######
Plugins
#######
After bundles are loaded,
all discovered plugins can be accessed via :func:`lilv_world_get_all_plugins`:
.. code-block:: c
LilvPlugins* plugins = lilv_world_get_all_plugins(world);
:struct:`LilvPlugins` is a collection of plugins that can be accessed by index or plugin URI.
The convenicne macro :macro:`LILV_FOREACH` is provided to make iterating over collections simple.
For example, to print the URI of every plugin:
.. code-block:: c
LILV_FOREACH (plugins, i, list) {
const LilvPlugin* p = lilv_plugins_get(list, i);
printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p)));
}
}
More typically,
you want to load a specific plugin,
which can be done with :func:`lilv_plugins_get_by_uri`:
.. code-block:: c
LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc");
const LilvPlugin* plugin = lilv_plugins_get_by_uri(list, plugin_uri);
:struct:`LilvPlugin` has various accessors that can be used to get information about the plugin.
See the :doc:`API reference <api/plugin>` for details.
*********
Instances
*********
:struct:`LilvPlugin` only represents the data of the plugin,
it does not load or access the actual plugin code.
To do that, you must instantiate a plugin to create :struct:`LilvInstance`:
.. code-block:: c
LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
Connecting Ports
================
Before running a plugin instance, its ports must be connected to some data.
This is done with :func:`lilv_instance_connect_port`.
Assuming the plugins has two control input ports and one audio output port,
in that order:
.. code-block:: c
float control_in_1 = 0.0f;
float control_in_2 = 0.0f;
float audio_out[128];
lilv_instance_connect_port(instance, 0, &control_in_1);
lilv_instance_connect_port(instance, 1, &control_in_2);
lilv_instance_connect_port(instance, 2, &audio_out);
Processing Data
===============
Once the ports are connected, the instance can be activated and run:
.. code-block:: c
lilv_instance_activate(instance);
lilv_instance_run(instance, 128);
// Copy buffers around and probably run several times here...
lilv_instance_deactivate(instance);
Once you are done with an instance,
it can be destroyed with :func:`lilv_instance_free`:
.. code-block:: c
lilv_instance_free(instance);

21
doc/c/uis.rst

@ -0,0 +1,21 @@
.. default-domain:: c
.. highlight:: c
###############
User Interfaces
###############
Plugins may have custom user interfaces, or `UIs`,
which are installed in bundles just like plugins.
The available UIs for a plugin can be accessed with :func:`lilv_plugin_get_uis`:
.. code-block:: c
LilvUIs* uis = lilv_plugin_get_uis(plugin);
:struct:`LilvUIs` is a collection much like `LilvPlugins`,
except it is of course a set of :struct:`LilvUI` rather than a set of :struct:`LilvPlugin`.
Also like plugins,
the :struct:`LilvUI` class has various accessors that can be used to get information about the UI.
See the :doc:`API reference <api/ui>` for details.

153
doc/c/world.rst

@ -0,0 +1,153 @@
.. default-domain:: c
.. highlight:: c
#####
World
#####
The world is the top-level object which represents an instance of Lilv.
It is used to discover and load LV2 data,
and stores an internal cache of loaded data for fast searching.
An application typically has a single world,
which is constructed once on startup and used throughout the application's lifetime.
************
Construction
************
A world must be created before anything else.
A world is created with :func:`lilv_world_new`, for example:
.. code-block:: c
LilvWorld* world = lilv_world_new();
***************
Setting Options
***************
Various options to control Lilv's behavior can be set with :func:`lilv_world_set_option`.
The currently supported options are :c:macro:`LILV_OPTION_FILTER_LANG`,
:c:macro:`LILV_OPTION_DYN_MANIFEST`, and :c:macro:`LILV_OPTION_LV2_PATH`.
For example, to set the LV2 path to only load plugins bundled in the application:
.. code-block:: c
LilvNode* lv2_path = lilv_new_file_uri(world, NULL, "/myapp/lv2");
lilv_world_set_option(world, LILV_OPTION_LV2_PATH, lv2_path);
************
Loading Data
************
Before using anything, data must be loaded from disk.
All LV2 data (plugins, UIs, specifications, presets, and so on) is installed in `bundles`,
a standard directory format
Discovering and Loading Bundles
===============================
Typical hosts will simply load all bundles in the standard LV2 locations on the system:
.. code-block:: c
lilv_world_load_all(world);
This will discover all bundles on the system,
as well as load the required data defined in any discovered specifications.
It is also possible to load a specific bundle:
.. code-block:: c
LilvNode* bundle_uri = lilv_new_file_uri(world, NULL, "/some/plugin.lv2");
lilv_world_load_bundle(world, bundle_uri);
The LV2 specification itself is also installed in bundles,
so if you are not using :func:`lilv_world_load_all`,
it is necessary to manually load the discovered specification data:
.. code-block:: c
lilv_world_load_specifications(world);
lilv_world_load_plugin_classes(world);
*************
Querying Data
*************
The world contains a model of all the loaded data in memory which can be queried.
Data Model
==========
LV2 data is a set of "statements",
where a statement is a bit like a simple machine-readable sentence.
The "subject" and "object" are as in natural language,
and the "predicate" is like the verb, but more general.
For example, we could make a statement about a plugin in english:
MyOsc has the name "Super Oscillator"
We can break this statement into 3 pieces like so:
.. list-table::
:header-rows: 1
* - Subject
- Predicate
- Object
* - MyOsc
- has the name
- "My Super Oscillator"
Statements use URIs to identify things.
In this case, we assume that this plugin has the URI ``http://example.org/Osc``.
The LV2 specification defines that ``http://usefulinc.com/ns/doap#name`` is the property used to describe a plugin's name.
So, this statement is:
.. list-table::
:header-rows: 1
* - Subject
- Predicate
- Object
* - ``http://example.org/Osc``
- ``http://usefulinc.com/ns/doap#name``
- "My Oscillator"
Finding Values
==============
Based on this model, you can find all values that match a certain pattern.
Patterns are just statements,
but with ``NULL`` used as a wildcard that matches anything.
So, for example, you can get the name of a plugin using :func:`lilv_world_find_nodes`:
.. code-block:: c
LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc");
LilvNode* doap_name = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name");
LilvNodes* values = lilv_world_find_nodes(world, plugin_uri, doap_name, NULL);
Note that a set of values is returned,
because some properties may have several values.
When you are only interested in one value,
you can use the simpler :func:`lilv_world_get` instead:
.. code-block:: c
LilvNode* value = lilv_world_get(world, plugin_uri, doap_name, NULL);
If you are only interested if a value exists at all,
use :func:`lilv_world_ask`:
.. code-block:: c
bool has_name = lilv_world_ask(world, plugin_uri, doap_name, NULL);

43
doc/c/wscript

@ -0,0 +1,43 @@
#!/usr/bin/env python
def build(bld):
dox_to_sphinx = bld.path.find_node("../../scripts/dox_to_sphinx.py")
index_xml = bld.path.get_bld().make_node("xml/index.xml")
files = [
("../summary.rst", "sphinx/summary.rst"),
("index.rst", "sphinx/index.rst"),
("overview.rst", "sphinx/overview.rst"),
("plugins.rst", "sphinx/plugins.rst"),
("uis.rst", "sphinx/uis.rst"),
("world.rst", "sphinx/world.rst"),
]
# Run Doxygen to generate XML documentation
bld(features="doxygen", doxyfile="Doxyfile")
# Substitute variables to make Sphinx configuration file
bld(features="subst",
source="../conf.py.in",
target="sphinx/conf.py",
LILV_VERSION=bld.env.LILV_VERSION)
# Copy static documentation files to Sphinx build directory
for f in files:
bld(features="subst", is_copy=True, source=f[0], target=f[1])
# Generate Sphinx markup from Doxygen XML
bld.add_group()
bld(rule="${PYTHON} " + dox_to_sphinx.abspath() + " -f ${SRC} ${TGT}",
source=index_xml,
target="sphinx/api/")
doc_dir = bld.env.DOCDIR + "/lilv-%s/" % bld.env.LILV_MAJOR_VERSION
# Run Sphinx to generate HTML documentation
for builder in ["html", "singlehtml"]:
bld(features="sphinx",
sphinx_source=bld.path.get_bld().make_node("sphinx"),
sphinx_output_format=builder,
sphinx_options=["-E", "-q", "-t", builder],
install_path=doc_dir + "c/%s/" % builder)

79
doc/conf.py.in

@ -0,0 +1,79 @@
# Project information
project = "Lilv"
copyright = "2020, David Robillard"
author = "David Robillard"
release = "@LILV_VERSION@"
# General configuration
exclude_patterns = ["xml"]
language = "en"
nitpicky = True
pygments_style = "friendly"
# Ignore everything opaque or external for nitpicky mode
_opaque = [
"FILE",
"LV2_Descriptor",
"LV2_Feature",
"LV2_Handle",
"LV2_URID_Map",
"LV2_URID_Unmap",
"LilvInstanceImpl",
"LilvNodeImpl",
"LilvPluginClassImpl",
"LilvPluginImpl",
"LilvPortImpl",
"LilvScalePointImpl",
"LilvStateImpl",
"LilvUIImpl",
"LilvWorldImpl",
"size_t",
"uint32_t",
"va_list",
]
nitpick_ignore = list(map(lambda x: ("c:identifier", x), _opaque))
# HTML output
html_theme = "sphinx_lv2_theme"
html_copy_source = False
html_short_title = "Lilv"
if tags.has('singlehtml'):
html_sidebars = {
"**": [
"globaltoc.html",
]
}
html_theme_options = {
"body_max_width": "65em",
"body_min_width": "50em",
"description": "A library for loading LV2 plugins",
"show_footer_version": True,
"show_logo_version": False,
"logo_name": True,
"nosidebar": False,
"page_width": "80em",
"sidebar_width": "11em",
"globaltoc_maxdepth": 3,
"globaltoc_collapse": False,
}
else:
html_theme_options = {
"body_max_width": "60em",
"body_min_width": "40em",
"description": "A library for loading LV2 plugins",
"show_footer_version": True,
"show_logo_version": False,
"logo_name": True,
"nosidebar": True,
"page_width": "60em",
"sidebar_width": "14em",
"globaltoc_maxdepth": 1,
"globaltoc_collapse": True,
}

20
doc/footer.html

@ -1,20 +0,0 @@
<!-- HTML footer for doxygen 1.8.15-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">Doxygen $doxygenversion</li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<div id="footer">
<address class="footer">$generatedby
<a href="http://www.doxygen.org/">Doxygen</a> $doxygenversion
</address>
</div>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

37
doc/header.html

@ -1,37 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<div id="header">
<div id="titlebox">
<!--BEGIN PROJECT_LOGO-->
<div id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></div>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<h1 id="title">$projectname</h1>
<!--END PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<div id="shortdesc">$projectbrief</div>
<!--END PROJECT_BRIEF-->
</div>
<div id="metabox">
<table id="meta">
<!--BEGIN PROJECT_NUMBER-->
<tr><th>Version</th><td>$projectnumber</td></tr>
<!--END PROJECT_NUMBER-->
</table>
</div>
</div>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

226
doc/layout.xml

@ -1,226 +0,0 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.8.15 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="interfaces" visible="yes" title="">
<tab type="interfacelist" visible="yes" title="" intro=""/>
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="structs" visible="yes" title="">
<tab type="structlist" visible="yes" title="" intro=""/>
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
</tab>
<tab type="exceptions" visible="yes" title="">
<tab type="exceptionlist" visible="yes" title="" intro=""/>
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<memberdecl>
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
<friends title=""/>
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
<variables title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<memberdecl>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="yes"/>
<detaileddescription title=""/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
</directory>
</doxygenlayout>

6
doc/mainpage.md

@ -1,6 +0,0 @@
Lilv is a C library to make the use of [LV2] plugins as simple as possible for
applications.
The complete API is documented in the [lilv](@ref lilv) group.
[LV2]: http://lv2plug.in/

2462
doc/reference.doxygen.in
File diff suppressed because it is too large
View File

739
doc/style.css

@ -1,739 +0,0 @@
body {
background: #FFF;
color: #222;
font-style: normal;
line-height: 1.6em;
margin-left: auto;
margin-right: auto;
padding: 1em;
max-width: 60em;
font-family: "DejaVu Serif",Palatino,serif;
text-rendering: optimizeLegibility;
}
h1, .title, #projectname, h2, h3, h4, h5, h6 {
line-height: 1.0125em;
color: #444;
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
margin: 1em 0 0.5em 0;
}
h1, .titlearea .header .titlebox, #projectname {
font-size: 300%;
font-weight: 400;
margin-bottom: 0.25em;
margin-top: 0;
}
.header .headertitle .title {
font-size: 180%;
font-weight: 400;
margin: 0.75em 0.25em 0.5em 0;
}
.ingroups {
display: inline;
}
.title .ingroups a {
font-size: small;
margin-left: 1em;
}
#titlebox, #metabox {
display: inline-block;
}
#titlebox{
display: inline-block;
width: 75%;
left: 0;
top: 0;
}
#title {
margin-bottom: 0.25em;
}
#shortdesc {
margin: 0;
color: #666;
display: inline-block;
font-style: italic;
padding: 0;
}
#titlearea {
margin: 0.25em auto 0.25em auto;
padding: 0;
position: relative;
clear: both;
line-height: 1.0em;
}
h2 {
font-size: 160%;
font-weight: 400;
}
h3 {
font-size: 140%;
font-weight: 400;
}
h4 {
font-size: 120%;
font-weight: 500;
}
h5, h6 {
font-size: 110%;
font-weight: 600;
}
h1 a, h1 a:link, h1 a:visited ,
h2 a, h2 a:link, h2 a:visited ,
h3 a, h3 a:link, h3 a:visited ,
h4 a, h4 a:link, h4 a:visited ,
h5 a, h5 a:link, h5 a:visited ,
h6 a, h6 a:link, h6 a:visited {
color: #444;
}
p {
margin: 0.5em 0 0.5em 0;
}
dt {
font-weight: 700;
}
dd {
margin-left: 2em;
}
caption {
font-weight: 700;
}
span.legend {
font-size: small;
text-align: center;
}
h3.version {
font-size: small;
text-align: center;
}
div.qindex,div.navtab {
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
padding: 2px;
}
div.navtab {
margin-right: 15px;
}
/* @group Link Styling */
a {
color: #546E00;
text-decoration: none;
}
.contents a:visited {
color: #344E00;
}
a:hover {
text-decoration: underline;
}
a.qindexHL {
background-color: #9CAFD4;
color: #FFF;
border: 1px double #869DCA;
}
code {
color: #444;
}
/* @end */
dl.el {
margin-left: -1cm;
}
.fragment {
font-family: "DejaVu Sans Mono",monospace,fixed;
}
pre.fragment {
border: 1px solid #C4C4C4;
background-color: #F9F9F9;
padding: 0.5em;
overflow: auto;
}
div.ah {
background-color: #000;
font-weight: 700;
color: #FFF;
margin-bottom: 3px;
margin-top: 3px;
padding: .2em;
border: thin solid #333;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
margin-bottom: 6px;
font-weight: 700;
}
a + h2.groupheader {
display: none;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
div.contents, #content {
padding: 0 0.25em 0 0.25em;
max-width: 60em;
margin-left: auto;
margin-right: auto;
}
td.indexkey {
background-color: #EBEFF6;
font-weight: 700;
border: 1px solid #C4CFE5;
margin: 2px 0;
padding: 2px 10px;
}
td.indexvalue {
background-color: #EBEFF6;
border: 1px solid #C4CFE5;
padding: 2px 10px;
margin: 2px 0;
}
table.memname {
font-family: "DejaVu Sans Mono",monospace,fixed;
}
tr.memlist {
background-color: #EEF1F7;
}
p.formulaDsp {
text-align: center;
}
img.formulaInl {
vertical-align: middle;
}
div.center {
text-align: center;
margin-top: 0;
margin-bottom: 0;
padding: 0;
}
div.center img {
border: 0;
}
address.footer {
text-align: right;
}
img.footer {
border: 0;
vertical-align: middle;
}
/* @group Code Colorization */
span.keyword {
color: #586E75;
}
span.keywordtype {
color: #546E00;
}
span.keywordflow {
color: #586E75;
}
span.comment {
color: #6C71C4;
}
span.preprocessor {
color: #D33682;
}
span.stringliteral {
color: #CB4B16;
}
span.charliteral {
color: #CB4B16;
}
/* @end */
td.tiny {
font-size: x-small;
}
.dirtab {
padding: 4px;
border-collapse: collapse;
border: 1px solid #A3B4D7;
}
th.dirtab {
background: #EBEFF6;
font-weight: 700;
}
hr {
height: 0;
border: none;
border-top: 1px solid #DDD;
margin: 2em 0 1em;
}
#footer {
bottom: 0;
clear: both;
font-size: x-small;
margin: 2em 0 0;
padding: 0 1em 1em 1em;
vertical-align: top;
color: #888;
}
/* @group Member Descriptions */
table.memberdecls {
border-spacing: 0.125em;
line-height: 1.3em;
}
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
margin: 0;
padding: 0;
}
.mdescLeft,.mdescRight {
color: #555;
}
.memItemLeft,.memItemRight,.memTemplParams {
border: 0;
font-family: "DejaVu Sans Mono",monospace,fixed;
}
.memItemLeft,.memTemplItemLeft {
white-space: nowrap;
padding-left: 2em;
padding-right: 1em;
}
.memItemLeft a.el {
font-weight: bold;
}
.memTemplParams {
color: #464646;
white-space: nowrap;
}
td.memSeparator {
display: none;
}
td.mlabels-right {
vertical-align: top;
padding-top: 4px;
color: #B4C342;
}
.memtitle {
display: none;
}
/* @end */
/* @group Member Details */
/* Styles for detailed member documentation */
.memtemplate {
color: #888;
font-style: italic;
font-family: "DejaVu Sans Mono",monospace,fixed;
font-size: small;
}
.memnav {
background-color: #EEE;
border: 1px solid #B4C342;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
}
.memitem {
padding: 0.25em 0.5em 0.25em 0.5em;
margin: 0 0 1em 0;
border-radius: 6px;
border: 1px solid #DDD;
}
.memproto {
font-size: 110%;
font-weight: 400;
line-height: 1em;
color: #000;
}
.memproto .paramname {
font-style: normal;
}
.memdoc {
padding: 0 0.25em 0 0.25em;
}
.paramkey {
text-align: right;
}
.paramtype {
color: #666;
padding-right: 0.5em;
white-space: nowrap;
}
.paramname {
color: #111;
white-space: nowrap;
font-family: "DejaVu Sans Mono",monospace,fixed;
font-style: italic;
padding-right: 0.5em;
}
.fieldname {
color: #000;
}
.fieldtable {
padding-top: 0.25em;
border-top: 1px dashed #DDD;
}
.fieldtable tbody tr:first-child {
display: none;
}
td.fieldname {
padding: 0 0.5em 0 0.25em;
vertical-align: top;
font-family: "DejaVu Sans Mono",monospace,fixed;
}
td.fieldtype {
color: #666;
padding: 0 0.5em 0 0;
vertical-align: top;
font-family: "DejaVu Sans Mono",monospace,fixed;
}
td.fielddoc p {
margin: 0;
vertical-align: top;
padding: 0 0.5em 0 0;
}
p.reference {
font-size: x-small;
font-style: italic;
}
/* @end */
/* @group Directory (tree) */
/* for the tree view */
.ftvtree {
font-family: sans-serif;
margin: 0;
}
/* these are for tree view when used as main index */
.directory {
font-size: small;
margin: 0.5em;
}
.directory h3 {
margin: 0;
margin-top: 1em;
font-size: 11pt;
}
.directory > h3 {
margin-top: 0;
}
.directory p {
margin: 0;
white-space: nowrap;
}
.directory div {
display: none;
margin: 0;
}
.directory img {
vertical-align: -30%;
}
td.entry {
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
font-weight: 400;
padding-right: 1em;
}
td.entry .arrow {
display: none;
}
td.entry b {
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
font-weight: 400;
font-size: 130%;
}
/* these are for tree view when not used as main index */
.directory-alt {
font-size: 100%;
font-weight: bold;
}
.directory-alt h3 {
margin: 0;
margin-top: 1em;
font-size: 11pt;
}
.directory-alt > h3 {
margin-top: 0;
}
.directory-alt p {
margin: 0;
white-space: nowrap;
}
.directory-alt div {
display: none;
margin: 0;
}
.directory-alt img {
vertical-align: -30%;
}
/* @end */
div.dynheader {
margin-top: 8px;
}
address {
font-style: normal;
color: #444;
}
table.doxtable {
border-collapse: collapse;
margin: 0.5em;
}
table.doxtable td,table.doxtable th {
border: 1px solid #DDD;
padding: 3px 7px 2px;
}
table.doxtable th {
background-color: #F3F3F3;
color: #000;
padding-bottom: 4px;
padding-top: 5px;
text-align: left;
font-weight: bold;
}
.tabsearch {
top: 0;
left: 10px;
height: 36px;
z-index: 101;
overflow: hidden;
font-size: 13px;
}
div.navpath {
color: #DDD;
}
.navpath ul {
overflow: hidden;
margin: 0;
padding: 0;
}
.navpath li {
float: left;
padding-left: 0;
margin-left: 0.5em;
padding-right: 1em;
}
.navpath a {
display: block;
text-decoration: none;
outline: none;
}
div.summary {
font-size: small;
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
margin: 0;
color: #FFF; /* Hide separator bars */
border-bottom: 1px solid #DDD;
padding: 0.25em 0;
}
div.summary a {
white-space: nowrap;
}
/* Metadata box (right aligned next to title) */
#metabox {
display: inline-block;
font-size: x-small;
margin: 0 0 0.25em 0;
position: absolute;
right: 0;
top: 0;
color: #666;
font-style: italic;
padding: 0 1em;
}
#meta {
border-style: hidden;
margin-right: 0.25em;
}
#meta tr, #meta th, #meta td {
background-color: transparent;
border: 0;
margin: 0;
font-weight: normal;
}
#meta th {
text-align: right;
}
#meta th:after {
content: ":";
}
div.line {
font-family: "DejaVu Sans Mono",monospace,fixed;
line-height: 1.4em;
white-space: pre-wrap;
}
.glow {
background-color: #2AA198;
box-shadow: 0 0 10px #2AA198;
}
span.lineno {
padding-right: 4px;
text-align: right;
border-right: 2px solid #546E00;
background-color: #E8E8E8;
white-space: pre;
}
span.lineno a {
background-color: #D8D8D8;
}
span.lineno a:hover {
background-color: #C8C8C8;
}
.tabs, .tabs2, .navpath {
padding: 0.25em 0;
border-bottom: 1px solid #DDD;
font-size: small;
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
margin: 0;
}
th {
text-align: left;
font-size: 110%;
font-weight: 500;
}
.mlabel {
padding: 0.125em;
}
/* tabs*/
.tablist {
margin: 0;
padding: 0;
display: table;
}
.tablist li {
display: table-cell;
line-height: 2em;
list-style: none;
border-bottom: 0;
}
.tablist a {
display: block;
padding: 0 1em 0 0;
font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
text-decoration: none;
outline: none;
}
.tabs3 .tablist a {
padding: 0 10px;
}
.tablist li.current a {
color: #222;
}
span.icon {
display: none;
}

9
doc/summary.rst

@ -0,0 +1,9 @@
Lilv is a library for working with LV2_ plugins.
It provides several types and functions that can be used to discover plugins,
investigate their data, load and run them, and save and restore their state.
Lilv is the standard implementation used by nearly all LV2 hosts.
It implements the details of the LV2 specification on POSIX, MacOS, and Windows,
and provides a simpler portable API for applications.
.. _LV2: http://lv2plug.in/

655
scripts/dox_to_sphinx.py

@ -0,0 +1,655 @@
#!/usr/bin/env python3
# Copyright 2020 David Robillard <d@drobilla.net>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""
Write Sphinx markup from Doxygen XML.
Takes a path to a directory of XML generated by Doxygen, and emits a directory
with a reStructuredText file for every documented symbol.
"""
import argparse
import os
import sys
import textwrap
import xml.etree.ElementTree
__author__ = "David Robillard"
__date__ = "2020-11-18"
__email__ = "d@drobilla.net"
__license__ = "ISC"
__version__ = __date__.replace("-", ".")
def load_index(index_path):
"""
Load the index from XML.
:returns: A dictionary from ID to skeleton records with basic information
for every documented entity. Some records have an ``xml_filename`` key
with the filename of a definition file. These files will be loaded later
to flesh out the records in the index.
"""
root = xml.etree.ElementTree.parse(index_path).getroot()
index = {}
for compound in root:
compound_id = compound.get("refid")
compound_kind = compound.get("kind")
compound_name = compound.find("name").text
if compound_kind in ["dir", "file", "page"]:
continue
# Add record for compound (compounds appear only once in the index)
assert compound_id not in index
index[compound_id] = {
"kind": compound_kind,
"name": compound_name,
"xml_filename": compound_id + ".xml",
"children": [],
}
name_prefix = (
("%s::" % compound_name) if compound_kind == "namespace" else ""
)
for child in compound.findall("member"):
if child.get("refid") in index:
assert compound_kind == "group"
continue
# Everything has a kind and a name
child_record = {
"kind": child.get("kind"),
"name": name_prefix + child.find("name").text,
}
if child.get("kind") == "enum":
# Enums are not compounds, but we want to resolve the parent of
# their values so they are not written as top level documents
child_record["children"] = []
if child.get("kind") == "enumvalue":
# Remove namespace prefix
child_record["name"] = child.find("name").text
index[child.get("refid")] = child_record
return index
def resolve_index(index, root):
"""
Walk a definition document and extend the index for linking.
This does two things: sets the "parent" and "children" fields of all
applicable records, and sets the "strong" field of enums so that the
correct Sphinx role can be used when referring to them.
"""
def add_child(index, parent_id, child_id):
parent = index[parent_id]
child = index[child_id]
if child["kind"] == "enumvalue":
assert parent["kind"] == "enum"
assert "parent" not in child or child["parent"] == parent_id
child["parent"] = parent_id
else:
if parent["kind"] in ["class", "struct", "union"]:
assert "parent" not in child or child["parent"] == parent_id
child["parent"] = parent_id
if child_id not in parent["children"]:
parent["children"] += [child_id]
compound = root.find("compounddef")
compound_kind = compound.get("kind")
if compound_kind == "group":
for subgroup in compound.findall("innergroup"):
add_child(index, compound.get("id"), subgroup.get("refid"))
for klass in compound.findall("innerclass"):
add_child(index, compound.get("id"), klass.get("refid"))
for section in compound.findall("sectiondef"):
if section.get("kind").startswith("private"):
for member in section.findall("memberdef"):
if member.get("id") in index:
del index[member.get("id")]
else:
for member in section.findall("memberdef"):
member_id = member.get("id")
add_child(index, compound.get("id"), member_id)
if member.get("kind") == "enum":
index[member_id]["strong"] = member.get("strong") == "yes"
for value in member.findall("enumvalue"):
add_child(index, member_id, value.get("id"))
def sphinx_role(record, lang):
"""
Return the Sphinx role used for a record.
This is used for the description directive like ".. c:function::", and
links like ":c:func:`foo`.
"""
kind = record["kind"]
if kind in ["class", "function", "namespace", "struct", "union"]:
return lang + ":" + kind
if kind == "define":
return "c:macro"
if kind == "enum":
return lang + (":enum-class" if record["strong"] else ":enum")
if kind == "typedef":
return lang + ":type"
if kind == "enumvalue":
return lang + ":enumerator"
if kind == "variable":
return lang + (":member" if "parent" in record else ":var")
raise RuntimeError("No known role for kind '%s'" % kind)
def child_identifier(lang, parent_name, child_name):
"""
Return the identifier for an enum value or struct member.
Sphinx, for some reason, uses a different syntax for this in C and C++.
"""
separator = "::" if lang == "cpp" else "."
return "%s%s%s" % (parent_name, separator, child_name)
def link_markup(index, lang, refid):
"""Return a Sphinx link for a Doxygen reference."""
record = index[refid]
kind, name = record["kind"], record["name"]
role = sphinx_role(record, lang)
if kind in ["class", "enum", "struct", "typedef", "union"]:
return ":%s:`%s`" % (role, name)
if kind == "function":
return ":%s:func:`%s`" % (lang, name)
if kind == "enumvalue":
parent_name = index[record["parent"]]["name"]
return ":%s:`%s`" % (role, child_identifier(lang, parent_name, name))
if kind == "variable