[PATCH] Add serdisplib driver This patch adds a serdisplib driver for the serdisplib library (http://serdisplib.sf.net) that is used for low-level accessing of dot-matrix devices (i.e. such displays that are drived by pixel and not by characters unlike HD44780, for example). I know there's already glcdlib. But that approach has several disadvantages: o Unnecessary library dependencies. o Complicated installation, i.e. you have to edit two configuration files. o Too much redraws. In fact, that was the reason for me to write this driver because my ctinclud display (http://www.ct-maeusekino.de) was quite unusable with the glcdlib driver. The problem is simply that lcdproc redraws the whole screen each second and it's the task of the driver to not to redraw it in reality. The problem is now that the glcdproc driver only has the view of characters, and cannot decide which pixels it actually has to redraw. And graphlcd which has the per-pixel view doesn't do that "caching" and simply redraws all. Of course, that _can_ be changed in graphlcd, but I'm sure that leads to endless discussions and because I didn't like the glcdlib -> graphlcd -> serdisplib approach anyway, I decided to write that driver. Some important design decisions: o The driver is split into lcdgraphic.c and serdisplib.c. All function that do the character -> pixel "rendering" are split out into lcdgraphic.c, so it would be possible to write another low-level driver that uses that function. However, in normal cases it makes more sense to add that part to serdisplib. o It only requires FreeType (http://freetype.sf.net) for font rendering. That's no new real dependency because in almost all cases, graphlcd was compiled with FreeType support. o Only mono space fonts are supported. o The driver implements symbols (arrow, etc.) using Unicode characters of the font. (The recommended font is Andale Mono which is available for free from http://corefonts.sf.net) o Works on i686 and x86_64. Please review. The patch is against 0.5.2. If you consider to add this into CVS, I'll provide documentation. And this time, I'll provide the documentation in time unlike with the ula200 driver. ;-) Signed-off-by: Bernhard Walle <bernhard.walle@gmx.de> --- LCDd.conf | 28 ++ acinclude.m4 | 27 +- server/drivers/Makefile.am | 5 server/drivers/lcdgraphic.c | 590 ++++++++++++++++++++++++++++++++++++++++++++ server/drivers/lcdgraphic.h | 195 ++++++++++++++ server/drivers/serdisplib.c | 379 ++++++++++++++++++++++++++++ 6 files changed, 1220 insertions(+), 4 deletions(-) --- a/acinclude.m4 +++ b/acinclude.m4 @@ -10,13 +10,13 @@ AC_ARG_ENABLE(drivers, [ irman,joy,lb216,lcdm001,lcterm,lirc,MD8800,ms6931,] [ mtc_s16209x,MtxOrb,NoritakeVFD,picolcd,pyramid,sed1330] [ sed1520,serialPOS,serialVFD,sli,stv5730,svga,t6963,text,] - [ tyan,ula200,xosd] + [ tyan,ula200,serdisplib,xosd] [ 'all' compiles all drivers;] [ 'all,!xxx,!yyy' de-selects previously selected drivers], drivers="$enableval", drivers=[bayrad,CFontz,CFontz633,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pyramid,text]) -allDrivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,joy,lb216,lcdm001,lcterm,lirc,MD8800,ms6931,mtc_s16209x,MtxOrb,NoritakeVFD,picolcd,pyramid,sed1330,sed1520,serialPOS,serialVFD,sli,stv5730,svga,t6963,text,tyan,ula200,xosd] +allDrivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,joy,lb216,lcdm001,lcterm,lirc,MD8800,ms6931,mtc_s16209x,MtxOrb,NoritakeVFD,picolcd,pyramid,sed1330,sed1520,serialPOS,serialVFD,sli,stv5730,svga,t6963,text,tyan,ula200,serdisplib,xosd] drivers=`echo $drivers | sed -e 's/,/ /g'` @@ -383,6 +383,29 @@ dnl else AC_MSG_WARN([The ula200 driver needs ftdi.h and usb.h]) ]) ;; + serdisplib) + LIBFREETYPE_CFLAGS=`pkg-config --cflags freetype2` + LIBFREETYPE_LIBS=`pkg-config --libs freetype2` + if test x"$LIBFREETYPE_CFLAGS" = "x" ; then + AC_MSG_WARN([The serdisplib driver needs freetype2]) + fi + + AC_CHECK_HEADERS([serdisplib/serdisp.h],[ + AC_CHECK_LIB(serdisp, serdisp_nextdisplaydescription,[ + LIBSERDISP="-lserdisp" + DRIVERS="$DRIVERS serdisplib${SO}" + actdrivers=["$actdrivers serdisplib"] + ],[ + AC_MSG_WARN([The serdisplib driver needs serdisplib]) + ]) + ],[ + AC_MSG_WARN([The serdisplib driver needs serdislib/serdisp.h]) + ]) + + AC_SUBST(LIBFREETYPE_CFLAGS) + AC_SUBST(LIBFREETYPE_LIBS) + AC_SUBST(LIBSERDISP) + ;; xosd) AC_CHECK_HEADERS([xosd.h],[ AC_CHECK_LIB(xosd, main,[ --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -19,12 +19,13 @@ AM_LDFLAGS = @LDSHARED@ #LIBS = pkglib_PROGRAMS = @DRIVERS@ -EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx ea65 EyeboxOne g15 glcdlib glk hd44780 icp_a106 imon IOWarrior irman joy lb216 lcdm001 lcterm lirc MD8800 ms6931 mtc_s16209x MtxOrb NoritakeVFD picolcd pyramid sed1330 sed1520 serialPOS serialVFD stv5730 svga t6963 text tyan sli ula200 xosd +EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx ea65 EyeboxOne g15 glcdlib glk hd44780 icp_a106 imon IOWarrior irman joy lb216 lcdm001 lcterm lirc MD8800 ms6931 mtc_s16209x MtxOrb NoritakeVFD picolcd pyramid sed1330 sed1520 serialPOS serialVFD stv5730 svga t6963 text tyan sli ula200 serdisplib xosd noinst_LIBRARIES = libLCD.a libbignum.a IOWarrior_CFLAGS = @libusb_cflags@ $(AM_CFLAGS) hd44780_CFLAGS = @libusb_cflags@ $(AM_CFLAGS) g15_CFLAGS = @libusb_cflags@ $(AM_CFLAGS) +serdisplib_CFLAGS = @LIBFREETYPE_CFLAGS@ $(AM_CFLAGS) CFontz_LDADD = libLCD.a libbignum.a CFontz633_LDADD = libLCD.a libbignum.a @@ -53,6 +54,7 @@ svga_LDADD = @LIBSVGA@ t6963_LDADD = libLCD.a tyan_LDADD = libLCD.a libbignum.a ula200_LDADD = libLCD.a @LIBFTDI@ +serdisplib_LDADD = libLCD.a @LIBSERDISP@ @LIBFREETYPE_LIBS@ sli_LDADD = libLCD.a xosd_LDADD = @LIBXOSD@ @@ -99,6 +101,7 @@ t6963_SOURCES = lcd.h lcd_lib.h t69 text_SOURCES = lcd.h text.h text.c report.h tyan_SOURCES = lcd.h lcd_lib.h tyan_lcdm.h tyan_lcdm.c report.h adv_bignum.h ula200_SOURCES = lcd.h lcd_lib.h ula200.h ula200.c report.h +serdisplib_SOURCES = lcd.h serdisplib.h serdisplib.c lcdgraphic.c lcdgraphic.h sli_SOURCES = lcd.h lcd_lib.h wirz-sli.h wirz-sli.c report.h xosd_SOURCES = lcd.h xosdlib_drv.c xosdlib_drv.h report.h --- /dev/null +++ b/server/drivers/lcdgraphic.c @@ -0,0 +1,590 @@ +// Description: + +/* Copyright (C) 2007 Bernhard Walle <bernhard.walle@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <syslog.h> +#include <stdint.h> + +#include "lcdgraphic.h" + + +/////////////////////////////////////////////////////////////////////////////// +// Defines + +#undef report +#define lcdgr_report lcdgr->drv->report + +/////////////////////////////////////////////////////////////////////////////// +// Returns the value of a pixel in the new_buffer buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x location of the pixel +// @param y the y location of the pixel +// +// @return the value of the pixel +// +static inline int get_pixel_new(struct lcdgraphic *lcdgr, int x, int y) +{ + if (x >= lcdgr->width || y >= lcdgr->height) + return -1; + else + return lcdgr->new_buffer[lcdgr->width * y + x]; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the value of a pixel in the disp_buffer buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x location of the pixel +// @param y the y location of the pixel +// +// @return the value of the pixel +// +static inline int get_pixel_disp(struct lcdgraphic *lcdgr, int x, int y) +{ + if (x >= lcdgr->width || y >= lcdgr->height) + return -1; + else + return lcdgr->disp_buffer[lcdgr->width * y + x]; +} + +/////////////////////////////////////////////////////////////////////////////// +// Sets the value of a pixel in the new_buffer buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x location of the pixel +// @param y the y location of the pixel +// @param value the value to which the buffer should be set +// +static void set_pixel_new(struct lcdgraphic *lcdgr, int x, int y, int value) +{ + if (x < lcdgr->width && y < lcdgr->height) + lcdgr->new_buffer[lcdgr->width * y + x] = value; +} + +/////////////////////////////////////////////////////////////////////////////// +// Sets the value of a pixel in the disp_buffer buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x location of the pixel +// @param y the y location of the pixel +// @param value the value to which the buffer should be set +// +static void set_pixel_disp(struct lcdgraphic *lcdgr, int x, int y, int value) +{ + if (x < lcdgr->width && y < lcdgr->height) + lcdgr->disp_buffer[lcdgr->width * y + x] = value; +} + +/////////////////////////////////////////////////////////////////////////////// +// Maps a lcdproc icon to a unicode code for an icon. +// +// @param icon the lcdproc icon constant +// +// @return Unicode value +// +static int icon2unicode(int icon) +{ + switch (icon) { + case ICON_BLOCK_FILLED: + return UNICODE_BLOCK_FILLED; + case ICON_HEART_FILLED: + return UNICODE_HEART_FILLED; + case ICON_HEART_OPEN: + return UNICODE_HEART_OPEN; + case ICON_ARROW_UP: + return UNICODE_ARROW_UP; + case ICON_ARROW_DOWN: + return UNICODE_ARROW_DOWN; + case ICON_ARROW_LEFT: + return UNICODE_ARROW_LEFT; + case ICON_ARROW_RIGHT: + return UNICODE_ARROW_RIGHT; + case ICON_SELECTOR_AT_LEFT: + return UNICODE_SELECTOR_AT_LEFT; + case ICON_SELECTOR_AT_RIGHT: + return UNICODE_SELECTOR_AT_RIGHT; + case ICON_ELLIPSIS: + return UNICODE_ELLIPSIS; + default: + return -1; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Initialises the instance +// +// @param lcdgr a pointer to a valid lcdgraphic structure (not initialised, +// only allocated +// @param settings a pointer to a settings structure, for a description of the +// members, see above +// @param functions a pointer to the functions structure, for a description of +// the members, see above +// +// @return 0 on success, != 0 on failure +// +int lcdgraphic_init(struct lcdgraphic *lcdgr, + struct lcdgraphic_settings *settings, + struct lcdgraphic_functions *functions) +{ + int ret; + + // check the arguments + if (!lcdgr || !functions || !settings) { + lcdgr_report(RPT_ERR, "lcdgraphic_init: one of the arguments is NULL"); + return -EINVAL; + } + + // check functions + if (!functions->setpixel || !functions->clear || !functions->flush) { + lcdgr_report(RPT_ERR, "lcdgraphic_init: all functions must be valid"); + return -EINVAL; + } + + // validation + if (settings->cwidth == 0 || settings->cheight == 0) { + lcdgr_report(RPT_ERR, "lcdgraphic_init: cwidth / cheight is 0"); + return -EINVAL; + } + + // zero all first + memset(lcdgr, 0, sizeof(struct lcdgraphic)); + + // assign some members + lcdgr->width = settings->width; + lcdgr->height = settings->height; + lcdgr->cwidth = settings->cwidth; + lcdgr->cheight = settings->cheight; + lcdgr->bwidth = settings->bheight; + lcdgr->drv = settings->drv; + strncpy(lcdgr->normal_font, settings->normal_font, PATH_MAX); + lcdgr->normal_font[PATH_MAX-1] = 0; + lcdgr->funcs = *functions; + lcdgr->all_dirty = 1; + + // calculate some stuff + lcdgr->num_pixels = lcdgr->width * lcdgr->height; + lcdgr->xchars = (lcdgr->width - 2*lcdgr->bwidth) / lcdgr->cwidth; + lcdgr->ychars = (lcdgr->height - 2*lcdgr->bheight) / lcdgr->cheight; + + // initialise freetype + ret = FT_Init_FreeType(&lcdgr->ft_library); + if (ret != 0) { + lcdgr_report(RPT_ERR, "Freetype initialisation failed"); + goto out; + } + + // load the font face for freetype + ret = FT_New_Face(lcdgr->ft_library, lcdgr->normal_font, 0, + &lcdgr->ft_normal_font); + if (ret != 0) { + lcdgr_report(RPT_ERR, "Freetype creation of font '%s' failed", + lcdgr->normal_font); + goto out; + } + + // allocate the buffers + lcdgr->disp_buffer = (int *)malloc(sizeof(int) * lcdgr->num_pixels); + if (!lcdgr->disp_buffer) { + lcdgr_report(RPT_ERR, "lcdgraphic_init: malloc of disp_buffer failed"); + ret = -ENOMEM; + goto out; + } + + lcdgr->new_buffer = (int *)malloc(sizeof(int) * lcdgr->num_pixels); + if (!lcdgr->new_buffer) { + lcdgr_report(RPT_ERR, "lcdgraphic_init: malloc of new_buffer failed"); + ret = -ENOMEM; + goto out; + } + + // clear buffers initially + memset(lcdgr->new_buffer, 0, sizeof(int) * lcdgr->num_pixels); + memset(lcdgr->disp_buffer, 0, sizeof(int) * lcdgr->num_pixels); + + return 0; + +out: + if (lcdgr->ft_normal_font) + FT_Done_Face(lcdgr->ft_normal_font); + if (lcdgr->ft_library) + FT_Done_FreeType(lcdgr->ft_library); + if (lcdgr->new_buffer) + free(lcdgr->new_buffer); + if (lcdgr->disp_buffer) + free(lcdgr->disp_buffer); + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +// Destroys the instance +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// +void lcdgraphic_destroy(struct lcdgraphic *lcdgr) +{ + if (!lcdgr) { + lcdgr_report(RPT_ERR, "lcdgraphic_destroy: lcdgr == NULL"); + return; + } + + if (lcdgr->ft_normal_font) + FT_Done_Face(lcdgr->ft_normal_font); + if (lcdgr->ft_library) + FT_Done_FreeType(lcdgr->ft_library); + + free(lcdgr->disp_buffer); + free(lcdgr->new_buffer); + + // allow multiple calls of that function + lcdgr->disp_buffer = NULL; + lcdgr->new_buffer = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the number of characters in one line. This function can be used +// inside the width callback of the display driver. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// +// @return the number of characters in a line +// +int lcdgraphic_width(struct lcdgraphic *lcdgr) +{ + if (!lcdgr) { + lcdgr_report(RPT_ERR, "lcdgraphic_destroy: lcdgr == NULL"); + return -1; + } + + return lcdgr->xchars; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the number of lines. This function can be used inside the height +// callback of the display driver. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// +// @return the number lines +// +int lcdgraphic_height(struct lcdgraphic *lcdgr) +{ + if (!lcdgr) { + lcdgr_report(RPT_ERR, "lcdgraphic_destroy: lcdgr == NULL"); + return -1; + } + + return lcdgr->ychars; +} + +/////////////////////////////////////////////////////////////////////////////// +// Clears the buffer. Doesn't draw anything. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// +void lcdgraphic_clear(struct lcdgraphic *lcdgr) +{ + if (!lcdgr) { + lcdgr_report(RPT_ERR, "lcdgraphic_destroy: lcdgr == NULL"); + return; + } + + memset(lcdgr->new_buffer, 0, sizeof(int) * lcdgr->num_pixels); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a string into the display buffer. This calls lcdgraphic_draw_char() +// internally. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the column (position in characters!) where the character +// should be drawn +// @param y the line (position in characters!) where the character +// should be drawn +// @param str the string that should be drawn +// +void lcdgraphic_draw_string(struct lcdgraphic *lcdgr, int x, int y, char *str) +{ + int i; + int num_chars = strlen(str); + + // check string length + if (x + num_chars > lcdgr->xchars) { + lcdgr_report(RPT_WARNING, "lcdgraphic_draw_string: %dx%d - %s too long", + x, y, str); + return; + } + + for (i = 0; i < num_chars; i++) + lcdgraphic_draw_char(lcdgr, x + i, y, str[i]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a Unicode character into the display buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the column (position in characters!) where the character +// should be drawn +// @param y the line (position in characters!) where the character +// should be drawn +// @param c the character that should be drawn (unicode code) +// @param scale for big numbers -- the number of cells that the rendered +// string should occupy +// @param width the width in characters +// +static int lcdgraphic_draw_char_unicode(struct lcdgraphic *lcdgr, + int x, + int y, + int c, + int scale, + int width) +{ + static int last_font_size = -1; + int xoffset, yoffset; // in pixel + int err; + FT_Bitmap *bitmap; + unsigned char *bitmap_buf; + int col, row; + FT_GlyphSlot glyph; + FT_Face face; + int cwidth, cheight; + int font_size; + + face = lcdgr->ft_normal_font; + xoffset = lcdgr->bwidth + x*lcdgr->cwidth; + yoffset = lcdgr->bheight + y*lcdgr->cheight; + + // set the font size + font_size = lcdgr->cheight * scale; + if (last_font_size != font_size) { + err = FT_Set_Pixel_Sizes(lcdgr->ft_normal_font, font_size, font_size); + if (err != 0) { + lcdgr_report(RPT_ERR, "Failed to set pixel size (%dx%x)", + lcdgr->cwidth * scale, lcdgr->cheight * scale); + return -1; + } + + last_font_size = font_size; + } + + // load the glyph and render it + err = FT_Load_Char(lcdgr->ft_normal_font, c, + FT_LOAD_RENDER | FT_LOAD_MONOCHROME); + if (err != 0) { + lcdgr_report(RPT_WARNING, "lcdgraphic_draw_char: loading char " + "'%c' (%d) failed", c, c); + + return -1; + } + + // clear the rectangle first + cwidth = lcdgr->cwidth * width; + cheight = lcdgr->cheight * scale; + for (col = 0; col < cwidth; col++) + for (row = 0; row < cheight; row++) + set_pixel_new(lcdgr, xoffset + col, yoffset + row, 0); + + // set some data elements for convenience + glyph = lcdgr->ft_normal_font->glyph; + bitmap = &glyph->bitmap; + bitmap_buf = bitmap->buffer; + + // and now copy the pixels + for (row = 0; row < bitmap->rows; row++) { + for (col = 0; col < bitmap->width; col++) { + int bitmap_left = glyph->bitmap_left; + + if (scale != width) + bitmap_left = (cwidth - bitmap->width)/2; + + set_pixel_new(lcdgr, xoffset + col + bitmap_left, + yoffset + row + lcdgr->cheight + (face->size->metrics.descender >> 6) + - glyph->bitmap_top, + bitmap_buf[col/8] >> (7 - (col % 8)) & 1); + } + bitmap_buf += bitmap->pitch; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a character into the display buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the column (position in characters!) where the character +// should be drawn +// @param y the line (position in characters!) where the character +// should be drawn +// @param c the character that should be drawn +// +void lcdgraphic_draw_char(struct lcdgraphic *lcdgr, int x, int y, char c) +{ + if (lcdgraphic_draw_char_unicode(lcdgr, x, y, c & 0xff, 1, 1) != 0) + lcdgraphic_draw_char_unicode(lcdgr, x, y, '?', 1, 1); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a big number into the display buffer. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x position +// @param num the actual number +// +void lcdgraphic_draw_num(struct lcdgraphic *lcdgr, int x, int num) +{ + int y; + const int BIG_HEIGHT = 3; + + if (num < 0 || num > 10) { + lcdgr_report(RPT_WARNING, "lcdgraphic_draw_num: num out of range (%d)", num); + return; + } + + y = lcdgr->ychars - (lcdgr->ychars - BIG_HEIGHT + 1)/2; + if (num == 10) + lcdgraphic_draw_char_unicode(lcdgr, x, y, ':', BIG_HEIGHT, 1); + else + lcdgraphic_draw_char_unicode(lcdgr, x, y, '0' + num, BIG_HEIGHT, BIG_HEIGHT); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws an icon (which standard lcdproc icon syntax) +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x position of the icon +// @param y the y position of the icon +// @param icon the icon constant +// +// @return 0 on success, -1 on failure (i.e. the core replaces the icon by a +// suitable character +// +int lcdgraphic_icon(struct lcdgraphic *lcdgr, int x, int y, int icon) +{ + int unicode; + + unicode = icon2unicode(icon); + if (unicode > 0) + return lcdgraphic_draw_char_unicode(lcdgr, x, y, unicode, 1, 1); + else + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a horizontal bar +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x position of the bar +// @param y the y position of the bar +// @param len the maximum length +// @param promille the current state +// @param options +// +void lcdgraphic_hbar(struct lcdgraphic *lcdgr, + int x, + int y, + int len, + int promille, + int options) +{ + int startx, midx, endx, starty, endy; + int col, row; + + /* calculate positions */ + startx = lcdgr->bwidth + x*lcdgr->cwidth; + midx = startx + promille * (len*lcdgr->cwidth) / 1000; + endx = startx + len*lcdgr->cwidth; + starty = lcdgr->bheight + y*lcdgr->cheight; + endy = starty + lcdgr->cheight - 1; /* don't draw the last line */ + + for (col = startx; col < endx; col++) + for (row = starty; row < endy; row++) + set_pixel_new(lcdgr, col, row, col < midx); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Draws a vertical bar +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// @param x the x position of the bar +// @param y the y position of the bar +// @param len the maximum length +// @param promille the current state +// @param options +// +void lcdgraphic_vbar(struct lcdgraphic *lcdgr, + int x, + int y, + int len, + int promille, + int options) +{ + int startx, endx, starty, midy, endy; + int col, row; + + /* calculation positions */ + startx = lcdgr->bwidth + x*lcdgr->cwidth; + endx = startx + lcdgr->cwidth - 1; /* don't draw the last column */ + starty = lcdgr->bheight + (y + 1)*lcdgr->cheight - 1; + midy = starty - promille * (len*lcdgr->cheight) / 1000; + endy = starty - len*lcdgr->cheight; + + for (col = startx; col < endx; col++) + for (row = starty; row > endy; row--) + set_pixel_new(lcdgr, col, row, row > midy); +} + +/////////////////////////////////////////////////////////////////////////////// +// Flushes the output to the display. Uses the provided callback functions +// of the underlying display. +// +// @param lcdgr a pointer to a valid lcdgraphic structure +// +void lcdgraphic_flush(struct lcdgraphic *lcdgr) +{ + int x, y; + + if (lcdgr->all_dirty) + lcdgr->funcs.clear(lcdgr->drv); + + for (y = 0; y < lcdgr->height; y++) { + for (x = 0; x < lcdgr->width; x++) { + int val = get_pixel_new(lcdgr, x, y); + + if (lcdgr->all_dirty || + (val != get_pixel_disp(lcdgr, x, y)) ) { + lcdgr->funcs.setpixel(lcdgr->drv, x, y, val); + set_pixel_disp(lcdgr, x, y, val); + } + } + } + + lcdgr->funcs.flush(lcdgr->drv); + lcdgr->all_dirty = 0; +} + +// vimx: set sw=4 ts=4 et: --- /dev/null +++ b/server/drivers/lcdgraphic.h @@ -0,0 +1,195 @@ +// Description: + +/* Copyright (C) 2007 Bernhard Walle <bernhard.walle@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ + +#ifndef LCDGRAPHIC_H +#define LCDGRAPHIC_H + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> +#include <iconv.h> + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include <serdisplib/serdisp.h> + + +#include "lcd.h" +#include "report.h" +#include "timing.h" + +/////////////////////////////////////////////////////////////////////////////// +// constants +// + + +/////////////////////////////////////////////////////////////////////////////// +// Unicode characters +// + +#define UNICODE_BLOCK_FILLED 0x2588 +#define UNICODE_HEART_OPEN 0x2661 +#define UNICODE_HEART_FILLED 0x2665 +#define UNICODE_ARROW_UP 0x2191 +#define UNICODE_ARROW_DOWN 0x2193 +#define UNICODE_ARROW_LEFT 0x2190 +#define UNICODE_ARROW_RIGHT 0x2192 +#define UNICODE_CHECKBOX_OFF -1 /* -1 == not implemented */ +#define UNICODE_CHECKBOX_ON -1 +#define UNICODE_CHECKBOX_GRAY -1 +#define UNICODE_SELECTOR_AT_LEFT -1 +#define UNICODE_SELECTOR_AT_RIGHT -1 +#define UNICODE_ELLIPSIS -1 + +/////////////////////////////////////////////////////////////////////////////// +// These are callback functions that the low level driver must provide +// +struct lcdgraphic_functions { + + // sets the colour of a pixel + void (*setpixel)(Driver *drvthis, int x, int y, int pixel); + + // clears the whole display + void (*clear)(Driver *drvthis); + + // flushes the display + void (*flush)(Driver *drvthis); +}; + + +/////////////////////////////////////////////////////////////////////////////// +// That structure should be passed by the user, while struct lcdgraphic is +// entirely private to the library +struct lcdgraphic_settings { + + // the width and the height (in number of pixels) of the display + int width, height; + + // the width and the height of one character + int cwidth, cheight; + + // defines unused area on the display (border width / border height) + int bwidth, bheight; + + // that's the cookie that is passed to the callback functions + // it's also used for reporting errors + Driver *drv; + + // font file used for normal characters (TTF, must be monospaced) + char normal_font[PATH_MAX]; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Private structure for drivers that use the functions of that library. +// Don't access the members by hand here. +// +struct lcdgraphic { + + // --- public members from the structure above ----- + + // the width and the height (in number of pixels) of the display + int width, height; + + // the width and the height of one character + int cwidth, cheight; + + // defines unused area on the display (border width / border height) + int bwidth, bheight; + + // that's the cookie that is passed to the callback functions + // it's also used for reporting errors + Driver *drv; + + // font file used for normal characters (TTF, must be monospaced) + char normal_font[PATH_MAX]; + + // a set of callback functions + struct lcdgraphic_functions funcs; + + // --- private members ----- + + // the framebuffer that's actually displayed + int *disp_buffer; + + // the buffer that shows the contents of the next update but wasn't + // flushed yet + int *new_buffer; + + // (calculated) convenience value because we need that often + int num_pixels; + + // (calculated) number of characters in both directions + int xchars, ychars; + + // all buffers are dirty, i.e. update all + int all_dirty; + + // freetype library handle + FT_Library ft_library; + + // handle for the normal font + FT_Face ft_normal_font; + + // handle for charset convertions + iconv_t iconv_handle; +}; + +// Forward declarations for 'clients' + +int lcdgraphic_init(struct lcdgraphic *lcdgr, + struct lcdgraphic_settings *settings, + struct lcdgraphic_functions *functions); + +int lcdgraphic_width(struct lcdgraphic *lcdgr); +int lcdgraphic_height(struct lcdgraphic *lcdgr); +void lcdgraphic_destroy(struct lcdgraphic *lcdgr); +void lcdgraphic_clear(struct lcdgraphic *lcdgr); +void lcdgraphic_flush(struct lcdgraphic *lcdgr); +void lcdgraphic_draw_string(struct lcdgraphic *lcdgr, int x, int y, char *str); +void lcdgraphic_draw_char(struct lcdgraphic *lcdgr, int x, int y, char c); +void lcdgraphic_draw_num(struct lcdgraphic *lcdgr, int x, int num); +int lcdgraphic_icon(struct lcdgraphic *lcdgr, int x, int y, int icon); + +void lcdgraphic_hbar(struct lcdgraphic *lcdgr, + int x, + int y, + int len, + int promille, + int options); + +void lcdgraphic_vbar(struct lcdgraphic *lcdgr, + int x, + int y, + int len, + int promille, + int options); + +#endif /* LCDGRAPHIC_H */ + +// vim: set sw=4 ts=4 et: --- /dev/null +++ b/server/drivers/serdisplib.c @@ -0,0 +1,379 @@ +// Description: + +/* Copyright (C) 2007 Bernhard Walle <bernhard.walle@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <syslog.h> + +#include <serdisplib/serdisp.h> + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "lcd.h" +#include "report.h" +#include "timing.h" +#include "lcdgraphic.h" + +/* Vars for the server core */ +MODULE_EXPORT char *api_version = API_VERSION; +MODULE_EXPORT int stay_in_foreground = 0; +MODULE_EXPORT int supports_multiple = 1; +MODULE_EXPORT char *symbol_prefix = "serdisplib_"; + +/////////////////////////////////////////////////////////////////////////////// +// constants +// +#define SERDISPLIB_MAX_DISPLAYNAME 32 +#define SERDISPLIB_MAX_DEVICENAME PATH_MAX + +/////////////////////////////////////////////////////////////////////////////// +// private data types +// +typedef struct { + + // the name of the display driver in serdisplib, e.g. 'ctinclud' + char display_name[SERDISPLIB_MAX_DISPLAYNAME]; + + // the name of the device in serdisplib, e.g. /dev/parport0 + char display_device[SERDISPLIB_MAX_DEVICENAME]; + + // the serdisplib connection handle + serdisp_CONN_t* serdisplib_conn; + + // the serdisplib handle + serdisp_t *serdisplib; + + // invert the display + int invert; + + // the lcdgraphic handle + struct lcdgraphic *lcdgraphic; + +} PrivateData; + + + +/////////////////////////////////////////////////////////////////////////////// +// Callback functions +// + +static void callback_setpixel(Driver *drvthis, int x, int y, int pixel) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + serdisp_setcolour(p->serdisplib, x, y, pixel ? SD_COL_BLACK : SD_COL_WHITE); +} + +static void callback_clear(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + serdisp_clearbuffer(p->serdisplib); +} + +static void callback_flush(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + serdisp_update(p->serdisplib); +} + +struct lcdgraphic_functions lcdgraphic_functions = { + .setpixel = callback_setpixel, + .clear = callback_clear, + .flush = callback_flush +}; + +/////////////////////////////////////////////////////////////////////////////// +// Init the driver and display +// +MODULE_EXPORT int +serdisplib_init(Driver *drvthis) +{ + PrivateData *p; + int ret; + struct lcdgraphic_settings settings; + const char *s; + + // Alocate and store private data + p = (PrivateData *) malloc(sizeof(PrivateData)); + if (!p) + return -1; + if (drvthis->store_private_ptr(drvthis, p)) + return -1; + memset(p, 0, sizeof(PrivateData)); + + // get the display name + s = drvthis->config_get_string(drvthis->name, "display_name", 0, NULL); + if (!s) { + report(RPT_ERR, "You must specify display_name in configuration"); + goto out; + } + strncpy(p->display_name, s, SERDISPLIB_MAX_DISPLAYNAME); + p->display_name[SERDISPLIB_MAX_DISPLAYNAME-1] = 0; + + // get the display device + s = drvthis->config_get_string(drvthis->name, "display_device", 0, NULL); + if (!s) { + report(RPT_ERR, "You must specify display_device in configuration"); + goto out; + } + strncpy(p->display_device, s, SERDISPLIB_MAX_DEVICENAME); + p->display_device[SERDISPLIB_MAX_DEVICENAME-1] = 0; + + // get the normal font + s = drvthis->config_get_string(drvthis->name, "normal_font", 0, NULL); + if (!s) { + report(RPT_ERR, "You must specify normal_font in configuration"); + goto out; + } + strncpy(settings.normal_font, s, PATH_MAX); + settings.normal_font[PATH_MAX-1] = 0; + + // character size + s = drvthis->config_get_string(drvthis->name, "char_size", 0, "6x10"); + if (!s) { + report(RPT_ERR, "Could not retrieve char_size from configuration"); + goto out; + } + if (sscanf(s, "%dx%d", &settings.cwidth, &settings.cheight) != 2) { + report(RPT_ERR, "Could not scan '%s' correctly", s); + goto out; + } + + // border size + s = drvthis->config_get_string(drvthis->name, "border_size", 0, "0x0"); + if (!s) { + report(RPT_ERR, "Could not retrieve border size from configuration"); + goto out; + } + if (sscanf(s, "%dx%d", &settings.bwidth, &settings.bheight) != 2) { + report(RPT_ERR, "Could not scan '%s' correctly", s); + goto out; + } + + // invert + p->invert = drvthis->config_get_bool(drvthis->name, "invert", 0, 1); + + + /* End of config file parsing */ + + // opening the output device */ + p->serdisplib_conn = SDCONN_open(p->display_device); + if (!p->serdisplib_conn) { + report(RPT_ERR, "Could not open %s: %s", p->display_device, + sd_geterrormsg()); + goto out; + } + + // opening and initialising the display + p->serdisplib = serdisp_init(p->serdisplib_conn, p->display_name, ""); + if (!p->serdisplib) { + report(RPT_ERR, "Error opening display %s: %s\n", p->display_name, + sd_geterrormsg()); + goto out; + } + + // invert settings + serdisp_setoption(p->serdisplib, "INVERT", p->invert + ? SD_OPTION_YES : SD_OPTION_NO); + + // allocate lcdgraphic handle + p->lcdgraphic = (struct lcdgraphic *)malloc(sizeof(struct lcdgraphic)); + if (!p->lcdgraphic) { + report(RPT_ERR, "Not enough memory to allocate struct lcdgraphic"); + goto out; + } + + // register at the lcdgraphic backend + settings.width = serdisp_getwidth(p->serdisplib); + settings.height = serdisp_getheight(p->serdisplib); + settings.drv = drvthis; + + ret = lcdgraphic_init(p->lcdgraphic, &settings, &lcdgraphic_functions); + if (ret != 0) { + report(RPT_ERR, "Error registering at lcdgraphic subsystem"); + goto out; + } + + return 0; + +out: + if (p->serdisplib_conn && !p->serdisplib) + SDCONN_close(p->serdisplib_conn); + if (p->serdisplib) + serdisp_quit(p->serdisplib); + drvthis->store_private_ptr(drvthis, NULL); + free(p); + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Clean-up +// +MODULE_EXPORT void +serdisplib_close(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if (p) { + if (p->serdisplib) + serdisp_quit(p->serdisplib); + if (p->lcdgraphic) + lcdgraphic_destroy(p->lcdgraphic); + } + drvthis->store_private_ptr(drvthis, NULL); + free(p); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Returns the display width +// +MODULE_EXPORT int +serdisplib_width (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + return lcdgraphic_width(p->lcdgraphic); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Returns the display height +// +MODULE_EXPORT int +serdisplib_height (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + return lcdgraphic_height(p->lcdgraphic); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Clear the framebuffer +// +MODULE_EXPORT void +serdisplib_clear (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_clear(p->lcdgraphic); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Place a character in the framebuffer +// +MODULE_EXPORT void +serdisplib_chr (Driver *drvthis, int x, int y, char ch) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_draw_char(p->lcdgraphic, x - 1, y - 1, ch); +} + +/////////////////////////////////////////////////////////////////////////////// +// Place a string in the framebuffer +// +MODULE_EXPORT void +serdisplib_string (Driver *drvthis, int x, int y, char *s) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_draw_string(p->lcdgraphic, x - 1, y - 1, s); +} + +/////////////////////////////////////////////////////////////////////////////// +// Place a big number in the framebuffer +// +MODULE_EXPORT void +serdisplib_num (Driver *drvthis, int x, int num) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_draw_num(p->lcdgraphic, x - 1, num); +} + +/////////////////////////////////////////////////////////////////////////////// +// Sets the backlight on or off +// +MODULE_EXPORT void +serdisplib_backlight (Driver *drvthis, int on) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + serdisp_setoption(p->serdisplib, "BACKLIGHT", on ? SD_OPTION_YES : SD_OPTION_NO); +} + +/////////////////////////////////////////////////////////////////////////////// +// Flush the framebuffer to the display +// +MODULE_EXPORT void +serdisplib_flush(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_flush(p->lcdgraphic); +} + +/////////////////////////////////////////////////////////////////////////////// +// Set default icon into a userdef char +// +MODULE_EXPORT int +serdisplib_icon(Driver *drvthis, int x, int y, int icon) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + return lcdgraphic_icon(p->lcdgraphic, x - 1, y - 1, icon); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a horizontal bar +// +MODULE_EXPORT void +serdisplib_hbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_hbar(p->lcdgraphic, x - 1, y - 1, len, promille, options); +} + +/////////////////////////////////////////////////////////////////////////////// +// Draws a vertical bar +// +MODULE_EXPORT void +serdisplib_vbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + lcdgraphic_vbar(p->lcdgraphic, x - 1, y - 1, len, promille, options); +} + + +// vim: set sw=4 ts=4 et: --- a/LCDd.conf +++ b/LCDd.conf @@ -40,7 +40,8 @@ # EyeboxOne, g15, glcdlib, glk, hd44780, icp_a106, imon, IOWarrior, # irman, joy, lb216, lcdm001, lcterm, lirc, MD8800, ms6931, mtc_s16209x, # MtxOrb, NoritakeVFD, picolcd, pyramid, sed1330, sed1520, serialPOS, -# serialVFD, sli, stv5730, svga, t6963, text, tyan, ula200, xosd +# serialVFD, sli, stv5730, svga, t6963, text, tyan, ula200, serdisplib, +# xosd Driver=curses # Tells the driver to bind to the given interface @@ -931,6 +932,31 @@ Size=20x4 # KeyMap_E=Enter # KeyMap_F=Escape +## serdisplib meta-driver for dot-matrix displays ## +[serdisplib] + +# the underlying serdisplib driver, e.g. ctinclud +display_name=ctinclud + +# the display device, e.g. serraw:/dev/ttyS0, parport:/dev/parport0 +# or USB:07c0/1501 (vendor ID, device ID) +display_device=USB:07c0/1501 + +# the font which is used for rendering, this font must be monospace +# and should contain some special Unicode characters like arrows +# (Andale Mono is recommended and can be fetched at +# http://corefonts.sf.net or from a Windows installation) +normal_font=/usr/share/fonts/truetype/andalemo.ttf + +# invert the display (black => white, white => black) +invert=0 + +# the size of one characters, the number of characters is calculated +# automatically from this and from border_size (see below) +char_size=6x10 + +# size of the border around the drawing area +border_size=0 ## Wirz SLI LCD driver ##