summaryrefslogtreecommitdiffstats
path: root/utils/plymouth-plugin-ucode/files/README.md
blob: 6aed0e394b8ed01a16b0c15f2e7ecb141bf626de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Writing a ucode plymouth theme

A theme is one ucode program. The plugin compiles it when the splash is
shown, calls the object it returns, and gives it a plutovg canvas over
plymouth's own pixel buffer.

## The theme file

`ScriptFile` in the `.plymouth` file points at it. The program runs once
and must **return** an object; top-level function declarations stay
local to the program and are not visible to the plugin.

    import * as pvg from 'plutovg';

    function setup(mode) { }
    function frame(t) { }
    function paint(cv, x, y, w, h) { }
    function event(name, detail) { }

    return { setup: setup, frame: frame, paint: paint, event: event };

`setup(mode)` runs once, with the mode plymouth was started in
(`boot-up`, `shutdown`, `reboot`, `system-upgrade`, ...).

`frame(elapsed)` runs at `FrameRate`, default 30 Hz. It advances state
and declares what changed; it must not draw.

The splash starts in preinit, before the overlay is mounted and before
there is a bus to watch, so a theme has two phases to fill. The shipped
one spends the first growing its graph, a node at a time, for as long as
the phase lasts; `ubus-connected` completes whatever is left of it at
once and opens the second, where the traffic drawn is the traffic on the
bus.

`paint(canvas, x, y, w, h)` draws one damaged rectangle. The canvas is
clipped to it and is valid only for the duration of the call: keeping a
reference to it and drawing later writes into a buffer plymouth may have
moved on from.

`event(name, detail)` receives what the system is doing, see below.

## Damage is the contract

Nothing reaches the screen unless the theme asks for it. Call
`plymouth.damage(x, y, w, h)` for every region that changed since the
last frame, including the region something moved *away* from. The plugin
turns each call into a `ply_pixel_display_draw_area()` and plymouth
calls `paint()` back for that region.

Damaging the whole screen every frame works and is slow: a full 1080p
repaint of a busy scene costs about 27 ms against 0.06 ms for the
handful of boxes that actually changed. Damage tightly.

## The plymouth object

    plymouth.screens()     array of { width, height, scale }
    plymouth.damage(x, y, w, h)
    plymouth.mode()        the mode string
    plymouth.theme_dir()   where the theme was installed
    plymouth.elapsed()     seconds since the splash appeared
    plymouth.log(msg)      to plymouth's trace output

## Events

`event(name, detail)`:

    setup-time      mode passed to setup() instead
    progress        { duration, fraction } from plymouth
    status          a status line
    output          console output during boot
    message         a message plymouth wants shown
    system-update   an integer percentage, during sysupgrade
    idle            plymouth is handing the display over
    ubus-connected  the splash reached ubus
    ubus            { type, data, path, present }

`ubus` covers both the live event stream and, with `present` set, the
objects that already existed when the splash connected. Objects that
registered before then are state rather than events and would otherwise
never be seen: on a Rock 5B `service`, `system` and every `hotplug.*`
object exist before preinit runs.

`data` is the raw JSON of the event. For `ubus.object.add` the `path`
field is provided directly, so a theme does not have to parse it.

## Pictograms

A theme draws a pictogram per startup stage from its own
`pictograms/<name>.svg`, named after the init script in `/etc/init.d`.
A package that is not part of the theme drops one in as

    /usr/share/plymouth/pictograms/<init script>.svg

and needs nothing else: no registration, and no variable in the init
script.

Which services exist is not knowable before the overlay is mounted, so
the shipped theme reads `/etc/rc.d/S*` once the bus comes up and shows a
stage, in start order, for every enabled service it finds a pictogram
for. Enabled but not yet started is drawn dark; the stage lights when
the service's own ubus object appears. A service that is not installed
is never drawn, and neither is one with no pictogram. Nothing at all is
drawn before that: the first phase is the graph by itself.

A stage also appears under a bare ubus object name, so a service that
rc.d does not start can still be shown by a pictogram named after its
object.

Some services announce nothing on the bus at all. sysntpd is the usual
one: it reports a sync by calling `hotplug.ntp`, which is a ubus method
call rather than a broadcast, so no listener ever sees it. Such a
service reports itself with a status line instead,

    plymouth update --status="service:<init script>"

from wherever it does have a hook, and the theme lights that stage. The
shipped theme installs exactly that as /etc/hotplug.d/ntp/00-plymouth.

## Measuring

`plymouth-theme-bench <theme dir> [width] [height] [frames] [png]` runs
a theme against an offscreen surface through the same contract and
reports setup, frame and paint cost with the share of the screen
repainted per frame. It needs no display, so a theme can be compared
across targets.