User:DerpDays/Plymouth/Theming

From Gentoo Wiki
Jump to:navigation Jump to:search

This is a guide on how to create themes for plymouth: a bootsplash for system boot/shutdown

Defining a theme

Each theme should have its own directory within the root plymouth themes directory which can be typically found at /usr/share/plymouth/themes, however this is dependent on the data directory passed to meson whilst building plymouth[1]. This tutorial will assume that the user is using the default theme location.

For a theme to be recognised by plymouth, a theme_name.plymouth file specimen is required to be present within the directory of the theme in order to define:

  • Theme name
  • Theme module/plugin (and the selected module's options)
  • Theme description

The rest of this tutorial will assume that the user is creating a theme called example, and all files will be assumed to be placed within it's relevant theme directory e.g. /usr/share/plymouth/themes/example/

CODE example.plymouth (general layout)
[Plymouth-Theme]
Name=Example
Description=This will be an example plugin
ModuleName=selected-module-name

[selected-module-name]
...module options

Theme Modules

Currently there are 7 different theme modules available which can also referred to as splash plugins, however this guide will only go into details about the script module since it is the most customisable, the rest of the modules offer limited capabilities/customisation with most being created specifically for default themes that already exist:

Module Name Customisability Description Implementation
script most customisable This allows for a script to be ran which has access to various functions to display sprites and text across the screen, it is the most customisable as a user can implement their own logic for the theme including complex animations. This will be the main focus of this guide. Source Code
two-step basic customisation This displays a two-step animation based on the loading of a two phased boot process, it includes a progress animation synced to the boot time, and finishes with a fast one-shot animation. Source Code
space-flares slighty (can swap images & font) Used for the solar theme: "Space theme with violent flaring blue star" Source Code
fade-throbber none Used for the fade-in theme: "Simple theme that fades in and out with shimmering stars" Source Code
tribar none Used for the tribar theme: "text mode theme with tricolor progress bar" Source Code
text none This is used for the text theme: "text-only boot splash" Source Code
details none This is used for the details theme which is the verbose text fallback theme Source Code

Writing a script theme

Plymouth uses its own language implementation for scripts, it is a C-style language, it is recommended to read the plymouth documentation on it's language which can help the user get a general understanding of how to write a script.

Specimen

To define a script theme, you use script as the module name in the example.plymouth file.

The script module has four options that can be defined:

  • ScriptFile: An absolute path to the script file.
  • ImageDir: An absolute path to a directory.
  • MonospaceFont: The font to use in text sprites (the font needs to be available in the initramfs). (optional)
  • ConsoleLogTextColor: The color to use for console logs (in hex: 0xffffff) (optional)
CODE example.plymouth
[Plymouth-Theme]
Name=Example
Description=This will be an example plugin
ModuleName=script

[script]
ScriptFile=/usr/share/plymouth/themes/example/example.script
ImageDir=/usr/share/plymouth/themes/example/

It is recommended to keep scripts and files in their respectives theme folders (so that they are populated in the initramfs).

Terminology

  • Image: An image is a resource that has been loaded.
  • Sprite: A sprite is an image that has been placed.


Hookable event callbacks

There are lots of different callbacks you can use in a script to track events/boot progress, each of these take a function as a parameter, which is called with the appropriate data when the relevant event is fired.

CODE
# The refresh function is called `x` amount of times per second, where `x` is the refresh rate set with `Plymouth.SetRefreshRate` or the default value (50).
fun on_refresh() {}

Plymouth.SetRefreshFunction(on_refresh);
CODE
# The boot progress function is called whenever progress is made during the boot process.
fun on_boot_progress(duration, progress) {}
# duration and progress are both `numbers`

Plymouth.SetBootProgressFunction(on_boot_progress);
CODE
# The root mounted function is called when the root partition is mounted (needs verification).
fun on_root_mounted() {}

Plymouth.SetRootMountedFunction(on_root_mounted);
CODE
# The keyboard input function is called whenever input is made on a keyboard, this does not trigger on non-text characters such as return (needs verification).
fun on_keyboard_input(char) {}
# char is a string containing the character pressed.

Plymouth.SetKeyboardInputFunction(on_keyboard_input);
CODE
# This is called whenever the status is changed.
fun on_status_update(new_status) {}
# new_status is a string containing the new status.

Plymouth.SetUpdateStatusFunction(on_status_update);
CODE
# This is called whenever the display should return to normal.
fun on_mode_normal() {}

Plymouth.SetDisplayNormalFunction(on_mode_normal);
CODE
# This is called whenever the display should show a password prompt.
fun on_mode_password(prompt, bullets) {}
# prompt is a string and bullets is a number indicating how many characters have been entered. 

Plymouth.SetDisplayPasswordFunction(on_mode_password);
CODE
# This is called whenever a question is asked.
fun on_mode_question(prompt, entry_text) {}
# prompt and entry_text are strings.

Plymouth.SetDisplayQuestionFunction(on_mode_question);
CODE
# This is called whenever a prompt needs displaying.
fun on_mode_prompt(prompt, entry_text, is_secret) {}
# prompt and entry_text are strings and is_secret is a bool.

Plymouth.SetDisplayPromptFunction(on_mode_prompt);
CODE
# This is called whenever a device is hotplugged into the machine.
fun on_hotplug() {}

Plymouth.SetDisplayHotplugFunction();
CODE
# This is called whenever an input needs validation.
fun on_validate_input(entry_text, additional_text) { return some_bool; }
# entry_text and additional_text are both strings, the function expects a boolean return value.

Plymouth.SetValidateInputFunction(on_validate_input);
CODE
# This is called when a new message needs to be displayed.
fun on_message(message) {}
# message is a string.

Plymouth.SetDisplayMessageFunction(on_message);
CODE
# This is called to hide a previously shown message.
fun on_hide_message(message) {}
# message is a string.

Plymouth.SetHideMessageFunction(on_hide_message);
CODE
# This is called when plymouth is quitting.
fun on_quit() {}

Plymouth.SetQuitFunction(on_quit);
CODE
# This is called when there is a system update.
fun on_system_update(status) {}
# status is a string of the new status of the system.

Plymouth.SetSystemUpdateFunction();

Other Builtin Functions

There are also other builtin functions which are listed below:

CODE
# returns a boolean depicting the current capslock state (true=on, false=off).
Plymouth.GetCapslockState()

# returns a string of the current mode that plymouth is in (normal, password, question, ...).
Plymouth.GetMode()

# sets the amount of times the refresh function is called per second, returns null.
Plymouth.SetRefreshRate(number);

In addition to this, there are also some mathematics functions under a different namespace:

CODE
# Returns absolute (positive) of a number.
Math.Abs(number);

# Returns the smallest number from two numbers.
Math.Min(number1, number2);

# Returns the largest number from two numbers.
Math.Max(number1, number2);

# Forces the value to be between min and max, otherwise either min or max are returned.
Math.Clamp(value, min, max);

# The value of Pi (3.14159...) to 21 d.p.
# This is not a function
Math.Pi;

# Cosine function
Math.Cos(radians);

# Sine function
Math.Sin(radians);

# Tangent function
Math.Tan(radians);

# Arc tangent function taking 2 values (see "man atan2")
Math.ATan2(x, y);

# Square root of a number
Math.Sqrt(number);
# Example: Math.Sqrt(4) # Result 2

# Returns the rounded down integer.
Math.Int(number);
# Example: Math.Int(Math.Pi) # Result: 3

# Returns a pseudo random number between 0 and 1. 
Math.Random();

Strings, images and sprites that each have their own associated functions:

CODE
# Create a string object
string_example = String("some string"); # or alternatively just "some string".

# Associated methods for strings:
# Returns the character at the given index
string_example.CharAt(number);
# Returns a substring starting at index `start` and ending at index `end`.
string_example.SubString(start, end);
# Returns the length of the string
string_example.Length();

Images allow you to create/load resources into plymouth, they also allow for basic transformations on each of the images.

CODE
# Creates an image object from the image at the given location.
image_example = Image(filename);
# Creates an image from text. All parameters apart from text are optional.
Image.Text = fun (text, red, green, blue, alpha, font, align)

# Associated methods for images:
# Rotates the image by the given angle
image_example.Rotate(angle);
# Crops the given image starting at x, y and ending at x+width, y+height.
image_example.Crop(x, y, width, height);
# Scales the given image to a given width and height.
image_example.Scale(width, height);
# Tiles the given image to a given width and height.
image_example.Tile(width, height);

There also are functions and methods for creating/modifying sprites.

CODE
# Creates a sprite, image is *optional*, if not provided then a sprite with no image is made, you can change the image later.
sprite_example = Sprite(image);

# Sprites also have methods associated with them:
# This sets the X coordinate of the sprite to number
sprite_example.SetX(number);
# This sets the Y coordinate of the sprite to number
sprite_example.SetY(number);
# This sets the Z coordinate of the sprite to number
sprite_example.SetZ(number);
# You can also get and set the sprite opacity:
sprite_example.SetOpacity(number);
sprite_example.GetOpacity();
# You can also get the width and height of the sprite:
sprite_example.GetWidth();
sprite_example.GetHeight();
# Finally you can also get and set the image of the sprite
sprite_example.SetImage(image);
sprite_example.GetImage();

# These can also all be set together using (WARNING: this is deprecated).
SetPosition(sprite_example, x,y,z)

You can also get and set attributes for the window

CODE
# Get the display width and height
Window.GetWidth();
Window.GetHeight();

# Get and set the position of the background window sprite
Window.GetX(number);
Window.GetY(number);

Window.SetX(number);
Window.SetY(number);

# And you can also change the background sprite color:
Window.SetBackgroundTopColor(r, g, b);
Window.SetBackgroundBottomColor(r, g, b);


Creating animations

One common way to create animations is to load all images for the animation and store them within a hashmap, then on each refresh we change the image that a sprite is displaying onto the next frame (and then loop to the start when the end of the animation is reached).

This animation assumes there are 250 sequential images frames at ImageDir/animation/frame-[0=..=249].png.

CODE
animation_first_frame = 0;
animation_last_frame = 249;
animation_current_frame = 0;

for (frame = animation_first_frame; frame <= animation_last_frame; frame++) {
    animation_images[frame] = Image("animation/frame-" + frame + ".png");
}

# Create a sprite to display the animation frames, we will assign a image later to it later.
animation_sprite = Sprite();
# align the animation in the centre of the screen (optional)
animation_width = animation_images[animation_first_frame].GetWidth();
animation_height = animation_images[animation_first_frame].GetHeight();
animation_sprite.SetX((window.GetWidth() / 2) - (animation_width / 2));
animation_sprite.SetY((window.GetHeight() / 2) - (animation_height / 2));
animation_sprite.SetZ(-1);

# now to animate we can change the image used in the sprite each frame,
fun on_refresh() {
    animation_current_frame += 1;
    if (animation_current_frame > animation_last_frame) {
        # loop back to the start when the last frame is reached.
        animation_current_frame = animation_first_frame;
    }
    animation_sprite.SetImage(animation_images[animation_current_frame]);
}
Plymouth.SetRefreshFunction(on_refresh);

Writing a two-step theme

This article is a stub. Please help out by expanding it - how to get started.

Writing a space-flares theme

Specimen

To define a space-flares theme, you use space-flares as the module name in the example.plymouth file.

The space-flares module has three options that can be defined:

  • ImageDir: An absolute path to a directory that contains images.
  • MonospaceFont: The font to use in text sprites (the font needs to be available in the initramfs). (optional)
  • ConsoleLogTextColor: The color to use for console logs (in hex: 0xffffff) (optional)
CODE example.plymouth
[Plymouth-Theme]
Name=Example
Description=This will be an example plugin
ModuleName=space-flares

[space-flares]
ImageDir=/usr/share/plymouth/themes/example/

=== Customising There are limited options for customising a solar-flares theme, you are limited to changing the following images (note: all of the images must be present):

  • lock.png
  • box.png
  • star.png
  • plant1.png (used if SHOW_PLANETS is defined in source)
  • plant2.png (used if SHOW_PLANETS is defined in source)
  • plant3.png (used if SHOW_PLANETS is defined in source)
  • plant4.png (used if SHOW_PLANETS is defined in source)
  • plant5.png (used if SHOW_PLANETS is defined in source)
  • progress_bar.png (used if SHOW_PROGRESS_BAR is defined in source)


The amount of images actually displayed depends on how plymouth is compiled[2], however it defaults to showing a progress bar, with no planets, no comets, and, no logo halo. You can also change more options by editing the source code at /src/plugins/splash/space-flares/plugin.c where you can define fps, blur, flare count, flare lines, and star Hz.

Activating a theme

To learn how to set a theme you may refer to: Plymouth#Themes

Troubleshooting

To temporarily disable plymouth , you can add the following kernel parameters:

CODE
plymouth.enable=0 disablehooks=plymouth

To write debug output into /var/log/plymouth-debug.log, you can add the following kernel parameter:

CODE
plymouth.debug

If themes are not loading properly it may be a good idea to check the user's initramfs to ensure that it has been populated with the plymouth binaries and themes[3]

See also

External resources

References