LCDproc Developer's Guide

The Hitchhiker's Guide to LCDproc 0.5

Markus Dolze

Peter Marschall

Guillaume Filion


Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".


This document is a guide to LCDproc written for developers. It covers LCDproc 0.5.5. Users should read the user guide.

Table of Contents

1. Introduction
About this Document
2. The LCDproc client language
Opening a session
Command reference
Basic stuff
Screens and widgets
Menu stuff
LCDd messages
3. Programming for LCDproc
Get the source
Download Yesterday's CVS Version of LCDproc as a Tarball
Download The Latest Version of LCDproc from CVS
Code style guideline
File format and indention
Naming conventions
Statement style
Submitting code
4. Shared files
report.h : Debugging and reporting
Sets reporting level and message destination
Report the message to the selected destination if important enough
Send debugging information if important enough
LL.h : Linked Lists (Doubly-Linked Lists)
Creating a list
Changing data
Iterations throught the list
Using the list as a stack or a queue
Shared files specific for drivers
port.h : Parallel port I/O
adv_bignum.h : Write Big-Numbers
5. The LCDproc driver API
Overview of Operation
Private Data
Functions in Detail
6. Adding your driver to LCDproc
Rules for accepting new drivers
Autoconf, automake, and Everything!
Autoconf and its friend, acinclude.m4
Automake and its friend,
Test your setup
It's all about documentation
Within the source code
The configuration file, LCDd.conf
The daemon's manual page, LCDd.8
The user guide
7. Making a release
Creating a source code release
Creating a documentation release
A. GNU Free Documentation License
How to use this License for your documents

List of Examples

3.1. Names of constants, variables and functions
3.2. Standard copyright notice
3.3. A function declaration
3.4. Space around operators
3.5. Function call
3.6. If-else with braces
3.7. If-else with single statements
3.8. Other compound statements
4.1. Calling lib_adv_bignum()
4.2. Enabling adv_bignum support in

Chapter 1. Introduction

Table of Contents

About this Document

About this Document

This document is meant to be a reference for LCDproc developers. It tries to indicate you where to find the relevant information about LCDproc's inner workings.


Please note that this document is still "under construction". If you run into any trouble feel free to write to the LCDproc mailing list. See for details on how to subscribe to the list.

Therefore you might want to have a look at, to get the latest version of this document, unless you want to generate it yourself from the docbook files in the CVS).

This document was written for LCDproc 0.5.5.

In several other places e-mails and other documents have been included in this document. The authors of those are listed below every such document.

Chapter 2. The LCDproc client language


The LCDproc clients, for example lcdproc, connect over the network to LCDd. In their communication they use a protocol, often referred to as the "widget language". In this chapter the widget language will be discussed.

Opening a session

The essence of talking to LCDd is quite simple. First you will need to connect to the LCDproc port (usually 13666) on the correct IP address (by default localhost). Once you have established the connection you should say "hello", to let LCDd know you are a good guy. It will respond by telling some LCDproc data, like version and screen width and height. Now your session is open and you can start sending 'real' commands.

LCDd can send a number of strings itself. As a response to your commands, it will usually send a "success" string, or a string starting with "huh" in case of any error. See further below for other strings sent by LCDd.

You can test all these commands by opening a TCP/IP connection manually, like with:

telnet localhost 13666

This way, you can check how the various commands work. It's in this case best to have no other clients. If you do have other clients, you will receive "listen" and "ignore" messages that will disturb your typing.

Command reference

In this section all commands and their parameters are listed, along with the responses you can expect. If you need a space or a special char in a string, you should quote the string with double quotes. If you need to use a double quote, escape it with a backslash. The listing is divided into subsections for

Basic stuff


Opens the session with the LCDd server program. This command is required before other commands can be issued.

The response will be a string in the format:

connect parameter...

The client should read all parameters it needs and store their values. The following parameters are in use:

LCDproc version

Indicates the version number of LCDd.

protocol version

Indicates the widget language version number. This number is only changed when the language of a newer version has become incompatible with the previous version.


Each part of the version number shall be treated as an independent numeric value. This means that 0.9 is followed by 0.10.


This word introduces the next key / value pairs that describe the display's properties.

wid int

Tells the client the width of the attached display device in characters.

hgt int

Tells the client the height of the attached display device in characters.

cellwid int

How many pixels is a character wide (space between character cells not included)

cellhgt int

How many pixels is a character high (space between character cells not included)

client_set -name name

Sets attributes for the current client. The current client is the one from the connection that you send this command on, in other words: yourself.

name is the client's name as visible to a user.

Screens and widgets

screen_add new_screen_id

Adds a screen to be displayed. The screen will be identified by the string new_screen_id, which is used later when manipulating on the screen.

screen_del screen_id

Removes the screen identified by screen_id from the client's screens.

screen_set screen_id attributes...

Sets attributes for the given screen. The following attributes exist:

-name name

Sets the screen's name as visible to a user.

-wid int -hgt int

Sets the size of the screen in characters. If unset, the full display size is assumed.

-priority pri_class

Sets the screen's priority. The following priority classes exist:


The screen will never be visible


The screen is only visible when no normal info screens exists


normal info screen, default priority


an active client


The screen has an important message for the user.


The client is doing interactive input.


a positive integer that maps to priority classes above according to the mapping given in the table below.

1 - 64foreground
65 - 192info
193 - ∞background

LCDd will only show screens with the highest priority at that moment. So when there are three info screens and one foreground screen, only the foreground screen will be visible. Only background, info and foreground screens will rotate; higher classes do not rotate because their purpose is not suitable for rotation.

-heartbeat { on | off | open }

Changes the heartbeat setting for this screen. If set to open, the default, the client's heartbeat setting will be used.

-backlight { on | off | toggle | open | blink | flash }

Changes the screen's backlight setting. If set to the default value open, the state will be determined by the client's setting. blink is a moderately striking backlight variation, flash is very strinking.

-duration value

A screen will be visible for this amount of time every rotation. The value is in eights of a second.

-timeout value

After the screen has been visible for a total of this amount of time, it will be deleted. The value is in eights of a second. Currently the client will not be informed of the deletion (TODO?).

-cursor { on | off | under | block}

Determines the visibility of a cursor. If on, a cursor will be visible. Depending on your hardware, this will be a hardware or software cursor. The specified cursor shape (block or under) might not be available in which case an other cursor shape will be used instead. Default is off.

-cursor_x int -cursor_y int

Set the cursor's x and y coordinates respectively. If not given, the cursor will be set to the leftmost (-cursor_x) resp. topmost (-cursor_y) position. Coordinates are always 1-based. So the default top-left corner is denoted by (1,1).

widget_add screen_id new_widget_id widgettype [-in frame_id]

Adds a widget to the given screen. The new_widget_id sets the identifier for this widget. The optional -in frame_id places the widget into the given frame. The following widget types exist:


A simple text.


A title bar on top of the screen.


A horizontal bar.


A vertical bar.


A predefined icon. For a list of valid names consult server/widget.c.


A variation of the string type that scrolls the text horizontally or vertically.


A frame with that can contain widgets itself. In fact a frame displays an other screen in it.


A big number. They have a size of 3x4 characters. The special number 10 is a colon, that you can use for a clock. This character is 1x4.

widget_del screen_id widget_id

Deletes the given widget from the screen.

widget_set screen_id widget_id widgettype_specific_parameters

Sets parameters for a widget. Because not all widgets are created equal, the various widget types require different parameters.


x y text

Displays text at position (x,y).



Uses text as the title to display.

hbar , vbar

x y length

Displays a horizontal (hbar) resp. vertical (vbar) starting at position (x,y) that is length pixels wide resp. high.


x y iconname

Displays the icon iconname at position (x,y).


left top right bottom direction speed text

Displays a scroller spanning from position (left,top) to (right,bottom) scrolling text in horizontal (h), vertical (v) or marquee (m) direction at a speed of speed, which is the number of movements per rendering stroke (8 times/second).


left top right bottom width height direction speed

Sets up a frame spanning from (left,top) to (right,bottom) that is width columns wide and height rows high. It scrolls in either horizontal (h) or vertical (v) direction at a speed of speed, which is the number of movements per rendering stroke (8 times/second).


x int

Displays decimal digit int at the horizontal position x, which is a normal character x coordinate on the display. The special value 10 for int displays a colon.

Menu stuff

In this section all commands for creation, modification of menus and for interaction with them are described. Although keys may be used for other tasks they are listed here too.

TODO: example for normal (static) menu structure.

Menus may be even be used for wizards (the user is automatically guided through a number of configuration options) by virtue of the options -next and -prev. Here a complete example:

	  client_set name Parenttest
	  # to be entered on escape from test_menu (but overwritten
	  # for test_{checkbox,ring})
	  menu_add_item "" ask menu "Leave menus?" -is_hidden true
	    menu_add_item "ask" ask_yes action "Yes" -next _quit_
	    menu_add_item "ask" ask_no action "No" -next _close_

	  menu_add_item "" test menu "Test"
	    menu_add_item "test" test_action action "Action"
	    menu_add_item "test" test_checkbox checkbox "Checkbox"
	    menu_add_item "test" test_ring ring "Ring" -strings "one\ttwo\tthree"
	    menu_add_item "test" test_slider slider "Slider" -mintext "" -maxtext "" -value "50"
	    menu_add_item "test" test_numeric numeric "Numeric" -value "42"
	    menu_add_item "test" test_alpha alpha "Alpha" -value "abc"
	    menu_add_item "test" test_ip ip "IP" -v6 false -value ""
	    menu_add_item "test" test_menu menu "Menu"
	    menu_add_item "test_menu" test_menu_action action "Submenu's action"

	  # no successor for menus. Since test_checkbox and test_ring have their
	  # own predecessors defined the "ask" rule will not work for them
	  menu_set_item "" test -prev "ask"

	  menu_set_item "test" test_action -next "test_checkbox"
	  menu_set_item "test" test_checkbox -next "test_ring" -prev "test_action"
	  menu_set_item "test" test_ring -next "test_slider" -prev "test_checkbox"
	  menu_set_item "test" test_slider -next "test_numeric" -prev "test_ring"
	  menu_set_item "test" test_numeric -next "test_alpha" -prev "test_slider"
	  menu_set_item "test" test_alpha -next "test_ip" -prev "test_numeric"
	  menu_set_item "test" test_ip -next "test_menu" -prev "test_alpha"
	  menu_set_item "test" test_menu_action -next "_close_"

	  menu_set_main ""

client_add_key [ -exclusively | -shared ] key...

Tells the server that the current client wants to make use of the given key(s). If you reserve the key(s) in shared mode, other clients can still reserve these keys too. If you reserve the key(s) in exclusive mode no other client can reserve them again. Key(s) reserved in shared mode will only be returned when a screen of the current client is active. These keys can be used for interaction with a visible screen (default). Key(s) reserved in exclusive mode will be returned regardless of which screen is active. They can be used to trigger a special feature or to make a screen come to foreground. Note that you cannot reserve a key in exclusive mode when an other client has reserved it in shared mode.

client_del_key key...

Ends the reservation of the given key(s).

menu_add_item menu_id new_item_id type [options]

Adds a new menu item to a menu. The main menu of a client, will be created automatically as soon as the client adds an item. This main menu has an empty id ("") and the name is identical to the name of the client. The options are described under menu_set_item below.


Some menu commands (menu_goto) and options (-prev, -next) assume that menu_ids are unique (at least within a clients menu hierarchy).

menu item types


This item should trigger an action. It consists of simple text.


Consists of a text and a status indicator. The status can be on (Y), off (N) or gray (o).


Consists of a text and a status indicator. The status can be one of the strings specified for the item.


Is visible as a text. When selected, a screen comes up that shows a slider. You can set the slider using the cursor keys. When Enter is pressed, the menu returns.


Allows the user to input an integer value. Is visible as a text. When selected, a screen comes up that shows the current numeric value, that you can edit with the cursor keys and Enter. The number is ended by selecting a 'null' input digit. After that the menu returns.


Is visible as a text. When selected, a screen comes up that shows the current string value, that you can edit with the cursor keys and Enter. The string is ended by selecting a 'null' input character. After that the menu returns.


Allows the user to input an IP number (v4 or v6). When selected, a screen comes up that shows an IP number that can be edited - digit by digit - via left/right (switch digit) and up/down keys (increase/decrease).


This is a submenu. It is visible as a text, with an appended >. When selected, the submenu becomes the active menu.

menu_del_item menu_id item_id

Removes a menu item item_id from menu menu_id. The menu with the special id "" (i.e. the empty string) is the client's main menu.

menu_set_item menu_id item_id item_specific_options

Sets parameters for the menu item(s). Each item type knows different parameters.

options for the various menu items

for all item types

-text string

The visible text of the item.

-is_hidden { true | false} (false)

If the item currently should not appear in a menu.

-next successor_id

Sets the menu item to show after hitting the ENTER key when this item is active. This works for all menu item types except menus i.e. also for menu item types without an own screen e.g., checkbox, ring and action.

Special values


Equivalent to -menu_result close: Close the menu.


Equivalent to -menu_result quit: Quit the menu system.


Equivalent to -menu_result none: Keep the item open.

-prev predecessor_id

Sets the menu item to show after hitting the ESCAPE key when this Item is active. This works for all menu item types i.e. also for menu item types without an own screen e.g., checkbox, ring and action.


If you define a predecessor for e.g., a checkbox and its parent menu too, the menu's predecessor is ignored in favor of the checkboxes one.

This option accepts the same special values as the -next option.


-menu_result { none | close | quit} (none)

Sets what to do with the menu when this action is selected: none: the menu stays as it is; close: the menu closes and returns to a higher level; quit: quits the menu completely so you can foreground your app.


-value { off | on | gray }

Set the value of the item.

-allow_gray { false | true} (false)

Sets if a grayed checkbox is allowed.


-value int (0)

Sets the index in the stringlist that is currently selected.

-strings string (empty)

This single string should contain the strings that can be selected. They should be tab-separated (\t).


-value int (0)

Sets its current value.

-mintext string ("") , -maxtext string ("")

The texts at the left and right side of the slider.

-minvalue int (0) , -maxvalue int (100)

The minimum and maximum values of the slider.

-stepsize int (1)

The stepsize of the slider. If you use 0, you can control the movement completely from your client.


-value int (0)

Sets its current value.

-minvalue int (0) , -maxvalue int (100)

The minimum and maximum values that are allowed. If one of them is negative, the user will be able to enter negative numbers too.

TODO: floats!


-value string ("")

Sets its current value.

-password_char string ("")

If used, instead of the typed characters, this character will be visible.

-minlength int (0) , -maxlength int (10)

Sets the minimum and maximum allowed lengths.

-allow_caps { false | true } (true) , -allow_noncaps { false | true } (false) , -allow_numbers { false | true } (false)

(Dis)allow these groups of characters.

-allowed_extra string ("")

The chars in this string are also allowed.


-value string ("")

Set the value of the item, e.g. "" (v4) or ":::ffff:ffff:ffff:ffff:ffff" (v6).

-v6 { false | true } (false)

Changes IP version from default v4.


This is a submenu. It is visible as a text, with an appended '>'. When selected, the submenu becomes the active menu.

-parent parentid

(Re)sets the parent of this menu. Parentid has to be of type menu. This function does not change any menu (neither the old nor the new parent) since this option is normally used with hidden menus. Otherwise use menu_add/del_item. Applying this option is equivalent to second argument of the menu_goto command.

menu_goto menu_id [parent_id]

Changes current menu to menu_id. Depending on the configure option --enable-permissive-menu-goto the client may switch to any (if enabled) or his menus only (if not enabled).


The menu item to go to (any menu type e.g. an action or a menu).


Resets the parent of menu_id. This optional parameter can be used to reuse a menu from different places (for wizards etc.). Use it with caution: This may lead to a messy menu structure in particular due to the fact that the menus are not changed !

menu_set_main menu_id

Sets the entry point into the menu system. Use this to make the server menu invisible. Note that you may only set the menu to your own clients menus unless the configure option --enable-permissive-menu-goto is used. (See menuscreens.c for the menu ids of the server menus.)


The new main menu, restricted to the client's own menus. Special values:

"" (i.e. the empty string)

The client's main menu.


Resets main to the "real" main menu.


backlight { on | off | toggle | blink | flash }

Sets the client's backlight state.

output { on | off | int }

Sets the general purpose output on some display modules to this value. Use on to set all outputs to high state, and off to set all to low state. The meaning of the integer value depends on your specific device, usually it is a bit pattern describing the state of each output line.


This command provides information about the driver.


This command does nothing and is always successful. Can be useful to be sent at regular intervals to make sure your connection is still alive.

sleep int

Sleep for the given number of seconds. int must be a positive integer in the range from 1 to 60.


This command is currently ignored on the server side.

LCDd messages

LCDd can send messages back to the client. These messages can be directly related to the last command, or generated for some other reason. Because messages can be generated at any moment, the client should read from the connection at regular intervals. A very simple client could simply ignore all received messages. Not reading the messages will cause trouble !


This is the response to a command in case everything went OK.

huh? error_description

This is the response to a command in case something has gone wrong. The description is not meant to be parsed, it's only meant for the programmer of the client. It might be that your command has only been partially executed, for example if you try to reserve 3 keys, and one fails. Your client might need to undo its actions completely.

listen screen_id , ignore screen_id

The screen with the screen_id given is now visible on the display (listen) or it is not visible anymore on the display (ignore).

key key

This message will be sent if there was a keypress that should be delivered to the current client.

menuevent event_type id [value]

The user did something with a client supplied menu. The type of event can be:

select (action)

The item was activated.

update (checkbox, ring, numeric, alpha)

The item was modified by the user, so LCDd sends an updated value.

plus (slider) , minus (slider)

The slider was moved to left (minus) or right (plus), so LCDd sends an updated value.


This item has been entered, which means it is currently active on the screen. The client could now for example update the value of the item. If it is a menu, it may be needed to update the values of the items in it too, because they may be visible too.


This item has been left, so it is currently not the (main) active item anymore.

Multiple messages may be generated by one action of the user.

Chapter 3. Programming for LCDproc

Get the source

If you want to start programming for LCDproc you will need the have the most current source code available. You can get it several ways:

  1. Download yesterday's CVS version of as a tarball (preferred).

  2. Download the latest version from CVS.

  3. Download the last stable release from Sourceforge. (This is not recommended as stable release may be months behind the current version.)

Download Yesterday's CVS Version of LCDproc as a Tarball

There are nightly distributions of the CVS branches of LCDproc. You can download them from For development we recommended to use the 'current' branch.

To extract the files run

$ tar xvfz lcdproc-CVS-current.tar.gz

Download The Latest Version of LCDproc from CVS

Of course you can download the latest stuff from CVS via anonymous login. For more information on how to use CVS see About CVS on Sourceforge.

Login to CVS:

$ cvs login

(Hit enter when prompted for a password.)

Get the files from CVS:

$ cvs checkout -P lcdproc

Once you've done that and want to update the downloaded files to the latest stuff you can use the "update" command of CVS (make sure to be in the lcdproc directory!):

$ cvs update -d

Now that once you have downloaded the files you can prepare them for compiling, but first you should (you don't have to) copy them to another place on your machine.

Code style guideline

LCDproc has been developed by many contributors over many years. You may find different programming styles (naming, indention, etc) in the source code.

When modifying an existing file, please take a careful look at its style and program continuing that style instead of mixing it up with another one even if it does not comply with the guidelines written below.

For newly added files the following guideline describes how source code should look like.


All new submitted files will be passed through BSD indent to enforce the style described below.

File format and indention

  • Language: The programming language used for LCDd (server core), drivers and the lcdproc client is C. No other programming language will be accepted.

  • File encoding: Files shall either encoded as UTF-8 or ISO-8859-1 and line endings shall be Unix type.

  • Line length: Lines of source code should be wrapped at column 80.

  • Indention: Tab indention shall be used (with tab width set to 8 characters). Only exception are switch labels which are indented a half tab (4 spaces).

  • License: LCDproc is released under GNU General Public License version 2 (GPL v2) and every file shall have a standard copyright notice.

Naming conventions

  • Function names: Function names shall be lowercase. We do not use CamelCase (some historical exceptions may exist). Multiple words are separated by underscore.

  • Variable names: We do not use Hungarian Notation. CamelCase may be used, but names shall begin with a lowercase letter.

  • Constants: Constants shall be written in uppercase using underscore to separate multiple words.

Example 3.1. Names of constants, variables and functions

/* Constants */

/* Variable names */
MODULE_EXPORT char * api_version = API_VERSION;
MODULE_EXPORT int stay_in_foreground = 0;
MODULE_EXPORT int supports_multiple = 1;

/* Function names */
void HD44780_position(Driver *drvthis, int x, int y);
static void uPause(PrivateData *p, int usecs);
unsigned char HD44780_scankeypad(PrivateData *p);


  • All code comments shall be C-style comments (/* */). Comments spanning multiple lines shall have a star at the beginning of each line.

  • C++-style comments (//) may be used to comment out single lines of code to disable these lines. Larger blocks of code which shall be disabled should be wrapped within C-style comments or using pre-processor directives (#if ... #endif).

    C++-style comments shall not be used in general.

  • We use Doxygen to document our source code. Functions shall be documented using Doxygen-style comments (/** *). See Doxygen Manual for more information and how to use it.

  • If you carefully formatted a comment, you may use the special comment /*- */ (comment start is star minus) to prevent automatic reformatting. This usually applies to the standard copyright notice.

    Example 3.2. Standard copyright notice

     * Copyright (C) 2010 Your Name <your_email_address>
     * This file is released under the GNU General Public License.
     * Refer to the COPYING file distributed with this package.

Statement style

  • Function declarations:

    Function declarations have their declaration and opening brace split across two lines.

    Function names start in column one. The return type is placed on the previous line.

    There is no space between the function name and '('.

    Example 3.3. A function declaration

     * This is a Doxygen function description.
     * \param y     The number of years
     * \param str   Pointer to a string containing X
     * \return      0 on success; -1 on error
    this_is_a_function(int y, char *str)

  • Operators:

    There shall be a space characters before/after an operator or assignment, except for increment (++) or decrement (--) operators.

    Example 3.4. Space around operators

    if (p->dispSizes[dispID - 1] == 1 && p->width == 16) {
    	if (x >= 8) {
    		x -= 8;
    		relY = 1;
    x--;			/* Convert 1-based coords to 0-based */

  • Function calls:

    There shall be no space between the function call and the opening brace '(' of the parameter list. Within the parameter list a space shall be after each parameter.

    Example 3.5. Function call

    lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 0);

  • Compound statements:

    Opening braces occur on the same line as the statement.

    Else statements: Else statements are placed on a line of their own, even is there is a previous closing brace.

    Opening and closing braces may be omitted on single line compound statements. However, if one part of an if-else-statement requires braces the other part shall have braces as well.

    Example 3.6. If-else with braces

    if (...) {
    else {

    Example 3.7. If-else with single statements

    if (...)
    	err = 1;

    Example 3.8. Other compound statements

    while (...) {
    for (a = 0; a < max; a++) {
    /* case labels are indented one half tab stop (4 spaces) */
    switch (icon) {
        case ICON_BLOCK_FILLED:
    	HD44780_set_char(drvthis, 6, block_filled);
        case ICON_HEART_FILLED:
    	HD44780_set_char(drvthis, 0, heart_filled);
        case ICON_HEART_OPEN:
    	HD44780_set_char(drvthis, 0, heart_open);
    	return -1;      /* Let the core do other icons */

Submitting code

When you have finished modifying the code you may decide to submit it to the LCDproc project. You usually do this by submitting a patch for review to the mailing list.

To create a patch you need the unmodified files and the files containing your modifications. Usually you do this by storing an unmodified copy of the sources in one directory and another copy with your modifications in another one. You then run diff like this:

diff-urN -X unmodified-dir/diff_ignore unmodified-dir your-source-dir > mymodifications.patch


Please use unified diff format (-u option) only!

When running diff using -X diff_ignore is strongly recommended. The file diff_ignore contains an exclusion list which makes cvs ignore all generated files (Makefiles, log files, object files, etc.)

If you have modified files in a source tree you checked out from CVS you can also run cvs diff from the working directory:

cvs diff-u > mymodifications.patch


Some versions of cvs diff will not handle new files because these are unknown to the repository. There are ways to make cvs believe the files existed previously (fake add) but this is not recommended. You will need to submit new files 'as-is' in this case.

Chapter 4. Shared files


Here we provide functions that should be used by all parts of the program.

report.h : Debugging and reporting

To enable the debug() function on all of the software, just type:

./configure --enable-debug and recompile with 'make'.

Procedure 4.1. Enabling the debug() function only in specific files:

  1. Configure without enabling debug (that is without --enable-debug).

  2. Edit the source file that you want to debug and put the following line at the top, before the #include "report.h" line: #define DEBUG.

  3. Then recompile with 'make'.

This way, the global DEBUG macro is off but is locally enabled in certain parts of the software.

The reporting levels have the following meaning.

Reporting Levels


Critical conditions: the program stops right after this. Only use this if the program is actually exited from the current function.


Error conditions: serious problem, program continues. Use this just before you return -1 from a function.


Warning conditions: Something that the user should fix, but the program can continue without a real problem. Ex: Protocol errors from a client.


Major event in the program: (un)loading of driver, client (dis)connect.


Minor event in the program: the activation of a setting, details of a loaded driver, a key reservation, a keypress, a screen switch.


Insignificant event: What function has been called, what subpart of a function is being executed, what was received and sent over the socket, etc.

Levels 4 (maybe) and 5 (certainly) should be reported using the debug function. The code that this function generates will not be in the executable when compiled without debugging. This way memory and CPU cycles are saved.

report.h file defines 3 functions for debugging and reporting:

Sets reporting level and message destination

int set_reporting(application_name,  
char * application_name;
int new_level;
int new_dest;

Returns -1 on error or 0 on success.

Report the message to the selected destination if important enough

void report(level,  
const int level;
const char *format;

Returns nothing (void).

The format parameter is the same as the one used by printf.

Send debugging information if important enough

Consider the debug function to be exactly the same as the report function. The only difference is that it is only compiled in if DEBUG is defined.

LL.h : Linked Lists (Doubly-Linked Lists)

Creating a list

To create a list, do the following:

LinkedList *list;
list = LL_new();
if(!list) handle_an_error();

The list can hold any type of data. You will need to typecast your datatype to a "void *", though. So, to add something to the list, the following would be a good way to start:

typedef struct my_data {
  char string[16];
  int number;
} my_data;

my_data *thingie;

for(something to something else) {
  thingie = malloc(sizeof(my_data));
  LL_AddNode(list, (void *)thingie);  // typecast it to a "void *"

For errors, the general convention is that "0" means success, and a negative number means failure. Check LL.c to be sure, though.

Changing data

To change the data, try this:

thingie = (my_data *)LL_Get(list);  // typecast it back to "my_data"
thingie->number = another_number;

You don't need to "Put" the data back, but it doesn't hurt anything.

LL_Put(list, (void *)thingie);

However, if you want to point the node's data somewhere else, you'll need to get the current data first, keep track of it, then set the data to a new location:

my_data * old_thingie, new_thingie;

old_thingie = (my_data *)LL_Get(list);
LL_Put(list, (void *)new_thingie);

// Now, do something with old_thingie.  (maybe, free it?)

Or, you could just delete the node entirely and then add a new one:

my_data * thingie;

thingie = (my_data *)LL_DeleteNode(list, NEXT);

thingie->number = 666;

LL_InsertNode(list, (void *)thingie);

Iterations throught the list

To iterate on each list item, try this:

do {
  my_data = (my_data *)LL_Get(list);
  /* ... do something to it ... */
} while(LL_Next(list) == 0);

Using the list as a stack or a queue

You can also treat the list like a stack, or a queue. Just use the following functions:

LL_Push()      // Regular stack stuff: add, remove, peek, rotate

LL_Shift()     // Other end of the stack (like in perl)

LL_Enqueue()   // Standard queue operations

There are also other goodies, like sorting and searching.


Array-like operations will come later, to allow numerical indexing:

LL_nGet(list, 3);
LL_nSwap(list, 6, 13);
LL_nPut(list, -4, data);   // Puts item at 4th place from the end..

More ideas for later:

LL_MoveNode(list, amount);  // Slides a node to another spot in the list
-- LL_MoveNode(list, -1); // moves a node back one toward the head

That's about it, for now... Be sure to free the list when you're done!

See LL.c for more detailed descriptions of these functions.

Shared files specific for drivers

Driving an LCD display is not easy; you need to address ports, to send bytes in a certain order, to respect timing, and unfortunately no two operating system let you do this in the same way. But don't despair! There's hope! Someone in a galaxy far far away, has Already done the dirty job for you! This dirty job has been put in shared files. These shared files are full cross platform and are auto-magically configured by the configure script. You only need to include them and use their functions to benefit from them.

These files are provided only for drivers, others are provided for all of LCDproc. These files are located in the shared directory, they have a dedicated chapter in this book.

port.h : Parallel port I/O

The file port.h, located in the server/drivers/ directory provide Input/Output and port permissions for the PC compatible parallel port, also known as the LPT port.

Of course, these functions will only work if the computer where LCDproc runs has parallel port! In these situations, the configure script will see this and disable drivers that need a parallel port.

The functions in port.h are defined and as 'static inline'. Therefore each driver including this header file gets its own copy of the functions and they are inlined into the driver's code. As a result calls to port_in() and port_out() are directly translated to inb() or outb() or assembly code by the compiler. There is less to no overhead in using them.

port.h file defines 6 static inline functions for port I/O:

Read a byte from port

static inline int port_in(port); 
unsigned short int port;

Returns the content of the byte.

Write a char(byte) 'val' to port

static inline void port_out(port,  
unsigned short int port;
unsigned char val;

Returns nothing (void).

Get access to a specific port

static inline int port_access(port); 
unsigned short int port;

Returns 0 if successful, -1 if failed.

Close access to a specific port

static inline int port_deny(port); 
unsigned short int port;

Returns 0 if successful, -1 if failed.

Get access to multiple sequential ports

static inline int port_access_full(port,  
unsigned short int port;
unsigned short int count;

Returns 0 if successful, -1 if failed.

Close access to multiple sequential ports

static inline int port_deny_full(port,  
unsigned short int port;
unsigned short int count;

Returns 0 if successful, -1 if failed.

Example use

#include "port.h"

 * Get access to these 3 ports:
 *  0x378 (CONTROL),
 *  0x379 (STATUS) and
 *  0x37A (DATA)
if (port_access_multiple(0x378, 3) == -1) {
	/* Access denied, do something */

/* Write a 'A' to the control port */
port_out(0x378, 'A');

/* Read from the status port */
char status = port_in(0x379);

/* Close the 3 ports */
port_deny_multiple(0x378, 3);

adv_bignum.h : Write Big-Numbers

adv_bignum.h is the headerfile for libbignum.a (made from adv_bignum.c) which contains everything needed to show big-numbers, including the fonts for the different displays. (All files are located in the server/drivers/ directory.)

There are only a few requirements to the calling driver:

  • The following functions have to be implemented by the driver:


    to determine the display's height and thus the maximal height of the big numbers to be displayed.


    to determine the number of user-definable characters that can be used in the generation of big numbers.


    to define a character necessary to write a big number. Of course this is only necessary if there really are user-definable characters, i.e. only if get_free_chars() returns a value greater 0.


    to actually write the characters the big numbers consist of.

  • The display's cellwidth has to be 5 (6 works also in some cases) and the cellheight 7 or 8.

  • The custom-characters (if any) have to be at character positions offset+0, offset+1, offset+2, ... offset+ get_free_chars()-1,

  • offset+ get_free_chars()-1 must be less than 32,

The library determines the correct font, depending on the display size and the number of user-defined characters itself. So it is easy to integrate into the driver.

Provided Functions

void lib_adv_bignum(drvthis,  
Driver *drvthis;
int x;
int num;
int offset;
int do_init;

The main thing the driver has to do is to call this function from its num() function with the parameters described below.


the pointer pointing to the Driver structure passed to the driver's num() function.


the horizontal position of the top-left corner of the big-number (the big-numbers don't have a y position). The placing of the characters is done by the client, so the driver only has to forward the position to the lib. The bignumlib has no influence on the placing of the characters.


the number (legal: 0 - 9, and :) to be written.


the character position where the user-definable characters start (usually 0). The user-definable characters (if any) are then expected to be at the character positions offset+0, offset+1, offset+2, ... offset+ get_free_chars()-1 and offset+ get_free_chars()-1 is required to be less than 32.


if not 0, lib_adv_bignum will set the custom characters of the display for the big-numbers.

The driver has to check if the custom-characters have to be set or if it is already done and tell it to the lib (using the do_init parameter). The common way is to use variable called p->ccmode or similar. In the different drivers there are some differences in the naming and handling of this variable. So the responsibility of checking and setting is left to the driver.

Example 4.1. Calling lib_adv_bignum()

#include "adv_bignum.h"

myDriver_num( Driver * drvthis, int x, int num )
  PrivateData *p = drvthis->private_data;
  int do_init = 0;

  if (p->ccmode != CCMODE_BIGNUM){     // Are the custom-characters set up correctly? If not:
    do_init = 1;                       // Lib_adv_bignum has to set the custom-characters.
    p->ccmode = CCMODE_BIGNUM;         // Switch custom-charactermode to bignum.

  // Lib_adv_bignum does everything needed to show the big-numbers.
  lib_adv_bignum(drvthis, x, num, 0, do_init);

All that's left to do is to add libbignum.a to the libs and adv_bignum.h sources of your driver in the Makefile (or the file that generates the Makefile).

Example 4.2. Enabling adv_bignum support in

myDriver_LDADD: libLCD.a libbignum.a

myDriver_SOURCES: lcd.h lcd_lib.h myDriver.c myDriver.h report.h adv_bignum.h

Internal Structure and Functions

The only purpose of lib_adv_bignum() is to determine the best display-dependent big-number function, based upon the values of the driver's height() and get_free_chars() functions, and call it.

The display-dependent functions are named adv_bignum_num_N_M(), where N is the display's height in lines and M the number of used user-definable characters. The bits of the user-characters are stored in static char bignum (take a look at the source and you will see what I mean). (On a display with a cellheight of 7 the lowest line stored is not shown.) While static char num_map defines the placing in the big number. (A big number is always 3 characters wide and 4 characters high. On a big number for 2 line displays the 2 lower lines are not in use.)

If user-definable characters have to be set, the driver's set_char() function will be called once for every user-definable character.

Now adv_bignum_write_num() is called. This function places the 6 or 12 characters the big-number consists of in the framebuffer using the drivers chr() function.

Chapter 5. The LCDproc driver API

This chapter describes the driver API of v0.5.5 of LCDproc.

Overview of Operation

The API defines functions which drivers may implement to provide certain services. If a driver implements a function, the function will be detected by the server. For some optional functions the server core provides default implementations.

The API consists of functions to:

  • print data to the display,

  • set display properties (backlight, brightness, contrast, extra LEDs),

  • read input from an input device (may be the display), and

  • inform the server of the driver/display capabilities.

The API is best described by starting with the struct lcd_logical_driver which is defined in server/drivers/lcd.h.

Below is a commented version of lcd_logical_driver. Each function is described more detailed in the section called “Functions in Detail”.

typedef struct lcd_logical_driver {

	//////// Variables to be provided by the driver module
	// The driver loader will look for symbols with these names !

	// pointer to a string describing the API version
	char *api_version;

	// Does this driver require to be in foreground ?
	int *stay_in_foreground;

	/ Does this driver support multiple instances ?
	int *supports_multiple;

	// What should alternatively be prepended to the function names ?
	char **symbol_prefix;

	//////// Functions to be provided by the driver module

	//// Mandatory functions (necessary for all drivers)

	// initialize driver: returns >= 0 on success
	int (*init)		(Driver *drvthis);

	// close driver
	void (*close)		(Driver *drvthis);

	//// Essential output functions (necessary for output drivers)

	// get display width / height (in characters; 1-based)
	int (*width)		(Driver *drvthis);
	int (*height)		(Driver *drvthis);

	// clear screen
	void (*clear)		(Driver *drvthis);

	// flush screen contents to LCD
	void (*flush)		(Driver *drvthis);

	// write string s at position (x,y)
	void (*string)		(Driver *drvthis, int x, int y, const char *str);

	// write char c at position (x,y)
	void (*chr)		(Driver *drvthis, int x, int y, char c);

	//// essential input functions (necessary for input drivers)

	// get key from driver: returns a string denoting the key pressed
	const char *(*get_key)	(Driver *drvthis);

	//// Extended output functions (optional; core provides alternatives)

	// draw a bar from pos (x,y) upward / to the right filling promille of len chars
	void (*vbar)		(Driver *drvthis, int x, int y, int len, int promille, int options);
	void (*hbar)		(Driver *drvthis, int x, int y, int len, int promille, int options);

	// display (big) number num at horizontal position x
	void (*num)		(Driver *drvthis, int x, int num);

	// set heartbeat state; animate heartbeat
	void (*heartbeat)	(Driver *drvthis, int state);

	// draw named icon at position (x,y)
	int (*icon)		(Driver *drvthis, int x, int y, int icon);

	// set cursor type and move it to position (x,y)
	void (*cursor)		(Driver *drvthis, int x, int y, int type);

	//// User-defined character functions

	// set special character / get free characters
	// - It is currently unclear how this system should work exactly
	// - The set_char function expects a simple block of data with 1 byte for each pixel-line.
	//   (So that is 8 bytes for a 5x8 char)
	void (*set_char)	(Driver *drvthis, int n, unsigned char *dat);
	int (*get_free_chars)	(Driver *drvthis);

	// get width / height of a character cell (in pixels)
	// - necessary to provide info about cell size to clients
	// - if not defined, the core will provide alternatives returning default values
	int (*cellwidth)	(Driver *drvthis);
	int (*cellheight)	(Driver *drvthis);

	//// Hardware functions

	// get / set the display's contrast
	int (*get_contrast)	(Driver *drvthis);
	void (*set_contrast)	(Driver *drvthis, int promille);

	// get / set brightness for given backlight state
	int (*get_brightness)	(Driver *drvthis, int state);
	void (*set_brightness)	(Driver *drvthis, int state, int promille);

	// set backlight state
	void (*backlight)	(Driver *drvthis, int state);

	// set output
	void (*output)		(Driver *drvthis, int state);

	//// Informational functions
	// get a string describing the driver and it's features
	const char * (*get_info) (Driver *drvthis);

	//////// Variables in server core, available for drivers

	// name of the driver instance (name of the config file section)
	// - do not change from the driver; consider it read-only
	// - to be used to access the driver's own section in the config file
	char * name;

	// pointer to the driver instance's private data
	// - filled by the server by calling store_private_ptr()
	// - the driver should cast this to it's own private structure pointer
	void * private_data;

	//////// Functions in server core, available for drivers

	// store a pointer to the driver instance's private data
	int (*store_private_ptr) (struct lcd_logical_driver * driver, void * private_data);

	// Config file functions, cwprovided by the server
	// - see configfile.h on how to use these functions
	// - as sectionname, always use the driver name: drvthis->name
	short (*config_get_bool) (char * sectionname, char * keyname,
				int skip, short default_value);
	long int (*config_get_int) (char * sectionname, char * keyname,
				int skip, long int default_value);
	double (*config_get_float) (char * sectionname, char * keyname,
				int skip, double default_value);
	const char *(*config_get_string) (char * sectionname, char * keyname,
				int skip, const char * default_value);
				// Returns a string in server memory space.
				// Copy this string.
	int config_has_section	(const char *sectionname);
	int config_has_key	(const char *sectionname, const char *keyname);

	// error reporting function
	// - see drivers/report.h for details
	void (*report)		( const int level, const char *format, .../*args*/ );

	// Display properties functions (for drivers that adapt to other loaded drivers)
	// - the return the size of another already loaded driver
	// - if no driver is loaded yet, the return values will be 0
	int (*get_display_width) ();
	int (*get_display_height) ();
} Driver;

Private Data

With the introduction of loadable modules it is necessary to stop using global variables to store a driver's data in. Instead, you should store it in a structure, that you allocate and store on driver's init. If you don't use this system, but use globals, you get queer results if you run two LCDd daemons on one machine. They will then use the same variables !

In the driver's private structure will probably at least be something like:

typedef struct MyDriver_private_data {
	int fd;				// file descriptor for the LCD device
        int width, height;		// dimension of the LCD (in characters, 1-based
        int cellwidth, cellheight;	// Size of each LCD cell, in pixels
        unsigned char *framebuf;	// Frame buffer...
} PrivateData;

You allocate and store this structure like this:

	PrivateData *p;

	// Allocate and store private data
	p = (PrivateData *) malloc(sizeof(PrivateData));
	if (p == NULL)
		return -1;
	if (drvthis->store_private_ptr( drvthis, p ) < 0)
		return -1;

	// initialize private data
	p->fd = -1;
	p->cellheight = 8;
	p->cellwidth = 6;

	(... continue with the rest of your init routine)

You retrieve this private data pointer by adding the following code to the beginning of your functions:

	PrivateData *p = (PrivateData *) drvthis->private_data;

Then you can access your data like:


Functions in Detail

int (*init)(drvthis); 
Driver *drvthis;

The init() function. It starts up the LCD, initializes all variables, allocates private data space and stores the pointer by calling store_private_ptr(); Returns <0 on error.

void (*close)(drvthis); 
Driver *drvthis;

Shut down the connection with the LCD. Called just before unloading the driver.

int (*width)(drvthis); 
Driver *drvthis;

Get the screen width in characters. The result is 1-based.

int (*height)(drvthis); 
Driver *drvthis;

Get the screen height in character lines. The result is 1-based.

void (*clear)(drvthis); 
Driver *drvthis;

Clear the framebuffer.

void (*flush)(drvthis); 
Driver *drvthis;

Flush the framebuffer to the LCD.

void (*string)(drvthis,  
Driver *drvthis;
int x;
int y;
const char *str;

Place string str into position (x,y) in the framebuffer. All coordinates are 1-based, i.e. (1,1) is top left. The driver should check for overflows, i.e. that the positional parameters are within the screen's boundaries and cut off the part of the string that is out of bounds.

void (*chr)(drvthis,  
Driver *drvthis;
int x;
int y;
char c;

Place a single character c into position (x,y) in the framebuffer. The driver should check for overflows, i.e. that the positional parameters are within the screen's boundaries and ignore the request if the character is out of bounds.

void (*vbar)(drvthis,  
Driver *drvthis;
int x;
int y;
int len;
int promille;
int options;

Draw a vertical bar at position (x,y) that has maximal length len, where a fraction of (promille / 1000) is filled.

void (*hbar)(drvthis,  
Driver *drvthis;
int x;
int y;
int len;
int promille;
int options;

Draw a horizontal bar at position (x,y) that has maximal length len, where a fraction of (promille / 1000) is filled.

void (*num)(drvthis,  
Driver *drvthis;
int x;
int num;

Display big number num at horizontal position x.

void (*heartbeat)(drvthis,  
Driver *drvthis;
int state;

Sets the heartbeat to the indicated state: 0=off, 1=on. Use HEARTBEAT_ON to say that we want to display/refresh the heartbeat. The driver choose how to do it.

int (*icon)(drvthis,  
Driver *drvthis;
int x;
int y;
int icon;

Draw named icon icon at position (x,y). If the driver returns -1 the server core will draw an appropriate replacement character.

void (*cursor)(drvthis,  
Driver *drvthis;
int x;
int y;
int type;

Move cursor to position (x,y), setting its type to type.

void (*set_char)(drvthis,  
Driver *drvthis;
char ch;
unsigned char *dat;

The set_char function expects a simple block of data with 1 byte for each pixel-line. (So that is 8 bytes for a 5x8 char)

int (*get_free_chars)(drvthis); 
Driver *drvthis;

Get total number of custom characters available.

int (*cellwidth)(drvthis); 
Driver *drvthis;

Return the width of a character cell in pixels. The result is 1-based.

int (*cellheight)(drvthis); 
Driver *drvthis;

Return the height of a character cell in pixels. The result is 1-based.

int (*get_contrast)(drvthis); 
Driver *drvthis;

Get the contrast value from the driver. The return value is an integer in the range from 0 to 1000. Many displays do not support getting or setting contrast using software.

int (*set_contrast)(drvthis,  
Driver *drvthis;
int promille;

Sets the contrast to the given value, which is an integer in the range from 0 to 1000. It is up to the driver to map the logical interval [0, 1000] into the interval that the hardware supports. Many displays do not support software setting of contrast.

int (*get_brightness)(drvthis,  
Driver *drvthis;
int state;

Get the brightness value from the driver for the given backlight state. The parameter state determines which one is returned. The return value is an integer in the range from 0 to 1000. Many displays do not support getting or setting brightness using software.

int (*set_brightness)(drvthis,  
Driver *drvthis;
int state;
int promille;

Set the brightness for the given backlight state to the value given. Value must be an integer in the range from 0 to 1000. It is up to the driver to map the logical interval [0, 1000] into the interval that the hardware supports. Many displays do not support software setting of brightness.

void (*backlight)(drvthis,  
Driver *drvthis;
int state;

Sets the backlight to the given brightness state. Often hardware can only support two values for the backlight: on and off. In that case any value of state > 0 will switch the backlight on.

void (*output)(drvthis,  
Driver *drvthis;
int state;

Sets the output value. Some displays/wirings have a general purpose output, which can be controlled by calling this function. See the 'output' command in the 'widget language'.

const char *(*get_key)(drvthis); 
Driver *drvthis;

Checks if a key has been pressed on the device. Returns NULL for "no key pressed", or a string describing the pressed key. These characters should match the keypad-layout.

const char *(*get_info)(drvthis); 
Driver *drvthis;

Returns a string describing the driver and its features.

short (*config_get_bool)(sectionname,  
char *sectionname;
char *keyname;
int skip;
short default_value;

Call to server. Retrieve a bool from the config file. Sectionname should be the name of the driver (as in the struct). If the key cannot be found, the default value will be returned. skip should be 0 usually, but if you want to retrieve multiple identical keys, then increase skip to get every next value.

long int (*config_get_int)(sectionname,  
char *sectionname;
char *keyname;
int skip;
long int default_value;

Call to server. Retrieve an integer from the config file.

double (*config_get_float)(sectionname,  
char *sectionname;
char *keyname;
int skip;
double default_value;

Call to server. Retrieve a float from the config file.

const char *(*config_get_string)(sectionname,  
char *sectionname;
char *keyname;
int skip;
const char *default;

Call to server. Retrieve a string from the config file. Fill result with a pointer to some available space. You can fill it with a default value. If the key is found, it will be overwritten with the value from the key. Note that you should always first copy the the returned string. It is in the address space of the server, and will be freed at the next call.

int config_has_section(sectionname); 
const char *sectionname;

Returns whether a section exists. Does not need to be called prior to a call to a config_get_* function.

int config_has_key(sectionname,  
const char *sectionname;
const char *keyname;

Returns the number of times a key exists. Does not need to be called prior to a call to a config_get_* function.

First version, Joris Robijn, 20011016
Corrected and expanded, Peter Marschall 20060411
Sync'd with lcd.h, Markus Dolze, 20090322

Chapter 6. Adding your driver to LCDproc


LCDproc is meant to be modular, it is relatively easy to add new input and output drivers to LCDproc.

This chapter will explain you the major steps and few gotchas of adding your own driver to LCDproc. Enjoy!

Be sure to read Chapter 3, Programming for LCDproc and Chapter 5, The LCDproc driver API as well.

As a starting point you may take a look at the debug driver. It is available as server/drivers/debug.c.

Rules for accepting new drivers

LCDproc is open source software. Anyone is free to take LCDproc's code, write his own driver and publish the modified sources somewhere again. If you want your driver to be included in LCDproc's code some conditions have to be met:

  1. The hardware (display or enclosing product) is publicly sold OR the schematics and firmware (if required) are publicly available. [1]

  2. The driver is released under (L)GPL and has an appropriate copyright notice.

  3. The submitter is or is acting on behalf of the original driver developer. [2]

  4. The driver description contains a valid email address for contacting the submitter or developer.

  5. The code is commented AND includes appropriate Doxygen comments, especially for internal / non-API functions.

  6. End user documentation (updates to man pages AND User's Guide in Docbook format) is available.

  7. Driver options are described in the end user documentation AND LCDd.conf.

  8. The driver adheres to the style guide as described in the section called “Code style guideline”.

Autoconf, automake, and Everything!

How I Learned to Stop Worrying and Love the Configure Script

It was decided pretty early in LCDproc's life to use GNU autoconf and GNU automake. This allows LCDproc to be ported to several platforms with much less effort. It can be quite daunting to understand how autoconf & automake interact with each others and with your code, but don't be discouraged. We have taken great care in making this as simple as possible for programmers to add their own driver to LCDproc. Hopefully, you'll only have to modify two files, one for autoconf and one for automake.

The first thing you need to do is to find a name for your driver, it should be as descriptive as possible; most drivers are named after their respective chipset, for example hd44780, mtc_s16209x, sed1330 and stv5730, others are named after the company that makes that particular LCD display, for example CFontz and MtxOrb. Remember that these names are case sensitive. In this chapter, we'll use myDriver (which is an absolute non-descriptive name).

Autoconf and its friend, acinclude.m4

You need to add your driver to function LCD_DRIVERS_SELECT of file acinclude.m4. This can be done in three steps.

Step 1

First you need to add your driver name to the list of possible choices in the help screen.


	[  --enable-drivers=<list> compile driver for LCDs in <list>.]
	[                  drivers may be separated with commas.]
	[                  Possible choices are:]
	[                    bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,]
	[                    glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,]
	[                    joy,lb216,lcdm001,lcterm,lirc,ms6931,mtc_s16209x,]
	[                    MtxOrb,NoritakeVFD,pyramid,sed1330,sed1520,serialVFD,]
	[                    sli,stv5730,svga,t6963,text,tyan,ula200,xosd]
	[                  'all' compiles all drivers;]
	[                  'all,!xxx,!yyy' de-selects previously selected drivers],


	[  --enable-drivers=<list> compile driver for LCDs in <list>.]
	[                  drivers may be separated with commas.]
	[                  Possible choices are:]
	[                    bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,]
	[                    glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,]
	[                    joy,lb216,lcdm001,lcterm,lirc,ms6931,mtc_s16209x,]
	[                    MtxOrb,NoritakeVFD,pyramid,sed1330,sed1520,serialVFD,]
	[                    sli,stv5730,svga,t6963,text,tyan,ula200,xosd,myDriver]
	[                  'all' compiles all drivers;]
	[                  'all,!xxx,!yyy' de-selects previously selected drivers],

Step 2

Second, you need to add your driver to the list of all drivers.


allDrivers=[bayrad,CFontz,CFontz633,...(big list)...,tyan,ula200,xosd]


allDrivers=[bayrad,CFontz,CFontz633,...(big list)...,tyan,ula200,xosd,myDriver]

Step 3

Then last, you need to add your driver to be big switch-case in this function, see below.

			DRIVERS="$DRIVERS myDriver${SO}"
			actdrivers=["$actdrivers myDriver"]

If your driver only works in some platform or requires a particular library or header, you can add your autoconf test here. You can see how other drivers do it, but if you're not sure on how to do this, just send an email to the mailing list and we'll make it for you.

Automake and its friend,

Already half of the job is done! Not to bad, wasn't it? The rest should be just as easy. In this section, you'll be adding your driver to the file server/drivers/ As you can guess, it's the Makefile for the drivers. This can be done in three (or two) simple steps.

Step 1

First, you need to add your driver to the list of drivers in this file, this list is called EXTRA_PROGRAMS.


EXTRA_PROGRAMS = bayrad CFontz ...(big list)... ula200 xosd


EXTRA_PROGRAMS = bayrad CFontz ...(big list)... ula200 xosd myDriver

Step 2

This second step is only needed if your driver needs a particular library. If it doesn't, you can skip to step 3.

You basically need to put you driver name followed by _LDADD and equal this to the name of the library that you need. Usually, these library are substituted by a autoconf variable, if you're not comfortable with this, you send an email to the mailing list and we'll set this up for you.

For example, we would put this for our fictional driver


Step 3

Last but not least, you need to specify which source files should be associated with your driver. You put your driver name followed by _SOURCES and equal this to a space separated list of the source and header files. See below for an example.

myDriver_SOURCES =  lcd.h myDriver.c myDriver.h report.h

Test your setup

You're almost done! You only need to check out if you didn't made any mistake. Just run sh to regenerate the configure script and Makefiles, then run ./configure --enable-drivers=myDriver and type make. If your driver compiles without error, then congratulations, you've just added your driver to LCDproc! Remember to submit a patch to the mailing list so that we can add it to the standard distribution, but do not forget the documentation.

If you had an error, just send us an email describing it to the mailing list and we'll try to help you.

It's all about documentation

Please do not forget to also add the required documentation, so that your driver can be used from others as well.

Within the source code

We use Doxygen to document functions and data types. The doxygen documentation can be created anytime by changing to the docs/ directory and running doxygen.

When documenting your driver's API functions you may use a short hand version and add 'API:' to the beginning of your comment and leave out the parameter and return value description (as we know what the API is doing). If you use some clever algorithm inside a function please add a few words about it.


Always document functions internal to the driver! We do know what the API does (or is expected to do) but we don't know about what your driver does internally.

Read the section called “Comments” on how for format comments.

The configuration file, LCDd.conf

Extend the LCDproc server's configuration file with a section that holds a standard configuration for your driver together with short descriptions of the options used.

## MyDriver for MyDevice ##

# Select the output device to use [default: /dev/lcd]

# Set the display size [default: 20x4]

The daemon's manual page, LCDd.8

Append your driver to the list of drivers in docs/LCDd.conf, the manual page of LCD, so that users can find your driver when doing man LCDd.

.B ms6931
MSI-6931 displays in 1U rack servers by MSI
.B mtc_s16209x
MTC_S16209x LCD displays by Microtips Technology Inc
.B MtxOrb
Matrix Orbital displays (except Matrix Orbital GLK displays)
.B MyDriver
displays connected using MyDevice
.B NoritakeVFD
Noritake VFD Device CU20045SCPB-T28A
.B pyramid
LCD displays from Pyramid (
.B sed1330
SED1330/SED1335 (aka S1D13300/S1D13305) based graphical displays

The user guide

Step 1

Please add a file myDriver.docbook, that describes the configuration of your driver and the hard/software needed, to the directory docs/lcdproc-user/drivers/.

Step 2

Define a Docbook entity for your driver file in lcdproc-user.docbook.

<!ENTITY ms6931 SYSTEM "drivers/ms6931.docbook">
<!ENTITY mtc_s16209x SYSTEM "drivers/mtc_s16209x.docbook">
<!ENTITY MtxOrb SYSTEM "drivers/mtxorb.docbook">
<!ENTITY MyDriver SYSTEM "drivers/MyDriver.docbook">
<!ENTITY NoritakeVFD SYSTEM "drivers/NoritakeVFD.docbook">
<!ENTITY pylcd SYSTEM "drivers/pylcd.docbook">
<!ENTITY sed1330 SYSTEM "drivers/sed1330.docbook">

Step 3

Add the freshly defined entity to drivers.docbook to include the documentation of your driver into the LCDproc User's Guide.


[1] Therefore I will not commit drivers for displays ripped out from an old telephone for your private hardware project and are not available otherwise.

[2] I will not submit drivers found somewhere on the internet and submitted without the original developer's written acknowledgement.

Chapter 7. Making a release

This chapter describes the steps necessary to create a software release of LCDproc. It is intended to guide the release manager when creating a new release.

Creating a source code release

Procedure 7.1. Steps to create a new software release of LCDproc

  1. Update year of release in HEAD. The following files need to be updated:

    • server/main.c

    • clients/lcdproc/main.c

    • docs/lcdproc-user/bookinfo.docbook

    • docs/lcdproc-dev/bookinfo.docbook

  2. Make a backup of the CVS repository to the local disk. This is needed if something goes wrong. Run: rsync -av '*' .

  3. Check out the CVS stable release branch. Run: cvs -d:ext:<your_sf_userid> checkout -r stable-0-5-x -d lcdproc-0-5-x lcdproc and change into the stable-0-5-x directory.

  4. Merge HEAD to stable. This step is only required if the release will be done from HEAD.

    1. Run: cvs -q update -j stable-0-5-x -j HEAD

    2. Revert files that need to retain their version number after merge. Due to some wired import of external files, some files always get their $Id$ tag updated, even if there has been no change. The following files should be reverted to their previous version (given that there has really no change happened):

      • clients/examples/

      • clients/metar/

      • contrib/interface-demo2/interface.c

      • contrib/interface-demo2/nstrcmp.c

      Run cvs update -C on these files.

    3. Update version numbers for stable branch. See Files that need version numbers updated for the list of affected files.

    4. Commit everything. Run: cvs commit -m "Sync with HEAD"

    1. Tag the stable tree to indicate the file revision where the branch was made. This eases later diffs against that point.

      Run: cvs tag LCDPROC_0_5_A_BP (where A is the version number of the next release and _BP means branch point).


      Tag names are all uppercase with underscores to separate elements!

    2. Create a release branch in CVS. Run: cvs tag -b lcdproc-0-5-A


      Release branch names are all lowercase with hyphens to separate elements!

    3. Now you have created a branch from which the new release will done. Before continuing either checkout this new branch to a new working directory or update the current one by running: cvs update -r lcdproc-0-5-A

  5. Update version numbers for release and commit the changes. See Files that need version numbers updated for the list of affected files.

  6. Set a CVS tag for the release. Run: cvs tag LCDPROC_0_5_A_RELEASE where A is the version number of the next release.

  7. Export the release source tree as anonymous. This step is intended to check if all files are correctly tagged with the new release in the source tree.

    Run: cvs -d -z3 export -r LCDPROC_0_5_A_RELEASE lcdproc

  8. Create the release tarball by running:

        make distcheck
  9. Test the newly created release tarball at least once. Try to build and install it with all drivers enabled. Better: Try to build and install with several library options turned on and off.

  10. Create the release notes. The release notes should be an easy to read summary of changes in this release. The ChangeLog file is not very good as release notes as it is just a chronological list of things that happen.

    The release notes should talk about:

    • Fingerprint (SHA-1) of the release tarball

    • Known bugs

    • New drivers

    • Other important changes within drivers, the server core and clients

    • Everything else the user has to now for upgrading his installed version

    grouped by topics of interest.

  11. Upload the new release and release notes to Sourceforge.

  12. Announce the new release.

    Send notice about the new release to the LCDproc mailing list () and the lcdproc-announce mailing list on Sourceforge (). Also mention the new release on the LCDproc website download and front page.

Files that need version numbers updated

  • BUGS

  • ChangeLog


  • server/menuscreens.c

  • docs/lcdproc-dev/bookinfo.docbook

  • docs/lcdproc-dev/driver-api.docbook

  • docs/lcdproc-dev/introduction.docbook

  • docs/lcdproc-user/bookinfo.docbook

  • docs/lcdproc-user/how-to-obtain.docbook

Creating a documentation release

Any release of LCDproc is accompanied the the user guide and developer guide. Here is how to create these documentation packages.

Procedure 7.2. Steps to create the documentation package

  1. Get the release tarball and extract it (or change to the CVS directory exported in the section called “Creating a source code release” and change to docs/lcdproc-user.

  2. Create the documentation package by running: xmlto -o lcdproc-0-5-A-user-html xhtml lcdproc-user.docbook

  3. Create a tarfile of the documentation package: tar -czf lcdproc-0-5-A-user-html.tar.gz lcdproc-0-5-A-user-html

  4. Repeat the above steps for the developers guide, replacing -user with -dev where appropriate.

  5. Upload the files to the Sourceforge file release system.

The online documentation consists of the user and developer guide, each converted to a single file for viewing online.

Procedure 7.3. Steps to create the online documentation

  1. Get the release tarball and extract it (or change to the CVS directory exported exported in the section called “Creating a source code release” and change to docs/lcdproc-user.

  2. Create the documentation file by running: xmlto xhtml-nochunks lcdproc-user.docbook

  3. Rename the file: mv lcdproc-user.html lcdproc-0-5-A-user.html

  4. Repeat the above steps for the developers guide, replacing -user with -dev where appropriate.

  5. Upload the files to our Sourceforge web site (not the file release system!) and change htdocs/docs/index.html to point to the new files.

Appendix A. GNU Free Documentation License

Version 1.1, March 2000

Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.


The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.


This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.


You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.


If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.


You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

  1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.

  2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).

  3. State on the Title page the name of the publisher of the Modified Version, as the publisher.

  4. Preserve all the copyright notices of the Document.

  5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.

  6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.

  7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.

  8. Include an unaltered copy of this License.

  9. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.

  10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.

  11. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.

  12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.

  13. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.

  14. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.


You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."


You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.


A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.


Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.


You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.


The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.

How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".

If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.