diff --git a/ChangeLog b/ChangeLog index 3d420aa..bac1a90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,8 @@ v0.5dev (ongoing development) + new driver: vlsys_m428 driver for Moneual MonCaso 320 (W. Hauck) * hd44780/serial: Fix handling of keys for LoS-Panel (M. Kirchner) * hd44780/serial: Change backlight handling (see commit message) + + glcd driver: Add FreeType 2 rendering support (B. Walle and M. Dolze) + + glcd driver: New connection type PNG, writes framebuffer as PNG image v0.5.5 + sed1330 driver: Add support for HG25504 (L. Lagendijk) diff --git a/LCDd.conf b/LCDd.conf index ef588ad..1e8ef9c 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -378,6 +378,20 @@ ConnectionType=t6963 # legal: yes, no] #delayBus=no +# If LCDproc has been compiled with FreeType 2 support this option can be used +# to turn if off intentionally. [default: yes; legal: yes, no] +#useFT2=no + +# Path to font file to use for FreeType 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). +normal_font=/usr/local/lib/X11/fonts/TTF/andalemo.ttf + +# Some fonts miss the Unicode characters used to represent icons. In this case +# the built-in 5x8 font can used if this option is turned off. [default: yes; +# legal: yes, no] +#fontHasIcons=no + ## glcdlib meta driver for graphical LCDs ## diff --git a/acinclude.m4 b/acinclude.m4 index ad734c8..a321c6e 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -170,6 +170,9 @@ dnl else if test "$ac_cv_port_have_lpt" = yes ; then GLCD_DRIVERS="$GLCD_DRIVERS glcd-t6963.o t6963_low.o" fi + if test "$enable_libpng" = yes ; then + GLCD_DRIVERS="$GLCD_DRIVERS glcd-glcd-png.o" + fi DRIVERS="$DRIVERS glcd${SO}" actdrivers=["$actdrivers glcd"] ;; @@ -1057,7 +1060,7 @@ AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, [AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])]) dnl -dnl Check if system has SA_RESTART defined. Copied from GNU's make configure. +dnl Check if system has SA_RESTART defined. Copied from GNUs make configure. dnl AC_DEFUN([LCD_SA_RESTART], [ AC_CACHE_CHECK([for SA_RESTART], lcd_cv_sa_restart, [ @@ -1070,3 +1073,53 @@ if test "$lcd_cv_sa_restart" != no; then [Define to 1 if defines the SA_RESTART constant.]) fi ]) + +dnl +dnl Check for PNG library +dnl +AC_DEFUN([LCD_PNG_LIB], [ +AC_MSG_CHECKING([if PNG support has been enabled]); +AC_ARG_ENABLE(libpng, + [AS_HELP_STRING([--disable-png],[disable PNG support using libpng])], + [ if test "$enableval" != "no"; then + enable_libpng=yes + fi ], + [ enable_libpng=yes ] +) +AC_MSG_RESULT($enable_libpng) + +if test "$enable_libpng" = "yes"; then + AC_PATH_PROG([_png_config], [libpng-config]) + _libpng_save_libs=$LIBS + _libpng_save_cflags=$CFLAGS + + if test x$_png_config != "x" ; then + _libpng_try_libs=`$_png_config --ldflags` + _libpng_try_cflags=`$_png_config --cflags` + fi + + LIBS="$LIBS $_libpng_try_libs" + CFLAGS="$CFLAGS $_libpng_try_cflags" + + AC_MSG_CHECKING([whether libpng is present and sane]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ],[ + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + ])],enable_libpng=yes,enable_libpng=no) + AC_MSG_RESULT([$enable_libpng]) + + if test "$enable_libpng" = "yes" ; then + AC_DEFINE(HAVE_LIBPNG, [1], [Define to 1 if you have libpng]) + AC_SUBST(LIBPNG_CFLAGS, $_libpng_try_cflags) + AC_SUBST(LIBPNG_LIBS, $_libpng_try_libs) + fi + + LIBS=$_libpng_save_libs + CFLAGS=$_libpng_save_cflags + + unset _libpng_save_libs + unset _libpng_save_cflags + unset _libpng_try_libs + unset _libpng_try_cflags + unset _png_config +fi +]) diff --git a/configure.in b/configure.in index cd7055e..0676f3e 100644 --- a/configure.in +++ b/configure.in @@ -254,7 +254,7 @@ AC_MSG_CHECKING([if libusb support has been enabled]); AC_ARG_ENABLE(libusb, [AS_HELP_STRING([--disable-libusb],[disable USB support using libusb])], [ if test "$enableval" != "no"; then - enable_libusb="yes" + enable_libusb=yes fi ], [ enable_libusb=yes ] ) @@ -316,6 +316,30 @@ fi AC_SUBST(LIBHID_LIBS) AC_SUBST(LIBHID_CFLAGS) +# PNG library +LCD_PNG_LIB + +dnl ###################################################################### +dnl freetype support +dnl ###################################################################### +AC_MSG_CHECKING([if freetype support has been enabled]); +AC_ARG_ENABLE(freetype, + [AS_HELP_STRING([--disable-freetype],[disable freetype support])], + [ if test "$enableval" != "no"; then + enable_freetype=yes + fi ], + [ enable_freetype=yes ] +) +AC_MSG_RESULT($enable_freetype) + +if test "$enable_freetype" = "yes"; then + ifdef([AC_CHECK_FT2], + [AC_CHECK_FT2([], + [AC_DEFINE(HAVE_FT2, [1], [Define to 1 if you have freetype])], + [enable_freetype=no])], + [AC_MSG_WARN([freetype does not seem to be installed])]) +fi + dnl ###################################################################### dnl ethlcd support dnl ###################################################################### diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index 937e656..a695916 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -26,6 +26,7 @@ EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g1 noinst_LIBRARIES = libLCD.a libbignum.a g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS) +glcd_CFLAGS = @FT2_CFLAGS@ @LIBPNG_CFLAGS@ $(AM_CFLAGS) hd44780_CFLAGS = @LIBUSB_CFLAGS@ @LIBFTDI_CFLAGS@ $(AM_CFLAGS) i2500vfd_CFLAGS = @LIBFTDI_CFLAGS@ $(AM_CFLAGS) IOWarrior_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS) @@ -42,8 +43,8 @@ CFontzPacket_LDADD = libLCD.a libbignum.a curses_LDADD = @LIBCURSES@ CwLnx_LDADD = libLCD.a libbignum.a g15_LDADD = @LIBG15@ -glcd_LDADD = libLCD.a @GLCD_DRIVERS@ -glcd_DEPENDENCIES = @GLCD_DRIVERS@ +glcd_LDADD = libLCD.a @GLCD_DRIVERS@ @FT2_LIBS@ @LIBPNG_LIBS@ +glcd_DEPENDENCIES = @GLCD_DRIVERS@ glcd-glcd-render.o glcdlib_LDADD = @LIBGLCD@ glk_LDADD = libbignum.a hd44780_LDADD = libLCD.a @HD44780_DRIVERS@ @LIBUSB_LIBS@ @LIBFTDI_LIBS@ libbignum.a @@ -87,8 +88,8 @@ debug_SOURCES = lcd.h report.h debug.c debug.h ea65_SOURCES = lcd.h ea65.h ea65.c report.h EyeboxOne_SOURCES = lcd.h lcd_lib.h EyeboxOne.c EyeboxOne.h report.h g15_SOURCES = lcd.h lcd_lib.h g15.h g15-num.c g15.c report.h -glcd_SOURCES = lcd.h report.h glcd_drv.c glcd_drv.h glcd-low.h glcd-drivers.h -EXTRA_glcd_SOURCES = glcd-t6963.c glcd-t6963.h t6963_low.c t6963_low.h +glcd_SOURCES = lcd.h report.h glcd_drv.c glcd_drv.h glcd-low.h glcd-drivers.h glcd-render.c glcd-render.h +EXTRA_glcd_SOURCES = glcd-t6963.c glcd-t6963.h t6963_low.c t6963_low.h glcd-png.c glcd-png.h glcdlib_SOURCES = lcd.h lcd_lib.h glcdlib.h glcdlib.c report.h glk_SOURCES = lcd.h glk.c glk.h glkproto.c glkproto.h report.h hd44780_SOURCES = lcd.h lcd_lib.h hd44780.h hd44780.c hd44780-drivers.h hd44780-low.h hd44780-charmap.h report.h adv_bignum.h diff --git a/server/drivers/glcd-drivers.h b/server/drivers/glcd-drivers.h index 522bfa7..affb708 100644 --- a/server/drivers/glcd-drivers.h +++ b/server/drivers/glcd-drivers.h @@ -16,10 +16,14 @@ #ifdef HAVE_PCSTYLE_LPT_CONTROL # include "glcd-t6963.h" #endif +#ifdef HAVE_LIBPNG +# include "glcd-png.h" +#endif /* symbolic names for connection types */ #define GLCD_CT_UNKNOWN 0 #define GLCD_CT_T6963 1 +#define GLCD_CT_PNG 2 /** Structure linking symbolic names to initialization routines */ typedef struct ConnectionMapping { @@ -37,6 +41,9 @@ typedef struct ConnectionMapping { static const ConnectionMapping connectionMapping[] = { #ifdef HAVE_PCSTYLE_LPT_CONTROL {"t6963", GLCD_CT_T6963, glcd_t6963_init}, +#endif +#ifdef HAVE_LIBPNG + {"png", GLCD_CT_PNG, glcd_png_init}, #endif /* default, end of structure element (do not delete) */ {NULL, GLCD_CT_UNKNOWN, NULL} diff --git a/server/drivers/glcd-low.h b/server/drivers/glcd-low.h index 8dceecc..2d6ffda 100644 --- a/server/drivers/glcd-low.h +++ b/server/drivers/glcd-low.h @@ -14,8 +14,6 @@ #define GLCD_MAX_WIDTH 640 #define GLCD_MAX_HEIGHT 480 -#define BYTES_PER_LINE (p->px_width / 8) - /** private data for the \c glcd driver */ typedef struct glcd_private_data { unsigned char *framebuf; /**< frame buffer */ @@ -27,6 +25,8 @@ typedef struct glcd_private_data { int height; /**< display height in characters */ struct hwDependentFns *glcd_functions; /**< pointers to low-level functions */ void *ct_data; /**< Connection type specific data */ + void *render_config; /**< Settings for the font renderer */ + char use_ft2; /**< (Not) use FreeType if available */ } PrivateData; /** Structure holding pointers to display specific functions */ @@ -51,4 +51,37 @@ typedef struct hwDependentFns { void (*close)(PrivateData *p); } GLCD_functions; +/* ================== Framebuffer functions and macros =================== */ + +#define BYTES_PER_LINE (p->px_width / 8) +#define FB_BYTES_TOTAL (p->px_height * BYTES_PER_LINE) + +/** + * Draw one pixel into the framebuffer using 1bpp (black and white). This + * function actually decides about the format of the framebuffer. Using this + * implementation (0,0) is top left and bytes contain pixels from left to right. + * + * \param p Pointer to driver's private data. + * \param x X-position + * \param y Y-position + * \param color Pixel color: 1 = set (black), 0 = not set (blank/white) + */ +static inline void +fb_draw_pixel(PrivateData *p, int x, int y, int color) +{ + unsigned int pos; /* Byte within the framebuffer */ + unsigned char bit; /* Bit within the framebuffer byte */ + + if (x < 0 || x >= p->px_width || y < 0 || y >= p->px_height) + return; + + pos = y * BYTES_PER_LINE + (x / 8); + bit = 0x80 >> (x % 8); + + if (color == 1) + p->framebuf[pos] |= bit; + else + p->framebuf[pos] &= ~bit; +} + #endif diff --git a/server/drivers/glcd-png.c b/server/drivers/glcd-png.c new file mode 100644 index 0000000..c1730e1 --- /dev/null +++ b/server/drivers/glcd-png.c @@ -0,0 +1,171 @@ +/** \file server/drivers/glcd-png.c + * This driver writes the framebuffer content to PNG images as + * /tmp/lcdproc######.png. + */ + +/*- + * Copyright (c) 2011 Markus Dolze + * + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lcd.h" +#include "report.h" +#include "glcd-low.h" +#include "glcd-png.h" + +/** Private data for the PNG connection type */ +typedef struct glcd_png_data { + unsigned char *backingstore; /**< backing buffer */ +} CT_png_data; + +/** + * API: Initialize the connection type driver. + * \param drvthis Pointer to driver structure. + * \retval 0 Success. + * \retval <0 Error. + */ +int +glcd_png_init(Driver *drvthis) +{ + PrivateData *p = (PrivateData *)drvthis->private_data; + CT_png_data *ct_data; + + report(RPT_INFO, "GLCD/png: intializing"); + + /* Set up connection type low-level functions */ + p->glcd_functions->blit = glcd_png_blit; + p->glcd_functions->close = glcd_png_close; + + /* Allocate memory structures */ + ct_data = (CT_png_data *) calloc(1, sizeof(CT_png_data)); + if (ct_data == NULL) { + report(RPT_ERR, "GLCD/png: error allocating connection data"); + return -1; + } + p->ct_data = ct_data; + + ct_data->backingstore = malloc(FB_BYTES_TOTAL); + if (ct_data->backingstore == NULL) { + report(RPT_ERR, "GLCD/png: unable to allocate backing store"); + return -1; + } + memset(ct_data->backingstore, 0x00, FB_BYTES_TOTAL); + + debug(RPT_DEBUG, "GLCD/png: init() done"); + + return 0; +} + +/** + * API: Write the framebuffer to the display + * \param p Pointer to glcd driver's private date structure. + */ +void +glcd_png_blit(PrivateData *p) +{ + CT_png_data *ct_data = (CT_png_data *) p->ct_data; + char filename[256]; + static int num = 0; + int row; + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_bytep row_pointer; + + /* Check if framebufer has changed. If not there's nothing to do */ + if (memcmp(p->framebuf, ct_data->backingstore, FB_BYTES_TOTAL) == 0) + return; + + snprintf(filename, sizeof(filename), "/tmp/lcdproc%06d.png", num++); + fp = fopen(filename, "wb"); + if (!fp) { + p->glcd_functions->drv_debug(RPT_ERR, "File %s could not be opened for writing", filename); + return; + } + + /* initialize stuff */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + p->glcd_functions->drv_debug(RPT_ERR, "png_create_write_struct failed"); + fclose(fp); + return; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + p->glcd_functions->drv_debug(RPT_ERR, "png_create_info_struct failed"); + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + fclose(fp); + return; + } + + if (setjmp(png_jmpbuf(png_ptr))) + goto err_out; + + png_init_io(png_ptr, fp); + + png_set_IHDR(png_ptr, info_ptr, p->px_width, p->px_height, + 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_set_invert_mono(png_ptr); + + png_write_info(png_ptr, info_ptr); + + /* Write the image row by row */ + row_pointer = p->framebuf; + for (row = 0; row < p->px_height; row++) { + png_write_row(png_ptr, row_pointer); + row_pointer += BYTES_PER_LINE; + } + + png_write_end(png_ptr, NULL); + fclose(fp); + fp = NULL; + png_destroy_write_struct(&png_ptr, &info_ptr); + + memcpy(ct_data->backingstore, p->framebuf, FB_BYTES_TOTAL); + + return; + +err_out: + p->glcd_functions->drv_debug(RPT_ERR, "Error writing PNG image"); + if (fp != NULL) + fclose(fp); + if (png_ptr != NULL) + png_destroy_write_struct(&png_ptr, &info_ptr); + return; +} + +/** + * API: Release low-level resources. + * \param p Pointer to glcd driver's private date structure. + */ +void +glcd_png_close(PrivateData *p) +{ + if (p->ct_data != NULL) { + CT_png_data *ct_data = (CT_png_data *) p->ct_data; + + if (ct_data->backingstore != NULL) + free(ct_data->backingstore); + + free(p->ct_data); + p->ct_data = NULL; + } +} diff --git a/server/drivers/glcd-png.h b/server/drivers/glcd-png.h new file mode 100644 index 0000000..9cbea5e --- /dev/null +++ b/server/drivers/glcd-png.h @@ -0,0 +1,8 @@ +#ifndef GLCD_PNG_H +#define GLCD_PNG_H + +int glcd_png_init(Driver *drvthis); +void glcd_png_blit(PrivateData *p); +void glcd_png_close(PrivateData *p); + +#endif diff --git a/server/drivers/glcd-render.c b/server/drivers/glcd-render.c new file mode 100644 index 0000000..e561eae --- /dev/null +++ b/server/drivers/glcd-render.c @@ -0,0 +1,348 @@ +/** \file server/drivers/glcd-render.c + * Render glyphs to a framebuffer using FreeType2 or fixed 5x8 font. + */ + +/*- + * Copyright (c) 2010 Bernhard Walle + * 2011 Markus Dolze + * + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef HAVE_FT2 +#include +#include FT_FREETYPE_H +#endif + +#include "lcd.h" +#include "report.h" +#include "glcd-low.h" +#include "glcd-render.h" +#include "glcd_font5x8.h" +#include "shared/defines.h" + +#ifdef HAVE_FT2 +/** Configuration for the Freetype renderer */ +typedef struct glcd_render_data { + FT_Library ft_library; /**< freetype library handle */ + FT_Face ft_normal_font; /**< handle for the normal font */ + char ft_has_icons; /**< flag is the font has icons */ +} RenderConfig; + +static int icon2unicode(int icon); +#endif + + +/** + * Initializes rendering code. Any rendering related configuration settings + * from LCDd.conf should be read here. + * + * \param drvthis Pointer to driver + * \return 0 on success, -1 on error + * + * \note This function must be implemented, even if not compiled with Freetype! + */ +int +glcd_render_init(Driver *drvthis) +{ +#ifdef HAVE_FT2 + PrivateData *p = drvthis->private_data; + int rc; + const char *tmp; + char font_file[255]; + RenderConfig *rconf; + + debug(RPT_INFO, "%s(): Freetype", __FUNCTION__); + + /* Allocate memory structures */ + rconf = (RenderConfig *) calloc(1, sizeof(RenderConfig)); + if (rconf == NULL) { + report(RPT_ERR, "%s: error allocating rendering config", drvthis->name); + return -1; + } + p->render_config = rconf; + + /* use_ft2 is available in PrivateDate for easy use! */ + p->use_ft2 = drvthis->config_get_bool(drvthis->name, "useFT2", 0, 1); + + /* Only configure FreeType if enabled */ + if (p->use_ft2) { + /* get font from config file */ + tmp = drvthis->config_get_string(drvthis->name, "normal_font", 0, NULL); + if (tmp == NULL) { + report(RPT_ERR, "%s: normal_font missing in configuration", drvthis->name); + goto err_out; + } + strncpy(font_file, tmp, sizeof(font_file)); + font_file[sizeof(font_file) - 1] = '\0'; + + /* initialize FreeType library */ + rc = FT_Init_FreeType(&rconf->ft_library); + if (rc != 0) { + report(RPT_ERR, "s: Freetype initialisation failed", drvthis->name); + goto err_out; + } + + /* load the font face for freetype */ + rc = FT_New_Face(rconf->ft_library, font_file, 0, &rconf->ft_normal_font); + if (rc != 0) { + report(RPT_ERR, "s: Creation of font '%s' failed", drvthis->name, font_file); + goto err_out; + } + + /* If the font does not have icons use default 5x8 font */ + rconf->ft_has_icons = drvthis->config_get_bool(drvthis->name, "fontHasIcons", 0, 1); + } +#endif + + debug(RPT_INFO, "%s() successful", __FUNCTION__); + return 0; + +#ifdef HAVE_FT2 +err_out: + glcd_render_close(drvthis); + return -1; +#endif +} + + +/** + * Releases on resources used by the rendering code. + * + * \param drvthis Pointer to driver + * \note This function must be implemented, even if not compiled with Freetype! + */ +void +glcd_render_close(Driver *drvthis) +{ +#ifdef HAVE_FT2 + PrivateData *p = drvthis->private_data; + RenderConfig *rconf = p->render_config; + + if (rconf != NULL) { + if (rconf->ft_normal_font != NULL) + FT_Done_Face(rconf->ft_normal_font); + if (rconf->ft_library != NULL) + FT_Done_FreeType(rconf->ft_library); + + free(rconf); + p->render_config = NULL; + } +#endif +} + + +#ifdef HAVE_FT2 +/** + * Draws character c to the framebuffer at position x,y using Freetype 2 for + * font rendering. Top left corner is (1/1). + * + * \param drvthis Pointer to driver structure. + * \param x Horizontal character position (column). + * \param y Vertical character position (row). + * \param c Character that gets written. + */ +void +glcd_render_char_unicode(Driver *drvthis, int x, int y, int c) +{ + static int last_font_size = -1; + PrivateData *p = drvthis->private_data; + RenderConfig *rconf = p->render_config; + int font_size; + int col, row; /* Position in the font bitmap */ + int px, py; /* Pixel position on the display */ + int rc; + FT_Face face; + FT_GlyphSlot glyph; + FT_Bitmap *bitmap; + unsigned char *bitmap_buf; + + if (x < 1 || x > p->width || y < 1 || y > p->height) + return; + + x--; /* convert coordinates to zero-based */ + y--; + + /* set the font size */ + font_size = max(p->cellheight, p->cellwidth); + if (last_font_size != font_size) { + rc = FT_Set_Pixel_Sizes(rconf->ft_normal_font, font_size, font_size); + if (rc != 0) { + report(RPT_ERR, "%s: Failed to set pixel size (%dx%x)", drvthis->name, + p->cellwidth, p->cellheight); + return; + } + + last_font_size = font_size; + } + + /* load the glyph and render it */ + rc = FT_Load_Char(rconf->ft_normal_font, c, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); + if (rc != 0) { + report(RPT_ERR, "%s: loading char '%c' (0x%x) failed", drvthis->name, c, c); + return; + } + + /* set some data elements for convenience */ + face = rconf->ft_normal_font; + glyph = rconf->ft_normal_font->glyph; + bitmap = &glyph->bitmap; + bitmap_buf = bitmap->buffer; + + py = y * p->cellheight; + /* clear the cell */ + for (row = 0; row < p->cellheight; row++, py++) { + px = x * p->cellwidth; + for (col = 0; col < p->cellwidth; col++, px++) { + fb_draw_pixel(p, px, py, 0); + } + } + + /* + * Note: the font metrics may result in negative py value! So protect + * it by restricting it to 0. + */ + py = max((y + 1) * p->cellheight + (face->size->metrics.descender >> 6) - glyph->bitmap_top, 0); + /* and now copy the pixels */ + for (row = 0; (row < bitmap->rows) && (row < p->cellheight); row++) { + px = x * p->cellwidth + glyph->bitmap_left; + for (col = 0; col < bitmap->width; col++) { + fb_draw_pixel(p, px, py, bitmap_buf[col / 8] >> (7 - (col % 8)) & 1); + px++; + } + bitmap_buf += bitmap->pitch; + py++; + } +} +#endif + +/** + * Draws char c from our standard 5x8 font definition to the framebuffer at + * position x,y. Top left corner is (1/1). + * + * \param drvthis Pointer to driver structure. + * \param x Horizontal character position (column). + * \param y Vertical character position (row). + * \param c Character that gets written. + */ +void +glcd_render_char(Driver *drvthis, int x, int y, unsigned char c) +{ + PrivateData *p = drvthis->private_data; + int font_x, font_y; /* Position in the font definition array */ + int px, py; /* Pixel position on the display */ + + if (x < 1 || x > p->width || y < 1 || y > p->height) + return; + + x--; /* convert coordinates to zero-based */ + y--; + + /* + * Algorithm: For each row in the font definition check if bit (dot) + * at each column is set. If yes, plot a dot into the framebuffer. If + * the bit is not set then clear the dot. Currently it is wrong to + * assume the framebuffer is clear (e.g. the heartbeat does not clear + * it's contents in advance). + */ + /* FIXME: What happens if font is larger than cell size? */ + py = y * p->cellheight; + for (font_y = 0; font_y < GLCD_FONT_HEIGHT; font_y++) { + px = x * p->cellwidth; + /* + * Note: Initializing font_x with font's width leaves one + * empty column to the left. + */ + for (font_x = GLCD_FONT_WIDTH; font_x >= 0; font_x--) { + if (glcd_iso8859_1[c][font_y] & (1 << font_x)) + fb_draw_pixel(p, px, py, 1); + else + fb_draw_pixel(p, px, py, 0); + px++; + } + py++; + } +} + + +#ifdef HAVE_FT2 +/** + * Maps a LCDproc icon to an Unicode codepoint for an icon. + * + * \param icon The LCDproc icon constant + * \return Unicode value + */ +static int +icon2unicode(int icon) +{ + switch (icon) { + case ICON_BLOCK_FILLED: + return 0x2588; /* Full block */ + case ICON_HEART_FILLED: + return 0x2665; /* Black heart suit */ + case ICON_HEART_OPEN: + return 0x2661; /* White heart suit */ + case ICON_ARROW_UP: + return 0x2191; /* Upwards arrow */ + case ICON_ARROW_DOWN: + return 0x2193; /* Downwards arrow */ + case ICON_ARROW_LEFT: + return 0x2190; /* Leftwards arrow */ + case ICON_ARROW_RIGHT: + return 0x2192; /* Rightwards arrow */ + case ICON_CHECKBOX_OFF: + return 0x2610; /* Ballot box */ + case ICON_CHECKBOX_ON: + return 0x2611; /* Ballot box with check */ + case ICON_CHECKBOX_GRAY: + return 0x2612; /* Ballot box with x */ + case ICON_ELLIPSIS: + return 0x2026; /* Horizontal ellipsis */ + default: + return -1; + } +} +#endif + + +/** + * Place an icon on the screen using either our built-in standard font or + * an Unicode icon rendered with Freetype. + * + * \param drvthis Pointer to driver structure. + * \param x Horizontal character position (column). + * \param y Vertical character position (row). + * \param icon synbolic value representing the icon. + * \retval 0 Icon has been successfully defined/written. + * \retval <0 Server core shall define/write the icon. + */ +int +glcd_render_icon(Driver *drvthis, int x, int y, int icon) +{ + int icon_char; + +#ifdef HAVE_FT2 + PrivateData *p = drvthis->private_data; + RenderConfig *rconf = p->render_config; + + if (p->use_ft2 && rconf->ft_has_icons) { + if ((icon_char = icon2unicode(icon)) != -1) { + glcd_render_char_unicode(drvthis, x, y, icon_char); + return 0; + } + return -1; + } +#endif + if ((icon_char = glcd_icon5x8(icon)) != -1) { + glcd_render_char(drvthis, x, y, icon_char); + return 0; + } + return -1; +} diff --git a/server/drivers/glcd-render.h b/server/drivers/glcd-render.h new file mode 100644 index 0000000..efd7df7 --- /dev/null +++ b/server/drivers/glcd-render.h @@ -0,0 +1,13 @@ +#ifndef GLCD_RENDER_H +#define GLCD_RENDER_H + +int glcd_render_init(Driver *drvthis); +void glcd_render_close(Driver *drvthis); +void glcd_render_char(Driver *drvthis, int x, int y, unsigned char c); +int glcd_render_icon(Driver *drvthis, int x, int y, int icon); + +#ifdef HAVE_FT2 +void glcd_render_char_unicode(Driver *drvthis, int x, int y, int c); +#endif + +#endif diff --git a/server/drivers/glcd-t6963.c b/server/drivers/glcd-t6963.c index fe28128..573d197 100644 --- a/server/drivers/glcd-t6963.c +++ b/server/drivers/glcd-t6963.c @@ -67,7 +67,7 @@ glcd_t6963_init(Driver *drvthis) /* Allocate memory structures */ ct_data = (CT_t6963_data *) calloc(1, sizeof(CT_t6963_data)); if (ct_data == NULL) { - report(RPT_ERR, "GLCD/T6963: error allocation connection data"); + report(RPT_ERR, "GLCD/T6963: error allocating connection data"); return -1; } p->ct_data = ct_data; @@ -79,12 +79,12 @@ glcd_t6963_init(Driver *drvthis) } ct_data->port_config = port_config; - ct_data->backingstore = malloc(BYTES_PER_LINE * p->px_height); + ct_data->backingstore = malloc(FB_BYTES_TOTAL); if (ct_data->backingstore == NULL) { report(RPT_ERR, "GLCD/T6963: unable to allocate backing store"); return -1; } - memset(ct_data->backingstore, 0x00, BYTES_PER_LINE * p->px_height); + memset(ct_data->backingstore, 0x00, FB_BYTES_TOTAL); /* Get port from config */ port_config->port = drvthis->config_get_int(drvthis->name, "Port", 0, DEFAULT_PORT); @@ -189,6 +189,7 @@ glcd_t6963_close(PrivateData *p) free(ct_data->backingstore); free(p->ct_data); + p->ct_data = NULL; } } @@ -201,7 +202,7 @@ static void t6963_graphic_clear(PrivateData *p) { CT_t6963_data *ct_data = (CT_t6963_data *) p->ct_data; - int num = BYTES_PER_LINE * p->px_height; + int num = FB_BYTES_TOTAL; int i; p->glcd_functions->drv_debug(RPT_DEBUG, "GLCD/T6963: Clearing graphic: %d bytes", num); diff --git a/server/drivers/glcd_drv.c b/server/drivers/glcd_drv.c index 81d3da1..546acf4 100644 --- a/server/drivers/glcd_drv.c +++ b/server/drivers/glcd_drv.c @@ -25,25 +25,15 @@ * \li glcd-drivers.h CT-driver registry. Add a pointer to your CT-driver's * init() function here. * \li glcd-low.h Base driver's PrivateData and ConnectionType API. + * \li glcd-render.c Render characters using FreeType 2 or standard font. * \li glcd_font5x8.h LCDproc's default fixed 5x8 font. */ /*- * Copyright (C) 2011, Markus Dolze * - * 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 + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. */ #ifdef HAVE_CONFIG_H @@ -60,7 +50,7 @@ #include "glcd-low.h" #include "glcd-drivers.h" #include "report.h" -#include "glcd_font5x8.h" +#include "glcd-render.h" /* Vars for the server core */ MODULE_EXPORT char *api_version = API_VERSION; @@ -68,9 +58,6 @@ MODULE_EXPORT int stay_in_foreground = 0; MODULE_EXPORT int supports_multiple = 0; MODULE_EXPORT char *symbol_prefix = "glcd_"; -static void glcd_render_char(Driver *drvthis, int x, int y, unsigned char c); -static inline void glcd_draw_pixel(PrivateData *p, int x, int y, int color); - /** * Initialize the driver. (Required) * \param drvthis Pointer to driver structure. @@ -162,12 +149,16 @@ glcd_init(Driver *drvthis) p->height = p->px_height / p->cellheight; /* Allocate framebuffer */ - p->framebuf = malloc(BYTES_PER_LINE * p->px_height); + p->framebuf = malloc(FB_BYTES_TOTAL); if (p->framebuf == NULL) { report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name); return -1; } - memset(p->framebuf, 0x00, BYTES_PER_LINE * p->px_height); + memset(p->framebuf, 0x00, FB_BYTES_TOTAL); + + /* Initialize renderer */ + if (glcd_render_init(drvthis) != 0) + return -1; glcd_clear(drvthis); @@ -192,6 +183,7 @@ glcd_close(Driver *drvthis) if (p->framebuf != NULL) free(p->framebuf); p->framebuf = NULL; + glcd_render_close(drvthis); free(p); } @@ -274,7 +266,7 @@ glcd_clear(Driver *drvthis) debug(RPT_DEBUG, "%s()", __FUNCTION__); - memset(p->framebuf, 0x00, BYTES_PER_LINE * p->px_height); + memset(p->framebuf, 0x00, FB_BYTES_TOTAL); } @@ -313,14 +305,17 @@ glcd_string(Driver *drvthis, int x, int y, const char string[]) debug(RPT_DEBUG, "%s(%i,%i,%.40s)", __FUNCTION__, x, y, string); - y--; /* Convert 1-based coords to 0-based... */ - x--; - - if ((y < 0) || (y >= p->height)) + if ((y < 1) || (y > p->height)) return; - for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) - glcd_render_char(drvthis, x, y, string[i]); + for (i = 0; (string[i] != '\0') && (x <= p->width); i++, x++) { +#ifdef HAVE_FT2 + if (p->use_ft2) + glcd_render_char_unicode(drvthis, x, y, string[i] & 0xFF); + else +#endif + glcd_render_char(drvthis, x, y, string[i]); + } } @@ -341,66 +336,81 @@ glcd_string(Driver *drvthis, int x, int y, const char string[]) MODULE_EXPORT void glcd_chr(Driver *drvthis, int x, int y, char c) { +#ifdef HAVE_FT2 + PrivateData *p = drvthis->private_data; +#endif debug(RPT_DEBUG, "%s(%i,%i,%c)", __FUNCTION__, x, y, c); - x--; - y--; - glcd_render_char(drvthis, x, y, c); +#ifdef HAVE_FT2 + if (p->use_ft2) + glcd_render_char_unicode(drvthis, x, y, c & 0xFF); + else +#endif + glcd_render_char(drvthis, x, y, c); } /** - * Place an icon on the screen. (Optional) - * \param drvthis Pointer to driver structure. - * \param x Horizontal character position (column). - * \param y Vertical character position (row). - * \param icon synbolic value representing the icon. - * \retval 0 Icon has been successfully defined/written. - * \retval <0 Server core shall define/write the icon. + * API: Place an icon on the screen. */ MODULE_EXPORT int glcd_icon(Driver *drvthis, int x, int y, int icon) { debug(RPT_DEBUG, "%s(%i,%i,%i)", __FUNCTION__, x, y, icon); - return (glcd_icon5x8(drvthis, x, y, icon)); + return (glcd_render_icon(drvthis, x, y, icon)); } - /** * API: Draws a vertical bar, from the bottom of the screen up. */ MODULE_EXPORT void glcd_vbar(Driver *drvthis, int x, int y, int len, int promille, int options) { + PrivateData *p = drvthis->private_data; + int xstart, xend, ystart, yend; + int col, row; + debug(RPT_DEBUG, "%s(%i,%i,%i,%i,%i)", __FUNCTION__, x, y, len, promille, options); - lib_vbar_static(drvthis, x, y, len, promille, options, GLCD_FONT_HEIGHT, 0x90); + + /* leave first column and top row empty */ + xstart = (x - 1) * p->cellwidth + 1; + xend = xstart + p->cellwidth - 1; + ystart = y * p->cellheight; + yend = ystart - (((long) 2 * len * p->cellheight) * promille / 2000) + 1; + + for (col = xstart; col < xend; col++) { + for (row = ystart; row > yend; row--) { + fb_draw_pixel(p, col, row, 1); + } + } } /** * API: Draws a horizontal bar to the right. - * - * \note For hBars the actual width of the font (5) has to be used instead - * of the cell width (6)! */ MODULE_EXPORT void glcd_hbar(Driver *drvthis, int x, int y, int len, int promille, int options) { - int pixels = ((long) 2 * len * GLCD_FONT_WIDTH) * promille / 2000; - int pos; + PrivateData *p = drvthis->private_data; + int xstart, xend, ystart, yend; + int col, row; debug(RPT_DEBUG, "%s(%i,%i,%i,%i,%i)", __FUNCTION__, x, y, len, promille, options); - for (pos = 0; pos < len; pos++) { - if (pixels >= GLCD_FONT_WIDTH ) - glcd_chr(drvthis, x + pos, y, 0x9e); - else if (pixels >= 1) - glcd_chr(drvthis, x + pos, y, 0x99 + pixels); - else - ; /* do nothing */ - pixels -= GLCD_FONT_WIDTH; + /* leave first column and top row empty */ + xstart = (x - 1) * p->cellwidth + 1; + xend = xstart + (((long) 2 * len * p->cellwidth) * promille / 2000) - 1; + ystart = (y - 1) * p->cellheight + 1; + yend = ystart + p->cellheight - 1; + + for (row = ystart; row < yend; row++) { + for (col = xstart; col < xend; col++) { + fb_draw_pixel(p, col, row, 1); + } } + } @@ -418,75 +428,3 @@ glcd_get_info(Driver *drvthis) return info_string; } - - -/** - * Draw one pixel into the framebuffer using 1bpp (black and white). This - * function actually decides about the format of the framebuffer. Using this - * implementation (0,0) is top left and bytes contain pixels from left to right. - * - * \param p Pointer to driver's private data. - * \param x X-position - * \param y Y-position - * \param color Pixel color: 1 = set (black), 0 = not set (blank) - */ -static inline void -glcd_draw_pixel(PrivateData *p, int x, int y, int color) -{ - unsigned int pos; /* Byte within the framebuffer */ - unsigned char bit; /* Bit within the framebuffer byte */ - - pos = y * BYTES_PER_LINE + (x / 8); - bit = 0x80 >> (x % 8); - - if (color == 1) - p->framebuf[pos] |= bit; - else - p->framebuf[pos] &= ~bit; -} - - -/** - * Draws char c from font definition to the framebuffer at position - * x,y. These are zero-based textmode positions. - * - * \param drvthis Pointer to driver structure. - * \param x Horizontal character position (column). - * \param y Vertical character position (row). - * \param c Character that gets written. - */ -static void -glcd_render_char(Driver *drvthis, int x, int y, unsigned char c) -{ - PrivateData *p = drvthis->private_data; - int font_x, font_y; /* Position in the font definition array */ - int px, py; /* Pixel position on the display */ - - if (x < 0 || x >= p->width || y < 0 || y >= p->height) - return; - - /* - * Algorithm: For each row in the font definition check if bit (dot) - * at each column is set. If yes, plot a dot into the framebuffer. If - * the bit is not set then clear the dot. Currently it is wrong to - * assume the framebuffer is clear (e.g. the heartbeat does not clear - * it's contents in advance). - */ - /* FIXME: What happens if font is larger than cell size? */ - py = y * p->cellheight; - for (font_y = 0; (font_y < GLCD_FONT_HEIGHT) && (py < p->px_height); font_y++) { - px = x * p->cellwidth; - /* - * Note: Initializing font_x with font's width leaves one - * empty column to the left. - */ - for (font_x = GLCD_FONT_WIDTH; (font_x > -1) && (px < p->px_width); font_x--) { - if (glcd_iso8859_1[c][font_y] & (1 << font_x)) - glcd_draw_pixel(p, px, py, 1); - else - glcd_draw_pixel(p, px, py, 0); - px++; - } - py++; - } -}