Appendix: Photon in Embedded Systems

This appendix includes the following:


This appendix makes the following assumptions:


The Photon microGUI is an embedded Graphical User Interface (GUI). This GUI is made up of numerous processes that use Neutrino message passing in order to create a highly responsive user experience. Photon is made up of these main components:

Photon Server

The Photon server is the core server process for the GUI. This process must be the first graphical process run in the system. It is responsible for handling region creation and destruction, clipping, and managing the Photon event space.

Graphics subsystem

This process, io-graphics, handles the Photon draw stream and loads the hardware driver. This process runs before any user application processes. You must specify the following when running this process:

Font support

This process (phfont) and associated libraries are used to render and gather metrics about fonts. Photon can render the following types of fonts:

Input support

This process is responsible for handling user input from a mouse, keyboard, or touchscreen. This process communicates with your input hardware and then emits Photon events, which are collected and delivered to graphical processes in the system.

User applications

Once all of the other processes are running you can start user applications.

Steps to boot into Photon

Here's an outline of the steps required to start Photon yourself, in the context of an embedded, closed system:

  1. Export (at a minimum) the PHOTON_PATH environment variable.
  2. Start the Photon server.
  3. Configure your fonts.
  4. Start the graphics driver.
  5. Start the input driver.
  6. Start the window manager, if required.
  7. Start your application(s).

Each of these steps requires certain files be installed in your target system. By predetermining exactly what graphics hardware you have and what fonts your application needs, you can keep the number of files (and size of the image) to an absolute minimum. This reduction in size may in turn have a positive impact on your system's startup time.

We'll go through all the steps in detail and discuss the files needed for each step. At the end of this process, you should know exactly what Photon files you'll need to run your embedded application.

The basics

Step 1. Export environment variables

The PHOTON_PATH environment variable points to the base directory of the Photon installation. By default, this directory is /usr/photon. This location is expected to hold at least the following subdirectories:

Photon font files and configuration files used by the font server (platform-independent).
graphics palettes (platform-independent). These palettes are required only when you're running your graphics driver(s) with a color depth of 8 bits.
Photon language translations (platform-independent) These files are required only if your application(s) handles non-UTF8 character encodings via the PxTranslate*() API.

You should set the PHOTON_PATH environment variable in the buildfile where you set other environment variables such as PATH:


The LD_LIBRARY_PATH points the default system search path for libraries. The procnto process uses its setting of LD_LIBRARY_PATH to initialize the privileged configuration string _CS_LIBPATH, which limits the paths that programs running as root can load libraries from.

The PATH environment variable points to the default system search path for binaries. You should set it to include the directories in your build image that contain binaries. These settings apply to any boot image.

Step 2. Start the Photon server

If you don't need to pass any command-line arguments to the Photon server, you can start it as follows:


Note: If you start Photon as a background process (that is, with the ampersand & after the command) you can tell that Photon started correctly by checking that /dev/photon appears in the filesystem. Use waitfor /dev/photon in your buildfile to check that the directory exists.

If your boot image is too large because you've included Photon or other executables, you can place them in another filesystem that you can mount at boot-time. For more information, see mkifs in the QNX Neutrino Utilities Reference.

If you do include any of the Photon executables in your boot image, you should also include /usr/photon/bin in MKIFS_PATH so mkifs can find them.

Files needed


Step 3. Configure fonts

If you're working on a board that has network access and can mount a network filesystem on your host machine, we recommend that you mount ${QNX_TARGET}/usr/photon/font_repository as /usr/photon/font_repository via NFS or CIFS. Although this approach uses the full Photon font system, it simplifies development significantly, and you can customize the embedded fonts later using the information in "Configuring Fonts" in the "Advanced topics" section.

For information about using NFS and CIFS to mount a network filesystem, see "CIFS filesystem" and "NFS filesystem" in the Working with Filesystems chapter of the Neutrino User's Guide.

Include the following libraries in your build file; io-graphics will load the font libraries for you when it starts:

Font manager plugin.
Font manager API library.
/lib/dll/font/, /lib/dll/font/, and /lib/dll/font/
Rendering plugins. Use the use utility to view specific support information for these plugins.
Disk block cache library, used by,, and
Bitstream FontFusion rendering library, used by and
Bitstream FontFusion font management library for font collections (.pfr and ttc), used by
Bitstream FontFusion font cache management library, used by and

Step 4. Start the graphics driver

The graphics subsystem consists of io-graphics, a hardware-specific driver DLL, and a collection of helper libraries. You need the following components to run the graphics subsystem on the target:

Graphics subsystem executable.
Photon draw event handler.
Utilities library for Photon event handling
Photon rendering routines (required by
Font manipulation library (also required by Photon applications).
Font server plugin.
A Photon palette file for the target display.

Additionally, you need a hardware-specific library (or graphics driver). By convention, graphics driver names begin with devg-, for example, If you need to rotate the display, you also require one of the rotate pseudo-drivers, or

Most graphics drivers depend on the following shared libraries:

Software fallback routines for graphics drivers.
Miscellaneous utilities for graphics drivers.

Make sure that all required libraries are accessible by the dynamic loader before you start io-graphics. Use the LD_LIBRARY_PATH environment variable or _CS_LIBPATH configuration string to point to the location of the shared libraries.

In an embedded environment, you should predetermine graphics hardware and mode information such as PCI vendor and device IDs, and resolution. You should switch into graphics mode and start the graphics driver by specifying the commands directly, without the use of convenience tools such as trappers and display-configuration applications.

The command-line arguments that you need to give to io-graphics depend on the kind of display device you're using. If there's a BSP for the board you're working with, see the BSP documentation for information about relevant command-line arguments. Here's a typical invocation for a PCI device:

io-graphics -dati_rage128 xres=1024,yres=768,bitpp=32,vid=0x1022,did=0x544

Here's an example for a non-PCI device:

io-graphics -dsa1110 xres=640,yres=480,bitpp=16,mode_opts=/usr/photon/conf/sa1110.conf

Note: For more information see io-graphics in the Neutrino Utilities Reference. For a richer set of features, use the configuration file option for io-graphics. See "The io-graphics configuration file" section.

Some examples you'll see use the legacy command-line options. You can't mix legacy options with regular options.

When io-graphics starts, it checks to see if a font server is running (i.e. it checks for the existence of /dev/phfont). If there isn't one running, io-graphics starts its own internal font server.

The graphics driver then initializes the display device. At this point, Photon applications can render to the screen.

Step 5. Start the input driver

Normally in a desktop environment, you use the inputtrap utility to automatically generate the correct command line and to invoke the appropriate devi-* driver. For example, it might invoke devi-hirun like this:

devi-hirun kbd fd -d/dev/kbd msoft fd &

See devi-hirun in the Neutrino Utilities Reference for more examples.

You typically run inputtrap because you don't know in advance what the appropriate command line should be.

In an embedded system, however, you typically specify the command line to the devi-* driver manually. This is because the input devices are often found at unusual locations, are incapable of PnP identification, or are simply not supported by an existing devi-* driver. In addition, the inputtrap utility tends to be quite large and could waste precious storage and memory in a constrained environment. If figuring out the appropriate command to run proves difficult, you can temporarily install inputtrap in your image (or mount a networked filesystem that contains the binary) and use it to generate the correct command line. See inputtrap and its query option in the Neutrino Utilities Reference.

If none of the shipped input drivers are able to work with your input hardware, you can customize the input drivers by using the Input Driver Development Kit (Input DDK). For example, you can change the size of the memory footprint, or you can create a custom module to support new devices.

Files needed

The appropriate devi-* driver in /usr/photon/bin

The appropriate .kbd keyboard mapping file in /usr/photon/keyboard

Step 6. Start the window manager

The Photon window manager (pwm) is an optional component that provides your system with windowing functionality you may not need. If your application user interface consists of one (or more than one) program that always fills the screen, uses a "card" or "slide" paradigm (that is, a UI composed of a series of stacked cards or slides that the program flips through), or uses dialogs that your application controls itself, then you probably don't require the window manager. On the other hand, if your UI is built using one (or more than one) program that relies on windowing behavior (such as windows or dialogs that you don't want to manage yourself), then pwm is probably a good fit for your system.

Files needed


Step 7. Start your application

If your application is a single executable and doesn't require the window manager, you may link statically against the Photon-related libraries (such as libAp.a, libph.a, and libphexlib.a). Linking statically avoids the need to include the corresponding shared components in your image, and will pull in only the symbols needed by your program, making the overall image smaller. Also, linking statically has an added benefit of slightly reducing runtime overhead. If you have multiple applications in your image (including pwm), you should always link against the shared libraries and include them in your image.

You can use the pidin utility on a host system to view the libraries that an application or OS component requires. For example, if you wanted to see the libraries required by phcalc, run it on a host system, and then run pidin -p phcalc mem.

The QNX IDE includes a tool called the Dietician that shrinks shared libraries by analyzing the executables in your system and removing the symbols that aren't needed. This realizes most of the benefits of linking statically while still allowing the libraries to be shared. However, if your system only consists of one application (and no window manager), linking statically is probably the better way to go.

Files needed

Note: The libraries in /usr/photon/lib (*.so.1) are provided for runtime compatibility with Photon for QNX Neutrino 6.0 (x86 only). The libraries for QNX Neutrino 6.1 and later are located in /usr/lib.


The following are observations that some customers have encountered when moving Photon to an embedded system.


By default, mkifs strips PhAB resource names from executable files. To prevent this, specify the +raw attribute for all Photon Application Builder applications. For example:


You can run strip yourself on Photon Application Builder applications first.

Flash filesystems

The following flash filesystem properties affect how you configure Photon:

Compression and Speed
PhAB executables, by default, have their resources bound into the executable file at the end of the binary data. Since the flash filesystem is slower when it's seeking in a compressed file, you'll probably want to keep the resource records in a separate file, instead of including them at the end of the binary. To do this, change the makefile so that the resources are bound to a separate file. For example, change the following dependency:
$(LD) $(LDFLAGS) $(ABOBJ) $(MYOBJ) -M -o mine
usemsg mine ../Usemsg
phabbind mine $(ABMOD)


$(LD) $(LDFLAGS) $(ABOBJ) $(MYOBJ) -M -o mine
usemsg mine ../Usemsg
phabbind mine.res $(ABMOD)

When your executable is launched, the PhAB library ( automatically finds the resource file, provided the following criteria are met:

  1. The resource file has the same basename as the binary, with the extension .res
  2. The resource file is in the same directory as the binary.

If you want to group your resource files in a separate directory, you can. Place them in the directory specified by the exported AB_RESOVRD environment variable, and the PhAB library will look for them there. The naming of the resource files must meet the first criterion, listed above.


Many embedded systems lack components that are typical on an x86 desktop machine, such as BIOS ROMs. Because many of the modeswitchers that Photon supports require a video BIOS to allow them to switch graphics modes, you might need a BIOS on the board. Check with us to see if a non-BIOS version is available.


Here are some other considerations:

CPU Speed
For some embedded systems, the CPU performance will be slower than the desktop. You'll want to consider this when you design your Photon applications for the embedded environment.
If the scrolling area pages down more than one page at a time when you click in the trough, try increasing the value of the mouse repeat delay in Photon. For example:
Photon -D1000 &

You can set the throttling parameters on both the Input and the Photon Server. By reducing the speed at which mouse events are emitted, you can reduce the traffic through the Photon system. On slower 386 and 486 platforms, it's common practice to lower the throttling on input to 10 or 20 ms.
Phindows and Phditto
If your target application needs to support remote diagnostics from either Phindows or phditto, you'll also need to install phrelay, a render library, and the services configuration file.


Let's look at the steps involved in embedding Photon for use in an embedded system by creating a simple buildfile that contains a few simple Photon applications.

Our goal is to build a Photon system with the following minimal capabilities that satisfies our system's requirements:

Note that a window manager isn't strictly required for an embedded system, but we'll include one to make our example easier to use.

We'll follow these steps:

Required binaries

The first step involves figuring out all the binaries required to run Photon. You can see everything that's running on a full system. Run Photon on your PC, and look at the output of the pidin arg command.

From that list, you need only a few of the programs:

Save the argument list for your system in a file. We'll need that output later.

Required libraries

On this embedded system you want only the components listed above, plus you'll run a couple of simple applications:

Run the applications, then look at the output of the pidin mem command. The resulting listing tells you every library that you need to make available to the embedded system. For a graphics driver, you'll use the generic SVGA driver (

So you need the following libraries (at least):

Required fonts

Now let's look at fonts. Sometimes an application expects a specific font, and codes directly to that font. If this is the case, you need to explicitly include every font that your application needs. If you standardize on a certain family/style of fonts or if you don't care what exact font you have (as long as the size is okay), then you can cut down on the number of fonts and use one font to replace several other families of fonts. For example, you can use Times as a replacement for Helvetica and Courier.

In this example, because you're using a few simple applications, and because you're trying to create the smallest image possible, you need only two fonts: a monospace and regular TrueType version of Prima Sans.

Now's a good time to create a play area on your system to begin testing the embedded system, and collecting required files.

Create a subdirectory called phembed in your home directory (or whichever directory you wish to keep your source files). Within that directory, create these subdirectories:

Now back to the fonts. In this example, you want to use the primasansmonobts TrueType font for everything. You'll also want to use a mouse, so you'll include the phcursor.phf file.

Here are the files you need:

Copy these files (except fontdir from /usr/photon/font_repository to /phembed/font_repository, then change directories to /phembed/font_repository.

You need to modify the fontmap and fontopts files to reflect the fonts, options and mappings you want for your embedded system. You can edit these files by hand (see phfont for more information on the structure of these files). In our case lets make sure that the fontmap file contains:

    ? = primasansmonobts

This ensures that all unknown fonts will be replaced with the primasansmonobts font, provided in the tt2009m_.ttf file.

To generate fontdir, use the mkfontdir like this:

mkfontdir -d /phembed/font_repository

Putting it all together

Now let's put all the pieces you need in place and create a buildfile for your embedded Photon system. Run mkifs to create an image.

  • For a sample buildfile that includes more Photon components, such as the background manager bkgmgr, see Getting Photon on your board in the Working with a BSP chapter of the Building Embedded Systems guide.
  • In a real buildfile, you can't use a backslash (\) to break a long line into shorter pieces, but we've done that here, just to make the buildfile easier to read.

[virtual=x86,bios +compress] .bootstrap = {
    startup-bios -v
    LD_LIBRARY_PATH=:/proc/boot:/usr/lib:/lib:/lib/dll \
      PHOTON_PATH=/usr/photon procnto -v
[+script] .script = {
    procmgr_symlink ../../proc/boot/ /usr/lib/

    display_msg Welcome to QNX Neutrino 6.3 on an x86 platform with Photon

slogger &


    display_msg Starting Photon
    waitfor /dev/photon 10

    display_msg Starting Input
    devi-hirun kbd kbddev ps2 mousedev &

    display_msg Starting Graphics
    io-graphics -dsvga photon,xres=1024,yres=768,bitpp=16,refresh=60 -pphoton
    waitfor /dev/phfont 10

    display_msg Starting Window Manager
    pwm &
    devc-pty &

    display_msg Starting Terminal
    pterm /proc/boot/ksh &


[type=link] /bin/sh = /proc/boot/ksh
[type=link] /dev/console = /dev/ser1
[type=link] /tmp = /dev/shmem

# standard libs

# photon libs

# io-graphics libs

# graphics driver
/etc/system/config/crtc-settings = /etc/system/config/crtc-settings
/usr/photon/palette/default.pal = /usr/photon/palette/default.pal

# font libs
/lib/dll/font/ = /lib/dll/font/
/lib/dll/font/ = /lib/dll/font/

# font config
/usr/photon/font_repository/tt2009m_.ttf = /usr/photon/font_repository/tt2009m_.ttf
/usr/photon/font_repository/phcursor.phf = /usr/photon/font_repository/phcursor.phf
/usr/photon/font_repository/fontopts = /usr/photon/font_repository/fontopts

/usr/photon/font_repository/fontdir = /phembed/font_repository/fontdir

/usr/photon/font_repository/fontmap = /phembed/font_repository/fontmap

# input config
/usr/photon/keyboard/en_US_101.kbd = /usr/photon/keyboard/en_US_101.kbd

[+raw] /usr/photon/bin/pterm = pterm
[+raw] /usr/photon/bin/phcalc_sm = phcalc_sm

# allow pterm to save its configuration to RAM, if the user changes it.
[type=link] /.ph/pterm = /dev/shmem

Note the following about the buildfile:

Once you've built your image using mkifs, you can transfer it to a test machine to see how it works. See "Transferring an OS image onto your board" in the Working with a BSP chapter of Building Embedded Systems for more information.


  1. When I start io-graphics, it seems to be running, but nothing appears on the screen.

    Check the system log; io-graphics may have sent error messages to the system logger, slogger. In order to debug the problem, make sure slogger is running before starting the graphics driver. Use sloginfo to display the system log messages.

  2. When I start an application, it exits with the message Ap: Unable to locate Photon.

    Make sure both the Photon server and the font manager are running. You can determine if they're running by making sure that /dev/photon and /dev/phfont exist.

  3. When I start an application, it exits with the message Ap: Unable to open resource file.

    If you include an application that was built by PhAB in an image created by mkifs, some information will be stripped out, since mkifs does a very aggressive binary strip. You can avoid this by using the +raw attribute; see the mkifs documentation for more information. Since setting the attribute will cause the application not to be stripped, you may want to use the strip utility to manually strip the binary before building the image, to reduce the image size.

Example: Using the IDE's System Builder

Building an embedded OS image that includes Photon using the IDE is similar to writing a buildfile and using the command line. You need to perform the same analysis (described in the example above) of the required binaries, libraries and fonts. The difference is that you don't write a buildfile; instead you create a project in the IDE's System Builder.

This example assumes that you're familiar with the IDE's System Builder. See the Building OS and Flash Images chapter in the IDE User's Guide for more information.

Note: Instead of building a new System Builder project from scratch, you can import the buildfile from the command-line example above. You can then modify the resulting System Builder project to suit your needs.

Here are the general steps required to make a System Builder project that's identical to the previous buildfile example:

  1. Create a new System Builder project. You'll need to select the target platform. Note that the project contains and the link to already.
  2. Add these binaries:

    You need to use the equivalent of [+raw] on pterm and phcalc_sm by selecting them, and setting the Strip File property to No.

  3. Next, add the required libraries. You'll find that some of them are already in the project, as the System Builder identifies libraries required by binaries, and adds them automatically.

    Standard libraries:

    Photon libraries:

    Graphics libraries:

    Font libraries:

  4. Now add the DLLs:
  5. Add these required files.

    Fonts and the font configuration files:

    Other required configuration files:

  6. Finally set up these symbolic links:

You can now build the image, transfer it to your target, and run it.

Advanced Topics

This section covers some of the more advanced topics in embedding Photon. It covers:

Configuring fonts

Configuring fonts and installing the font server components in the correct location is the most difficult part of embedding Photon.

To configure the font system, you need to:

  1. decide whether to run an internal or external font server
  2. determine which fonts your system requires
  3. determine the font binaries required
  4. set up the font configuration files.

Internal or external?

The first decision you must make about the font service is how the server is started. It can run as a stand alone process (we refer to this as an external server) or as a plugin to io-graphics (which we call an internal server).

We recommend to run an external server in these conditions:

To run an external font server, start phfont before io-graphics. To run an internal font server, simply start io-graphics.

Required fonts

When building an embedded system, you also need to make careful decisions about the level of font support, including which fonts you need, and whether or not you need scalable fonts, since extra fonts make for a larger image size and potentially longer startup time.

The first step is to decide which fonts you need:

You can map, or substitute, font names by using the fontmap file. For more information on the format of fontmap and other font configuration files, see phfont in the QNX Neutrino Utilities Reference.

Required fonts binaries

You may be able to reduce the number of binaries required by the Photon font system, depending on the types of fonts you need to render on your target. Each font type has an associated plugin that supports that type, and each plugin in turn requires additional libraries. Each plugin requires and The following table summarizes additional, plugin-specific, requirements:

Fonts supported Plugin Required libs
Bitstream TrueDoc (.pfr)
TrueType collections (.ttc)
Photon bitmap (.phf)
TrueType (.ttf), Adobe Type1 (.pfa), Adobe Type2 (.cff), Bitstream Stroke (.ffs), Bitstream Speedo (.spd, public encryption only), Bitstream T2K (.t2k)

You can use the -b commandline option for phfont or io-graphics to generate a font usage report. The report file contains information about font names and font files used by your application while the font server was running. This allows you to put the only required fonts and DLLs on your target system. Note that the font usage report doesn't contain a record of dynamically loaded fonts (see the PfDynamicLoad*() set of functions).

Configure the font server

The font system is configured with various files. The minimum configuration requires:

Recommended additional configuration files are:

For more information about the format of each of these files, see phfont.

You can configure the fonts on the embedded system itself, but it's easier to use your development system to configure the fonts to mimic the desired configuration for the embedded system, then assemble the font data and configuration files in the appropriate Embedded File System (EFS) build image directory.

Note: If you're using a self-hosted development system to mimic your target, then the font server can aid in determining which fonts you need by logging failed requests for fonts that aren't mapped (explicitly or otherwise). See phfont for more information.

In an embedded system with only a few applications, chances are you'll need far fewer fonts than a desktop system requires. In this situation, you can provide minimal configuration files (all located in /usr/photon/font_repository):

This file needs to list only the fonts you're installing in /usr/photon/font_repository. You can edit this file in one of two ways:
Make a copy of the default file and edit it.
Make a copy of the default file and edit it.

This file can consist of a single line:


(If you aren't including PrimaSans BT in your image, change this to the name of the font you want to use as a default).

Adding a splash screen

Normally, the graphics driver switches into graphics mode when the graphics subsystem (io-graphics) is started. However, you may sometimes wish to switch into graphics mode earlier in the boot process in order to display a splash screen. In this case, you can modify the IPL code or startup to perform a switch into graphics mode, and to display the splash screen image. For more information, see the Writing an IPL Program and Customizing Image Startup Programs chapters in Building Embedded Systems.

Startup sets aside the area of memory used for the frame buffer, and then later passes the physical address of this memory to the graphics driver.

When io-graphics starts, it calls into the graphics driver to reset the video mode, and clear the display. This can cause an undesirable "glitch" on the display between the time the graphics subsystem starts and the application starts. In order to achieve a smooth transition from the splash screen to the running Photon application, the graphics driver requires special support. Some graphics drivers, including the generic "flat" driver, already have this support.

The flat driver is a simple case, since it always assumes the device is already in graphics mode. On the command line, you specify the physical address of the frame buffer (which is the same frame buffer that startup draws the splash screen in). As well, you need to specify the -N option to io-graphics in order to prevent the display from being cleared. Here's an example:

io-graphics -g800x600x16 -amem=0x1f000000,2048 -N

Note: If your embedded environment has a touchscreen or pen input device, you can adjust the input values for pointer events by specifying the -D, -R, and -U options. For example, to prevent random changes in position because the size of a finger press is larger than a pixel, specify the -U option.

For more information, see Photon in the QNX Neutrino Utilities Reference.