Archive for the ‘ Plymouth ’ Category

Falling blocks game in Plymouth

So, you have sat down at your computer and you’re waiting for it to boot, then suddenly you realise that it is doing a full fsck which is going to take a few minutes. What to do. You have two options:

  1. Sit quietly watching the little bar move slowly across
  2. Plymouth falling blocks game!

This is not a serious proposal, I just wanted to exercise the scripting system to see if I could find any bugs, but if you want to have a play with it, the script is available.

Plymouth theming guide (part 4)

This the final part of the theming guide. As I mentioned in the last post, you already know everything you need to write your themes. In this post I will show a couple themes I wrote to explore the system and some fancy high level aspects of the scripting language. You don’t need to know these, they are just a cute method to solve a problem that others may find useful.

Themes

To install these themes, extract them to /usr/share/plymouth/themes and run the appropriate plymouth-set-default-theme line.

Vizta

A very simple theme matching what the Vista theme does. It has just 30 lines of custom code.

source

Dandelion

This theme has the target of stressing the system. A couple months ago it was running at about 3 fps, now my machine hits 30 fps and spends some time idle.

source

Inheritance

When I first proposed doing a scripting system Ray said “If you do this, I guess I’d recommend you keep it very simple”. I agreed, so what I aimed to do was to implement all the features with the minimum space. The solution I came up was to kill several birds with one stone. The stone being an inheritance system.

The important operation for inheritance is the single bar (“|“), inherit operator. Think of it as a very lazy OR function. As an example take the statement:

A = B | C;

This creates a new object (A) which is an inheritance of B and C. A now gains all the properties of B and, as a fall-back, C. There are three ways of using inheritance objects.

Direct

Consider the following statement:

A = "Seven" | 7;
B = String(A).CharAt(2);
C = 80 + A;

So A is an inheritance of the string “Seven” and the number 7. This means when in situations where a string is preferred, it will act as a string, and when the a number is preferred, it will act as a number. The value in B will be the third character in the string (“v”), and the value in C will be the number 87.

This kind of inheritance is used to allow native C objects (images, sprites etc.) to be able to be used as hashes to find appropriate functions to call. An object is created which inherits from the native object and the object template hash (which holds all the functions). The constructor function is also connected to the template using an inheritance. Thus you can treat it as a function, or as a hash of functions and it will adopt the appropriate behaviour.

text_image = Image.Text("string"); # Treat Image as a hash
png_image = Image("filename.png"); # Treat Image as a function

Hashed

Hashes are the main containers in the system. When an object inherits from two hashes, accessing the inner elements will trigger a crawl through all hashes for that element.

A.a = "a";
A.b = "b";   # A: a="a", b="b"
B.b = "B";
B.c = "C";   # B: b="B", c="c"
C = A | B;   # C: a="a", b="b", c="c"

When accessing the elements within C, the system looks within A, then B (also within any object they inherit from). Thus, elements within B can be overridden by A. This is very much like the __proto__ in Mozilla JavaScript.

Functional

Just like hashes, when functions are inherited, the system goes through them in the inheritance sequence, executing each one and moving onto the next if it has failed.

Fib = fun (v) {if (v <= 2) return 1; else fail;}
    | fun (v) {if (FibCache[v]) return FibCache[v]; else fail;}
    | fun (v) {return FibCache[v] = Fib(v-1) + Fib(v-2);};

If you have ever programmed the ML languages, you will already be somewhat familiar with this. In those, you can put constants in the function variable decelerations and functions which do not match these to the passed parameters, fail. Here, at any point in the function, you can choose to fail. The system then tries to execute the next function in the inheritance.

Plymouth theming guide (part 3)

Now the basics have been covered in part 2 of this guide, we can now tackle some more advanced tasks. This part is for beginners to intermediate. If you have never programmed, then this will be a rough introduction to the field and thus you may need to ask for help about some confusing aspects.

Until now, all the examples have been simple assignments and there hasn’t been much “programming”. At the end of the last part, the background image needed to be scaled without distorting the aspect ratio. If the image is wider than the screen, then the image needs to be scaled up to the height of the screen and let the sides hang over the edges. If the image is taller than the screen, the image should be scaled to the screen width and allow the top and bottom to go beyond the edge of the screen.

scale_ratio

You can get the screen ratio using the Window.GetWidth and Window.GetHeight functions introduced in the previous part. The ratio is height to width. The greater the number, the taller the proportions.

screen_ratio = Window.GetHeight() / Window.GetWidth();

GetWidth and GetHeight functions also work on images.

flower_image_ratio = flower_image.GetHeight() / flower_image.GetWidth();

To create the scaled image, the scale factor needs to be determined. If the screen is tall, we make the scale factor such that the image will be the same hight as the screen, and allow it to dangle off the edges. For a wide screen, make the image match the width and allow the top and bottom to go beyond the edges. The scale factor can then be used to scale the image width and height.

if (screen_ratio > flower_image_ratio)
  {  # Screen ratio is taller than image ratio, we will match the screen height
     scale_factor =  Window.GetHeight() / flower_image.GetHeight();
  }
else
  {  # Screen ratio is wider than image ratio, we will match the screen width
     scale_factor =  Window.GetWidth() / flower_image.GetWidth();
  }
scaled_flower_image = flower_image.Scale(flower_image.GetWidth()  * scale_factor,
                                         flower_image.GetHeight() * scale_factor);
flower_sprite = Sprite(scaled_flower_image); # Create the a sprite using the scaled image

Now the image is scaled to the correct width and height, it needs to be placed in the centre of the screen.

flower_sprite.SetX(Window.GetWidth()  / 2 - scaled_flower_image.GetWidth()  / 2); # Place in the centre 
flower_sprite.SetY(Window.GetHeight() / 2 - scaled_flower_image.GetHeight() / 2);
flower_sprite.SetZ(-10000); # Place right at the back

mytheme_placed

If everything went right, your code should look like this:

flower_image = Image("flower.png");

screen_ratio = Window.GetHeight() / Window.GetWidth();
flower_image_ratio = flower_image.GetHeight() / flower_image.GetWidth();

if (screen_ratio > flower_image_ratio)
  {  # Screen ratio is taller than image ratio, we will match the screen height
     scale_factor =  Window.GetHeight() / flower_image.GetHeight();
  }
else
  {  # Screen ratio is wider than image ratio, we will match the screen width
     scale_factor =  Window.GetWidth() / flower_image.GetWidth();
  }

scaled_flower_image = flower_image.Scale(flower_image.GetWidth()  * scale_factor,
                                         flower_image.GetHeight() * scale_factor);
flower_sprite = Sprite(scaled_flower_image); # Create the a sprite using the scaled image

flower_sprite.SetX(Window.GetWidth()  / 2 - scaled_flower_image.GetWidth () / 2); # Place in the centre
flower_sprite.SetY(Window.GetHeight() / 2 - scaled_flower_image.GetHeight() / 2);
flower_sprite.SetZ(-10000); # Place right at the back

If you wish for your background to match the one in your desktop, copy it to the theme directory and edit the script to load the image of that filename. Only PNG files are allowed.

mytheme_constantine

Finally to install your theme into the ramdisk, so it is loaded when your computer starts, add –rebuild-initrd to the plymouth-set-default-theme command. Do be careful not to install a theme which deadlocks. Make sure you have a backup kernel or initrd image if all goes wrong

plymouth-set-default-theme mytheme --rebuild-initrd

Animation

Start by making a new sprite with an image of a butterfly:

butterfly_image = Image("butterfly0.png");
butterfly_sprite = Sprite(butterfly_image);

animation_step1

At the bottom of the block of code you are editing, there is a function named refresh_callback. This is called 50 times a second (unless the system is busy) just before refreshing the screen. Within the function, you can move the sprites around and these movements will be updated on the screen. Take a look at this example:

progress = 0;
fun refresh_callback ()
  {
    progress++;
    butterfly_sprite.SetX(progress);
  }

animation_step2

At every refresh, the butterfly will be moved one pixel to the right. To make the butterfly flap it’s wings, several frames are needed. Instead just loading one image, this requires an array of images to be loaded. Replace the original two lines that loaded the butterfly image and created the sprite, with these:

for (i = 0; i < 4; i++)
  butterfly_images[i] = Image("butterfly" + i + ".png");
butterfly_sprite = Sprite();

You can append strings to numbers to create the four filenames. Secondly, if a sprite has not been attached to an image, it will simply be empty until one is set. Adding the following line into therefresh_callback function should rotate through the 4 butterfly frames. Because the progress is divided by 6 in order to slow the animation to one frame every 6 refreshes, we use a Math library function to round down a number to an integer.

butterfly_sprite.SetImage(butterfly_images[Math.Int(progress / 6) % 4]);

animation_step3

There are some other math functions available, sin and cos are often useful. Replace the SetX line with the following to make the butterfly go around in a circle around the centre of the screen.

theta = progress / 100;
butterfly_sprite.SetX(Window.GetWidth() / 2 + Math.Sin(theta) * 200);
butterfly_sprite.SetY(Window.GetHeight() / 2 - Math.Cos(theta) * 200);

animation_step4

Now the butterfly looks a little weird pointing the wrong way as it is going around in a circle. We need to rotate the butterfly image so it is flying forward. Add .Rotate(theta) to the image while setting it to the sprite. This creates a new image which is rotated by the angle theta.

butterfly_sprite.SetImage(butterfly_images[Math.Int(progress / 5) % 4].Rotate(theta));

animation_step5

If you have reached this point, you should now have all the knowledge you need to start creating your own themes. For a full list of available functions and script syntax take a look at the freedesktop plymouth script wiki. Good luck, have fun and make some nice splash screens.

Plymouth theming guide (part 2)

The first part of guide showed how to set up the system to be able to view plymouth themes and make simple adjustments to ones that are already present. This part will show how to make a custom theme, without delving down to the level of writing C.

One of my targets for this year was to add a simple way of designing themes without having to worry about many low level aspects (redrawing parts of the screen, keeping pointers to all elements to free them etc). The scripting language allows the designer to get stuck into the design, while the system is already optimised to take care of and minimise the areas to be refreshed. It is now quite stable and this is the method used by Mandriva for their default themes.

The following walk-though will show you how to make a small example splash. This part should be very easy and even if you have never programmed before, you should have no problems. First download and install the template:

wget -O /tmp/mytheme.tar.gz http://brej.org/blog/wp-content/uploads/2009/12/mytheme.tar.gz
cd /usr/share/plymouth/themes/
tar xzf /tmp/mytheme.tar.gz

Now the theme is installed, and you already have a system set up like explained in part 1, you can switch to it:

plymouth-set-default-theme mytheme

and then test it:

plymouthd --debug --debug-file=/tmp/plymouth-debug-out ; plymouth --show-splash ; for ((I=0;I<10;I++)); do sleep 1 ; plymouth --update=event$I ; done ; plymouth --quit

mytheme_template

Notice this time some debug information is generated and the file /tmp/plymouth-debug-out contains more. The debug information is useful to find any syntax errors in your script. It is a good idea to keep a text editor with this file open and revert to saved after every execution to watch for any errors. A syntax error contains the line and column number of the expression like so:

Parser error "/usr/share/plymouth/themes/mytheme/mytheme.script" L:8 C:42 : Expected ';' after an expression

In the mytheme directory there is a file named mytheme.script. Open this using your favourite text editor. At the top of the example script, there are some lines which create the theme, and below this are sections which control the password dialogue, progress bar etc. You should leave things below the dialogue line alone for now.

Lines starting with # are comments, so lets skip to the first two executed lines:

flower_image = Image("flower.png");
flower_sprite = Sprite(flower_image);

In the mytheme directory, you will notice some images. One of these is flower.png (from Nicu CC-BY-SA), which the first line loads. The loaded image is assigned to flower_image, which can then be used by sprites. A sprite is an image with a position on the screen. The second line creates a sprite using the flower image. By default this is placed at X=0, Y=0, which is the top left of the screen. To change the position of the sprite use the SetX and SetY functions on the sprite. Adding the following line will move the sprite to 100 pixels to the right.

flower_sprite.SetX(100);

mytheme_setx

In a similar way, setting the Y position will move the image down the screen.

flower_sprite.SetY(400);

mytheme_sety

Notice there is no problem if the sprite runs off the edge of the screen. Also notice how the progress bar is on top of the flower sprite. The sprites have a defined order of which is on top. To demonstrate this, lets add another sprite. You can reuse the flower image and place it overlapping with the original flower sprite.

other_flower_sprite = Sprite(flower_image);
other_flower_sprite.SetX(-100);
other_flower_sprite.SetY(200);

mytheme_overlap

Negative coordinates are acceptable. The new sprite will be placed on top of the previous one. To override this, each sprite has a Z value. The higher the Z value, the higher up the stack the sprite is (drawn on top of others). To place the original flower sprite on top you set its Z value to be higher. The default Z value is 0, and you can set it using SetZ.

flower_sprite.SetZ(10);

mytheme_setz

Now not only is the original the sprite above the other, it is also above the progress bar, which also has the Z value set to 0. In general, wallpapers should have Z set to high negative values to force them to the very back and the password dialogue has a Z value of 10000 to place it on top of any theme components. Thus, make sure you don’t place any sprites higher than 10000, otherwise you may find it difficult to type in your password.

Lets take the sprite back to the background and reposition it. Often you want to set X, Y and Z at the same time. This can be done using the SetPosition function.

flower_sprite.SetPosition(0, 0, -10000);

mytheme_setposition

As well as the X, Y and Z values, the sprite also has opacity. The default setting of 1 makes the sprite solid. Values between 0 and 1 make the sprite partly transparent. This can be set using SetOpacity.

other_flower_sprite.SetOpacity(0.3);

mytheme_setopacity

Setting this to 0 will make the sprite invisible. This should return the display the original setup.

other_flower_sprite.SetOpacity(0);

mytheme_template

The image of the flowers is very nice, but it is the wrong shape and size. You could distribute an image for every possible screen resolution, but this would be terribly space consuming. Instead you can scale the image to suit your needs. A new scaled image can be created using Scale and to change the image the sprite is showing, use SetImage.

resized_flower_image = flower_image.Scale(640, 480);
flower_sprite.SetImage(resized_flower_image);

mytheme_scale

Here the image is scaled to 640×480, but this is not the screen resolution. For now, assume there is only one monitor connected (I will explain more about this later). To get the width and height of the screen, use Window.GetWidth() and Window.GetHeight(). You can use these to scale the image to the screen dimensions.

screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
resized_flower_image = flower_image.Scale(screen_width, screen_height);
flower_sprite.SetImage(resized_flower_image);

mytheme_fullscreen

Now the image is scaled to cover the whole screen, but is the wrong aspect ratio (although that is difficult to see in this example). Correcting this will require some programming which will be shown in the next part of the guide.

Plymouth theming guide (part 1)

Judging by the many forum postings and articles, many people do enjoy a nice boot splash and there is a real desire to customise their spin or even the individual machine to their preferred theme. In this guide I will try and show you how to make your own custom splash, walking though the process of viewing, changing and installing. Even if you are a Linux novice, you should be able to follow the steps without much trouble. This guide assumes a Fedora system, although there should be very few changes between the different installations.

Set-up

Obviously you can view plymouth running by turning on your computer, but that does get rather tiresome if you wish to test frequently. Without restarting your machine, there are three sensible methods of testing plymouth: using a  victim machine, using a virtual victim machine or using the X11 plugin.

Victim machine

This is the easiest method, although it does require a second physical machine to work with.

If you do not have a KMS compatible system, then you will need to add vga=0×318 to your kernel line. This pushes the console to a VESA graphical mode. The 0×318 actually stipulates 1024x768x32bit mode. To see all the modes and find one most suitable for you, add vga=askme and a list will appear (don’t forget the 0x).

SSH into the machine as root (yes you do need to be root). The splash will be tested on tty1, but tty1 has two problems: there is an X session running on it and if you quit that, getty will start and capture keyboard events. To turn off getty on tty1, edit /etc/event.d/tty1 an comment out all the start and stop lines with a hash.

#start on stopped rc2
#start on stopped rc3
#start on stopped rc4

#stop on runlevel 0
#stop on runlevel 1
#stop on runlevel 6

Then drop to init level 3, which kills X and brings you to an empty console, by running init 3. Now install all the themes you wish to try out:

yum install plymouth\*

This is how to do this in Fedora, you should know the way to do this in your distro already.

Virtual victim machine

The alternate to using a physical machine is to use a virtual one. Both VMWare and QEmu work very well. You will have to do an OS install on the virtual machine. These do not yet support KMS (support is coming soon apparently), so you will need to add the vga parameter on the kernel line. The setup is exactly the same as  one for a physical victim machine.

X11 plugin

This method allows you to execute plymouth within X. This is the preferred testing method BUT it can crash your X if you accidentally pick a text theme. It also does require you to compile your own version of plymouth to get the x11 renderer. As root get the latest git checkout:

git clone git://anongit.freedesktop.org/plymouth

Next configure and compile it:

> cd plymouth
> ./autogen.sh --prefix=/usr/ --with-system-root-install
> make
> make install

Now it is installed, make sure you have the DISPLAY set correctly so even if you run as root, it can connect to your X session and display the windows.

plymouth-x11

When you run it using the directions below, you will notice you get two screens which allows you to test the behaviour of a two monitor setup.

Viewing

Now you have the system set up, you can manually execute plymouth to examine a theme. First, as root, start the plymouth daemon (plymouthd lives in /sbin/ so make sure you have that in your path):

> plymouthd

Now it is executing, you can control it using the plymouth program. To show the splash run:

> plymouth --show-splash

This should display the splash.

plymouth_charge

To quit, execute

> plymouth quit

The splash should then quit and the terminal return to its text mode. You may have noticed the progress did not advance very quickly. The progress is estimated using events, based on the event arrival time of the previous boot. To show the splash for 10 seconds and watch the progress moving execute the following one-liner:

> plymouthd; plymouth --show-splash ; for ((I=0; I<10; I++)); do plymouth --update=test$I ; sleep 1; done; plymouth quit

There are several themes installed on your system. To get the full list run:

> plymouth-set-default-theme --list

To change to any of these themes replace –list with the theme name e.g.:

> plymouth-set-default-theme spinfinity

Now you can examine any theme you wish. But what if you don’t like any of them?

Theme editing

It is important to understand the difference between a theme and a plugin. A theme uses a plugin and stipulates some parameters the plugins uses. This information is stored in the directories under /usr/share/plymouth/themes/. Each directory has a .plymouth file which describes the theme, stating the plugin it uses and any parameters the plugin allows to be set. To create a new theme, simply copy an entire directory to a new name, rename the .plymouth file to the new name, and update its contents. In addion, you can now also change the images that your new custom theme uses.

This is sufficient if you wish to recycle one of the already present plugins. For more customisability you can write your own plugin in C which is, to be honest, not very easy. The much simpler alternative is to use the script plugin and write a script to describe the theme, and that is what I shall be describing in the next post.