Maemo coding style and programming guidelines

General overview

The purpose of this document is to set guidelines and recommendations for maemo developers and new program code written to the platform. This document assumes that programming is done in C language, but most of the guidelines are not language-specific. It is also required that developers possesses a certain level of knowledge in Linux and GTK+ programming.

So why are programming guidelines needed? There is at least the following rationale:

Readability of code
Many people write program code for maemo and have different coding styles because C allows it, therefore C programs can look very different and be difficult to read for someone who is used to a particular style.
Correctness and robustness of code
Some people are more disciplined programmers and others are more relaxed; this can lead to “sloppy programming” that is, incomplete error handling, no sanity checks, hacking and so on.
Maintainability of code
Many programmers tend to write “ingenious” code, which is almost impossible for other to read, and thus almost impossible to maintain and debug. So, let us follow the KISS principle.

“Debugging is twice as hard as writing code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” (Brian W. Kernighan)

Platform requirements
Some guidelines must be followed by maemo developers because they have direct impact on the platform (for example, power usage).

In addition to this document it is a good thing to browse through the GNOME Programming Guidelines1 and pay attention to Glib/Gnome-specific issues. There is also a coding style document by Linus Torvalds for the Linux kernel2, which contains hints on how to configure your editor with specific indentation style used in maemo code.

Although coding style alone is not a guarantee of good software, it at least guarantees a good level of legibility and maintainability.

Maemo coding practices

Use the following maemo coding practices:

  • The Basic good programming section contains generic guidelines about coding style, variable usage and naming, error checking and many generic guides that good developers may already be familiar with. The most important thing about code style is consistency; choose one style and always use it.
  • Maemo developers must be careful with memory usage and memory leaks, you must always consider employing and initilising local variables. Protect your code from memory leaks by freeing memory as soon as possible or else consider using automatic variables, which are stored in a stack (and not in a heap) and which guarantee automatic allocation and de-allocation.
  • Do not fragment memory pool with malloc(3) and small/multiple free(3)s, try to reuse memory or free it in large blocks. A more effective method than manually searching for memory leaks is to use an automatic tool. For more information see Section Memory usage.
  • If you plan to use threads, remember how difficult threads can be to debug. Do not use threads because it is easy or fashionable, consider using it only to isolate function calls in parts of your code. Note that asynchronous function calling can replace threads in many ways.
  • System message notification and application-to-application communication makes use of D-BUS, which is a lightweight message passing protocol. D-BUS must not be used to pass long or frequent messages between applications. Note that D-BUS delivery is not guaranteed, your application must check for it. For more information on D-BUS guidelines, see Section D-BUS.
  • When you are developing a new application in the maemo platform, it is your responsibility to catch hardware events such as button press and touchpad actions in your application. Touchpad events are similar to mouse events in a desktop environment. Hardware buttons are a special case because they have special semantics, such as zooming, canceling operations and other actions expected by maemo users. For more information on Hardware events, see Section Event handling.
  • Embedded devices offer many challenges, primarily for desktop developers. While a Desktop user accepts a certain level of resource wasting, users of maemo devices do not accept such waste. One of the main objectives of maemo developers is to keep resource usage at minimum. For a full review of this topic, see Section Power saving.

Basic good programming

Most of the following conventions are useful for any programmer, and experienced programmers must already know them, but there are also some that are related to the programming of embedded devices, such as avoiding the use of dynamic memory, prioritizing the reliability of code and above all saving resources.

Coding style

Always prefer simple to “clever”, and be wary of lazy coding and especially “hacks”. Clever code is harder to read, therefore harder to debug and more difficult to prove correct. Careful coding means taking care of all possible error situations and taking the time to think while writing the code.

Comment on your source code and write it in English. In general, comments describe what is being done, not how. Comment on what functions do as much as possible and keep comments always synchronised with the code. Describe the formats (such as strings, files and input) to ease the understanding of code that reads the format.

Do not use more than 3–4 indentation levels. If you use tab characters, make sure the code does not overflow from a terminal screen (with 80 columns) if the tab size is 8. Do not write functions longer than 50–100 lines of code, unless it is absolutely necessary and keep lines 1–75 characters long.

Use empty lines to group code lines that logically belong together and use 1–2 empty lines to separate functions from each other. The command indent -kr <inputfile> -o <outputfile> can carry out some of these functions for your code.

Variable usage

Always favour local over global variables, that is, keep the visibility of a variable at minimum. Do not use global variables to pass values between functions, pass them through arguments. Note that global variables require extra care when programming with threads so avoid them.

Initialise all variables to a known illegal value (like 0xDEADBEEF) if you cannot give them a meaningful value from the beginning. There is at least two reasons for this:

  • Known initial values contribute to predictable behaviour in the program, that is, reduces random behaviour in error situations caused by uninitialised memory, which helps to find errors.
  • Illegal initial values increase the probability for early detection of an erroneous state of the program, as an illegal value (such as a null pointer) is more easily detected than a random one.

Error checking and handling

Examine the return codes of all system calls and functions that can potentially fail. Usually the system only calls fail in rare situations, but these must also be dealt with. At least, it is good to have a log entry stating the reason for a program exit when the program cannot continue, instead of calling a segmentation fault because memory allocation failed.

Prepare for network disconnections. If the program uses network connections, it must be written so that it can recover from lost connections and broken sockets. The program must operate correctly even when a disconnection occurs.

Variable and function naming

Use descriptive, lower-case names for variables and functions, and make use of underscores (_) to indicate word boundaries. Avoid using non-static and unnecessary global names. If you really need to employ a global variable, type or function, use a prefix that is specific to the library or module where the name is, for example, gtk_widget_.

Employ each variable for one purpose only and leave the optimisation to the compiler. Short names for local variables are fine when their use is simple, for example, i, j and k for index variables. In functions with pointer arguments, use const when possible to indicate who changes (or does not change) the pointed memory.

Constants and header files

Do not use “magic values”, that is, do not use literal constants (such as numbers) as coefficients for buffer sizes. Use #define in a header file to name them and use the name in the code instead of the value. In that way it is easier to adjust constants later and make sure that all occurrences are changed when the constant is modified. But avoid things that are named like "#define ONE 1" and so on. If you have a great deal of constants, consider putting them into a separate header file.

Write your header files to be protected against multi-inclusion. Also, make sure that they work when included from C++ code; this is achieved by using #ifdef __cplusplus extern "C" { ... }, or by using G_BEGIN_DECLS ... G_END_DECLS in Glib/GTK+ code.

Memory usage

Prefer static memory (variables) to dynamic memory where the needed memory size is not large and/or is used regularly in the program. Static memory does not leak, a pointer to static memory is less likely to cause a segmentation fault, and using static memory keeps the memory usage more constant.

Always free unneeded dynamic memory to save resources. Remember that shared memory is not released automatically on program exit.

Avoid memory heap fragmentation by mixing calls of free() and malloc(). In a case where several lumps of allocated memory have to be freed and reallocated, the use of free must be a single sequence and the successive allocation another sequence.

Thread usage

Threads can cause very nasty bugs, and the advantages offered by them are usually insignificant. Avoiding threads also makes using static memory easier. You can only use threads to isolate portions that are considered untrusted and/or prone to failure, that is, plug-ins, Bluetooth or Wireless scanning, from the main execution flow.

Maemo conventions

This section contains a collection of coding-related guidelines that complement the generic rules in Section Basic good programming. You must pay special attention to the error handling discussion in Section Error checking and handling.

D-BUS

The D-BUS message bus is a central part of the platform architecture. D-BUS is primarily meant for lightweight asynchronous messaging, not for heavy data transmission. This section contains conventions concerning use of D-BUS.

One design principle of D-BUS was that it must be lightweight and fast. However, it is not as lightweight and fast as a Unix socket. Note also that, by default, D-BUS communication is not reliable. The system has a single D-BUS system bus that is used by many processes simultaneously. A big load on the system bus could probably slow down some GUI response times. From the previous sentences you get the following conventions:

  • Do not use the D-BUS system bus for heavy messaging or to regularly transfer large amounts of data, because it slows down other processes' communication and it requires extra processing power (and with it, electrical power). Consider using a Unix socket, pipe, shared memory or file for that purpose.
  • Avoid broadcasting on a D-BUS, especially when there is a lot of listeners, such as on the system bus.
  • When sending a message with, for example, dbus_connection_send(), do not assume that the message is surely be received by the server. Some things can happen to make the message disappear on the way: the server can fail before receiving the message when the message is buffered in the client or in the daemon, or message buffer in the daemon or in the server can be full and because of that the message is discarded. Therefore you must send and receive replies if you want reliable message passing..
  • In D-BUS messages specified by the application framework (Incl. Task Navigator, Control Panel, Status Bar, and some LibOSSO messages), use UTF-8 encoding as the character encoding for all string values.
  • The libOSSO library of the application framework contains high-level functions (osso_rpc_*) that wraps D-BUS API for message sending and receiving.
  • All maemo applications need to be initialised with osso_initialize() function, which connects to the D-BUS session bus and the system bus. One symptom of missing initialisation is that the application starts from Task Navigator but closes automatically after a few seconds.
  • Use the underscore (foo_bar) instead of “fancy caps” (FooBar) in D-BUS names (such as methods and messages). Do not repeat the service or interface name in, for example, method and message names. Name the D-BUS method names using a verb describing the function of the method when possible.

Your application must listen to D-BUS messages when you want to catch system D-BUS messages such as “battery low” and “shutdown”. When these messages are received. Your application may want to ask the user to save files that are open, or react to them at any time.

There is a separate system message apart from the hardware state messages presented earlier, which comes when applications are required to close themselves. For more information about these topics, see: Maemo 2.x Tutorial3, section LibOSSO library and Maemo Connectivity Architecture4, section D-BUS interface of ICD.

Signal handling

The application must exit() cleanly on the TERM signal because on clean exit GTK stores the clipboard contents to the clipboard manager and it is not lost.

The TERM signal is sent, for example, when the Desktop kills the application in the background (if application has announced itself to be killable).

Event handling

The maemo user interface (Hildon) is based on GTK+ toolkit (the same used in Gnome) and so many aspects of maemo's event handling are much the same as with GTK+. There are a few points which are specific to maemo because it is designed to be used with a touchscreen. Some points to remember here are:

  • It is not possible to move the cursor's position in touchscreen without pressing the button all the time. This difference may affect drawing applications or games, where the listening events need to be designed so the user can click the left and right side of screen without ever moving with mouse from left to right.
  • The touchscreen can take only one kind of click, so only the left of the mouse is used, leaving no functionality under the right mouse button.
  • When menus from the right of the mouse are required, it is still possible to show them by pressing down the mouse for a little longer than a fast click. These kind of context-sensitive menus are supported by the platform, see the GTK+ widget tap and hold setup function in the Hildon API Documentation5.
  • GtkWidget in maemo has been changed to pass a special insensitive_press signal when the user presses on an insensitive widget. This was added because of actions such as showing a banner when disabled widgets are pressed in, for example, a menu.

Touch screen events

The main interaction with users in maemo is through touch screen events, pay attention for the following actions:

  • Single tap
  • Highlight and activate
  • Stylus down and hold
  • Drag and drop
  • Panning
  • Stylus down
  • Stylus up
  • Stylus down and cancel

Of course, all those touchscreen actions are treated by GTK as regular mouse events, like button presses, motion and scrolling. For a full description of these actions, see Hildon User Interface Style Guide version 1.06, section 5.

Minimising problems in X applications

The application must not grab the X server, pointer or keyboard, because the user would not be able to use the rest of the device. Exceptions: System-UI application and Task Navigator, Statusbar, Application menu, Context-sensitive menu and combobox popup widgets.

Applications must not carry out operations that may block, for example, check for remote files to see whether to dim a menu item, while it has menus or popups open.

If the operation may block (for example, when the device gets out of WLAN or Bluetooth range), the whole device user interface freezes if blocking happens with the menu open. This kind of check must be done before opening menu/popup, or in a separate thread.

All application dialogs must be transient to one of the application's windows, window group or its other dialogs. Dialogs that are not transient to anything are interpreted as system modal dialogs and block the rest of UI.

Hardware button events

Table 1. Hardware button events

Hardware keyEvent
Home keyHILDON_HARDKEY_HOME
Menu keyHILDON_HARDKEY_MENU
Navigation keysHILDON_HARDKEY_UP, HILDON_HARDKEY_DOWN, HILDON_HARDKEY_LEFT, HILDON_HARDKEY_RIGHT
Select keyHILDON_HARDKEY_SELECT
Cancel keyHILDON_HARDKEY_ESC
Full screen keyHILDON_HARDKEY_FULLSCREEN
Increase and decrease keysHILDON_HARDKEY_INCREASE, HILDON_HARDKEY_DECREASE

It is important not to use the GDK_* keycodes directly; use the HILDON_HARDKEY_* macros instead (which are just macros to the GDK keycodes). This is because hardware keys may relate to different GDK keycodes in different maemo hardware.

Also, your application must not act on keystrokes if it does not need to. For example, Home and Fullscreen keys have system-wide meanings, and the application is notified by other means that it is going to be untopped or put in fullscreen mode.

For more on this topic, see the above-mentioned Hildon UI Guide, the Maemo 2.0 Tutorial, section Using maemo input mechanism.

State saving and auto-saving

State saving is a method to save the GUI state of an application, so the same GUI can be rebuilt after the application has restarted. In practice, the state is saved into a file stored in volatile memory, therefore the saved state does not last over a reboot of the device.

The application must save its state on any of the following events:

  • When the application moves from the foreground to background of the GUI.
  • Before the application exits due to a memory management measure.

The application must always load the saved state (if it exists) on startup. However, the application can decide to not use the loaded state, by its own calculation. The saved state must always be usable by the application, even if it decides not to use it.

Applications must implement auto-saving if they handle user data. User data is information that the user has entered into the application and that is usually stored permanently, for example, text in a note. Auto-saving ensures that the user does not lose too much unsaved data if the application or system crashes or the battery is removed. Auto-saving can also mean that the user does not ever need to explicitly save his/her data.

Auto-saving can be triggered by an event, for example, the battery low event or system shutdown. The application must auto-save:

  • When it closes (for example, because of a system shutdown or restart).
  • When it is put on the background.
  • At regular, configurable intervals when the application is on top, unless it has just recently auto-saved due to another event.
  • On receiving a message requesting auto-saving.

Changes made to dialogs are not auto-saved. Every application must provide an event handler to implement the fourth item. Naturally, the application does not have to auto-save when there is no new, unsaved data.

You can find more information about state saving in LibOSSO documentation7.

Configurability

Maemo applications must use GConf for configurations whose changes need to be monitored. GConf is the configuration management system used in Gnome and it is recommended to read the documentation available at GConf project page8.

Normal configurations (that do not need to be monitored) must be placed in a file under the ~/.osso/ folder. The file can be easily written/interpreted by the GKeyFile parser from Glib. Applications must have sensible defaults, ensuring that the application functions even if the configuration file is missing.

Using GConf

Naming GConf keys: make a new GConf directory (for GConf keys) under the GConf directory /apps/Maemo for your program. For example, if your program is named “tetris”, your program private GConf keys go under the GConf directory /apps/Maemo/tetris.

File system

File management operations for user files must be done through an API provided by the application framework. In other words: do not use the Unix file I/O API for files and folders that are visible to the user. Note that you can still use the Unix (POSIX) file I/O API in other parts of the file system.

The main reason for this is that maemo needs to make the file management operations behave case-insensitively, even on the case-sensitive Linux file system. Additionally, the auto-naming of files and so on, can be implemented in the application framework.

All applications are normally installed under the /usr directory and must use the directory hierarchy described for /usr in Filesystem Hierarchy Standard (FHS)9. In addition to the directories specified in the document, the following are employed under /usr:

  • share/icons/hicolor/<size>/apps for icon files
  • share/sounds for sound files
  • share/themes for GUI themes

Configuration files are placed under /etc/Maemo/<program name>. Note that some configurations are managed as described in Section Configurability.

Variable data files that are not user-specific (but system-specific) must be created under /var, as suggested in Filesystem Hierarchy Standard.

User-specific files go preferably under the directory /home/user/apps/<program name>. The user's home directory /home/user can be used quite freely when there is no risk of a conflict. However, the directory /home/user/MyDocs is reserved for the user's own files (such as document, image, sound and video files), that is, for files that can be “seen” through the GUI.

The directory for the program temporary files is /tmp/<program name>/. Use mkstemp(3) immediately followed by unlink(2) to create a temporary file. The reason for calling unlink(2) early is to ensure that the file is deleted even if the application crashes or is killed with the kill signal. Note that unlink(2) does not delete the file until the program exits or closes the file.

The available space on the main flash is very limited, and is very easily exhausted by applications or by the user (for example, by copying images, music or videos to it). If the flash is full, every system call that tries to write to the disk fails, and the device is slower because JFFS2 filesystem needs some free space.

Therefore, no file can grow without limitation and file sizes in general must be kept small. Applications must not write to flash when it is starting up, otherwise the write may fail and the application does not start.

Memory profiling

Memory profiling is a difficult task and requires good tools. You must take great care with memory leaks because desktop users reboot the system often and memory leaks are less visible for them, but maemo users expect to reboot the system less (or ever never), so the smallest memory leak is noticeable.

For more information about variables and memory usage, see Section Memory usage There are many memory tools available for profiling and for finding memory leaks. Using Valgrind is recommended.

When compiling your program with debug and profile information, and use standard profiling tools like gprof to find bottlenecks, as well as valgrind to find memory leaks/overruns. The XResTop tool (a top-like application to monitor X resource usage by clients) can also be used to monitor the leaking of X resources.

Secure programming

This section lists conventions to be used to prevent security vulnerabilities in programs. At the same time, these can make programs more robust. For more information on secure programming, see Secure Programming for Linux and Unix HOWTO10 and Secure Programming11.

Hildon applications (such as GTK+) cannot run with elevated privileges, and must be avoided sid/gid in your programs. If your applications really require higher privileges then you must split your code in two: create a backend with higher privileges that communicate with your user interface through IPC.

These are the general rules for secure programming:

  • Use the exec(3) family of functions instead of system(3) and popen(3) when executing programs. Also, do not give shell access to the user.
  • Buffer overflows must be prevented. See Section Buffer overflows.
  • Backed up data should be as secure as the original data. That is, security and secrecy requirements for the backup data must be the same as for the original data that was backed up.
  • The default permissions for a new file (umask value) must have permissions for the user only.
  • Temporary files must be created using mkstemp(3).
  • Avoid storing secrets, such as passwords, directly into the code.
  • Do not use crypt(3).
  • Do not use higher privileges than necessary for the job at hand. Drop extra privileges when they are no longer needed, if possible.
  • Always check the success of all system functions, such as malloc(3).
  • Random numbers for cryptography must be read from the /dev/random or /dev/urandom device.

Buffer overflows

To avoid buffer overflows you can use the safe versions of some system functions listed in the table below. For Glib/GTK code, the Glib versions are best, for example, g_strlcpy() guarantees that the result string is NULL-terminated even if the buffer is too small, while strncpy() does not.

Table 2. System functions

UnsafeSafeBest (GLib)
strcpy()strncpy()g_strlcpy()
strcat()strncat()g_strlcat()
sprintf()snprintf()g_snprintf()
gets()fgets()

When using scanf(3), sscanf(3) or fscanf(3), limit the amount of data to be read by specifying the maximum length of the data in the format string. When reading an environment variable with getenv(3), do not assume anything for the size of the variable; it is easy to overflow a static buffer by copying the variable into it.

Packaging

All software above the system level is installed as Debian packages. It is recommended that you read the Debian New Maintainers' Guide12, which explain the basic packaging process of generic Debian packages. The maemo packages are a special case among Debian packages and the difference between them are explained in Making an application package13.

Power saving

Modern CPUs are concerned about efficient power usage and many techniques were developed in order to reduce this power consumption. One of those techniques is CPU frequency reduction, which scale downs the CPU's clock after a period of low usage.

Idle mode represents the CPU's state of minimum resources usage and it is the best scenario available to save power. In this state many core functions are disabled and only when a external event happens, like tapping on screen the CPU is awakened and consumes more energy.

The maemo developers must be aware that maemo platform employs these techniques to reduce power and programs must be written so that they consume as little electrical energy as possible by means of giving maximum opportunity for the CPU to become idle.

You can now derive the following rules of thumb:

  • If your program is not processing or doing something useful, leave it idle. Many toolkits (such as GTK+) have a idle() function, call it when you know your program has nothing to do.
  • Do not unnecessarily encumber the CPU, as power consumption is proportional to the CPU load. Many process running and competing for CPU leads to more power waste.
  • Do not use periodical alarms or timers unless absolutely necessary. Think carefully about what a reasonable period is for updating a progress bar on the screen. Continuous (permanent) timers that tick faster than once in every 5 seconds must not be used.
  • Busy waiting mode is not acceptable, even if it is busy waiting on empty loops or NOPs: significant power saving is achieved only when the CPU is idle. The example shows how almost the same code can either prevent or support sleep.
    Power Management Unfriendly code:
     
    SDL_EventState (SDL_SYSWMEVENT, SDL_ENABLE);
    
      while (!done)
      {
        SDL_WaitEvent(0);
        while (SDL_PollEvent (&event))
          {
            if (event.type == SDL_SYSWMEVENT)
            {
                 ....
                 here, for example, check
                 event.syswm.msg->event.xevent.type
                 etc ...
            }
      }
    
    
    Power Management Friendly code:
    
    while(wait_outcome = SDL_WaitEvent(0))
    {
       SDL_PollEvent (&event);
       if (event.type == SDL_SYSWMEVENT)
       {
           ....
           here for example check
           event.syswm.msg->event.xevent.type
           etc ...
           break;   // <----NOTE THIS
       }
    }
    if(wait_outcome == 0)
        handle_sdl_error();
              
    This optimisation also provides Power Management-friendly code: improved responsiveness because it prevents a “waiting” application from monopolising the CPU, leaving machine time to concurrent processes.
  • Stop all UI timers when going into the background and when the screen is turned off. Application-specific exceptions for this rule can be granted when necessary.
  • Avoid updating the GUI when the application running on the background and when the screen has been turned off. Remove unnecessary graphical elements or constantly updated screen components.
  • Doing polling for a resource wastes CPU and power; instead, use asynchronous notifications sent by a separate daemon.
  • Group small writes to file system into bigger chunks.
  • Minimise remote peripherals usage like Bluetooth or Wireless. Try to keep values in cache and avoid repeated and/or redundant queries to such devices. Minimise the use of hardware in general. For example, screen updates or external storage devices.

For more information, see Software Matters for Power Consumption14.

References

[1] GNOME Programming Guidelines, by Federico Mena Quintero, Miguel de Icaza and Morten Welinder: http://developer.gnome.org/doc/guides/programming-guidelines/book1.html

[2] Linux kernel coding style: http://pantransit.reptiles.org/prog/CodingStyle.html

[3] Maemo 2.x Tutorial: http://www.maemo.org/platform/docs/howtos/Maemo_tutorial.html

[4] Maemo Connectivity Architecture: http://www.maemo.org/platform/docs/howtos/howto_connectivity_guide.html

[5] Hildon API Documentation: http://www.maemo.org/platform/docs/api-index.html

[6] Hildon User Interface Style Guide, version 1.0: http://maemo.org/community/UI_Style_Guide_Summary_1.0.pdf

[7] LibOSSO API: http://www.maemo.org/platform/docs/api/libosso/html/index.html

[8] GConf configuration system project page: http://www.gnome.org/projects/gconf/

[9] Filesystem Hierarchy Standard (FHS), maintained by freestandards.org: http://www.pathname.com/fhs/

[10] Secure Programming for Linux and Unix HOWTO, by David A. Wheeler: http://www.dwheeler.com/secure-programs/

[12] Debian New Maintainers' Guide, by Josip Rodin: http://www.debian.org/doc/manuals/maint-guide/

[13] Making an application package: http://maemo.org/platform/docs/howtos/howto_making_an_application_package.html

[14] Software Matters for Power Consumption, by Nathan Tennies on Embedded.com: http://www.embedded.com/story/OEG20030121S0057



Improve this page