We are now going to demonstrate how to fill the screen with the color red. This seems a fitting color choice since our device is named the Raspberry Pi.
This program starts off looking very similar to our previous one; however, it will introduce you to some new concepts, such as memory mapping, setting, and unmapping.
Start by creating a new program in the c_programs
directory called fifth_c_prog.c
:
vim fifth_c_prog.c
We are now ready to start writing our application.
Copy and paste the following code into your fifth_c_prog.c
file.
Once you have done this, we will walk through the code to see what exactly is going on here:
#include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <string.h> int main(void) { struct fb_fix_screeninfo info; int framebuff_filedesc = 0; char *device_map = 0; framebuff_filedesc = open("/dev/fb0", O_RDWR); if (framebuff_filedesc == -1) { printf("Error: Unable to open frame buffer device.\n"); return(1); } if (ioctl(framebuff_filedesc, FBIOGET_FSCREENINFO, &info)) { printf("Error: Unable to read fixed info.\n"); return(1); } device_map = (char*)mmap(0, info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, framebuff_filedesc, 0); if ((int)device_map == -1) { printf("Error: Failed to mmap to device_map variable.\n"); return(1); } memset(device_map, 0x80, info.smem_len); munmap(device_map, info.smem_len); close(framebuff_filedesc); return 0; }
Let's start by looking at the top of the program and the headers we have included:
#include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <string.h>
The first three we also used in fourth_c_prog.c
but we have included two more new ones you may not be familiar with.
The first is sys/mman.h
. This library contains code for memory management. We will be using features from this library including mmap
and munmap
. A description of the library's functionality can be found at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_mman.h.html.
Following this is string.h
, which is used for string manipulation. We need to reference this library so we can include the memset
function. This is used later in our program to set the screen color.
That wraps up our headers. Let's delve into the main()
method now and look at some of the similarities and differences with our previous program:
struct fb_fix_screeninfo info;
Once again we have declared a variable that is of the struct
type. The main difference to what we declared before, however, is that it uses the fb_fix_screeninfo
structure rather than the variable equivalent.
fb_fix_screeninfo
contains device-independent immutable information about the frame buffer. This is in contrast to fb_var_screeninfo
, which you saw earlier. This contains device independent mutable information about the frame buffer, such as the bits per pixel.
Following this variable definition we declare a further two variables:
int framebuff_filedesc = 0; char *device_map = 0;
The first variable is the same as we declared in our program to check the framebuffer. However, we have also included a new variable called device_map
. This will be used to store the values of the mmap
function.
Next, we open a connection to the framebuffer and check to see if we were successful:
framebuff_filedesc = open("/dev/fb0", O_RDWR); if (framebuff_filedesc == -1) { printf("Error: Unable to open frame buffer device.\n"); return(1); }
Following this we make an ioctl
call to assign the FBIOGET_FSCREENINFO
values to our info
variable:
if (ioctl(framebuff_filedesc, FBIOGET_FSCREENINFO, &info)) { printf("Error: Unable to read fixed info.\n"); return(1); }
As before, if there is an error in attempting to do this, we exit the program.
Our next block of code introduces us to the mmap
function:
device_map = (char*)mmap(0, info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, framebuff_filedesc, 0);
The mmap
function is used to map or unmap devices or files into memory. You can read a detailed description about the function at http://man7.org/linux/man-pages/man2/mmap.2.html.
In our code we pass a number of parameters into the method; in fact, we pass six in total. The first parameter is the starting address for the new mapping. We have passed in the value 0
and the Linux kernel will handle creating this mapping.
Following this we pass in length bytes derived from our info
variable. The smem_len
attribute is the length of the frame buffer memory.
Next we pass in two values separated by a pipe specifying the memory protection. These are PROT_READ
and PROT_WRITE
. This means the page can be read and written to. Here, page
means a chunk of either virtual or physical memory.
Our next parameter is flags
. Here we can specify whether updates to a mapping are available to other processes mapping in the same area.
In our case, we pass in the value MAP_SHARED
. This means other processes that map this file can see the mappings associated with it.
The final two values we pass in are the framebuffer file description and the offset. In our case the file description is the file we opened earlier using this command:
framebuff_filedesc = open("/dev/fb0", O_RDWR);
The offset
value is set to 0
and thus the length value starts from this position in the framebuff_filedesc
file.
After assigning the results of the mmap
to device_map
we check if there was an error. You will notice that we cast the value of device_map
as an integer:
if ((int)device_map == -1) { printf("Error: Failed to mmap to device_map variable.\n"); return(1); }
If there was an issue we exit the program, displaying a message to the screen.
Our final block of code consists of the following:
memset(device_map, 0x80, info.smem_len); munmap(device_map, info.smem_len); close(framebuff_filedesc); return 0
Let's take a look at what exactly is going on here.
You can see we use the memset()
method from the String
library to turn the screen red. This is achieved by passing in a number of parameters.
First is the device_map
variable we just covered. This gives us a reference to the memory space we want to update.
The next value, 0x80
, represents red, the color we fill the screen with. Last of all, we have reused the info
variable to get the length of the frame buffer.
Directly after memset
we unmap using the munmap
function. This deletes the mapping for the address range. You can read more about this function if you wish at http://linux.die.net/man/2/munmap.
The last two lines of our application are very simple. The close(framebuff_filedesc)
function closes the handler to the framebuffer. We then use the return
statement to exit the program.
Let's try this code out now.
We will once again be using the gcc
compiler.
The command to compile our application is as follows:
gcc -o fifth_c_prog fifth_c_prog.c
You do not need to link any libraries. Once we have our program we can run it as follows:
./fifth_assem_prog
The screen should now fill red, covering up any windows that are open, including the terminal. If you move your mouse around, the desktop will start to re-render, replacing the red.
Here we have seen how to fill the screen with color, but how about drawing a line on it?