diff --git a/LCDd.conf b/LCDd.conf index 2882ef9..b2590f6 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -359,6 +359,37 @@ ConnectionType=t6963 # the ConnectionType. [default: 128x64; legal: 1x1 - 640x480] #Size=128x64 +# Width ond height of a character cell in pixels. This value is only used if +# the driver has been compiled with FreeType and it is enabled. Otherwise the +# default 6x8 cell is used. +#CellSize=12x16 + +# 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 + +# Set the initial contrast if supported by connection type. +# [default: 600; legal: 0 - 1000] +#Contrast=600 + +# Set brightness of the backlight if the backlight is switched 'on'. +# [default: 800; legal: 0 - 1000] +#Brightness=1000 + +# Set brightness of the backlight if the backlight is switched 'off'. Set this +# to zero to completely turn off the backlight. [default: 100; legal: 0 - 1000] +#OffBrightness=0 + # t6963: Parallel port to use [default: 0x378; legal: 0x200 - 0x400] #Port=0x378 @@ -370,20 +401,6 @@ 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/server/drivers/glcd-low.h b/server/drivers/glcd-low.h index 2d6ffda..2013ebd 100644 --- a/server/drivers/glcd-low.h +++ b/server/drivers/glcd-low.h @@ -13,6 +13,9 @@ #define GLCD_DEFAULT_CELLHEIGHT 8 #define GLCD_MAX_WIDTH 640 #define GLCD_MAX_HEIGHT 480 +#define GLCD_DEFAULT_CONTRAST 600 +#define GLCD_DEFAULT_BRIGHTNESS 800 +#define GLCD_DEFAULT_OFFBRIGHTNESS 100 /** private data for the \c glcd driver */ typedef struct glcd_private_data { @@ -23,6 +26,11 @@ typedef struct glcd_private_data { int cellheight; /**< character cell height */ int width; /**< display width in characters */ int height; /**< display height in characters */ + int contrast; /**< current contrast */ + int brightness; /**< current brightness (for backlight on) */ + int offbrightness; /**< current brightness (for backlight off) */ + int last_output_state; /**< cache last value to output() */ + int backlightstate; /**< state of backlight currently used */ struct hwDependentFns *glcd_functions; /**< pointers to low-level functions */ void *ct_data; /**< Connection type specific data */ void *render_config; /**< Settings for the font renderer */ @@ -39,13 +47,13 @@ typedef struct hwDependentFns { void (*blit)(PrivateData *p); /* Switch the backlight on or off */ - /* void (*set_backlight)(PrivateData *p, char state); */ + void (*set_backlight)(PrivateData *p, int state); - /* Set the contrast (value from 0-1000) */ - /* void (*set_contrast)(PrivateData *p, unsigned int value); */ + /* Set contrast */ + void (*set_contrast)(PrivateData *p, int value); /* Output "data" to output latch if there is one */ - /* void (*output)(PrivateData *p, int data); */ + void (*output)(PrivateData *p, int data); /* Close the interface on shutdown */ void (*close)(PrivateData *p); diff --git a/server/drivers/glcd-render.c b/server/drivers/glcd-render.c index e561eae..fa119c7 100644 --- a/server/drivers/glcd-render.c +++ b/server/drivers/glcd-render.c @@ -26,6 +26,7 @@ #include "glcd-low.h" #include "glcd-render.h" #include "glcd_font5x8.h" +#include "sed1520fm.h" #include "shared/defines.h" #ifdef HAVE_FT2 @@ -52,12 +53,16 @@ static int icon2unicode(int icon); int glcd_render_init(Driver *drvthis) { -#ifdef HAVE_FT2 PrivateData *p = drvthis->private_data; + p->cellwidth = GLCD_DEFAULT_CELLWIDTH; + p->cellheight = GLCD_DEFAULT_CELLHEIGHT; + +#ifdef HAVE_FT2 int rc; const char *tmp; char font_file[255]; RenderConfig *rconf; + int w, h; debug(RPT_INFO, "%s(): Freetype", __FUNCTION__); @@ -99,6 +104,20 @@ glcd_render_init(Driver *drvthis) /* If the font does not have icons use default 5x8 font */ rconf->ft_has_icons = drvthis->config_get_bool(drvthis->name, "fontHasIcons", 0, 1); + + /* Read display size in pixels */ + tmp = drvthis->config_get_string(drvthis->name, "CellSize", 0, "6x8"); + if ((sscanf(tmp, "%dx%d", &w, &h) != 2) + || (w <= 4) || (w > 24) + || (h <= 6) || (h > 32)) { + report(RPT_WARNING, "%s: cannot read CellSize: %s, Using default %dx%d", + drvthis->name, tmp, GLCD_DEFAULT_CELLWIDTH, GLCD_DEFAULT_CELLHEIGHT); + w = GLCD_DEFAULT_CELLWIDTH; + h = GLCD_DEFAULT_CELLHEIGHT; + } + p->cellwidth = w; + p->cellheight = h; + debug(RPT_INFO, "%s: using cellsize %dx%d", drvthis->name, p->cellwidth, p->cellheight); } #endif @@ -148,16 +167,18 @@ glcd_render_close(Driver *drvthis) * \param x Horizontal character position (column). * \param y Vertical character position (row). * \param c Character that gets written. + * \param yscale Use multiple of cellheight + * \param xscale Use multiple of cellwidth */ void -glcd_render_char_unicode(Driver *drvthis, int x, int y, int c) +glcd_render_char_unicode(Driver *drvthis, int x, int y, int c, int yscale, int xscale) { 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 r_width, r_height; /* Size of the cell used to render char into */ int rc; FT_Face face; FT_GlyphSlot glyph; @@ -168,19 +189,30 @@ glcd_render_char_unicode(Driver *drvthis, int x, int y, int c) 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); + /* + * Implementation note: This function can be used to render characters + * that are multiple the size of one cell. Currently this is used to + * draw big numbers. Therefore use a scaled cell (render_cell) for all + * calculations. + */ + r_height = p->cellheight * yscale; + r_width = p->cellwidth * xscale; + + /* + * Set the font size. We set the font pixel width and height to the + * same value (r_height), otherwise characters look too much condensed. + */ + if (last_font_size != r_height) { + debug(RPT_INFO, "%s: Setting font size to %d", drvthis->name, r_height); + rc = FT_Set_Pixel_Sizes(rconf->ft_normal_font, r_height, r_height); 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; + last_font_size = r_height; } /* load the glyph and render it */ @@ -196,24 +228,33 @@ glcd_render_char_unicode(Driver *drvthis, int x, int y, int c) bitmap = &glyph->bitmap; bitmap_buf = bitmap->buffer; - py = y * p->cellheight; - /* clear the cell */ - for (row = 0; row < p->cellheight; row++, py++) { + /* Clear the cell. */ + py = max(y * p->cellheight - r_height, 0); + for (row = 0; row < r_height; row++, py++) { px = x * p->cellwidth; - for (col = 0; col < p->cellwidth; col++, px++) { + for (col = 0; col < r_width; 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. + * Copy the pixels. Important: 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++) { + py = max(y * p->cellheight + (face->size->metrics.descender >> 6) - glyph->bitmap_top, 0); + for (row = 0; (row < bitmap->rows) && (row < r_height); row++) { + px = x * p->cellwidth; + /* + * Hack: If scales are not the same, ignore Freetype's idea of + * character position, but just center it. Currently only used + * for the ':' of the bignum. + */ + if (yscale == xscale) + px += glyph->bitmap_left; + else + px += (r_width - bitmap->width)/2; + + for (col = 0; (col < bitmap->width) && (col < r_width); col++) { fb_draw_pixel(p, px, py, bitmap_buf[col / 8] >> (7 - (col % 8)) & 1); px++; } @@ -297,12 +338,12 @@ icon2unicode(int icon) 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_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: @@ -334,7 +375,7 @@ glcd_render_icon(Driver *drvthis, int x, int y, int icon) if (p->use_ft2 && rconf->ft_has_icons) { if ((icon_char = icon2unicode(icon)) != -1) { - glcd_render_char_unicode(drvthis, x, y, icon_char); + glcd_render_char_unicode(drvthis, x, y, icon_char, 1, 1); return 0; } return -1; @@ -346,3 +387,45 @@ glcd_render_icon(Driver *drvthis, int x, int y, int icon) } return -1; } + + +/** + * Draw a big digit (or colon) using the built-in 16x24 font. Although the font + * is stored in different pixel layout (column format, LSB top) this does not + * matter as we need to address pixels in frame buffer anyway. The digit is + * centered vertically. + * + * \note Works only for displays with pixel height >= 24! Smaller displays are + * not supported and nothing will be drawn. + * + * \param drvthis Pointer to driver structure. + * \param x Horizontal character position (column). + * \param num The digit to draw (0-10 with 10 = colon) + */ +void +glcd_render_bignum(Driver *drvthis, int x, int num) +{ + PrivateData *p = drvthis->private_data; + int c, z; /* Column and byte within font definition */ + int px, py; /* Pixel coordinates within the frame buffer */ + + if (p->px_height < chr_hgt_NUM) + return; + + x--; + + px = x * p->cellwidth; + for (c = 0; c < widtbl_NUM[num]; c++) { + /* center vertically */ + py = (p->px_height - chr_hgt_NUM) / 2; + for (z = 0; z < chr_hgt_NUM; z++) { + /* Test if pixel bit is set and draw it */ + if (chrtbl_NUM[num][c * 3 + z / 8] & (1 << (z % 8))) + fb_draw_pixel(p, px, py, 1); + else + fb_draw_pixel(p, px, py, 0); + py++; + } + px++; + } +} diff --git a/server/drivers/glcd-render.h b/server/drivers/glcd-render.h index efd7df7..2085a27 100644 --- a/server/drivers/glcd-render.h +++ b/server/drivers/glcd-render.h @@ -5,9 +5,10 @@ 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); +void glcd_render_bignum(Driver *drvthis, int x, int num); #ifdef HAVE_FT2 -void glcd_render_char_unicode(Driver *drvthis, int x, int y, int c); +void glcd_render_char_unicode(Driver *drvthis, int x, int y, int c, int yscale, int xscale); #endif #endif diff --git a/server/drivers/glcd_drv.c b/server/drivers/glcd_drv.c index 546acf4..be063bf 100644 --- a/server/drivers/glcd_drv.c +++ b/server/drivers/glcd_drv.c @@ -51,6 +51,7 @@ #include "glcd-drivers.h" #include "report.h" #include "glcd-render.h" +#include "shared/defines.h" /* Vars for the server core */ MODULE_EXPORT char *api_version = API_VERSION; @@ -72,7 +73,7 @@ glcd_init(Driver *drvthis) int (*init_fn) (Driver *drvthis)= NULL; const char *s; char size[200]; - int w, h; + int w, h, tmp; report(RPT_DEBUG, "%s()", __FUNCTION__); @@ -109,6 +110,9 @@ glcd_init(Driver *drvthis) p->glcd_functions->drv_debug = debug; p->glcd_functions->blit = NULL; p->glcd_functions->close = NULL; + p->glcd_functions->set_contrast = NULL; + p->glcd_functions->set_backlight = NULL; + p->glcd_functions->output = NULL; /* Read display size in pixels */ strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, GLCD_DEFAULT_SIZE), sizeof(size)); @@ -123,6 +127,37 @@ glcd_init(Driver *drvthis) p->px_width = w; p->px_height = h; + /* Set contrast */ + tmp = drvthis->config_get_int(drvthis->name, "Contrast", 0, GLCD_DEFAULT_CONTRAST); + if ((tmp < 0) || (tmp > 1000)) { + report(RPT_WARNING, "%s: Contrast must be between 0 and 1000; using default %d", + drvthis->name, GLCD_DEFAULT_CONTRAST); + tmp = GLCD_DEFAULT_CONTRAST; + } + p->contrast = tmp; + + /* Set brightness */ + tmp = drvthis->config_get_int(drvthis->name, "Brightness", 0, GLCD_DEFAULT_BRIGHTNESS); + if ((tmp < 0) || (tmp > 1000)) { + report(RPT_WARNING, "%s: Brightness must be between 0 and 1000; using default %d", + drvthis->name, GLCD_DEFAULT_BRIGHTNESS); + tmp = GLCD_DEFAULT_BRIGHTNESS; + } + p->brightness = tmp; + + /* Set backlight-off "brightness" */ + tmp = drvthis->config_get_int(drvthis->name, "OffBrightness", 0, GLCD_DEFAULT_OFFBRIGHTNESS); + if ((tmp < 0) || (tmp > 1000)) { + report(RPT_WARNING, "%s: OffBrightness must be between 0 and 1000; using default %d", + drvthis->name, GLCD_DEFAULT_OFFBRIGHTNESS); + tmp = GLCD_DEFAULT_OFFBRIGHTNESS; + } + p->offbrightness = tmp; + + /* Invalidate cached values */ + p->last_output_state = -1; + p->backlightstate = -1; + /* Do local (=connection type specific) display init */ if (init_fn(drvthis) != 0) return -1; @@ -134,6 +169,15 @@ glcd_init(Driver *drvthis) return -1; } + /* + * Currently this driver supports only display which width is a multiple + * of 8! + */ + if ((p->px_width % 8) != 0) { + report(RPT_ERR, "%s: Pixel width must be mutiple of 8", drvthis->name); + return -1; + } + /* * Calculate these values AFTER driver initialization, as the driver * may update them. @@ -143,10 +187,6 @@ glcd_init(Driver *drvthis) drvthis->name, p->px_width, p->px_height); return -1; } - p->cellwidth = GLCD_DEFAULT_CELLWIDTH; - p->cellheight = GLCD_DEFAULT_CELLHEIGHT; - p->width = p->px_width / p->cellwidth; - p->height = p->px_height / p->cellheight; /* Allocate framebuffer */ p->framebuf = malloc(FB_BYTES_TOTAL); @@ -160,6 +200,10 @@ glcd_init(Driver *drvthis) if (glcd_render_init(drvthis) != 0) return -1; + /* Cellwidth / height are set by the renderer */ + p->width = p->px_width / p->cellwidth; + p->height = p->px_height / p->cellheight; + glcd_clear(drvthis); return 0; @@ -311,7 +355,7 @@ glcd_string(Driver *drvthis, int x, int y, const char string[]) 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); + glcd_render_char_unicode(drvthis, x, y, string[i] & 0xFF, 1, 1); else #endif glcd_render_char(drvthis, x, y, string[i]); @@ -343,7 +387,7 @@ glcd_chr(Driver *drvthis, int x, int y, char c) #ifdef HAVE_FT2 if (p->use_ft2) - glcd_render_char_unicode(drvthis, x, y, c & 0xFF); + glcd_render_char_unicode(drvthis, x, y, c & 0xFF, 1, 1); else #endif glcd_render_char(drvthis, x, y, c); @@ -428,3 +472,170 @@ glcd_get_info(Driver *drvthis) return info_string; } + + +/** + * Write a big number to the screen. + * \param drvthis Pointer to driver structure. + * \param x Horizontal character position (column). + * \param num Character to write (0 - 10 with 10 representing ':') + */ +MODULE_EXPORT void +glcd_num(Driver *drvthis, int x, int num) +{ + PrivateData *p = drvthis->private_data; + + debug(RPT_DEBUG, "%s(%i,%i)", __FUNCTION__, x, num); + + if (x < 1 || x > p->width || num < 0 || num > 10) + return; + +#ifdef HAVE_FT2 + if (p->use_ft2) { + int y; + int sc; + + sc = min(p->height, 3); + y = p->height - (p->height - sc)/2; + + if (num == 10) + glcd_render_char_unicode(drvthis, x, y, ':', sc, 1); + else + glcd_render_char_unicode(drvthis, x, y, '0' + num, sc, sc); + return; + } +#endif + glcd_render_bignum(drvthis, x, num); +} + + +/** + * Get current LCD contrast. This is only the locally stored contrast. + * \param drvthis Pointer to driver structure. + * \return Stored contrast in promille. + */ +MODULE_EXPORT int +glcd_get_contrast(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; + + debug(RPT_INFO, "%s()", __FUNCTION__); + + return p->contrast; +} + + +/** + * Change LCD contrast. + * \param drvthis Pointer to driver structure. + * \param promille New contrast value in promille. + */ +MODULE_EXPORT void +glcd_set_contrast(Driver *drvthis, int promille) +{ + PrivateData *p = drvthis->private_data; + + debug(RPT_INFO, "%s(%i)", __FUNCTION__, promille); + + /* Check it */ + if (promille < 0 || promille > 1000) + return; + + /* Cache the value */ + p->contrast = promille; + + /* call local function */ + if (p->glcd_functions->set_contrast != NULL) + p->glcd_functions->set_contrast(p, promille); +} + + +/** + * Retrieve brightness. This only reads the locally stored values. + * \param drvthis Pointer to driver structure. + * \param state Brightness state (on/off) for which we want the value. + * \return Stored brightness in promille. + */ +MODULE_EXPORT int +glcd_get_brightness(Driver *drvthis, int state) +{ + PrivateData *p = drvthis->private_data; + + debug(RPT_INFO, "%s(%i)", __FUNCTION__, state); + + return (state == BACKLIGHT_ON) ? p->brightness : p->offbrightness; +} + + +/** + * Set on/off brightness. + * \param drvthis Pointer to driver structure. + * \param state Brightness state (on/off) for which we want to store the value. + * \param promille New brightness in promille. + */ +MODULE_EXPORT void +glcd_set_brightness(Driver *drvthis, int state, int promille) +{ + PrivateData *p = drvthis->private_data; + + debug(RPT_INFO, "%s(%i,%i)", __FUNCTION__, state, promille); + + /* Check it */ + if (promille < 0 || promille > 1000) + return; + + /* store the software value since there is not get */ + if (state == BACKLIGHT_ON) { + p->brightness = promille; + } + else { + p->offbrightness = promille; + } + + /* Force update on next render cycle */ + p->backlightstate = -1; +} + +/** + * Turn the backlight on or off. + * \param drvthis Pointer to driver structure. + * \param on New backlight status. + */ +MODULE_EXPORT void +glcd_backlight(Driver *drvthis, int on) +{ + PrivateData *p = drvthis->private_data; + debug(RPT_DEBUG, "%s(%i)", __FUNCTION__, on); + + /* Immediately return if no change is necessary */ + if (p->backlightstate == on) + return; + + if (p->glcd_functions->set_backlight != NULL) + p->glcd_functions->set_backlight(p, on); + + p->backlightstate = on; +} + + +/** + * Send out-of-band data to the device. + * \param drvthis Pointer to driver structure. + * \param value Integer. Meaning is specific to the device + */ +MODULE_EXPORT void +glcd_output(Driver *drvthis, int value) +{ + PrivateData *p = drvthis->private_data; + debug(RPT_DEBUG, "%s(%i)", __FUNCTION__, value); + + /* return is no change */ + if (p->last_output_state == value) + return; + + p->last_output_state = value; + + /* call output function only if it is defined for the commenction type */ + if (p->glcd_functions->output != NULL) + p->glcd_functions->output(p, value); +} diff --git a/server/drivers/glcd_drv.h b/server/drivers/glcd_drv.h index 64061ce..7e2c46a 100644 --- a/server/drivers/glcd_drv.h +++ b/server/drivers/glcd_drv.h @@ -12,10 +12,18 @@ MODULE_EXPORT void glcd_flush(Driver *drvthis); MODULE_EXPORT void glcd_string(Driver *drvthis, int x, int y, const char string[]); MODULE_EXPORT void glcd_chr(Driver *drvthis, int x, int y, char c); +MODULE_EXPORT void glcd_num(Driver *drvthis, int x, int num); MODULE_EXPORT void glcd_vbar(Driver *drvthis, int x, int y, int len, int promille, int options); MODULE_EXPORT void glcd_hbar(Driver *drvthis, int x, int y, int len, int promille, int options); MODULE_EXPORT int glcd_icon(Driver *drvthis, int x, int y, int icon); MODULE_EXPORT const char *glcd_get_info(Driver *drvthis); +MODULE_EXPORT int glcd_get_contrast (Driver *drvthis); +MODULE_EXPORT void glcd_set_contrast (Driver *drvthis, int promille); +MODULE_EXPORT int glcd_get_brightness (Driver *drvthis, int state); +MODULE_EXPORT void glcd_set_brightness (Driver *drvthis, int state, int promille); +MODULE_EXPORT void glcd_backlight (Driver *drvthis, int promille); +MODULE_EXPORT void glcd_output(Driver *drvthis, int value); + #endif