Taming Sprawling Interfaces – Raspberry Pi BCM Host

If you’ve done any amount of programming, you know that one of the hardest challenges is just getting your head around the task.  There’s the environment, the language, the core libraries, build system, etc.

I was born and raised a UNIX head, and I still don’t know how the Makefile/autoconf/configure thing works.  It’s always been a black art, and I’ve never been able to go beyond the very basics from scratch.  Every time I read one of those makefiles, blood starts dripping from my pores like Zorg in Fifth Element.  Pure Evil!

So, I wanted to tackle the task of doing some UI programming on the Raspberry Pi.  If you must know, my ultimate aim is to be able to easily morph my programming environment to provide me with whatever language environment I could want.  For example, maybe I want to program using a GDI/User32 interface.  Or maybe X, or maybe HTML5 Canvas, or WebGL, or whatever.  The Raspberry Pi is perfect for this task because it’s some hardware, but it’s so cheap, I just view it as a runtime environment, just like any other.  The trick of course is to get all the appropriate libraries and frameworks in place to do the deed.

Well, you’re not likely to see GDI anywhere but Windows (perhaps Wine), so if you want it here, you’ll have to recreate it.

But, let’s begin at the beginning.  The Raspberry Pi is little more than a cell phone without a case or radio.  Of course it includes a USB port and HDMI, so it makes for a nice little quickly attachable/programmable kit.  One of the key features of the Pi is that it can do hardware acceleration for graphics operations.  The most clearly evident demonstration of this is the running of XBMC on the Pi.  You can decode/display 1080p video on the device, without it breaking a sweat.  Same goes for most simple 3D and 2D graphics.  How to unlock though?  From the terminal, it’s not totally evident, and from X Windows (which is not currently hardware accelerated), it’s even less evident.  What to do?

The Pi ships with some libraries, in particular, there libbcm_host.so.  this library is supplied by Broadcom, and it’s on every device.  It provides what’s known as the VideoCore APIs.  This library contains, amongst other things, the raw display access that any application will need.  But, like many APIs, it is sprawling…

I really want to use this though, because I want to go all the way down to the framebuffer, without anything getting in my way.  I want to do everything from Windows and views to mouse and keyboard, because I want to be able to emulate many different types of systems.  Obviously, this is where I’ll have to start if I am to pursue such an ambition.

First task, apply what I learned from my Simplified API explorations.  On the Pi, located in the directory ‘/opt/vc/include’, you’ll find the root of the bcm_host world.  The most innocent header file: ‘bcm_host.h’ lives here.  Within this header file, there are only three functions:

void bcm_host_init(void);
void bcm_host_deinit(void);

int32_t graphics_get_display_size( const uint16_t display_number,
                                                    uint32_t *width,
                                                    uint32_t *height);

That’s it, nothing to see here, move right along. Actually, the very first one is most important. You must call bcm_host_init() before anything is called. This is similar to libraries such as WinSock, where you have to call a startup function before anything else in the library.

The third function there; graphics_get_display_size(), will tell you the size (in pixels) of the display you specify (0 – default LCD). That’s a handy thing to have, as you start to contemplate building up your graphics system, you need to know how large the screen is.

Alrighty then, first task is to write the zero-th level of hell for this thing, but wait… The rest of this header looks like this:

#include "interface/vmcs_host/vc_dispmanx.h"
#include "interface/vmcs_host/vc_tvservice.h"
#include "interface/vmcs_host/vc_cec.h"
#include "interface/vmcs_host/vc_cecservice.h"
#include "interface/vmcs_host/vcgencmd.h"

Turns out those first three functions were deceptively the tip of the iceburg. The rest of the system is encapsulated in this large tree of interdependent other header files. Each one containing constants, structures, functions, and links to more header files. Such a deep dive requires a deep breath, and patience. It can be conquered, slowly but surely.

First file to create: bcm_host.lua

local ffi = require "ffi"

ffi.cdef [[
void bcm_host_init(void);
void bcm_host_deinit(void);

int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height);
]]

require "libc"
require "vc_dispmanx"

require "interface/vmcs_host/vc_tvservice"
require "interface/vmcs_host/vc_cec"
require "interface/vmcs_host/vc_cecservice"
require "interface/vmcs_host/vcgencmd"

And that’s it. According to my API wrapping guidelines, the first level is meant for the programmer who wants to act like they are a C programmer. No hand holding, not little helpers. I cheat a little bit and provide some simple typedefs here and there, but that’s about it.

Next up is the BCMHost.lua file:

local ffi = require "ffi"

require "bcm_host"

local lib = ffi.load("bcm_host");

--[[
	The bcm_host_init() function must be called
	before any other functions in the library can be
	utilized.  This will be done automatically
	if the developer does:
		require "bcm_host"
--]]

lib.bcm_host_init();

local GetDisplaySize = function(display_number)
	display_number = display_number or 0
	local pWidth = ffi.new("uint32_t[1]");
	local pHeight = ffi.new("uint32_t[1]");

	local err = lib.graphics_get_display_size(display_number, pWidth, pHeight);

	-- Return immediately if there was an error
	if err ~= 0 then
		return false, err
	end

	return pWidth[0], pHeight[0];
end

return {
	Lib = lib,

	GetDisplaySize = GetDisplaySize,
}

For the less he-man programmer, this is a bit more gentle, and a lot more Lua-like. First of all, it includes the previous raw interface file (require “bcm_host”). This gives us our basic definitions.

Then it loads the library, and initializes it:

local lib = ffi.load("bcm_host")

lib.bcm_host_init();

At this point, we can confidently call functions in the library. The last bit of code in this file provides a convenience wrapper around getting the display size. Of course any programmer could just call the ‘graphics_get_display_size’ function directly, but, they’d have to remember to allocate a could of int arrays to receive the values back, and they’d have to check the return value against 0, for success, and then unpack the values from the arrays, and of course check to see whether you want to deal with a particular screen, or just the default screen… Or you could just do this:

local BCH = require "BcmHost"
local width, height = BCH.GetDisplaySize();

And finally, just to make this a ‘module’ so that things are nicely encapsulated:

return {
	Lib = lib,

	GetDisplaySize = GetDisplaySize,
}

There, now that wasn’t too bad. Of course, like I said, this is just the tip of the iceberg. We can now load the bcm_host library, and get the size of the display. There’s a heck of a lot more work to be done in order to get a ‘window’ displayed on the screen with some accelerated 3D Graphics, but this is a good start.

Next time around, I’ll tackle the Display Manager, which is where the real fun begins.

The entirety of my efforts so far can be found in the LJIT2RPi project on GitHub.



Leave a comment