release
dev builds
extras
themes manual
wiki
device status forums
mailing lists
IRC bugs
patches
dev guide



Search | Go
Wiki > Main > HowtoWritePlugins

Beginners guide to programming a plugin for Rockbox


Introduction

This guide is going to assume a few things.

  1. That you are somewhat familiar with programming, not necessarily C.
  2. That you have already set up the compiling environment and know how to compile. If not, see the DevelopmentGuide.
  3. You want to start programming a plugin.
Also, if something is not mentioned in this guide, a really good place to look for answers is other plugins' source code. See if the function has already been written, or learn the syntax for them by looking.


The basics

Let's start off by looking at helloworld.c. Read the comments in each line to see what they do.

    #include "plugin.h"
This line is necessary for the plugin to make any function calls. Without it the plugin is fairly useless.

To make a call to a function built into the rockbox plugin API, you'd do it like this:
    rb->function();

    enum plugin_status plugin_start(const void* parameter) 
    {
This is the plugin entry point. It's where all your code goes. If You are making a plugin which takes a file as input, the parameter variable will be sent to the plugin, which you can use.

    (void)parameter;

You only need this if you don't use the parameter variable, while not strictly necessary, it stops any compiler warnings. This parameter is used if your plugin is a Viewer.

It's after this point in the code that you can start writing your own stuff.

    rb->splash(HZ*2, "Hello world!");

This line of code will display "Hello World!" in the middle of the screen for 2 seconds. The rb->splash() function is particularly useful for debugging purposes, as it is a simple way to get output from your plugins during runtime.

    return PLUGIN_OK;

This lets Rockbox know that your plugin has finished doing what it needs to do. This can be called in a few places if need be.

}

And that's the end of the plugin!

Now to include it in the makefile. First save your sourcecode for plugin into the rockbox\apps\plugins folder then add the name of your plugin (ex: helloWorld.c) into the SOURCES textfile which is in the same directory. You also need to add your plugin to the CATEGORIES textfile which is in the same directory as well (e.g.: "testing,apps" will have plugin "testing" show up under the applications category. In case of "helloworld.c" your plugin name would be "helloworld" and to make it show up under applications you will add "helloworld,apps" to the CATEGORIES text file).

Now run make and then run make install


Adding a plugin that has multiple files

If you visit rockbox\apps\plugins folder, you will realise some plugins are organised into a folder, such as chessbox, doom, sudoku and others. If you have your plugin formed of multiple files put them in a folder (subdirectory), and put it's name into the list in the SUBDIRS textfile in same ( rockbox\apps\plugins) folder. You need to put a Makefile and a file named SOURCES into that subdirectory as well. Sudoku directory sets a good example, you can copy its Makefile and replace the necessary names. -- OzgurOzturk - 27 Jun 2007


Creating a custom/user-defined function

When creating a custom function, you have to take a couple of things into account.
  1. What input it will take
  2. What it will output
The function should be put in the file before the plugin entry point, or it won't work. If you want a function which doesn't take any input or give any output we do it like this:

void nameoffunction(void);        /* First we declare the function. This is to help the compiler do its thing.
    This line is called a prototype of a function.   */

void nameoffunction(void)        /* Then we make the actual function. All code should be between the two {}'s */
{
    /* Code goes here */
    return;                 /* return; lets the main part of the plugin know that the function has finished,
               and it can continue doing it's thing.   */
}

If you want a function that takes true or false input (boolean), and outputs true or false as well, you'd do it like this:

bool nameoffunction(bool input);     /* Notice the void at the start of the prototype has become a bool?
               This is because the output is boolean. So to change the type of output
               you have to change this part of the prototype (and don't forget to change it in the
               function as well).
               The part in the brackets declares a new variable which will only be used in function,
               it is destroyed once the function is done. 
               This variable is the input of the function, which is set when the function is 
               first called. */

bool nameoffunction(bool input)      /* The start of the actual function is always the same as the prototype, but with {}'s  */
{                                                       /* instead of a semicolon.*/

    if(input) {              /* This is just some example code which would get the input, check if it's true, and */
        return false;           /* if it is, then make the function output false */
    }
return true;              /* Or if the input was false, then the output will be true. */
}

A function which returns true or false can be useful for when a function is absolutely critical for the plugin to work, for example opening a file in a BMP reader. If the plugin can't open the file, the function would return false, to indicate an error to the main part of the plugin. To call a function which requires input, do so like this:
nameoffunction(input);
Where input is of the type the function is expecting (eg boolean in the previous example).


Commonly used functions and how to use them

rb->lcd_set_foreground();

rb->lcd_set_background();

These two funtions operate the same way, but one changes the foreground and the other changes the background (as you might have guessed). These functions only work on targets with bitmap LCDs, and depending on which one you're using you would either give it a colour or a shade. To set a shade:
   rb->lcd_set_foreground(LCD_BLACK);
Other shades include:
   LCD_DARKNAVY
   LCD_LIGHTNAVY
   LCD_WHITE

Colours use a different method. To set a colour, use the LCD_RGBPACK function, sending it the red, green and blue numbers (out of 255). For example:
   rb->lcd_set_foreground(LCD_RGBPACK(255,0,0));   /* Pure red */
   rb->lcd_set_background(LCD_RGBPACK(0,0,255));   /* Pure blue */

The LCD_RGBPACK macro produces a value that is the right size for a pixel value for the Rockbox target platform, with the right number of bits used for each color.

rb->lcd_update();

This function should be called when all the info that you've written to the screen is ready to be displayed. Try to minimise the use of this function, as it can be slow at times, and can cause delays. This function requires no input, so it can be called like so:
   rb->lcd_update();

rb->lcd_clear_display();

This will clear the display, writing the background colour to the entire screen. Again, no input required, so it can just be called as is.

rb->splash();

We covered this one earlier in the helloworld.c run through. It simply writes the string of your choice to the middle of the screen, for a certain amount of time, with no lcd_update(); necessary.

The first argument it takes is for how long it should be displayed, in operating system ticks. The macro 'HZ' contains the number of ticks per second (currently 100). We multiply this number by the number of seconds we want the message displayed (or divided for a fraction of a second). The second argument is the string you want displayed. eg:
   rb->splash(HZ, "Test");

rb->splashf();

This function works similarly to splash but takes a printf-style format code as the second argument followed by the appropriate arguments for that specific format code. eg:
   rb->splashf(2*HZ, "Too many channels: %d", channels);

rb->lcd_putsxy();

This will put a string of text in the location of your choice. Pass it the x and y values (in pixel units) of the top left corner of your string, and the string you want displayed. eg:
   rb->lcd_putsxy(0, 15, "Something");
Which would put the text "Something" (without the quotes), over the very left, 15 pixels from the top.

Note that all strings are expected to be in the UTF-8 character encoding.

Also, lcd_putsxy() is the only string-drawing GUI function where "x" and "y" are in pixel units. All of the others which take "x" and "y" arguments, like lcd_puts, lcd_puts_style, lcd_puts_scroll, etc., either use or try to approximate character-cell behavior. That is, the "x" parameter is the column, and the "y" parameter is the line.

rb->snprintf();

This function is used for filling string variables with mixed data simply and safely. The first argument is the variable you want to fill. It should have been declared something like this:
char string[15];
This would create a string variable which is 14 characters long (the extra one is needed to terminate the string). You could then fill this variable like so:
   rb->snprintf(string, 15, "Fourteen Chars.");
The first argument is the variable to fill, the second is a limiter so that you can't overfill the variable (all sorts of nasty stuff happens when you do). This might seem redundant, but as you will see, this function has other uses.

You can use it to fill a string variable with other mixed input, for example:
   rb->snprintf(string, 15, "%d %s", filesize, unit);
Where filesize was an integer containing a filesize, and unit was a short string containing Mb, kb or b etc. %d is used when you want to replace it with an integer, and %s for a string. This is where the limiter comes into play, because you don't know how long the filesize is going to be, you need to prevent a buffer overflow.

To print the variable you just filled, you can use either splash or putsxy.

Using Images

Targets that have LCD displays rather than character cell displays also support drawing of bitmaps or images, via the function lcd_bitmap (and lcd_bitmap_part). Images must be of the type const fb_data *, which should be a width x height array of byte or short values packed with the bits of the image. To find out the specifics for your device, look in SRC/firmware/export/config-TARGET.h, where TARGET is the name of your platform. You'll find macros defined for LCD_WIDTH, LCD_HEIGHT, and LCD_DEPTH. LCD_DEPTH tells you how many bits should be used in each fb_data value for each pixel, and look in SRC/firmware/export/lcd.h for information about how they are arranged in the framebuffer.

If your target device has an LCD display, the API function read_bmp_file is provided, to allow you to read a Windows BMP file from a file. The source code for this function is in SRC/apps/recorder/bmp.c, which is handy reading if you are doing any image manipulation.

Where to find the full API

It's described in SRC/apps/plugin.h, and most of the source files are in SRC/apps/.

You will find a quite comprehensive description of the Rockbox graphics API here: GraphicsAPI


Useful bits and pieces

Some button handling, and a check for USB being plugged in while the plugin is running. The default_handler() call on unhandled events should be put in all code that handles button input.
int button, quit = 0;

while(!quit) {
    button=rb->button_get(true);
    switch(button) {
        case BUTTON_OFF:
            quit=true;
            return PLUGIN_OK;
        default:
            if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
                return PLUGIN_USB_CONNECTED;
            }
        break;
    }
}
return PLUGIN_OK;

On low memory targets the plugin buffer is 32K, on other targets the plugin buffer is 512K.


How to verify the plugin

In order to make sure that the plugin is working on all supported platforms and will go from Flyspray to Subversion see HowtoVerifyPlugins.


r33 - 02 Apr 2021 - 20:46:06 - UnknownUser

Copyright © by the contributing authors.