[{"data":1,"prerenderedAt":44},["ShallowReactive",2],{"project-plugin-hostd-panel":3},{"page":4,"others":28},{"slug":5,"src":6,"title":6,"source":7,"toc":8,"html":27},"panel","docs\u002Fpanel.md","https:\u002F\u002Fgithub.com\u002FFreeMixer\u002Fplugin-hostd\u002Fblob\u002Fb4ea6a9\u002Fdocs\u002Fpanel.md",[9,12,15,18,21,24],{"id":10,"title":11},"the-verb","The verb",{"id":13,"title":14},"where-the-panel-comes-from","Where the panel comes from",{"id":16,"title":17},"the-file","The file",{"id":19,"title":20},"the-format-tables-stay-strict","The format tables stay strict",{"id":22,"title":23},"why-a-verb-of-its-own","Why a verb of its own",{"id":25,"title":26},"drawing-a-panel-the-three-verbs-together","Drawing a panel: the three verbs together","\u003Ch1 id=\"the-panel-a-plugin-publishes-served-to-a-host\">The panel a plugin publishes, served to a host\u003C\u002Fh1>\n\u003Cp>A plugin of omx-plugins states how its panel is laid out through the CLAP extension\n\u003Ccode>org.openmixer.panel-description\u002F1\u003C\u002Fcode> (omx-clap-core's \u003Ccode>src\u002Fomx_clap_ext.h\u003C\u002Fcode>, omx-plugins' \u003Ccode>include\u002Fomx_panel_description.h\u003C\u002Fcode>):\nits family, a fingerprint, and five tables, the sections, the controls (each with its parameter, section, widget and\nvisibility rule), the roles, the displays and the placement defaults. A host that draws the panel itself, such as\nZynthian's engine, reads it from the daemon with \u003Ccode>panel_get\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"the-verb\">The verb\u003C\u002Fh2>\n\u003Cpre>\u003Ccode>panel_get &lt;uri&gt;          resp 0 &quot;&lt;path&gt;&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>&lt;uri&gt;\u003C\u002Fcode> is the one \u003Ccode>add\u003C\u002Fcode> takes (\u003Ccode>clap:&lt;path&gt;#&lt;id&gt;\u003C\u002Fcode>, \u003Ccode>lv2:&lt;uri&gt;\u003C\u002Fcode>). The reply is the path of the plugin's panel file,\none quoted word as \u003Ccode>descriptor_get\u003C\u002Fcode> answers its descriptor. The codes are \u003Ccode>descriptor_get\u003C\u002Fcode>'s:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Code\u003C\u002Fth>\n\u003Cth>When\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>-529\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>no descriptor, or a descriptor with no panel beside it, for the plugin's binary as it is now: no add since it changed, a binary that cannot be found, a descriptor filed by a daemon that predates \u003Ccode>panel_get\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>-530\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the scan of its add has not filed the panel yet: ask again\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>-531\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the last scan filed no panel: the descriptor failed, or the panel stage did (the scanner states no panel, see below); the next add scans again\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>-528\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>descriptor_dir could not be written\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>-902\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>no \u003Ccode>&lt;uri&gt;\u003C\u002Fcode>, or more than one word\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"where-the-panel-comes-from\">Where the panel comes from\u003C\u002Fh2>\n\u003Cp>The answered \u003Ccode>add\u003C\u002Fcode> of a plugin whose files have no descriptor runs the scan, in a child of its own, as it does for\n\u003Ccode>descriptor_get\u003C\u002Fcode>. The scan has two stages:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Ccode>plugin-hostd-probe describe &lt;target&gt;\u003C\u002Fcode>, whose stdout is the descriptor, filed as\n\u003Ccode>&lt;descriptor_dir&gt;\u002F&lt;id&gt;@&lt;version&gt;-&lt;hash&gt;.json\u003C\u002Fcode>;\u003C\u002Fli>\n\u003Cli>once that file is in place, \u003Ccode>plugin-hostd-probe panel &lt;target&gt;\u003C\u002Fcode>, whose stdout is the panel, filed under the same\nname in \u003Ccode>&lt;descriptor_dir&gt;\u002Fpanels\u002F\u003C\u002Fcode>. Both are written once, atomically.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Ccode>panel\u003C\u002Fcode> runs \u003Ccode>omx-clap-scan --json\u003C\u002Fcode> on the plugin's file (omx-clap-core 0.20.0 or later) and copies the \u003Ccode>panel\u003C\u002Fcode> object\nthe scan prints for the plugin's id, byte for byte. The daemon never reads the plugin's \u003Ccode>decl.json\u003C\u002Fcode> and never calls the\nextension: what the scan printed is what a host gets. An add of a plugin whose descriptor is in place and whose panel\nis not (a descriptor of an older daemon, a panel stage that failed) runs the panel stage alone.\u003C\u002Fp>\n\u003Ch2 id=\"the-file\">The file\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-json\">{&quot;plugin&quot;: {&quot;format&quot;: &quot;clap&quot;, &quot;id&quot;: &quot;org.openmixer.delay&quot;}, &quot;panel&quot;: {&quot;family&quot;: &quot;...&quot;, &quot;fingerprint&quot;: &quot;...&quot;,\n &quot;sections&quot;: [...], &quot;controls&quot;: [...], &quot;roles&quot;: [...], &quot;displays&quot;: [...], &quot;placementDefaults&quot;: [...]}}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>\u003Ccode>panel\u003C\u002Fcode> is the scan's object as omx-clap-scan(1) states it: each entry with the members of its structure, a code by\nomx-plugins' word for it, an id that is none as \u003Ccode>null\u003C\u002Fcode>, a visibility mask as a hex string.\u003C\u002Fli>\n\u003Cli>\u003Ccode>panel\u003C\u002Fcode> is \u003Ccode>null\u003C\u002Fcode> for a plugin that serves no description.\u003C\u002Fli>\n\u003Cli>a table the scan refused for its count (past 4096 entries) stays refused: \u003Ccode>{&quot;refused&quot;: &quot;too-many&quot;, &quot;count&quot;: &lt;n&gt;, &quot;max&quot;: 4096}\u003C\u002Fcode> in place of its array, the other tables as they are.\u003C\u002Fli>\n\u003Cli>an LV2 plugin's \u003Ccode>panel\u003C\u002Fcode> is \u003Ccode>null\u003C\u002Fcode>: the description is a CLAP extension, and nothing is run for it.\u003C\u002Fli>\n\u003Cli>a scan that states no \u003Ccode>panel\u003C\u002Fcode> for the plugin (a scanner before omx-clap-scan 0.20.0, a plugin that fails to\ninitialise) is refused, and \u003Ccode>panel_get\u003C\u002Fcode> answers \u003Ccode>-531\u003C\u002Fcode>: a missing panel is never read as a null one.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The field dump (\u003Ccode>omx-clap-scan --fields\u003C\u002Fcode>) prints the same description under its own scopes; the format table drops\neach of those fields (docs\u002Fformats.md#what-a-table-drops), so the descriptor carries none of it.\u003C\u002Fp>\n\u003Ch2 id=\"the-format-tables-stay-strict\">The format tables stay strict\u003C\u002Fh2>\n\u003Cp>A scan scope or field the format table has no row for is refused, and the add of that plugin answers \u003Ccode>-531\u003C\u002Fcode> for its\npanel. A new scope or field of omx-clap-scan therefore needs its row in \u003Ccode>data\u002Fformats\u002Fclap.json\u003C\u002Fcode> first, with the rule\nthat says whether the descriptor carries it or drops it; a daemon is not to learn a scope by ignoring what it does not\nknow. Whether a refusal that costs one plugin its whole panel should rise to a single, higher-altitude refusal is a\ndesign question of its own; until it is decided the tables stay strict.\u003C\u002Fp>\n\u003Ch2 id=\"why-a-verb-of-its-own\">Why a verb of its own\u003C\u002Fh2>\n\u003Cp>The panel could have been a word more of \u003Ccode>descriptor_get\u003C\u002Fcode>'s reply, or a member of the descriptor. The protocol's own\nconventions decide against both:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Reply size. A message is at most \u003Ccode>line_max\u003C\u002Fcode>, 4096 bytes, the socket's buffer, and one panel table may hold 4096\nentries. A panel never fits a reply, so it is a file the reply names, as the descriptor is.\u003C\u002Fli>\n\u003Cli>No optional reply words. Every reply of the protocol is one fixed grammar, word by word (the protocol JSON's\n\u003Ccode>reply\u003C\u002Fcode>); what is optional is a verb, answered or refused with \u003Ccode>-902\u003C\u002Fcode>, never a field a client must guess the\npresence of. A second path in \u003Ccode>descriptor_get\u003C\u002Fcode>'s reply would change a \u003Ccode>required\u003C\u002Fcode> verb's grammar for every client\nthat parses it. A new verb leaves \u003Ccode>descriptor_get\u003C\u002Fcode> as it is, and a daemon that predates \u003Ccode>panel_get\u003C\u002Fcode> answers it\n\u003Ccode>-902\u003C\u002Fcode>, which no client misreads.\u003C\u002Fli>\n\u003Cli>The descriptor is omx-contract's document. A \u003Ccode>scanned\u003C\u002Fcode> descriptor follows \u003Ccode>omx-contract.schema.json\u003C\u002Fcode>, which has no\npanel; the descriptor file stays what plugin-qualify and omx-contract's reader read.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>So \u003Ccode>panel_get\u003C\u002Fcode> is a daemon verb, required for every profile like \u003Ccode>descriptor_get\u003C\u002Fcode>, with its file beside the\ndescriptor's and the same codes.\u003C\u002Fp>\n\u003Ch2 id=\"drawing-a-panel-the-three-verbs-together\">Drawing a panel: the three verbs together\u003C\u002Fh2>\n\u003Cp>A native renderer combines what the daemon serves; none of the three replaces another.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What\u003C\u002Fth>\n\u003Cth>Verb\u003C\u002Fth>\n\u003Cth>Keyed by\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>the structure: sections, which control is in which, its widget, when it is shown, roles, displays\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>panel_get &lt;uri&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the plugin's files\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>what a value means: unit, scale, range, default, step, the stable symbol\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>param_info &lt;instance&gt; &lt;symbol&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the running instance\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>the pages of 8 a hardware surface steps through\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>remote_pages &lt;instance&gt;\u003C\u002Fcode>, \u003Ccode>remote_page_get &lt;instance&gt; &lt;page&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the running instance\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Col>\n\u003Cli>After \u003Ccode>add &lt;uri&gt; &lt;instance&gt;\u003C\u002Fcode>, ask \u003Ccode>panel_get &lt;uri&gt;\u003C\u002Fcode> until it is no \u003Ccode>-530\u003C\u002Fcode>. Only a \u003Ccode>null\u003C\u002Fcode> panel, and an LV2 plugin\nuntil its bundle is read for a panel, mean the plugin states no layout: draw the\ncontrols in parameter order, as a host did before. Every other code is a failure to show, not a plugin without a\nlayout: \u003Ccode>-531\u003C\u002Fcode> is a panel stage that failed and \u003Ccode>-528\u003C\u002Fcode> a descriptor directory that could not be written, and the\nplugin may well have a layout. Name the failure to the user, and draw in parameter order only as the fallback.\u003C\u002Fli>\n\u003Cli>For each control of \u003Ccode>panel.controls\u003C\u002Fcode> (in table order), its \u003Ccode>paramId\u003C\u002Fcode> is the CLAP parameter id, the number\n\u003Ccode>omx-clap-scan\u003C\u002Fcode> lists a parameter by and the \u003Ccode>&lt;symbol&gt;\u003C\u002Fcode> the instance verbs take for a CLAP instance. The daemon does not parse that word:\n\u003Ccode>sup_instance_verb\u003C\u002Fcode> (src\u002Fsupervisor.c) passes the line of a format with native instance verbs, CLAP among them, to the\nworker untouched, and omx-clap-host(1) states that it addresses a CLAP parameter by its decimal CLAP id.\n\u003Ccode>param_info &lt;instance&gt; &lt;paramId&gt;\u003C\u002Fcode> gives its unit and scale,\nso a knob shows \u003Ccode>dB\u003C\u002Fcode> and moves on its own scale; \u003Ccode>-511\u003C\u002Fcode> means the worker holds no checked contract, and the range\nand default come from \u003Ccode>omx-clap-scan\u003C\u002Fcode>'s parameter list.\u003C\u002Fli>\n\u003Cli>A control whose \u003Ccode>visibleParamId\u003C\u002Fcode> is not \u003Ccode>null\u003C\u002Fcode> is shown while bit \u003Ccode>v\u003C\u002Fcode> of \u003Ccode>visibleMask\u003C\u002Fcode> is set, \u003Ccode>v\u003C\u002Fcode> the value of that\nparameter (\u003Ccode>param_get\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>\u003Ccode>remote_page_get\u003C\u002Fcode> gives the plugin's own grouping in eights, each slot a symbol: a surface with eight encoders\npages through those, the panel's sections and widgets dressing each slot.\u003C\u002Fli>\n\u003Cli>A refused table is drawn as the plugin having none of it, and named to the user: its count is in the refusal.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>What the panel cannot give yet: an enum's labels and the remote pages in the scan itself;\nuntil then a choice's labels come from the worker (\u003Ccode>param_info\u003C\u002Fcode> and the plugin's value texts) and the pages from\n\u003Ccode>remote_page_get\u003C\u002Fcode>, both on a running instance.\u003C\u002Fp>\n",[29,32,35,38,41],{"slug":30,"title":31},"","README.md",{"slug":33,"title":34},"formats","docs\u002Fformats.md",{"slug":36,"title":37},"own-host","the highest SCHED_FIFO priority, which the probe asks for, and a lock as large as the probe's memory limit",{"slug":39,"title":40},"placement","docs\u002Fplacement.md",{"slug":42,"title":43},"presets","Presets in the host",1793954996705]