Wires the web process extension into the kiosk browser's command line.
There is no process and no supervision tree here — the whole runtime lives in
the web process extension that Cog loads. This module answers three questions
and nothing else: where the .so is, which directories it should scan, and
what the device wants configured.
Usage
Merge two things into however you already start the browser —
browser_args/0 for the command line, browser_env/1 for the environment:
args = ["--platform=drm", url] ++ Myelin.browser_args()
env = [{"XDG_RUNTIME_DIR", runtime_dir}] ++ Myelin.browser_env()
MuonTrap.Daemon.start_link("cog", args, env: env)Scripts are read once, when the web process starts, so changes take effect after Cog restarts.
Nothing runs until you ask for it
The scripts that ship with this library are in bundled_dir/0, which is already
on the search path — but every one of them is dormant. Switching one on is a
line of configuration, and that is the whole install:
config :myelin,
scripts: %{
"keyboard" => %{enabled: true},
"screensaver" => %{enabled: true}
}Copy one into your own application only when you want to change it —
mix myelin.copy keyboard — and the copy replaces the shipped version,
because later entries on the search path win.
Configuration
Two things are set per device, both read from your application's config:
config :myelin,
trusted_origins: ["http://localhost:4000"],
scripts: %{"screensaver" => %{enabled: true, idle: 300}}The keys under scripts are directory names.
trusted_origins decides where <meta name="myelin-…"> tags may configure
anything. It starts empty, so on a page you have not listed the device
configuration is the only thing that counts — which is the point on a kiosk that
visits pages you do not control.
Both travel to the extension in MYELIN_CONFIG. That is not a
flourish: the extension runs in Cog's web process, not on the BEAM, so it
cannot call Application.get_env/2. A file or the process environment are the
only two channels, and a file would mean writing one at boot.
Summary
Functions
Whether the extension was built.
Arguments to append to the kiosk browser's command line.
Environment to merge into the kiosk browser's.
Directory of the scripts that ship with this library.
The device configuration, as it will be encoded.
Directory to pass to Cog's --web-extensions-dir.
Full path of the extension shared object.
Directories the extension scans, in order. Later entries win.
Functions
@spec available?() :: boolean()
Whether the extension was built.
False on MIX_TARGET=host, where the Makefile skips the native build.
@spec browser_args() :: [String.t()]
Arguments to append to the kiosk browser's command line.
With Cog that is --web-extensions-dir=…, pointed at extension_dir/0.
Returns [] when the extension is not built, so a host build does not point
the browser at a directory that does not exist.
Environment to merge into the kiosk browser's.
Takes the same :extra option as script_path/1:
Myelin.browser_env(extra: [Application.app_dir(:my_app, "priv/myelin")])Two variables:
MYELIN_PATH— the search path fromscript_path/1, colon separated. Always set, because the bundled scripts are always on it.MYELIN_CONFIG—config/0as JSON, when anything is configured.
Reading is all this does: no file is written, nothing is created, no process starts.
Also set {"G_MESSAGES_DEBUG", "myelin"} to have the extension log
which manifests it found and which scripts it injected.
OTP 27
The JSON is encoded with :json, which arrived in OTP 27. That keeps this
library free of a JSON dependency; the cost is the floor.
@spec bundled_dir() :: String.t()
Directory of the scripts that ship with this library.
It is on the search path already, so switching one on takes configuration and nothing else:
config :myelin, scripts: %{"keyboard" => %{enabled: true}}
@spec config() :: map()
The device configuration, as it will be encoded.
Contains only what is actually set, so an application that configures nothing
gets %{}. Useful for seeing what a release will hand over:
mix run -e 'IO.inspect Myelin.config()'
@spec extension_dir() :: String.t()
Directory to pass to Cog's --web-extensions-dir.
It holds nothing but the extension, because WebKit loads every .so it
finds there.
@spec extension_path() :: String.t()
Full path of the extension shared object.
Directories the extension scans, in order. Later entries win.
bundled_dir/0 comes first, then anything you configure, then anything you
pass. So a copy of a shipped script in your own application replaces the
shipped one, which is what mix myelin.copy is for:
config :myelin, extra_dirs: ["/opt/my_app/myelin"]
Myelin.script_path(extra: [Application.app_dir(:my_app, "priv/myelin")])To iterate on a device without a firmware build — scp a script over, restart
Cog — add the writable partition:
config :myelin, extra_dirs: ["/data/myelin"]