From c878b68adf9c150bed43b6a5eb354e023cddbe7e Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 13 Oct 2016 20:53:21 -0400 Subject: Clean up protocol documentation --- doc/style.css | 456 +++++++++++++++++++++++++++++++++++++++++++- src/AtomWriter.cpp | 135 +++++++++---- src/server/events/Delta.cpp | 28 --- 3 files changed, 556 insertions(+), 63 deletions(-) diff --git a/doc/style.css b/doc/style.css index 56a87164..172be516 100644 --- a/doc/style.css +++ b/doc/style.css @@ -131,6 +131,7 @@ dl.el { .fragment { font-family: monospace, fixed; font-size: 105%; + padding-bottom: 1em; } pre.fragment { @@ -311,7 +312,7 @@ h2.groupheader { .memItemLeft,.memItemRight,.memTemplParams { border: 0; - font-family: monospace; + font-family: monospace, fixed; font-size: 90%; } @@ -681,15 +682,464 @@ th { .tablist a:hover { color: #fff; - text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0); text-decoration: none; } .tablist li.current a { color: #fff; - text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0); } span.icon { display: none; } + +/* nav bar */ + +.sm { + position: relative; + z-index: 9999; +} + +.sm,.sm ul,.sm li { + display: block; + list-style: none; + margin: 0; + padding: 0; + line-height: normal; + direction: ltr; + text-align: left; + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +.sm-rtl,.sm-rtl ul,.sm-rtl li { + direction: rtl; + text-align: right; +} + +.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6 { + margin: 0; + padding: 0; +} + +.sm ul { + display: none; +} + +.sm li,.sm a { + position: relative; +} + +.sm a { + display: block; +} + +.sm a.disabled { + cursor: not-allowed; +} + +.sm:after { + content: "\00a0"; + display: block; + height: 0; + font: 0/0 serif; + clear: both; + visibility: hidden; + overflow: hidden; +} + +.sm,.sm *,.sm :before,.sm :after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +#doc-content { + overflow: auto; + display: block; + padding: 0; + margin: 0; + -webkit-overflow-scrolling: touch; +} + +.sm-dox { + background-image: none; + background-color: #333; + background: linear-gradient(to bottom, #333 0%, #111 100%); + color: #fff; +} + +.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active { + padding: 0 12px; + padding-right: 43px; + font-size: small; + font-weight: 600; + line-height: auto; + text-decoration: none; + text-shadow: none; + color: inherit; + outline: 0; +} + +.sm-dox a:hover { + background-image: none; + background-repeat: repeat-x; + color: inherit; + text-shadow: 0 1px 1px #000; +} + +.sm-dox a.current { + color: #d23600; +} + +.sm-dox a.disabled { + color: #bbb; +} + +.sm-dox a span.sub-arrow { + position: absolute; + top: 50%; + margin-top: -14px; + left: auto; + right: 3px; + width: 28px; + height: 28px; + overflow: hidden; + font: bold 12px/28px monospace !important; + text-align: center; + text-shadow: none; + background: rgba(255,255,255,0.5); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +.sm-dox a.highlighted span.sub-arrow:before { + display: block; + content: '-'; +} + +.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a { + -moz-border-radius: 5px 5px 0 0; + -webkit-border-radius: 5px; + border-radius: 5px 5px 0 0; +} + +.sm-dox>li:last-child>a,.sm-dox>li:last-child>:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul { + -moz-border-radius: 0 0 5px 5px; + -webkit-border-radius: 0; + border-radius: 0 0 5px 5px; +} + +.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>:not(ul) a.highlighted { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} + +.sm-dox ul { + background: rgba(162,162,162,0.1); +} + +.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active { + font-size: 12px; + border-left: 8px solid transparent; + line-height: auto; + text-shadow: none; + background-color: #fff; + background-image: none; +} + +.sm-dox ul a:hover { + background-image: none; + background-repeat: repeat-x; + color: inherit; + text-shadow: 0 1px 1px #000; +} + +.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active { + border-left: 16px solid transparent; +} + +.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active { + border-left: 24px solid transparent; +} + +.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active { + border-left: 32px solid transparent; +} + +.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active { + border-left: 40px solid transparent; +} + +@media(min-width:768px) { + .sm-dox ul { + position: absolute; + width: 12em; + } + + .sm-dox li { + float: left; + } + + .sm-dox.sm-rtl li { + float: right; + } + + .sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li { + float: none; + } + + .sm-dox a { + white-space: nowrap; + } + + .sm-dox ul a,.sm-dox.sm-vertical a { + white-space: normal; + } + + .sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a { + white-space: nowrap; + } + + .sm-dox { + padding: 0 10px; + background-image: none; + background-color: #000; + line-height: normal; + background-image: none; + background-color: #333; + background: linear-gradient(to bottom, #333 0%, #111 100%); + color: #ddd; + } + + .sm-dox a span.sub-arrow { + top: 50%; + margin-top: -2px; + right: 12px; + width: 0; + height: 0; + border-width: 4px; + border-style: solid dashed dashed; + border-color: #ddd transparent transparent; + background: transparent; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + } + + .sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted { + padding: 0 1em 0 0; + background-image: none; + background-repeat: no-repeat; + background-position: right; + -moz-border-radius: 0 !important; + -webkit-border-radius: 0; + border-radius: 0 !important; + } + + .sm-dox a:hover { + background-image: none; + background-repeat: repeat-x; + color: #fff; + text-shadow: 0 1px 1px #000; + } + + .sm-dox a:hover span.sub-arrow { + border-color: #fff transparent transparent; + } + + .sm-dox a.has-submenu { + padding-right: 24px; + } + + .sm-dox li { + border-top: 0; + } + + .sm-dox>li>ul:before,.sm-dox>li>ul:after { + content: ''; + position: absolute; + top: -18px; + left: 30px; + width: 0; + height: 0; + overflow: hidden; + border-width: 9px; + border-style: dashed dashed solid; + border-color: transparent transparent #bbb; + } + + .sm-dox>li>ul:after { + top: -16px; + left: 31px; + border-width: 8px; + border-color: transparent transparent #fff; + } + + .sm-dox ul { + border: 1px solid #bbb; + padding: 5px 0; + background: initial; + -moz-border-radius: 5px !important; + -webkit-border-radius: 5px; + border-radius: 5px !important; + -moz-box-shadow: 0 5px 9px rgba(0,0,0,0.2); + -webkit-box-shadow: 0 5px 9px rgba(0,0,0,0.2); + box-shadow: 0 5px 9px rgba(0,0,0,0.2); + } + + .sm-dox ul a span.sub-arrow { + right: 8px; + top: 50%; + margin-top: -5px; + border-width: 5px; + border-color: transparent transparent transparent #555; + border-style: dashed dashed dashed solid; + } + + .sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted { + color: #555; + background-image: none; + border: 0 !important; + color: #555; + background-image: none; + } + + .sm-dox ul a:hover { + background-image: none; + background-repeat: repeat-x; + color: #fff; + text-shadow: 0 1px 1px #000; + } + + .sm-dox ul a:hover span.sub-arrow { + border-color: transparent transparent transparent #fff; + } + + .sm-dox span.scroll-up,.sm-dox span.scroll-down { + position: absolute; + display: none; + visibility: hidden; + overflow: hidden; + background: initial; + height: 36px; + } + + .sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover { + background: #eee; + } + + .sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow { + border-color: transparent transparent #d23600; + } + + .sm-dox span.scroll-down:hover span.scroll-down-arrow { + border-color: #d23600 transparent transparent; + } + + .sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow { + position: absolute; + top: 0; + left: 50%; + margin-left: -6px; + width: 0; + height: 0; + overflow: hidden; + border-width: 6px; + border-style: dashed dashed solid; + border-color: transparent transparent #555; + } + + .sm-dox span.scroll-down-arrow { + top: 8px; + border-style: solid dashed dashed; + border-color: #555 transparent transparent; + } + + .sm-dox.sm-rtl a.has-submenu { + padding-right: 12px; + padding-left: 24px; + } + + .sm-dox.sm-rtl a span.sub-arrow { + right: auto; + left: 12px; + } + + .sm-dox.sm-rtl.sm-vertical a.has-submenu { + padding: 10px 20px; + } + + .sm-dox.sm-rtl.sm-vertical a span.sub-arrow { + right: auto; + left: 8px; + border-style: dashed solid dashed dashed; + border-color: transparent #555 transparent transparent; + } + + .sm-dox.sm-rtl>li>ul:before { + left: auto; + right: 30px; + } + + .sm-dox.sm-rtl>li>ul:after { + left: auto; + right: 31px; + } + + .sm-dox.sm-rtl ul a.has-submenu { + padding: 10px 20px !important; + } + + .sm-dox.sm-rtl ul a span.sub-arrow { + right: auto; + left: 8px; + border-style: dashed solid dashed dashed; + border-color: transparent #555 transparent transparent; + } + + .sm-dox.sm-vertical { + padding: 10px 0; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + } + + .sm-dox.sm-vertical a { + padding: 10px 20px; + } + + .sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted { + background: initial; + } + + .sm-dox.sm-vertical a.disabled { + background-image: none; + } + + .sm-dox.sm-vertical a span.sub-arrow { + right: 8px; + top: 50%; + margin-top: -5px; + border-width: 5px; + border-style: dashed dashed dashed solid; + border-color: transparent transparent transparent #555; + } + + .sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after { + display: none; + } + + .sm-dox.sm-vertical ul a { + padding: 10px 20px; + } + + .sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted { + background: #eee; + } + + .sm-dox.sm-vertical ul a.disabled { + background: initial; + } +} diff --git a/src/AtomWriter.cpp b/src/AtomWriter.cpp index 584b17b7..f2687fab 100644 --- a/src/AtomWriter.cpp +++ b/src/AtomWriter.cpp @@ -14,6 +14,39 @@ along with Ingen. If not, see . */ +/** @page protocol Ingen Protocol + * @tableofcontents + * + * @section introduction Controlling Ingen + * + * Ingen is controlled via a simple RDF-based protocol. This conceptual + * protocol can be used in two concrete ways: + * + * 1. When Ingen is running as a process, a socket accepts messages in + * (textual) Turtle syntax, and responds in the same syntax. Transfers are + * delimited by NULL characters in the stream, so the client knows when to + * finish parsing to interpret responses. By default, Ingen listens on + * unix:///tmp/ingen.sock and tcp://localhost:16180 + * + * 2. When Ingen is running as an LV2 plugin, the control and notify ports + * accept and respond using (binary) LV2 atoms. Messages are read and written + * as events with atom:Object bodies. The standard rules for LV2 event + * transmission apply, but note that the graph manipulations described here are + * executed asynchronously and not with sample accuracy, so the response may + * come at a later frame or cycle. + * + * For documentation purposes, this page discusses messages in Turtle syntax, + * but the same protocol is used in the LV2 case, just in a more compact binary + * encoding. + * + * Conceptually, Ingen represents a tree of objects, each of which has a path + * (like /main/in or /main/osc/out) and a set of properties. A property is a + * URI key and some value. All changes to Ingen are represented as + * manipulations of this tree of dictionaries. The core of the protocol is + * based on the LV2 patch extension, which defines several messages for + * manipulating data in this model which resemble HTTP methods. + */ + #include #include #include @@ -70,6 +103,17 @@ AtomWriter::finish_msg() _out.len = 0; } +/** @page protocol + * @subsection Bundles + * + * An [ingen:BundleStart](http://drobilla.net/ns/ingen#BundleStart) marks the + * start of a bundle in the operation stream. A bundle groups a series of + * messages for coarse-grained undo or atomic execution. + * + * @code{.ttl} + * [] a ingen:BundleStart . + * @endcode + */ void AtomWriter::bundle_begin() { @@ -79,6 +123,15 @@ AtomWriter::bundle_begin() finish_msg(); } +/** @page protocol + * + * An [ingen:BundleEnd](http://drobilla.net/ns/ingen#BundleEnd) marks the end + * of a bundle in the operation stream. + * + * @code{.ttl} + * [] a ingen:BundleEnd . + * @endcode + */ void AtomWriter::bundle_end() { @@ -135,16 +188,11 @@ AtomWriter::forge_request(LV2_Atom_Forge_Frame* frame, LV2_URID type) } } -/** @page protocol Ingen Protocol - * @tableofcontents - * - * @section methods Methods - */ - /** @page protocol + * @section methods Methods * @subsection Put * - * Use [patch:Put](http://lv2plug.in/ns/ext/patch#Put) to set properties on an + * Send a [Put](http://lv2plug.in/ns/ext/patch#Put) to set properties on an * object, creating it if necessary. * * If the object already exists, all existing object properties with keys that @@ -152,7 +200,7 @@ AtomWriter::forge_request(LV2_Atom_Forge_Frame* frame, LV2_URID type) * left unchanged. * * If the object does not yet exist, the message must contain sufficient - * information to create it (e.g. supported rdf:type properties). + * information to create it, including at least one rdf:type property. * * @code{.ttl} * [] @@ -187,7 +235,7 @@ AtomWriter::put(const Raul::URI& uri, /** @page protocol * @subsection Patch * - * Use [patch:Patch](http://lv2plug.in/ns/ext/patch#Patch) to manipulate the + * Send a [Patch](http://lv2plug.in/ns/ext/patch#Patch) to manipulate the * properties of an object. A set of properties are first removed, then * another is added. Analogous to WebDAV PROPPATCH. * @@ -239,17 +287,17 @@ AtomWriter::delta(const Raul::URI& uri, /** @page protocol * @subsection Copy * - * Use [patch:Copy](http://lv2plug.in/ns/ext/copy#Copy) to copy an object from + * Send a [Copy](http://lv2plug.in/ns/ext/copy#Copy) to copy an object from * its current location (subject) to another (destination). * * If both the subject and destination are inside Ingen, like block paths, then the old object * is copied by, for example, creating a new plugin instance. * - * If the subject is a path and the destination is inside Ingen, then the - * subject must be an Ingen graph file or bundle, which is loaded to the - * specified destination path. + * If the subject is a filename (file URI or atom:Path) and the destination is + * inside Ingen, then the subject must be an Ingen graph file or bundle, which + * is loaded to the specified destination path. * - * If the subject is inside Ingen and the destination is a path, then the + * If the subject is inside Ingen and the destination is a filename, then the * subject is saved to an Ingen bundle at the given destination. * * @code{.ttl} @@ -276,8 +324,8 @@ AtomWriter::copy(const Raul::URI& old_uri, /** @page protocol * @subsection Move * - * Use [patch:Move](http://lv2plug.in/ns/ext/move#Move) to move an object from - * its current location (subject) to another (destination). + * Send a [Move](http://lv2plug.in/ns/ext/move#Move) to move an object from its + * current location (subject) to another (destination). * * Both subject and destination must be paths in Ingen with the same parent, * moving between graphs is currently not supported. @@ -306,7 +354,7 @@ AtomWriter::move(const Raul::Path& old_path, /** @page protocol * @subsection Delete * - * Use [patch:Delete](http://lv2plug.in/ns/ext/delete#Delete) to remove an + * Send a [Delete](http://lv2plug.in/ns/ext/delete#Delete) to remove an * object from the engine and destroy it. * * All properties of the object are lost, as are all references to the object @@ -332,7 +380,7 @@ AtomWriter::del(const Raul::URI& uri) /** @page protocol * @subsection Set * - * Use [patch:Set](http://lv2plug.in/ns/ext/patch#Set) to set a property on an + * Send a [Set](http://lv2plug.in/ns/ext/patch#Set) to set a property on an * object. Any existing value for that property is removed. * * @code{.ttl} @@ -366,7 +414,9 @@ AtomWriter::set_property(const Raul::URI& subject, * @subsection Undo * * Use [ingen:Undo](http://drobilla.net/ns/ingen#Undo) to undo the last change - * to the engine. + * to the engine. Undo transactions can be delimited using bundle markers, if + * the last operations(s) received were in a bundle, then an Undo will undo the + * effects of that entire bundle. * * @code{.ttl} * [] a ingen:Undo . @@ -384,8 +434,7 @@ AtomWriter::undo() /** @page protocol * @subsection Undo * - * Use [ingen:Redo](http://drobilla.net/ns/ingen#Redo) to undo the last change - * to the engine. + * Use [ingen:Redo](http://drobilla.net/ns/ingen#Redo) to redo the last undone change. * * @code{.ttl} * [] a ingen:Redo . @@ -403,7 +452,7 @@ AtomWriter::redo() /** @page protocol * @subsection Get * - * Use [patch:Get](http://lv2plug.in/ns/ext/patch#Get) to get the description + * Send a [Get](http://lv2plug.in/ns/ext/patch#Get) to get the description * of the subject. * * @code{.ttl} @@ -429,7 +478,7 @@ AtomWriter::get(const Raul::URI& uri) */ /** @page protocol - * @subsection Connect Connecting Two Ports + * @subsection Connect Connecting Ports * * Ports are connected by putting an arc with the desired tail (an output port) * and head (an input port). The tail and head must both be within the @@ -461,7 +510,7 @@ AtomWriter::connect(const Raul::Path& tail, } /** @page protocol - * @subsection Disconnect Disconnecting Two Ports + * @subsection Disconnect Disconnecting Ports * * Ports are disconnected by deleting the arc between them. The description of * the arc is the same as in the put command used to create the connection. @@ -489,14 +538,12 @@ AtomWriter::disconnect(const Raul::Path& tail, } /** @page protocol - * @subsection DisconnectAll Fully Disconnecting an Object + * @subsection DisconnectAll Fully Disconnecting Anything * - * Disconnect a port completely is similar to disconnecting a specific port, - * but rather than specifying a specific tail and head, the special property - * ingen:incidentTo is used to specify any arc that is connected to a port or - * block in either direction. This works with ports and blocks (including - * graphs, which act as blocks for the purpose of this operation and are not - * modified internally). + * All arcs to or from anything can be removed by giving the special property + * ingen:incidentTo rather than a specific head and tail. This works for both + * ports and blocks (where the effect is to disconnect everything from ports on + * that block). * * @code{.ttl} * [] @@ -593,7 +640,31 @@ AtomWriter::error(const std::string& msg) } /** @page protocol - * @tableofcontents + * @section loading Loading and Unloading Bundles + * + * The property ingen:loadedBundle on the engine can be used to load + * or unload bundles from Ingen's world. For example: + * + * @code{.ttl} + * # Load /old.lv2 + * [] + * a patch:Put ; + * patch:subject ; + * patch:body [ + * ingen:loadedBundle + * ] . + * + * # Replace /old.lv2 with /new.lv2 + * [] + * a patch:Patch ; + * patch:subject ; + * patch:remove [ + * ingen:loadedBundle + * ]; + * patch:add [ + * ingen:loadedBundle + * ] . + * @endcode */ } // namespace Ingen diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 62d08367..d2c56f0c 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -126,34 +126,6 @@ get_file_node(LilvWorld* lworld, const URIs& uris, const Atom& value) return NULL; } -/** @page protocol - * @subsection loading Loading and Unloading Bundles - * - * The property ingen:loadedBundle on the engine can be used to load - * or unload bundles from Ingen's world. For example: - * - * @code{.ttl} - * # Load /old.lv2 - * [] - * a patch:Put ; - * patch:subject ; - * patch:body [ - * ingen:loadedBundle - * ] . - * - * # Replace /old.lv2 with /new.lv2 - * [] - * a patch:Patch ; - * patch:subject ; - * patch:remove [ - * ingen:loadedBundle - * ]; - * patch:add [ - * ingen:loadedBundle - * ] . - * @endcode - */ - bool Delta::pre_process(PreProcessContext& ctx) { -- cgit v1.2.1