From 23abcf06bccc36b906c4735bf0ac5848b325b135 Mon Sep 17 00:00:00 2001 From: TangoCash Date: Sun, 9 Oct 2022 16:15:19 +0200 Subject: [PATCH] add display support for osmio4k+ use --enable-lcd as configure option Origin commit data ------------------ Branch: ni/coolstream Commit: https://github.com/neutrino-images/ni-neutrino/commit/e2b7f4c6c110d58841cbbac1de7434da0482609d Author: TangoCash Date: 2022-10-09 (Sun, 09 Oct 2022) ------------------ This commit was generated by Migit --- configure.ac | 25 + lib/Makefile.am | 5 + lib/liblcddisplay/Makefile.am | 19 + lib/liblcddisplay/fontrenderer.cpp | 371 +++++++++++ lib/liblcddisplay/fontrenderer.h | 106 ++++ lib/liblcddisplay/lcddisplay.cpp | 950 ++++++++++++++++++++++++++++ lib/liblcddisplay/lcddisplay.h | 148 +++++ src/Makefile.am | 5 + src/driver/Makefile.am | 11 + src/driver/display.h | 5 + src/driver/lcdd.cpp | 961 +++++++++++++++++++++++++++++ src/driver/lcdd.h | 313 ++++++++++ src/gui/vfd_setup.cpp | 48 ++ src/gui/vfd_setup.h | 9 + 14 files changed, 2976 insertions(+) create mode 100644 lib/liblcddisplay/Makefile.am create mode 100644 lib/liblcddisplay/fontrenderer.cpp create mode 100644 lib/liblcddisplay/fontrenderer.h create mode 100644 lib/liblcddisplay/lcddisplay.cpp create mode 100644 lib/liblcddisplay/lcddisplay.h create mode 100644 src/driver/lcdd.cpp create mode 100644 src/driver/lcdd.h diff --git a/configure.ac b/configure.ac index f2a6422bd..071ad8d68 100644 --- a/configure.ac +++ b/configure.ac @@ -186,6 +186,30 @@ AC_ARG_ENABLE(lcd4linux, AC_DEFINE(ENABLE_LCD4LINUX, 1, [enable LCD4Linux support])) AM_CONDITIONAL(ENABLE_LCD4LINUX, test "$enable_lcd4linux" = "yes") +# lcd +AC_ARG_ENABLE(lcd, + AS_HELP_STRING(--enable-lcd,include lcd support), + ,[enable_lcd=no]) +AM_CONDITIONAL(ENABLE_LCD,test "$enable_lcd" = "yes") + +if test "$enable_lcd" = "yes"; then +AC_DEFINE(ENABLE_LCD,1,[include lcd support]) +fi + +# lcd update +AM_CONDITIONAL(LCD_UPDATE,test "$enable_lcd_update" = "yes") + +if test "$enable_lcd" = "yes"; then +AC_DEFINE(LCD_UPDATE,1,[include lcd update support]) +fi + +# lcdapi +AM_CONDITIONAL(ENABLE_LCDAPI,test "$enable_lcd" = "yes") + +if test "$enable_lcd" = "yes"; then +AC_DEFINE(ENABLE_LCDAPI,1,[include lcd api support]) +fi + AC_ARG_ENABLE(graphlcd, AS_HELP_STRING([--enable-graphlcd], [enable GraphLCD support @<:@default=no@:>@]), AC_DEFINE(ENABLE_GRAPHLCD, 1, [enable GraphLCD support])) @@ -476,6 +500,7 @@ lib/connection/Makefile lib/hardware/coolstream/Makefile lib/jsoncpp/Makefile lib/libconfigfile/Makefile +lib/liblcddisplay/Makefile lib/libdvbsub/Makefile lib/libeventserver/Makefile lib/libiw/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index c6cfc59d8..a0e2d3914 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -18,6 +18,11 @@ SUBDIRS += \ libupnpclient endif +if ENABLE_LCD +SUBDIRS += \ + liblcddisplay +endif + if BOXTYPE_CST SUBDIRS += \ hardware/coolstream diff --git a/lib/liblcddisplay/Makefile.am b/lib/liblcddisplay/Makefile.am new file mode 100644 index 000000000..e8b91e67f --- /dev/null +++ b/lib/liblcddisplay/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/system \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libeventserver + +AM_CPPFLAGS += -fno-rtti -fno-exceptions + +noinst_LIBRARIES = liblcddisplay.a + +liblcddisplay_a_SOURCES = lcddisplay.cpp fontrenderer.cpp + +AM_CXXFLAGS = \ + @FREETYPE_CFLAGS@ diff --git a/lib/liblcddisplay/fontrenderer.cpp b/lib/liblcddisplay/fontrenderer.cpp new file mode 100644 index 000000000..e3f6d51f6 --- /dev/null +++ b/lib/liblcddisplay/fontrenderer.cpp @@ -0,0 +1,371 @@ +/* + $Header$ + + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Copyright (C) 2003 thegoodguy + baseroutines by tmbinc + + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "fontrenderer.h" + +#include +#include + +#include + +#include +#include FT_FREETYPE_H + +/* tested with freetype 2.3.9, and 2.1.4 */ +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3 +#define FT_NEW_CACHE_API +#endif + +// fribidi +#if 0// defined (ENABLE_FRIBIDI) +#include +#endif + +FT_Error LcdFontRenderClass::myFTC_Face_Requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface) +{ + return ((LcdFontRenderClass *)request_data)->FTC_Face_Requester(face_id, aface); +} + + +LcdFontRenderClass::LcdFontRenderClass(CLCDDisplay *fb) +{ + framebuffer = fb; + + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::LcdFontRenderClass: initializing core...\n"); + + fflush(stdout); + if (FT_Init_FreeType(&library)) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::LcdFontRenderClass: failed.\n"); + return; + } + dprintf(DEBUG_NORMAL, "\n"); + font = 0; + pthread_mutex_init(&render_mutex, NULL); +} + +LcdFontRenderClass::~LcdFontRenderClass() +{ + FTC_Manager_Done(cacheManager); + FT_Done_FreeType(library); +} + +void LcdFontRenderClass::InitFontCache() +{ + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::InitFontCache: Intializing font cache...\n"); + + fflush(stdout); + if (FTC_Manager_New(library, 3, 0, 0, myFTC_Face_Requester, this, &cacheManager)) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::InitFontCache: manager failed!\n"); + return; + } + if (!cacheManager) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::InitFontCache: error.\n"); + return; + } + if (FTC_SBitCache_New(cacheManager, &sbitsCache)) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::InitFontCache: sbit failed!\n"); + return; + } + if (FTC_ImageCache_New(cacheManager, &imageCache)) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::InitFontCache: imagecache failed!\n"); + } + printf("\n"); +} + +FT_Error LcdFontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face *aface) +{ + fontListEntry *font = (fontListEntry *)face_id; + if (!font) + return -1; + + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::FTC_Face_Requester: FTC_Face_Requester (%s/%s)\n", font->family, font->style); + + int error; + if ((error = FT_New_Face(library, font->filename, 0, aface))) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::FTC_Face_Requester: failed: %i\n", error); + return error; + } + return 0; +} + +FTC_FaceID LcdFontRenderClass::getFaceID(const char *family, const char *style) +{ + for (fontListEntry *f = font; f; f = f->next) + { + if ((!strcmp(f->family, family)) && (!strcmp(f->style, style))) + return (FTC_FaceID)f; + } + for (fontListEntry *f = font; f; f = f->next) + { + if (!strcmp(f->family, family)) + return (FTC_FaceID)f; + } + return 0; +} + +#ifdef FT_NEW_CACHE_API +FT_Error LcdFontRenderClass::getGlyphBitmap(FTC_ImageType font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + return FTC_SBitCache_Lookup(sbitsCache, font, glyph_index, sbit, NULL); +} +#else +FT_Error LcdFontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + return FTC_SBit_Cache_Lookup(sbitsCache, font, glyph_index, sbit); +} +#endif + +const char *LcdFontRenderClass::AddFont(const char *const filename) +{ + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::AddFont: adding font %s...\n", filename); + + fflush(stdout); + int error; + fontListEntry *n; + + FT_Face face; + if ((error = FT_New_Face(library, filename, 0, &face))) + { + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::AddFont: failed: %i\n", error); + return NULL; + } + + n = new fontListEntry; + + n->filename = strdup(filename); + n->family = strdup(face->family_name); + n->style = strdup(face->style_name); + FT_Done_Face(face); + + n->next = font; + dprintf(DEBUG_NORMAL, "LcdFontRenderClass::AddFont: OK (%s/%s)\n", n->family, n->style); + font = n; + + return n->style; +} + +LcdFontRenderClass::fontListEntry::~fontListEntry() +{ + free(filename); + free(family); + free(style); +} + +LcdFont *LcdFontRenderClass::getFont(const char *family, const char *style, int size) +{ + FTC_FaceID id = getFaceID(family, style); + if (!id) + return 0; + return new LcdFont(framebuffer, this, id, size); +} + +LcdFont::LcdFont(CLCDDisplay *fb, LcdFontRenderClass *render, FTC_FaceID faceid, int isize) +{ + framebuffer = fb; + renderer = render; +#ifdef FT_NEW_CACHE_API + font.face_id = faceid; + font.width = isize; + font.height = isize; + font.flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_MONOCHROME; +#else + font.font.face_id = faceid; + font.font.pix_width = isize; + font.font.pix_height = isize; + font.image_type = ftc_image_mono; + font.image_type |= ftc_image_flag_autohinted; +#endif +} + +FT_Error LcdFont::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit) +{ + return renderer->getGlyphBitmap(&font, glyph_index, sbit); +} + +extern int UTF8ToUnicode(const char *&text, const bool utf8_encoded); //defined in src/driver/fontrenderer.cpp +#if 0 //defined (ENABLE_FRIBIDI) +std::string fribidiShapeChar(const char *text, const bool utf8_encoded); +#endif + +void LcdFont::RenderString(int x, int y, const int width, const char *text, const int color, const int selected, const bool utf8_encoded) +{ + int err; + pthread_mutex_lock(&renderer->render_mutex); + +// fribidi +#if 0// defined (ENABLE_FRIBIDI) + std::string Text = fribidiShapeChar(text, utf8_encoded); + text = Text.c_str(); +#endif + +#ifdef FT_NEW_CACHE_API + FTC_ScalerRec scaler; + + scaler.face_id = font.face_id; + scaler.width = font.width; + scaler.height = font.height; + scaler.pixel = true; + + if ((err = FTC_Manager_LookupSize(renderer->cacheManager, &scaler, &size)) != 0) +#else + if ((err = FTC_Manager_Lookup_Size(renderer->cacheManager, &font.font, &face, &size)) != 0) +#endif + { + dprintf(DEBUG_NORMAL, "LcdFont::RenderString: FTC_Manager_Lookup_Size failed! (%d)\n", err); + pthread_mutex_unlock(&renderer->render_mutex); + return; + } + int left = x, step_y = (size->metrics.height >> 6) * 3 / 4 + 4; + + int pos = 0; + for (; *text; text++) + { + pos++; + FTC_SBit glyph; + //if ((x + size->metrics.x_ppem > (left+width)) || (*text=='\n')) + if (x + size->metrics.x_ppem > (left + width)) + { + //width clip + break; + } + if (*text == '\n') + { + x = left; + y += step_y; + } + + int unicode_value = UTF8ToUnicode(text, utf8_encoded); + + if (unicode_value == -1) + break; + +#ifdef FT_NEW_CACHE_API + int index = FT_Get_Char_Index(size->face, unicode_value); +#else + int index = FT_Get_Char_Index(face, unicode_value); +#endif + + if (!index) + continue; + if (getGlyphBitmap(index, &glyph)) + { + dprintf(DEBUG_NORMAL, "LcdFont::RenderString: failed to get glyph bitmap.\n"); + continue; + } + + int rx = x + glyph->left; + int ry = y - glyph->top; + if (pos == selected) + { + framebuffer->draw_fill_rect(x - 2, y - glyph->height - 2, x + glyph->width + 2, y + 2, CLCDDisplay::PIXEL_INV); + } + + for (int ay = 0; ay < glyph->height; ay++) + { + int ax = 0; + int w = glyph->width; + int xpos = rx; + for (; ax < w; ax++) + { + unsigned char c = glyph->buffer[ay * abs(glyph->pitch) + (ax >> 3)]; + if ((c >> (7 - (ax & 7))) & 1) + framebuffer->draw_point(xpos, ry, color); + xpos ++; + } + ry++; + } + + x += glyph->xadvance + 1; + } + pthread_mutex_unlock(&renderer->render_mutex); +} + +int LcdFont::getRenderWidth(const char *text, const bool utf8_encoded) +{ + pthread_mutex_lock(&renderer->render_mutex); + +// fribidi +#if 0// defined (ENABLE_FRIBIDI) + std::string Text = fribidiShapeChar(text, utf8_encoded); + text = Text.c_str(); +#endif + + FT_Error err; +#ifdef FT_NEW_CACHE_API + FTC_ScalerRec scaler; + scaler.face_id = font.face_id; + scaler.width = font.width; + scaler.height = font.height; + scaler.pixel = true; + + err = FTC_Manager_LookupSize(renderer->cacheManager, &scaler, &size); +#else + err = FTC_Manager_Lookup_Size(renderer->cacheManager, &font.font, &face, &size); +#endif + if (err != 0) + { + dprintf(DEBUG_NORMAL, "LcdFont::getRenderWidth: FTC_Manager_Lookup_Size failed! (0x%x)\n", err); + pthread_mutex_unlock(&renderer->render_mutex); + return -1; + } + int x = 0; + for (; *text; text++) + { + FTC_SBit glyph; + + int unicode_value = UTF8ToUnicode(text, utf8_encoded); + + if (unicode_value == -1) + break; + +#ifdef FT_NEW_CACHE_API + int index = FT_Get_Char_Index(size->face, unicode_value); +#else + int index = FT_Get_Char_Index(face, unicode_value); +#endif + + if (!index) + continue; + if (getGlyphBitmap(index, &glyph)) + { + dprintf(DEBUG_NORMAL, "LcdFont::getRenderWidth: failed to get glyph bitmap.\n"); + continue; + } + + x += glyph->xadvance + 1; + } + pthread_mutex_unlock(&renderer->render_mutex); + return x; +} diff --git a/lib/liblcddisplay/fontrenderer.h b/lib/liblcddisplay/fontrenderer.h new file mode 100644 index 000000000..5b81f59c8 --- /dev/null +++ b/lib/liblcddisplay/fontrenderer.h @@ -0,0 +1,106 @@ +/* + $Header$ + + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + baseroutines by tmbinc + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __LCDFONTRENDERER__ +#define __LCDFONTRENDERER__ + +#include "lcddisplay.h" + +#include +#include FT_FREETYPE_H +#include FT_CACHE_H +#include FT_CACHE_IMAGE_H +#include FT_CACHE_SMALL_BITMAPS_H + +#include + +class LcdFontRenderClass; +class LcdFont +{ + CLCDDisplay *framebuffer; +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3 + FTC_ImageTypeRec font; +#else + FTC_Image_Desc font; + FT_Face face; +#endif + LcdFontRenderClass *renderer; + FT_Size size; + + FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit); + + public: + void RenderString(int x, int y, int width, const char *text, int color, int selected = 0, const bool utf8_encoded = false); + + int getRenderWidth(const char *text, const bool utf8_encoded = false); + + LcdFont(CLCDDisplay *fb, LcdFontRenderClass *render, FTC_FaceID faceid, int isize); + ~LcdFont() {} +}; + +class LcdFontRenderClass +{ + CLCDDisplay *framebuffer; + + struct fontListEntry + { + char *filename, *style, *family; + fontListEntry *next; + ~fontListEntry(); + } *font; + + FT_Library library; + FTC_Manager cacheManager; /* the cache manager */ + FTC_ImageCache imageCache; /* the glyph image cache */ + FTC_SBitCache sbitsCache; /* the glyph small bitmaps cache */ + + FTC_FaceID getFaceID(const char *family, const char *style); +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3 + FT_Error getGlyphBitmap(FTC_ImageType font, FT_ULong glyph_index, FTC_SBit *sbit); +#else + FT_Error getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit); +#endif + + public: + pthread_mutex_t render_mutex; + const char *AddFont(const char *const filename); + void InitFontCache(); + + FT_Error FTC_Face_Requester(FTC_FaceID face_id, FT_Face *aface); + + static FT_Error myFTC_Face_Requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface); + + //FT_Face getFace(const char *family, const char *style); + LcdFont *getFont(const char *family, const char *style, int size); + LcdFontRenderClass(CLCDDisplay *fb); + ~LcdFontRenderClass(); + + friend class LcdFont; +}; + +#endif /* __LCDFONTRENDERER__ */ diff --git a/lib/liblcddisplay/lcddisplay.cpp b/lib/liblcddisplay/lcddisplay.cpp new file mode 100644 index 000000000..a3066c7fa --- /dev/null +++ b/lib/liblcddisplay/lcddisplay.cpp @@ -0,0 +1,950 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + baseroutines by Shadow_ + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include "lcddisplay.h" + +#include + +#include /* uint8_t */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BYTE_ORDER +#error "no BYTE_ORDER defined!" +#endif + +#ifndef max +#define max(a,b)(((a)<(b)) ? (b) : (a)) +#endif + +#ifndef min +#define min(a,b)(((a)<(b)) ? (a) : (b)) +#endif + + +void CLCDDisplay::setSize(int w, int h, int b) +{ + xres = w; + yres = h; + bpp = 8; + bypp = 1; + surface_bpp = b; + + real_offset = 0; + real_yres = yres; + if (yres == 32) + real_offset = 16; + if (yres < 64) + yres = 48; + + switch (surface_bpp) + { + case 8: + surface_bypp = 1; + break; + case 15: + case 16: + surface_bypp = 2; + break; + case 24: // never use 24bit mode + case 32: + surface_bypp = 4; + break; + default: + surface_bypp = (bpp + 7) / 8; + } + + surface_stride = xres * surface_bypp; + surface_buffer_size = xres * yres * surface_bypp; + surface_data = new unsigned char[surface_buffer_size]; + memset(surface_data, 0, surface_buffer_size); + + printf("[CLCDDisplay] %s surface buffer %p %d bytes, stride %d\n", __FUNCTION__, surface_data, surface_buffer_size, surface_stride); + + _stride = xres * bypp; + raw_buffer_size = xres * yres * bypp; + _buffer = new unsigned char[raw_buffer_size]; + memset(_buffer, 0, raw_buffer_size); + + printf("[CLCDDisplay] %s lcd buffer %p %d bytes, stride %d, type %d\n", __FUNCTION__, _buffer, raw_buffer_size, _stride, is_oled); +} + +CLCDDisplay::CLCDDisplay() +{ + paused = 0; + available = false; + + raw_buffer_size = 0; + xres = 132; + yres = 32; + bpp = 8; + + flipped = false; + inverted = 0; + is_oled = 0; + last_brightness = 0; + + //open device + fd = open("/dev/dbox/oled0", O_RDWR); + + if (fd < 0) + { + xres = 128; + if (!access("/proc/stb/lcd/oled_brightness", W_OK) || !access("/proc/stb/fp/oled_brightness", W_OK)) + is_oled = 2; + fd = open(LCD_DEVICE, O_RDWR); + } + else + { + printf("found OLED display!\n"); + is_oled = 1; + } + + if (fd < 0) + { + printf("couldn't open LCD - load lcd.ko!\n"); + return; + } + else + { + int i = LCD_MODE_BIN; + ioctl(fd, LCD_IOCTL_ASC_MODE, &i); + + FILE *f = fopen("/proc/stb/lcd/xres", "r"); + if (f) + { + int tmp; + if (fscanf(f, "%x", &tmp) == 1) + xres = tmp; + fclose(f); + f = fopen("/proc/stb/lcd/yres", "r"); + if (f) + { + if (fscanf(f, "%x", &tmp) == 1) + yres = tmp; + fclose(f); + f = fopen("/proc/stb/lcd/bpp", "r"); + if (f) + { + if (fscanf(f, "%x", &tmp) == 1) + bpp = tmp; + fclose(f); + } + } + is_oled = 3; + } + } + + setSize(xres, yres, bpp); + + available = true; + iconBasePath = ""; +} + +//e2 +void CLCDDisplay::setInverted(unsigned char inv) +{ + inverted = inv; + update(); +} + +void CLCDDisplay::setFlipped(bool onoff) +{ + flipped = onoff; + update(); +} + +int CLCDDisplay::setLCDContrast(int contrast) +{ + return(0); + + int fp; + + fp = open("/dev/dbox/fp0", O_RDWR); + + if (fp < 0) + fp = open("/dev/dbox/lcd0", O_RDWR); + if (fp < 0) + { + printf("[LCD] can't open /dev/dbox/fp0(%m)\n"); + return (-1); + } + + if (ioctl(fd, LCD_IOCTL_SRV, &contrast) < 0) + { + printf("[LCD] can't set lcd contrast(%m)\n"); + } + close(fp); + + return (0); +} + +int CLCDDisplay::setLCDBrightness(int brightness) +{ + printf("setLCDBrightness %d\n", brightness); + + FILE *f = fopen("/proc/stb/lcd/oled_brightness", "w"); + if (!f) + f = fopen("/proc/stb/fp/oled_brightness", "w"); + if (f) + { + if (fprintf(f, "%d", brightness) == 0) + printf("write /proc/stb/lcd/oled_brightness failed!! (%m)\n"); + fclose(f); + } + else + { + int fp; + if ((fp = open("/dev/dbox/fp0", O_RDWR)) < 0) + { + printf("[LCD] can't open /dev/dbox/fp0\n"); + return (-1); + } + + if (ioctl(fp, FP_IOCTL_LCD_DIMM, &brightness) < 0) + printf("[LCD] can't set lcd brightness (%m)\n"); + close(fp); + } + + if (brightness == 0) + { + memset(_buffer, inverted, raw_buffer_size); + update(); + } + + last_brightness = brightness; + + return (0); +} + +bool CLCDDisplay::isAvailable() +{ + return available; +} + +CLCDDisplay::~CLCDDisplay() +{ + delete [] _buffer; + + if (fd >= 0) + { + close(fd); + fd = -1; + } +} + +void CLCDDisplay::pause() +{ + paused = 1; +} + +void CLCDDisplay::resume() +{ + //clear the display + if (ioctl(fd, LCD_IOCTL_CLEAR) < 0) + printf("[lcddisplay] LCD_IOCTL_CLEAR failed (%m)\n"); + + //graphic (binary) mode + int i = LCD_MODE_BIN; + if (ioctl(fd, LCD_IOCTL_ASC_MODE, &i) < 0) + printf("[lcddisplay] LCD_IOCTL_ASC_MODE failed (%m)\n"); + // + + paused = 0; +} + +void CLCDDisplay::convert_data() +{ +#ifndef PLATFORM_GENERIC + unsigned int x, y, z; + char tmp; + +#if 0 + unsigned int height = yres; + unsigned int width = xres; + + for (x = 0; x < width; x++) + { + for (y = 0; y < (height / 8); y++) + { + tmp = 0; + + for (z = 0; z < 8; z++) + if (_buffer[(y * 8 + z) * width + x] == LCD_PIXEL_ON) + tmp |= (1 << z); + + lcd[y][x] = tmp; + } + } +#endif +#endif +} + +void CLCDDisplay::update() +{ +#ifndef PLATFORM_GENERIC + if ((fd >= 0) && (last_brightness > 0)) + { + for (unsigned int y = 0; y < yres; y++) + { + for (unsigned int x = 0; x < xres; x++) + { + surface_fill_rect(x, y, x + 1, y + 1, _buffer[y * xres + x]); + } + } + + if (is_oled == 0 || is_oled == 2) + { + unsigned int height = yres; + unsigned int width = xres; + + + unsigned char raw[132 * 8]; + int x, y, yy; + + memset(raw, 0x00, 132 * 8); + + for (y = 0; y < 8; y++) + { + // display has only 128 but buffer must be 132 + for (x = 0; x < 128; x++) + { + int pix = 0; + for (yy = 0; yy < 8; yy++) + { + pix |= (surface_data[(y * 8 + yy) * width + x] >= 108) << yy; + } + + if (flipped) + { + /* 8 pixels per byte, swap bits */ +#define BIT_SWAP(a) ((((a << 7)&0x80) + ((a << 5)&0x40) + ((a << 3)&0x20) + ((a << 1)&0x10) + ((a >> 1)&0x08) + ((a >> 3)&0x04) + ((a >> 5)&0x02) + ((a >> 7)&0x01))&0xff) + raw[(7 - y) * 132 + (132 - 1 - x - 2)] = BIT_SWAP(pix ^ inverted); + } + else + { + raw[y * 132 + x + 2] = pix ^ inverted; + } + } + } + + write(fd, raw, 132 * 8); + + } + else if (is_oled == 3) + { + /* for now, only support flipping / inverting for 8bpp displays */ + if ((flipped || inverted) && surface_stride == xres) + { + unsigned int height = yres; + unsigned int width = xres; + unsigned char raw[surface_stride * height]; + + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x++) + { + if (flipped) + { + /* 8bpp, no bit swapping */ + raw[(height - 1 - y) * width + (width - 1 - x)] = surface_data[y * width + x] ^ inverted; + } + else + { + raw[y * width + x] = surface_data[y * width + x] ^ inverted; + } + } + } + write(fd, raw, surface_stride * height); + } + else + { + //write(fd, surface_data, surface_stride * yres); + // +#ifdef PLATFORM_GIGABLUE + unsigned char gb_buffer[surface_stride * yres]; + for (int offset = 0; offset < surface_stride * yres; offset += 2) + { + gb_buffer[offset] = (surface_data[offset] & 0x1F) | ((surface_data[offset + 1] << 3) & 0xE0); + gb_buffer[offset + 1] = ((surface_data[offset + 1] >> 5) & 0x03) | ((surface_data[offset] >> 3) & 0x1C) | ((_buffer[offset + 1] << 5) & 0x60); + } + write(fd, gb_buffer, surface_stride * yres); +#else + write(fd, surface_data + surface_stride * real_offset, surface_stride * real_yres); +#endif + // + } + } + else /* is_oled == 1 */ + { + unsigned char raw[64 * 64]; + int x, y; + memset(raw, 0, 64 * 64); + + for (y = 0; y < 64; y++) + { + int pix = 0; + for (x = 0; x < 128 / 2; x++) + { + pix = (surface_data[y * 132 + x * 2 + 2] & 0xF0) | (surface_data[y * 132 + x * 2 + 1 + 2] >> 4); + if (inverted) + pix = 0xFF - pix; + if (flipped) + { + /* device seems to be 4bpp, swap nibbles */ + unsigned char byte; + byte = (pix >> 4) & 0x0f; + byte |= (pix << 4) & 0xf0; + raw[(63 - y) * 64 + (63 - x)] = byte; + } + else + { + raw[y * 64 + x] = pix; + } + } + } + write(fd, raw, 64 * 64); + } + } +#endif +} + +void CLCDDisplay::surface_fill_rect(int area_left, int area_top, int area_right, int area_bottom, int color) +{ + int area_width = area_right - area_left; + int area_height = area_bottom - area_top; + + if (surface_bpp == 8) + { + for (int y = area_top; y < area_bottom; y++) + memset(((uint8_t *)surface_data) + y * surface_stride + area_left, color, area_width); + } + else if (surface_bpp == 16) + { + uint32_t icol; + +#if 0 + if (surface_clut.data && color < surface_clut.colors) + icol = (surface_clut.data[color].a << 24) | (surface_clut.data[color].r << 16) | (surface_clut.data[color].g << 8) | (surface_clut.data[color].b); + else +#endif + icol = 0x10101 * color; +#if BYTE_ORDER == LITTLE_ENDIAN + uint16_t col = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + uint16_t col = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + for (int y = area_top; y < area_bottom; y++) + { + uint16_t *dst = (uint16_t *)(((uint8_t *)surface_data) + y * surface_stride + area_left * surface_bypp); + int x = area_width; + while (x--) + *dst++ = col; + } + } + else if (surface_bpp == 32) + { + uint32_t col; + +#if 0 + if (surface_clut.data && color < surface_clut.colors) + col = (surface_clut.data[color].a << 24) | (surface_clut.data[color].r << 16) | (surface_clut.data[color].g << 8) | (surface_clut.data[color].b); + else +#endif + col = 0x10101 * color; + + col ^= 0xFF000000; + +#if 0 + if (surface_data_phys && gAccel::getInstance()) + if (!gAccel::getInstance()->fill(surface, area, col)) + continue; +#endif + + for (int y = area_top; y < area_bottom; y++) + { + uint32_t *dst = (uint32_t *)(((uint8_t *)surface_data) + y * surface_stride + area_left * surface_bypp); + int x = area_width; + while (x--) + *dst++ = col; + } + } +} + +void CLCDDisplay::draw_point(const int x, const int y, const int state) +{ + if ((x < 0) || (x >= xres) || (y < 0) || (y >= yres)) + return; + + if (state == LCD_PIXEL_INV) + _buffer[y * xres + x] ^= 1; + else + _buffer[y * xres + x] = state; +} + +/* + * draw_line + * + * args: + * x1 StartCol + * y1 StartRow + * x2 EndCol + * y1 EndRow + * state LCD_PIXEL_OFF/LCD_PIXEL_ON/LCD_PIXEL_INV + * + */ +void CLCDDisplay::draw_line(const int x1, const int y1, const int x2, const int y2, const int state) +{ + int dx = abs(x1 - x2); + int dy = abs(y1 - y2); + int x; + int y; + int End; + int step; + + if (dx > dy) + { + int p = 2 * dy - dx; + int twoDy = 2 * dy; + int twoDyDx = 2 * (dy - dx); + + if (x1 > x2) + { + x = x2; + y = y2; + End = x1; + step = y1 < y2 ? -1 : 1; + } + else + { + x = x1; + y = y1; + End = x2; + step = y2 < y1 ? -1 : 1; + } + + draw_point(x, y, state); + + while (x < End) + { + x++; + if (p < 0) + p += twoDy; + else + { + y += step; + p += twoDyDx; + } + draw_point(x, y, state); + } + } + else + { + int p = 2 * dx - dy; + int twoDx = 2 * dx; + int twoDxDy = 2 * (dx - dy); + + if (y1 > y2) + { + x = x2; + y = y2; + End = y1; + step = x1 < x2 ? -1 : 1; + } + else + { + x = x1; + y = y1; + End = y2; + step = x2 < x1 ? -1 : 1; + } + + draw_point(x, y, state); + + while (y < End) + { + y++; + if (p < 0) + p += twoDx; + else + { + x += step; + p += twoDxDy; + } + draw_point(x, y, state); + } + } +} + +void CLCDDisplay::draw_fill_rect(int left, int top, int right, int bottom, int state) +{ + int x, y; + for (x = left + 1; x < right; x++) + { + for (y = top + 1; y < bottom; y++) + { + draw_point(x, y, state); + } + } +} + +void CLCDDisplay::draw_rectangle(int left, int top, int right, int bottom, int linestate, int fillstate) +{ + // coordinate checking in draw_pixel (-> you can draw lines only + // partly on screen) + + draw_line(left, top, right, top, linestate); + draw_line(left, top, left, bottom, linestate); + draw_line(right, top, right, bottom, linestate); + draw_line(left, bottom, right, bottom, linestate); + draw_fill_rect(left, top, right, bottom, fillstate); +} + +void CLCDDisplay::draw_polygon(int num_vertices, int *vertices, int state) +{ + // coordinate checking in draw_pixel (-> you can draw polygons only + // partly on screen) + + int i; + for (i = 0; i < num_vertices - 1; i++) + { + draw_line(vertices[(i << 1) + 0], + vertices[(i << 1) + 1], + vertices[(i << 1) + 2], + vertices[(i << 1) + 3], + state); + } + + draw_line(vertices[0], + vertices[1], + vertices[(num_vertices << 1) - 2], + vertices[(num_vertices << 1) - 1], + state); +} + +struct rawHeader +{ + uint8_t width_lo; + uint8_t width_hi; + uint8_t height_lo; + uint8_t height_hi; + uint8_t transp; +} __attribute__((packed)); + +bool CLCDDisplay::paintIcon(std::string filename, int x, int y, bool invert) +{ + struct rawHeader header; + uint16_t stride; + uint16_t height; + unsigned char *pixpos; + + int _fd; + filename = iconBasePath + filename; + + _fd = open(filename.c_str(), O_RDONLY); + + if (_fd == -1) + { + printf("\nerror while loading icon: %s\n\n", filename.c_str()); + return false; + } + + read(_fd, &header, sizeof(struct rawHeader)); + + stride = ((header.width_hi << 8) | header.width_lo) >> 1; + height = (header.height_hi << 8) | header.height_lo; + + unsigned char pixbuf[200]; + while (height-- > 0) + { + read(fd, &pixbuf, stride); + pixpos = (unsigned char *) &pixbuf; + for (int count2 = 0; count2 < stride; count2++) + { + unsigned char compressed = *pixpos; + + draw_point(x + (count2 << 1), y, ((((compressed & 0xf0) >> 4) != header.transp) ^ invert) ? PIXEL_ON : PIXEL_OFF); + draw_point(x + (count2 << 1) + 1, y, (((compressed & 0x0f) != header.transp) ^ invert) ? PIXEL_ON : PIXEL_OFF); + + pixpos++; + } + y++; + } + + close(_fd); + return true; +} + +void CLCDDisplay::clear_screen() +{ + memset(_buffer, 0, raw_buffer_size); +} + +void CLCDDisplay::dump_screen(raw_display_t *screen) +{ + memmove(*screen, _buffer, raw_buffer_size); +} + +void CLCDDisplay::load_screen_element(const raw_lcd_element_t *element, int left, int top) +{ + unsigned int i; + + //if (element->buffer) + // for (i = 0; i < element->header.height; i++) + // memmove(_buffer+((top+i) * xres)+left, element->buffer+(i * element->header.width), element->header.width); + // + if ((element->buffer) && (element->header.height <= yres - top)) + for (i = 0; i < min(element->header.height, yres - top); i++) + memmove(_buffer + ((top + i) * xres) + left, element->buffer + (i * element->header.width), min(element->header.width, xres - left)); + // +} + +void CLCDDisplay::load_screen(const raw_display_t *const screen) +{ + raw_lcd_element_t element; + element.buffer_size = raw_buffer_size; + element.buffer = *screen; + element.header.width = xres; + element.header.height = yres; + load_screen_element(&element, 0, 0); +} + +bool CLCDDisplay::load_png_element(const char *const filename, raw_lcd_element_t *element) +{ + png_structp png_ptr; + png_infop info_ptr; + unsigned int i; + unsigned int pass; + unsigned int number_passes; + int bit_depth; + int color_type; + int interlace_type; + png_uint_32 width; + png_uint_32 height; + png_byte *fbptr; + FILE *fh; + bool ret_value = false; + + if ((fh = fopen(filename, "rb"))) + { + if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) + { + if (!(info_ptr = png_create_info_struct(png_ptr))) + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + else + { +#if (PNG_LIBPNG_VER < 10500) + if (!(setjmp(png_ptr->jmpbuf))) +#else + if (!setjmp(png_jmpbuf(png_ptr))) +#endif + { + unsigned int lcd_height = yres; + unsigned int lcd_width = xres; + + png_init_io(png_ptr, fh); + + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); + + if ( + ((color_type == PNG_COLOR_TYPE_PALETTE) || + ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)) && + (bit_depth <= 8) && + (width <= lcd_width) && + (height <= lcd_height) + ) + { + printf("[CLCDDisplay] %s %s %dx%dx%d, type %d\n", __FUNCTION__, filename, width, height, bit_depth, color_type); + element->header.width = width; + element->header.height = height; + element->header.bpp = bit_depth; + if (!element->buffer) + { + element->buffer_size = width * height; + element->buffer = new unsigned char[element->buffer_size]; + lcd_width = width; + lcd_height = height; + } + + memset(element->buffer, 0, element->buffer_size); + + png_set_packing(png_ptr); /* expand to 1 byte blocks */ + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + + if (color_type & PNG_COLOR_MASK_COLOR) +#if (PNG_LIBPNG_VER < 10200) + png_set_rgb_to_gray(png_ptr); +#else + png_set_rgb_to_gray(png_ptr, 1, NULL, NULL); +#endif + + number_passes = png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr, info_ptr); + + if (width == png_get_rowbytes(png_ptr, info_ptr)) + { + ret_value = true; + + for (pass = 0; pass < number_passes; pass++) + { + fbptr = (png_byte *)element->buffer; + for (i = 0; i < element->header.height; i++) + { + //fbptr = row_pointers[i]; + //png_read_rows(png_ptr, &fbptr, NULL, 1); + + png_read_row(png_ptr, fbptr, NULL); + /* if the PNG is smaller, than the display width... */ + if (width < lcd_width) /* clear the area right of the PNG */ + memset(fbptr + width, 0, lcd_width - width); + fbptr += lcd_width; + } + } + png_read_end(png_ptr, info_ptr); + } + } + } + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + } + } + fclose(fh); + } + return ret_value; +} + +bool CLCDDisplay::load_png(const char *const filename) +{ + raw_lcd_element_t element; + element.buffer_size = raw_buffer_size; + element.buffer = _buffer; + return load_png_element(filename, &element); +} + +bool CLCDDisplay::dump_png_element(const char *const filename, raw_lcd_element_t *element) +{ + png_structp png_ptr; + png_infop info_ptr; + unsigned int i; + png_byte *fbptr; + FILE *fp; + bool ret_value = false; + + /* create file */ + fp = fopen(filename, "wb"); + if (!fp) + printf("[CLCDDisplay] File %s could not be opened for writing\n", filename); + else + { + /* initialize stuff */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + printf("[CLCDDisplay] png_create_write_struct failed\n"); + else + { + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + printf("[CLCDDisplay] png_create_info_struct failed\n"); + else + { + if (setjmp(png_jmpbuf(png_ptr))) + printf("[CLCDDisplay] Error during init_io\n"); + else + { + unsigned int lcd_height = yres; + unsigned int lcd_width = xres; + + png_init_io(png_ptr, fp); + + + /* write header */ + if (setjmp(png_jmpbuf(png_ptr))) + printf("[CLCDDisplay] Error during writing header\n"); + + png_set_IHDR(png_ptr, info_ptr, element->header.width, element->header.height, + element->header.bpp, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + + /* write bytes */ + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("[CLCDDisplay] Error during writing bytes\n"); + return ret_value; + } + + ret_value = true; + + fbptr = (png_byte *)element->buffer; + for (i = 0; i < element->header.height; i++) + { + png_write_row(png_ptr, fbptr); + fbptr += lcd_width; + } + + /* end write */ + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("[CLCDDisplay] Error during end of write\n"); + return ret_value; + } + + png_write_end(png_ptr, NULL); + } + } + } + fclose(fp); + } + + return ret_value; +} + +bool CLCDDisplay::dump_png(const char *const filename) +{ + raw_lcd_element_t element; + element.buffer_size = raw_buffer_size; + element.buffer = _buffer; + element.header.width = xres; + element.header.height = yres; + element.header.bpp = 8; + return dump_png_element(filename, &element); +} diff --git a/lib/liblcddisplay/lcddisplay.h b/lib/liblcddisplay/lcddisplay.h new file mode 100644 index 000000000..a79994c24 --- /dev/null +++ b/lib/liblcddisplay/lcddisplay.h @@ -0,0 +1,148 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + baseroutines by Shadow_ + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __lcddisplay__ +#define __lcddisplay__ + +#include +#include /* uint8_t */ + +#define LCD_DEVICE "/dev/dbox/lcd0" + +#define LCD_PIXEL_OFF 0x00 +#define LCD_PIXEL_ON 0xff +#define LCD_PIXEL_INV 0x1000000 + +// ioctls + +#define LCD_IOCTL_CLEAR (26) + +#define FP_IOCTL_LCD_DIMM 3 + +#ifndef LCD_IOCTL_ASC_MODE +#define LCDSET 0x1000 +#define LCD_IOCTL_ASC_MODE (21|LCDSET) +#define LCD_MODE_ASC 0 +#define LCD_MODE_BIN 1 +#endif + +#ifndef LCD_IOCTL_SRV +#define LCDSET 0x1000 +#define LCD_IOCTL_SRV (10|LCDSET) +#endif + +typedef unsigned char *raw_display_t; + +struct raw_lcd_element_header_t +{ + uint16_t width; + uint16_t height; + uint8_t bpp; +} __attribute__((packed)); + +struct raw_lcd_element_t +{ + raw_lcd_element_header_t header; + int buffer_size; + raw_display_t buffer; +}; + +class CLCDDisplay +{ + private: + int fd, paused; + std::string iconBasePath; + bool available; + + unsigned char inverted; + bool flipped; + int is_oled; //1=oled, 2=lcd, 3=??? + int last_brightness; + + raw_display_t _buffer; + int _stride; + raw_display_t surface_data; + int surface_stride; + int surface_bpp, surface_bypp; + int surface_buffer_size; + int real_offset; + int real_yres; + + public: + enum + { + PIXEL_ON = LCD_PIXEL_ON, + PIXEL_OFF = LCD_PIXEL_OFF, + PIXEL_INV = LCD_PIXEL_INV + }; + + CLCDDisplay(); + ~CLCDDisplay(); + + void pause(); + void resume(); + + void convert_data(); + void setIconBasePath(std::string bp) + { + iconBasePath = bp; + }; + bool isAvailable(); + + void update(); + + void surface_fill_rect(int area_left, int area_top, int area_right, int area_bottom, int color); + void draw_point(const int x, const int y, const int state); + void draw_line(const int x1, const int y1, const int x2, const int y2, const int state); + void draw_fill_rect(int left, int top, int right, int bottom, int state); + void draw_rectangle(int left, int top, int right, int bottom, int linestate, int fillstate); + void draw_polygon(int num_vertices, int *vertices, int state); + + bool paintIcon(std::string filename, int x, int y, bool invert); + void clear_screen(); + void dump_screen(raw_display_t *screen); + void load_screen_element(const raw_lcd_element_t *element, int left, int top); + void load_screen(const raw_display_t *const screen); + bool dump_png_element(const char *const filename, raw_lcd_element_t *element); + bool dump_png(const char *const filename); + bool load_png_element(const char *const filename, raw_lcd_element_t *element); + bool load_png(const char *const filename); + + void setSize(int w, int h, int b); + int setLCDContrast(int contrast); + int setLCDBrightness(int brightness); + void setInverted(unsigned char); + void setFlipped(bool); + bool isOled() const + { + return !!is_oled; + } + int raw_buffer_size; + int xres, yres, bpp; + int bypp; +}; + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index cf1d9b180..5a4c35812 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -127,6 +127,11 @@ neutrino_LDADD = \ -lrt -lpthread \ -lz +# LCD display +if ENABLE_LCD +neutrino_LDADD += $(top_builddir)/lib/liblcddisplay/liblcddisplay.a +endif + if ENABLE_GRAPHLCD neutrino_LDADD += \ driver/glcd/libneutrino_driver_glcd.a \ diff --git a/src/driver/Makefile.am b/src/driver/Makefile.am index 5d2b3b360..a756692cc 100644 --- a/src/driver/Makefile.am +++ b/src/driver/Makefile.am @@ -51,6 +51,10 @@ libneutrino_driver_a_SOURCES = \ streamts.cpp \ volume.cpp +if ENABLE_LCD +AM_CPPFLAGS += -I$(top_srcdir)/lib/liblcddisplay +endif + if ENABLE_LCD4LINUX libneutrino_driver_a_SOURCES += \ lcd4l.cpp @@ -76,11 +80,18 @@ libneutrino_driver_a_SOURCES += \ simple_display.cpp endif if BOXTYPE_ARMBOX +if ENABLE_LCD +libneutrino_driver_a_SOURCES += \ + fb_accel_arm.cpp \ + lcdd.cpp +else libneutrino_driver_a_SOURCES += \ fb_accel.cpp \ fb_accel_arm.cpp \ simple_display.cpp endif +endif + if BOXTYPE_MIPSBOX libneutrino_driver_a_SOURCES += \ fb_accel_mips.cpp \ diff --git a/src/driver/display.h b/src/driver/display.h index a68819f5a..591713d59 100644 --- a/src/driver/display.h +++ b/src/driver/display.h @@ -3,8 +3,13 @@ #include #endif #if HAVE_GENERIC_HARDWARE || HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE +#if BOXMODEL_OSMIO4KPLUS +#include +#define CVFD CLCD +#else #include #endif +#endif #ifdef ENABLE_GRAPHLCD #include #endif diff --git a/src/driver/lcdd.cpp b/src/driver/lcdd.cpp new file mode 100644 index 000000000..c58216227 --- /dev/null +++ b/src/driver/lcdd.cpp @@ -0,0 +1,961 @@ +/* + $Id: lcdd.cpp 2013/10/12 mohousch Exp $ + + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Copyright (C) 2008 Novell, Inc. Author: Stefan Seyfried + (C) 2009 Stefan Seyfried + (C) 2022 TangoCash + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +extern CRemoteControl *g_RemoteControl; /* neutrino.cpp */ + +/* from edvbstring.cpp */ +static bool is_UTF8(const std::string &string) +{ + unsigned int len = string.size(); + + for (unsigned int i = 0; i < len; ++i) + { + if (!(string[i] & 0x80)) // normal ASCII + continue; + + if ((string[i] & 0xE0) == 0xC0) // one char following. + { + // first, length check: + if (i + 1 >= len) + return false; // certainly NOT utf-8 + + i++; + + if ((string[i] & 0xC0) != 0x80) + return false; // no, not UTF-8. + } + else if ((string[i] & 0xF0) == 0xE0) + { + if ((i + 1) >= len) + return false; + + i++; + + if ((string[i] & 0xC0) != 0x80) + return false; + + i++; + + if ((string[i] & 0xC0) != 0x80) + return false; + } + } + + return true; // can be UTF8 (or pure ASCII, at least no non-UTF-8 8bit characters) +} + +CLCD::CLCD() + : configfile('\t') +{ + muted = false; + percentOver = 0; + volume = 0; + timeout_cnt = 0; + icon_dolby = false; + has_lcd = true; + is4digits = false; + clearClock = 0; + fheight_s = 8; + fheight_t = 12; + fheight_r = 16; + fheight_b = 10; + lcd_width = g_info.hw_caps->display_xres; + lcd_height = g_info.hw_caps->display_yres; +} + +CLCD::~CLCD() +{ +} + +CLCD *CLCD::getInstance() +{ + static CLCD *lcdd = NULL; + + if (lcdd == NULL) + { + lcdd = new CLCD(); + } + + return lcdd; +} + +void CLCD::count_down() +{ + if (timeout_cnt > 0) + { + timeout_cnt--; + + if (timeout_cnt == 0) + { + setlcdparameter(); + } + } +} + +void CLCD::wake_up() +{ + if (atoi(g_settings.lcd_setting_dim_time.c_str()) > 0) + { + timeout_cnt = atoi(g_settings.lcd_setting_dim_time.c_str()); + setlcdparameter(); + } +} + +void *CLCD::TimeThread(void *) +{ + while (1) + { + sleep(1); + struct stat buf; + + if (stat("/tmp/lcd.locked", &buf) == -1) + { + CLCD::getInstance()->showTime(); + CLCD::getInstance()->count_down(); + } + else + CLCD::getInstance()->wake_up(); + } + + return NULL; +} + +void CLCD::init(const char *fontfile, const char *fontname, const char *fontfile2, const char *fontname2, const char *fontfile3, const char *fontname3) +{ + if (!lcdInit(fontfile, fontname, fontfile2, fontname2, fontfile3, fontname3)) + { + dprintf(DEBUG_NORMAL, "CLCD::init: LCD-Init failed!\n"); + has_lcd = false; + } + + if (pthread_create(&thrTime, NULL, TimeThread, NULL) != 0) + { + perror("CLCD::init: pthread_create(TimeThread)"); + return ; + } +} + +bool CLCD::lcdInit(const char *fontfile, const char *fontname, const char *fontfile2, const char *fontname2, const char *fontfile3, const char *fontname3) +{ + fontRenderer = new LcdFontRenderClass(&display); + const char *style_name = fontRenderer->AddFont(fontfile); + const char *style_name2; + const char *style_name3; + const char *style_name4; + + if (fontfile2 != NULL) + style_name2 = fontRenderer->AddFont(fontfile2); + else + { + style_name2 = style_name; + fontname2 = fontname; + } + + if (fontfile3 != NULL) + style_name3 = fontRenderer->AddFont(fontfile3); + else + { + style_name3 = style_name; + fontname3 = fontname; + } + + style_name4 = style_name; + fontRenderer->InitFontCache(); + fonts.time = fontRenderer->getFont(fontname, style_name, fheight_t); + time_size = fonts.time->getRenderWidth("88:88") + 4; + fonts.small = fontRenderer->getFont(fontname2, style_name2, fheight_s); + fonts.regular = fontRenderer->getFont(fontname3, style_name3, fheight_r); + fonts.big = fontRenderer->getFont(fontname, style_name4, fheight_b); + setAutoDimm(g_settings.lcd_setting[SNeutrinoSettings::LCD_AUTODIMM]); + + if (!display.isAvailable()) + { + dprintf(DEBUG_NORMAL, "CLCD::lcdInit: exit...(no lcd-support)\n"); + return false; + } + + setMode(MODE_TVRADIO); + return true; +} + +void CLCD::displayUpdate() +{ + struct stat buf; + + if (stat("/tmp/lcd.locked", &buf) == -1) + { + display.update(); + } +} + +void CLCD::setlcdparameter(int dimm, const int contrast, const int power, const int inverse, const int bias) +{ + if (!has_lcd) + return; + + if (!display.isAvailable()) + return; + + int fd; + + if (power == 0) + dimm = 0; + + // dimm + display.setLCDBrightness(dimm); + // contrast + display.setLCDContrast(contrast); + + // power ??? + + //reverse + if (inverse) + display.setInverted(CLCDDisplay::PIXEL_ON); + else + display.setInverted(CLCDDisplay::PIXEL_OFF); +} + +void CLCD::setlcdparameter(void) +{ + if (!has_lcd) + return; + + last_toggle_state_power = g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER]; + int dim_time = atoi(g_settings.lcd_setting_dim_time); + int dim_brightness = g_settings.lcd_setting_dim_brightness; + bool timeouted = (dim_time > 0) && (timeout_cnt == 0); + int brightness, power = 0; + + if (timeouted) + brightness = dim_brightness; + else + brightness = g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS]; + + if (last_toggle_state_power && (!timeouted || dim_brightness > 0)) + power = 1; + + if (mode == MODE_STANDBY) + brightness = g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS]; + + setlcdparameter(brightness, + g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST], + power, + g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE], + 0 /*g_settings.lcd_setting[SNeutrinoSettings::LCD_BIAS]*/); +} + +static std::string removeLeadingSpaces(const std::string &text) +{ + int pos = text.find_first_not_of(" "); + + if (pos != -1) + return text.substr(pos); + + return text; +} + +static std::string splitString(const std::string &text, const int maxwidth, LcdFont *font, bool dumb, bool utf8) +{ + int pos; + std::string tmp = removeLeadingSpaces(text); + + if (font->getRenderWidth(tmp.c_str(), utf8) > maxwidth) + { + do + { + if (dumb) + tmp = tmp.substr(0, tmp.length() - 1); + else + { + pos = tmp.find_last_of("[ .-]+"); // TODO characters might be UTF-encoded! + + if (pos != -1) + tmp = tmp.substr(0, pos); + else // does not fit -> fall back to dumb split + tmp = tmp.substr(0, tmp.length() - 1); + } + } + while (font->getRenderWidth(tmp.c_str(), utf8) > maxwidth); + } + + return tmp; +} + +static int getProgress() +{ + int Progress = CLCD::getInstance()->percentOver; + t_channel_id channel_id = 0; + uint64_t ID = CNeutrinoApp::getInstance()->getMode(); + + if (ID == NeutrinoModes::mode_tv || ID == NeutrinoModes::mode_webtv || ID == NeutrinoModes::mode_radio || ID == NeutrinoModes::mode_webradio) + { + CZapitChannel *channel = CZapit::getInstance()->GetCurrentChannel(); + + if (channel) + channel_id = channel->getEpgID(); + + CSectionsdClient::CurrentNextInfo CurrentNext; + CEitManager::getInstance()->getCurrentNextServiceKey(channel_id, CurrentNext); + + if (CurrentNext.flags & CSectionsdClient::epgflags::has_current) + { + time_t cur_duration = CurrentNext.current_zeit.dauer; + time_t cur_start_time = CurrentNext.current_zeit.startzeit; + + if ((cur_duration > 0) && (cur_duration < 86400)) + { + Progress = 100 * (time(NULL) - cur_start_time) / cur_duration; + } + } + } + + return Progress; +} + +void CLCD::showTextScreen(const std::string &big, const std::string &small, const bool perform_wakeup, const bool centered) +{ + if (!has_lcd) + return; + + bool big_utf8 = false; + bool small_utf8 = false; + std::string cname[2]; + std::string event[2]; + int namelines = 0, maxnamelines = 1; + + if (!big.empty()) + { + bool dumb = false; + big_utf8 = is_UTF8(big); + + while (true) + { + namelines = 0; + std::string title = big; + + do // first try "intelligent" splitting + { + cname[namelines] = splitString(title, lcd_width, (small.empty()) ? fonts.regular : fonts.big, dumb, big_utf8); + title = removeLeadingSpaces(title.substr(cname[namelines].length())); + namelines++; + } + while (title.length() > 0 && namelines < maxnamelines); + + if (title.length() == 0) + break; + + dumb = !dumb; // retry with dumb splitting; + + if (!dumb) // second retry -> get out; + break; + } + } + + if (!small.empty()) + { + bool dumb = false; + small_utf8 = is_UTF8(small); + + while (true) + { + namelines = 0; + std::string title = small; + + do // first try "intelligent" splitting + { + event[namelines] = splitString(title, lcd_width, fonts.small, dumb, small_utf8); + title = removeLeadingSpaces(title.substr(event[namelines].length())); + namelines++; + } + while (title.length() > 0 && namelines < maxnamelines); + + if (title.length() == 0) + break; + + dumb = !dumb; // retry with dumb splitting; + + if (!dumb) // second retry -> get out; + break; + } + } + + // + int t_h = lcd_height - (fheight_t + 2); + + if (!showclock) + t_h = lcd_height; + + display.draw_fill_rect(-1, 0, lcd_width, t_h, CLCDDisplay::PIXEL_OFF); + // + int xb = 1; + int xs = 1; + int yb = (t_h - fheight_r) / 2 + fheight_r - 2; + int ys = 0; + + if (!small.empty()) + { + yb = 1 + fheight_b - 3; + ys = yb + fheight_s + 1; + } + + int wb = (small.empty()) ? fonts.regular->getRenderWidth(cname[0].c_str(), big_utf8) : fonts.big->getRenderWidth(cname[0].c_str(), big_utf8); + int ws = fonts.time->getRenderWidth(event[0].c_str(), small_utf8); + + if (centered) + { + xb = (lcd_width - wb) / 2; + xs = (lcd_width - ws) / 2; + } + + if (!small.empty()) + { + fonts.big->RenderString(xb, yb, lcd_width, cname[0].c_str(), CLCDDisplay::PIXEL_ON, 0, big_utf8); + fonts.small->RenderString(xs, ys, lcd_width, event[0].c_str(), CLCDDisplay::PIXEL_ON, 0, small_utf8); + } + else + fonts.regular->RenderString(xb, yb, lcd_width, cname[0].c_str(), CLCDDisplay::PIXEL_ON, 0, big_utf8); + + if (perform_wakeup) + wake_up(); + + displayUpdate(); +} + +void CLCD::showServicename(const std::string name, const bool perform_wakeup) +{ + printf("CLCD::showServicename '%s' epg: '%s'\n", name.c_str(), epg_title.c_str()); + + if (!name.empty()) + servicename = name; + + if (mode != MODE_TVRADIO) + return; + + showTextScreen(servicename, epg_title, perform_wakeup, true); + return; +} + +void CLCD::setEPGTitle(const std::string title) +{ + if (title == epg_title) + { + //fprintf(stderr,"CLCD::setEPGTitle: not changed\n"); + return; + } + + epg_title = title; + showServicename("", false); +} + +void CLCD::setMovieInfo(const AUDIOMODES playmode, const std::string big, const std::string small, const bool centered) +{ + movie_playmode = playmode; + movie_big = big; + movie_small = small; + movie_centered = centered; + + if (mode != MODE_MOVIE) + return; + + //showAudioPlayMode(movie_playmode); + showTextScreen(movie_big, "", true, movie_centered); +} + +void CLCD::setMovieAudio(const bool is_ac3) +{ + movie_is_ac3 = is_ac3; + + if (mode != MODE_MOVIE) + return; + + showPercentOver(percentOver, true, MODE_MOVIE); +} + +void CLCD::showTime() +{ + if (!has_lcd) + return; + + if (showclock) + { + char timestr[21]; + struct timeval tm; + struct tm *t; + invert = !invert; + gettimeofday(&tm, NULL); + t = localtime(&tm.tv_sec); + + if (mode == MODE_STANDBY) + { + display.clear_screen(); // clear lcd + //ShowNewClock(&display, t->tm_hour, t->tm_min, t->tm_sec, t->tm_wday, t->tm_mday, t->tm_mon, CNeutrinoApp::getInstance()->recordingstatus); + + if (!invert) + strftime((char *) ×tr, 20, "%H:%M:%S\n%d.%m.%y", t); + else + strftime((char *) ×tr, 20, "%H.%M:%S\n%d.%m.%y", t); + + display.draw_fill_rect(0, 0, lcd_width, lcd_height, CLCDDisplay::PIXEL_OFF); + fonts.time->RenderString(lcd_width / 4, lcd_height / 2, lcd_width, timestr, CLCDDisplay::PIXEL_ON); + } + else + { + if (CNeutrinoApp::getInstance()->recordingstatus && clearClock == 1) + { + if (!invert) + strftime((char *) ×tr, 20, "%H:%M", t); + else + strftime((char *) ×tr, 20, "%H.%M", t); + + clearClock = 0; + } + else + { + if (!invert) + strftime((char *) ×tr, 20, "%H:%M", t); + else + strftime((char *) ×tr, 20, "%H.%M", t); + + clearClock = 1; + } + + display.draw_fill_rect(lcd_width - time_size - 1, lcd_height - 12, lcd_width, lcd_height, clearClock ? CLCDDisplay::PIXEL_OFF : CLCDDisplay::PIXEL_ON); + fonts.time->RenderString(lcd_width - 4 - fonts.time->getRenderWidth(timestr), lcd_height - 2, time_size, timestr, clearClock ? CLCDDisplay::PIXEL_ON : CLCDDisplay::PIXEL_OFF); + showPercentOver(getProgress(), true); + } + + displayUpdate(); + } +} + +void CLCD::showRCLock(int duration) +{ + if (!has_lcd) + return; +} + +void CLCD::showVolume(const char vol, const bool perform_update) +{ + if (!has_lcd) + return; + + volume = vol; + showTextScreen(servicename, "Volume", true, true); + showPercentOver(vol, true); + //showTextScreen(servicename, "", true, true); + return; +} + +void CLCD::showPercentOver(const unsigned char perc, const bool perform_update, const MODES m) +{ + if (!has_lcd) + return; + + int left, top, width, height = 6; + percentOver = perc; + + if (!showclock) + return; + + left = 2; + top = lcd_height - height - 1 - 2; + width = lcd_width - left - 4 - time_size; + display.draw_rectangle(left - 2, top - 2, left + width + 2, top + height + 1, CLCDDisplay::PIXEL_ON, CLCDDisplay::PIXEL_OFF); + + if (perc == (unsigned char) -1) + { + display.draw_line(left, top, left + width, top + height - 1, CLCDDisplay::PIXEL_ON); + } + else + { + int dp; + + if (perc == (unsigned char) -2) + dp = width + 1; + else + dp = perc * (width + 1) / 100; + + display.draw_fill_rect(left - 1, top - 1, left + dp, top + height, CLCDDisplay::PIXEL_ON); + + if (perc == (unsigned char) -2) + { + // draw a "+" to show that the event is overdue + display.draw_line(left + width - 2, top + 1, left + width - 2, top + height - 2, CLCDDisplay::PIXEL_OFF); + display.draw_line(left + width - 1, top + (height / 2), left + width - 3, top + (height / 2), CLCDDisplay::PIXEL_OFF); + } + } + + if (perform_update) + displayUpdate(); +} + +void CLCD::showMenuText(const int position, const char *text, const int highlight, const bool utf_encoded) +{ + if (!has_lcd) + return; + + showTextScreen(text, "", true, true); +} + +void CLCD::showAudioTrack(const std::string &artist, const std::string &title, const std::string &album) +{ + if (!has_lcd) + return; +} + +void CLCD::showAudioPlayMode(AUDIOMODES m) +{ + if (!has_lcd) + return; + + display.draw_fill_rect(-1, 51, 10, 62, CLCDDisplay::PIXEL_OFF); + + switch (m) + { + case AUDIO_MODE_PLAY: + { + int x = 3, y = 53; + display.draw_line(x, y, x, y + 8, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 1, y + 1, x + 1, y + 7, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 2, y + 2, x + 2, y + 6, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 3, y + 3, x + 3, y + 5, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 4, y + 4, x + 4, y + 4, CLCDDisplay::PIXEL_ON); + break; + } + + case AUDIO_MODE_STOP: + display.draw_fill_rect(1, 53, 8, 61, CLCDDisplay::PIXEL_ON); + break; + + case AUDIO_MODE_PAUSE: + display.draw_line(1, 54, 1, 60, CLCDDisplay::PIXEL_ON); + display.draw_line(2, 54, 2, 60, CLCDDisplay::PIXEL_ON); + display.draw_line(6, 54, 6, 60, CLCDDisplay::PIXEL_ON); + display.draw_line(7, 54, 7, 60, CLCDDisplay::PIXEL_ON); + break; + + case AUDIO_MODE_FF: + { + int x = 2, y = 55; + display.draw_line(x, y, x, y + 4, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 1, y + 1, x + 1, y + 3, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 2, y + 2, x + 2, y + 2, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 3, y, x + 3, y + 4, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 4, y + 1, x + 4, y + 3, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 5, y + 2, x + 5, y + 2, CLCDDisplay::PIXEL_ON); + } + break; + + case AUDIO_MODE_REV: + { + int x = 2, y = 55; + display.draw_line(x, y + 2, x, y + 2, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 1, y + 1, x + 1, y + 3, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 2, y, x + 2, y + 4, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 3, y + 2, x + 3, y + 2, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 4, y + 1, x + 4, y + 3, CLCDDisplay::PIXEL_ON); + display.draw_line(x + 5, y, x + 5, y + 4, CLCDDisplay::PIXEL_ON); + } + break; + } + + wake_up(); + displayUpdate(); +} + +void CLCD::showAudioProgress(const char perc, bool isMuted) +{ + if (!has_lcd) + return; + + if (mode == MODE_AUDIO) + { + display.draw_fill_rect(11, 53, 73, 61, CLCDDisplay::PIXEL_OFF); + int dp = int(perc / 100.0 * 61.0 + 12.0); + display.draw_fill_rect(11, 54, dp, 60, CLCDDisplay::PIXEL_ON); + + if (isMuted) + { + if (dp > 12) + { + display.draw_line(12, 56, dp - 1, 56, CLCDDisplay::PIXEL_OFF); + display.draw_line(12, 58, dp - 1, 58, CLCDDisplay::PIXEL_OFF); + } + else + display.draw_line(12, 55, 72, 59, CLCDDisplay::PIXEL_ON); + } + + displayUpdate(); + } +} + +void CLCD::setMode(const MODES m, const char *const title) +{ + if (!has_lcd) + return; + + mode = m; + menutitle = title; + setlcdparameter(); + + switch (m) + { + case MODE_TVRADIO: + case MODE_WEBTV: + display.clear_screen(); // clear lcd + showclock = true; + showServicename(servicename); + showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + + case MODE_MOVIE: + display.clear_screen(); // clear lcd + showclock = false; + setMovieInfo(movie_playmode, movie_big, movie_small, movie_centered); + break; + + case MODE_AUDIO: + { + display.clear_screen(); // clear lcd + showAudioPlayMode(AUDIO_MODE_STOP); + showVolume(volume, false); + showclock = true; + showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + } + + case MODE_AVINPUT: + display.clear_screen(); // clear lcd + showVolume(volume, false); + showclock = true; + showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + + case MODE_MENU_UTF8: + showclock = false; + display.clear_screen(); // clear lcd + displayUpdate(); + break; + + case MODE_SHUTDOWN: + showclock = false; + display.clear_screen(); // clear lcd + displayUpdate(); + break; + + case MODE_STANDBY: + showclock = true; + showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + /* "showTime()" clears the whole lcd in MODE_STANDBY */ + break; + } + + wake_up(); +} + + +void CLCD::setBrightness(int bright) +{ + g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS] = bright; + setlcdparameter(); +} + +int CLCD::getBrightness() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS]; +} + +void CLCD::setBrightnessStandby(int bright) +{ + g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] = bright; + setlcdparameter(); +} + +int CLCD::getBrightnessStandby() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS]; +} + +void CLCD::setContrast(int contrast) +{ + g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST] = contrast; + setlcdparameter(); +} + +int CLCD::getContrast() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST]; +} + +void CLCD::setScrollMode(int scroll_repeats) +{ + printf("CLCD::%s scroll_repeats:%d\n", __func__, scroll_repeats); +} + +void CLCD::setPower(int power) +{ + g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER] = power; + setlcdparameter(); +} + +int CLCD::getPower() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER]; +} + +void CLCD::togglePower(void) +{ + last_toggle_state_power = 1 - last_toggle_state_power; + setlcdparameter((mode == MODE_STANDBY) ? g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] : g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS], + g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST], + last_toggle_state_power, + g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE], + 0 /*g_settings.lcd_setting[SNeutrinoSettings::LCD_BIAS]*/); +} + +void CLCD::setInverse(int inverse) +{ + g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE] = inverse; + setlcdparameter(); +} + +int CLCD::getInverse() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE]; +} + +void CLCD::setAutoDimm(int /*autodimm*/) +{ +} + +int CLCD::getAutoDimm() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_AUTODIMM]; +} + +void CLCD::setMuted(bool mu) +{ + muted = mu; + showVolume(volume); +} + +void CLCD::resume() +{ + if (!has_lcd) + return; + + display.resume(); +} + +void CLCD::pause() +{ + if (!has_lcd) + return; + + display.pause(); +} + +void CLCD::UpdateIcons() +{ +} + +void CLCD::ShowIcon(vfd_icon icon, bool show) +{ +} + +void CLCD::ShowIcon(fp_icon i, bool on) +{ + vfd_icon icon = (vfd_icon) i; + ShowIcon(icon, on); +} + +void CLCD::Lock() +{ +} + +void CLCD::Unlock() +{ +} + +void CLCD::Clear() +{ + if (!has_lcd) + return; + + if (mode == MODE_SHUTDOWN) + { + display.clear_screen(); // clear lcd + displayUpdate(); + } + + return; +} + +bool CLCD::ShowPng(char *filename) +{ + if (!has_lcd) + return false; + + return display.load_png(filename); +} + +bool CLCD::DumpPng(char *filename) +{ + if (!has_lcd) + return false; + + return display.dump_png(filename); +} diff --git a/src/driver/lcdd.h b/src/driver/lcdd.h new file mode 100644 index 000000000..7e0ec00e5 --- /dev/null +++ b/src/driver/lcdd.h @@ -0,0 +1,313 @@ +/* + $Id: lcdd.h 2013/10/12 mohousch Exp $ + + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __lcdd__ +#define __lcdd__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef LCD_UPDATE +// TODO Why is USE_FILE_OFFSET64 not defined, if file.h is included here???? +#ifndef __USE_FILE_OFFSET64 +#define __USE_FILE_OFFSET64 1 +#endif +#include +#endif // LCD_UPDATE + +#include +#include + +#include + +#define LCDDIR_VAR CONFIGDIR "/icons/lcdd" + +typedef enum +{ + VFD_ICON_BAR8 = 0x00000004, + VFD_ICON_BAR7 = 0x00000008, + VFD_ICON_BAR6 = 0x00000010, + VFD_ICON_BAR5 = 0x00000020, + VFD_ICON_BAR4 = 0x00000040, + VFD_ICON_BAR3 = 0x00000080, + VFD_ICON_BAR2 = 0x00000100, + VFD_ICON_BAR1 = 0x00000200, + VFD_ICON_FRAME = 0x00000400, + VFD_ICON_HDD = 0x00000800, + VFD_ICON_MUTE = 0x00001000, + VFD_ICON_DOLBY = 0x00002000, + VFD_ICON_POWER = 0x00004000, + VFD_ICON_TIMESHIFT = 0x00008000, + VFD_ICON_SIGNAL = 0x00010000, + VFD_ICON_TV = 0x00020000, + VFD_ICON_RADIO = 0x00040000, + VFD_ICON_HD = 0x01000001, + VFD_ICON_1080P = 0x02000001, + VFD_ICON_1080I = 0x03000001, + VFD_ICON_720P = 0x04000001, + VFD_ICON_480P = 0x05000001, + VFD_ICON_480I = 0x06000001, + VFD_ICON_USB = 0x07000001, + VFD_ICON_MP3 = 0x08000001, + VFD_ICON_PLAY = 0x09000001, + VFD_ICON_COL1 = 0x09000002, + VFD_ICON_PAUSE = 0x0A000001, + VFD_ICON_CAM1 = 0x0B000001, + VFD_ICON_COL2 = 0x0B000002, + VFD_ICON_CAM2 = 0x0C000001, + VFD_ICON_CLOCK, + VFD_ICON_FR, + VFD_ICON_FF, + VFD_ICON_DD, + VFD_ICON_SCRAMBLED, + VFD_ICON_LOCK +} vfd_icon; + +typedef enum +{ + FP_ICON_BAR8 = 0x00000004, + FP_ICON_BAR7 = 0x00000008, + FP_ICON_BAR6 = 0x00000010, + FP_ICON_BAR5 = 0x00000020, + FP_ICON_BAR4 = 0x00000040, + FP_ICON_BAR3 = 0x00000080, + FP_ICON_BAR2 = 0x00000100, + FP_ICON_BAR1 = 0x00000200, + FP_ICON_FRAME = 0x00000400, + FP_ICON_HDD = 0x00000800, + FP_ICON_MUTE = 0x00001000, + FP_ICON_DOLBY = 0x00002000, + FP_ICON_POWER = 0x00004000, + FP_ICON_TIMESHIFT = 0x00008000, + FP_ICON_SIGNAL = 0x00010000, + FP_ICON_TV = 0x00020000, + FP_ICON_RADIO = 0x00040000, + FP_ICON_HD = 0x01000001, + FP_ICON_1080P = 0x02000001, + FP_ICON_1080I = 0x03000001, + FP_ICON_720P = 0x04000001, + FP_ICON_480P = 0x05000001, + FP_ICON_480I = 0x06000001, + FP_ICON_USB = 0x07000001, + FP_ICON_MP3 = 0x08000001, + FP_ICON_PLAY = 0x09000001, + FP_ICON_COL1 = 0x09000002, + FP_ICON_PAUSE = 0x0A000001, + FP_ICON_CAM1 = 0x0B000001, + FP_ICON_COL2 = 0x0B000002, + FP_ICON_CAM2 = 0x0C000001, + FP_ICON_CLOCK, + FP_ICON_FR, + FP_ICON_FF, + FP_ICON_DD, + FP_ICON_SCRAMBLED, + FP_ICON_LOCK +} fp_icon; + +class CLCDPainter; +class LcdFontRenderClass; + +class CLCD +{ + public: + + enum MODES + { + MODE_TVRADIO, + MODE_AVINPUT, + MODE_SHUTDOWN, + MODE_STANDBY, + MODE_MENU_UTF8, + MODE_AUDIO, + MODE_MOVIE, + MODE_PIC, + MODE_WEBTV + }; + enum AUDIOMODES + { + AUDIO_MODE_PLAY, + AUDIO_MODE_STOP, + AUDIO_MODE_FF, + AUDIO_MODE_PAUSE, + AUDIO_MODE_REV + }; + + private: + + class FontsDef + { + public: + LcdFont *regular; + LcdFont *time; + LcdFont *big; + LcdFont *small; + }; + + CLCDDisplay display; + LcdFontRenderClass *fontRenderer; + FontsDef fonts; + int fheight_r, fheight_t, fheight_b, fheight_s; + int time_size; + + MODES mode; + AUDIOMODES movie_playmode; + + std::string servicename; + std::string epg_title; + std::string movie_big; + std::string movie_small; + std::string menutitle; + char volume; + bool muted; + bool showclock; + bool movie_centered; + bool movie_is_ac3; + bool icon_dolby; + CConfigFile configfile; + pthread_t thrTime; + int last_toggle_state_power; + int clearClock; + unsigned int timeout_cnt; + bool invert = false; + + unsigned int lcd_width; + unsigned int lcd_height; + + void count_down(); + + static void *TimeThread(void *); + bool lcdInit(const char *fontfile1, const char *fontname1, + const char *fontfile2 = NULL, const char *fontname2 = NULL, + const char *fontfile3 = NULL, const char *fontname3 = NULL); + void setlcdparameter(int dimm, int contrast, int power, int inverse, int bias); + void displayUpdate(); + void showTextScreen(const std::string &big, const std::string &small, bool perform_wakeup, bool centered = false); + + public: + CLCD(); + ~CLCD(); + + bool has_lcd; + bool is4digits; + void wake_up(); + void setled(void) + { + return; + }; + void setlcdparameter(void); + + static CLCD *getInstance(); + void init(const char *fontfile, const char *fontname, + const char *fontfile2 = NULL, const char *fontname2 = NULL, + const char *fontfile3 = NULL, const char *fontname3 = NULL); + + void setMode(const MODES m, const char *const title = ""); + MODES getMode() + { + return mode; + }; + + void showServicename(const std::string name, const bool perform_wakeup = true); // UTF-8 + void setEPGTitle(const std::string title); + void setMovieInfo(const AUDIOMODES playmode, const std::string big, const std::string small, const bool centered = false); + void setMovieAudio(const bool is_ac3); + std::string getMenutitle() + { + return menutitle; + }; + void showTime(); + /** blocks for duration seconds */ + void showRCLock(int duration = 2); + void showVolume(const char vol, const bool perform_update = true); + void showPercentOver(const unsigned char perc, const bool perform_update = true, const MODES m = MODE_TVRADIO); + void showMenuText(const int position, const char *text, const int highlight = -1, const bool utf_encoded = false); + void showAudioTrack(const std::string &artist, const std::string &title, const std::string &album); + void showAudioPlayMode(AUDIOMODES m = AUDIO_MODE_PLAY); + void showAudioProgress(const char perc, bool isMuted = false); + void setBrightness(int); + void setBacklight(int) { return;}; + int getBrightness(); + + void setBrightnessStandby(int); + int getBrightnessStandby(); + + void setScrollMode(int scroll_repeats); + + void setContrast(int); + int getContrast(); + + void setPower(int); + int getPower(); + + void togglePower(void); + + void setInverse(int); + int getInverse(); + + void setAutoDimm(int); + int getAutoDimm(); + void setBrightnessDeepStandby(int) + { + return ; + }; + int getBrightnessDeepStandby() + { + return 0; + }; + + void repaintIcons() + { + return; + }; + void setMuted(bool); + + void resume(); + void pause(); + + void Lock(); + void Unlock(); + void Clear(); + void UpdateIcons(); + void ShowIcon(fp_icon icon, bool show); + void ShowIcon(vfd_icon icon, bool show); + void ShowText(const char *s) + { + showServicename(std::string(s)); + }; + void LCDshowText(int /*pos*/) + { + return ; + }; + + bool ShowPng(char *filename); + bool DumpPng(char *filename); + + unsigned char percentOver; +}; + +#endif diff --git a/src/gui/vfd_setup.cpp b/src/gui/vfd_setup.cpp index cf44e002d..76acd2249 100644 --- a/src/gui/vfd_setup.cpp +++ b/src/gui/vfd_setup.cpp @@ -166,6 +166,16 @@ int CVfdSetup::showSetup() } CMenuOptionChooser *oj; +#ifdef ENABLE_LCD +#if 0 + // option power + oj = new CMenuOptionChooser("Power LCD"/*LOCALE_LCDMENU_POWER*/, &g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER], OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, new CLCDNotifier(), CRCInput::RC_nokey); + vfds->addItem(oj); +#endif + // option invert + oj = new CMenuOptionChooser("Invert LCD"/*LOCALE_LCDMENU_INVERSE*/, &g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE], OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, new CLCDNotifier(), CRCInput::RC_nokey); + vfds->addItem(oj); +#endif if (g_info.hw_caps->display_has_statusline) { // status line options @@ -174,6 +184,7 @@ int CVfdSetup::showSetup() vfds->addItem(oj); } +#ifndef ENABLE_LCD // info line options oj = new CMenuOptionChooser(LOCALE_LCD_INFO_LINE, &g_settings.lcd_info_line, LCD_INFO_OPTIONS, LCD_INFO_OPTION_COUNT, vfd_enabled); oj->setHint("", LOCALE_MENU_HINT_VFD_INFOLINE); @@ -200,6 +211,7 @@ int CVfdSetup::showSetup() oj = new CMenuOptionChooser(LOCALE_LCDMENU_NOTIFY_RCLOCK, &g_settings.lcd_notify_rclock, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, vfd_enabled); oj->setHint("", LOCALE_MENU_HINT_VFD_NOTIFY_RCLOCK); vfds->addItem(oj); +#endif // ENABLE_LCD } if (g_info.hw_caps->display_type == HW_DISPLAY_LED_NUM) @@ -273,25 +285,41 @@ int CVfdSetup::showBrightnessSetup() brightnessstandby = CVFD::getInstance()->getBrightnessStandby(); brightnessdeepstandby = CVFD::getInstance()->getBrightnessDeepStandby(); +#ifdef ENABLE_LCD + nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESS, &brightness, true, 0, 255, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#else nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESS, &brightness, true, 0, 15, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#endif nc->setHint("", LOCALE_MENU_HINT_VFD_BRIGHTNESS); nc->setActivateObserver(this); mn_widget->addItem(nc); +#ifdef ENABLE_LCD + nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, &brightnessstandby, true, 0, 255, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#else nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, &brightnessstandby, true, 0, 15, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#endif nc->setHint("", LOCALE_MENU_HINT_VFD_BRIGHTNESSSTANDBY); nc->setActivateObserver(this); mn_widget->addItem(nc); if (g_info.hw_caps->display_can_deepstandby) { +#ifdef ENABLE_LCD + nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESSDEEPSTANDBY, &brightnessdeepstandby, true, 0, 255, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#else nc = new CMenuOptionNumberChooser(LOCALE_LCDCONTROLER_BRIGHTNESSDEEPSTANDBY, &brightnessdeepstandby, true, 0, 15, this, CRCInput::RC_nokey, NULL, 0, 0, NONEXISTANT_LOCALE, true); +#endif nc->setHint("", LOCALE_MENU_HINT_VFD_BRIGHTNESSDEEPSTANDBY); nc->setActivateObserver(this); mn_widget->addItem(nc); } +#ifdef ENABLE_LCD + nc = new CMenuOptionNumberChooser(LOCALE_LCDMENU_DIM_BRIGHTNESS, &g_settings.lcd_setting_dim_brightness, true, -1, 255, NULL, CRCInput::RC_nokey, NULL, 0, -1, LOCALE_OPTIONS_OFF, true); +#else nc = new CMenuOptionNumberChooser(LOCALE_LCDMENU_DIM_BRIGHTNESS, &g_settings.lcd_setting_dim_brightness, vfd_enabled, -1, 15, NULL, CRCInput::RC_nokey, NULL, 0, -1, LOCALE_OPTIONS_OFF, true); +#endif nc->setHint("", LOCALE_MENU_HINT_VFD_BRIGHTNESSDIM); nc->setActivateObserver(this); mn_widget->addItem(nc); @@ -385,10 +413,12 @@ bool CVfdSetup::changeNotify(const neutrino_locale_t OptionName, void * /* data { CVFD::getInstance()->setled(); } +#ifndef ENABLE_LCD else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_LEDCONTROLER_BACKLIGHT_TV)) { CVFD::getInstance()->setBacklight(g_settings.backlight_tv); } +#endif else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_LCDMENU_SCROLL) || ARE_LOCALES_EQUAL(OptionName, LOCALE_LCDMENU_SCROLL_REPEATS)) { CVFD::getInstance()->setScrollMode(g_settings.lcd_scroll); @@ -425,3 +455,21 @@ void CVfdSetup::activateNotify(const neutrino_locale_t OptionName) CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); } } + +#ifdef ENABLE_LCD +// lcd notifier +bool CLCDNotifier::changeNotify(const neutrino_locale_t, void * Data) +{ + int state = *(int *)Data; + + dprintf(DEBUG_NORMAL, "CLCDNotifier: state: %d\n", state); +#if 0 + CVFD::getInstance()->setPower(state); +#else + CVFD::getInstance()->setPower(1); +#endif + CVFD::getInstance()->setlcdparameter(); + + return true; +} +#endif diff --git a/src/gui/vfd_setup.h b/src/gui/vfd_setup.h index a8e39411b..f219a8133 100644 --- a/src/gui/vfd_setup.h +++ b/src/gui/vfd_setup.h @@ -59,4 +59,13 @@ class CVfdSetup : public CMenuTarget, CChangeObserver, CActivateObserver int exec(CMenuTarget *parent, const std::string &actionKey); }; +#ifdef ENABLE_LCD +// lcd notifier +class CLCDNotifier : public CChangeObserver +{ + public: + bool changeNotify(const neutrino_locale_t, void *Data); +}; +#endif + #endif