diff options
-rw-r--r-- | glcddrivers/framebuffer.c | 296 | ||||
-rw-r--r-- | glcddrivers/framebuffer.h | 9 | ||||
-rw-r--r-- | graphlcd.conf | 21 |
3 files changed, 272 insertions, 54 deletions
diff --git a/glcddrivers/framebuffer.c b/glcddrivers/framebuffer.c index a77900d..254f487 100644 --- a/glcddrivers/framebuffer.c +++ b/glcddrivers/framebuffer.c @@ -37,6 +37,7 @@ cDriverFramebuffer::cDriverFramebuffer(cDriverConfig * config) int cDriverFramebuffer::Init() { +#if 0 // default values width = config->width; if (width <= 0) @@ -44,7 +45,10 @@ int cDriverFramebuffer::Init() height = config->height; if (height <= 0) height = 240; +#endif zoom = 1; + damage = 0; + depth = 1; for (unsigned int i = 0; i < config->options.size(); i++) { @@ -57,11 +61,34 @@ int cDriverFramebuffer::Init() syslog(LOG_ERR, "%s error: zoom %d not supported, using default (%d)!\n", config->name.c_str(), z, zoom); } + else if (config->options[i].name == "ReportDamage" || config->options[i].name == "Damage" ) + { + if (config->options[i].value == "none") { + damage = 0; + } else if (config->options[i].value == "ugly") { + damage = 1; + } else if (config->options[i].value == "udlfb") { + damage = 2; + } else if (config->options[i].value == "auto") { + damage = -1; + } + else + syslog(LOG_ERR, "%s error: ReportDamage='%s' not supported, continuing w/o damage reporting!\n", + config->name.c_str(), config->options[i].value.c_str()); + } } - // Open the file for reading and writing - fbfd = open("/dev/fb0", O_RDWR); - if (1 == fbfd) + if (config->device == "") + { + fbfd = open("/dev/fb0", O_RDWR); + } + else + { + fbfd = open(config->device.c_str(), O_RDWR); + } + + //fbfd = open("/dev/fb0", O_RDWR); + if (fbfd < 0) { syslog(LOG_ERR, "%s: cannot open framebuffer device.\n", config->name.c_str()); return -1; @@ -82,11 +109,78 @@ int cDriverFramebuffer::Init() return -1; } + if ( ! ( vinfo.bits_per_pixel == 8 || vinfo.bits_per_pixel == 16 || vinfo.bits_per_pixel == 24 || vinfo.bits_per_pixel == 32 ) ) + { + syslog(LOG_ERR, "%s: bpp %d not supported.\n", config->name.c_str(), vinfo.bits_per_pixel); + return -1; + } + + // get colour info + if (vinfo.bits_per_pixel > 8) { + rlen = vinfo.red.length; + glen = vinfo.green.length; + blen = vinfo.blue.length; + alen = vinfo.transp.length; + + roff = vinfo.red.offset; + goff = vinfo.green.offset; + boff = vinfo.blue.offset; + aoff = vinfo.transp.offset; + } else { + // init colour map + struct fb_cmap cmap; + uint16_t r[256], g[256], b[256]; + int i; + + // RGB332 code from fbsplash-project taken as guideline + for ( i = 0; i < 256; i ++ ) { + r[i] = (( i & 0xe0) << 8) + ((i & 0x20) ? 0x1fff : 0); + g[i] = (( i & 0x1c) << 11) + ((i & 0x04) ? 0x1fff : 0); + b[i] = (( i & 0x03) << 14) + ((i & 0x01) ? 0x3fff : 0); + } + cmap.start = 0; + cmap.len = 256; + cmap.red = r; + cmap.green = g; + cmap.blue = b; + cmap.transp = 0; + if (ioctl(fbfd, FBIOPUTCMAP, &cmap)) { + syslog(LOG_ERR, "%s: Error setting colour map for bpp=8.\n", config->name.c_str()); + return -1; + } + } + // Figure out the size of the screen in bytes screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; syslog(LOG_INFO, "%s: V01: xres: %d, yres %d, vyres: %d, bpp: %d, linelenght: %d\n", config->name.c_str(),vinfo.xres,vinfo.yres,vinfo.yres_virtual,vinfo.bits_per_pixel,finfo.line_length); + // auto calc w/h depending on zoom + if (zoom == 1) { + width = vinfo.xres >> 1; + height = vinfo.yres >> 1; + } else { + width = vinfo.xres; + height = vinfo.yres; + } + depth = vinfo.bits_per_pixel; + + // init bounding box + bbox[0] = width - 1; // x top + bbox[1] = height - 1; // y top + bbox[2] = 0; // x bottom + bbox[3] = 0; // y bottom + + + // damage reporting == auto: detect framebuffer driver + if (damage == -1) { + if (strncasecmp(finfo.id, "udlfb", 16) == 0) { + damage = 2; // udlfb + } else { /* not supported / not detected */ + damage = 0; // not detected -> no damage reporting + } + } + // reserve another memory to draw into offbuff = new char[screensize]; if (!offbuff) @@ -148,7 +242,9 @@ int cDriverFramebuffer::CheckSetup() void cDriverFramebuffer::SetPixel(int x, int y, uint32_t data) { int location; - int outcol; + unsigned char col1, col2, col3, alpha = 0; + uint32_t colraw; + int changed = 0; if (x >= width || y >= height) return; @@ -161,70 +257,107 @@ void cDriverFramebuffer::SetPixel(int x, int y, uint32_t data) // Figure out where in memory to put the pixel - location = (x*(1+zoom)+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + - (y*(1+zoom)+vinfo.yoffset) * finfo.line_length; - - if (data == GRAPHLCD_White) { - if (vinfo.bits_per_pixel <= 8) { - outcol = 15; - } else { - outcol = 255; - } - } else { - outcol = 0; - } + location = ( (x << zoom) + vinfo.xoffset) * (vinfo.bits_per_pixel >> 3) + + ( (y << zoom) + vinfo.yoffset) * finfo.line_length; if (vinfo.bits_per_pixel <= 8) { - *(offbuff + location) = outcol; - if (zoom == 1) - { - *(offbuff + location + 1) = outcol; - *(offbuff + location + finfo.line_length) = outcol; - *(offbuff + location + finfo.line_length + 1) = outcol; + col1 = ((data & 0x00FF0000) >> (16 + 5) << 5) | // RRRg ggbb + ((data & 0x0000FF00) >> ( 8 + 5) << 2) | // rrrG GGbb + ((data & 0x000000FF) >> ( 6) ); // rrrg ggBB + if ( *(offbuff + location) != col1) { + changed = 1; + *(offbuff + location) = col1; + if (zoom == 1) + { + *(offbuff + location + 1) = col1; + *(offbuff + location + finfo.line_length) = col1; + *(offbuff + location + finfo.line_length + 1) = col1; + } } } else if (vinfo.bits_per_pixel <= 16) { - *(offbuff + location) = outcol; - *(offbuff + location + 1) = outcol; - if (zoom == 1) - { - *(offbuff + location + 2) = outcol; - *(offbuff + location + 3) = outcol; - *(offbuff + location + finfo.line_length) = outcol; - *(offbuff + location + finfo.line_length + 1) = outcol; - *(offbuff + location + finfo.line_length + 2) = outcol; - *(offbuff + location + finfo.line_length + 3) = outcol; + + + colraw = ((data & 0x00FF0000) >> (16 + 8 - rlen) << roff) | // red + ((data & 0x0000FF00) >> ( 8 + 8 - glen) << goff) | // green + ((data & 0x000000FF) >> ( 0 + 8 - blen) << boff); // blue + col1 = colraw & 0x0000FF; + col2 = (colraw & 0x00FF00) >> 8; + if ( *(offbuff + location) != col1 || *(offbuff + location + 1) != col2 ) { + changed = 1; + *(offbuff + location) = col1; + *(offbuff + location + 1) = col2; + if (zoom == 1) + { + *(offbuff + location + 2) = col1; + *(offbuff + location + 3) = col2; + *(offbuff + location + finfo.line_length) = col1; + *(offbuff + location + finfo.line_length + 1) = col2; + *(offbuff + location + finfo.line_length + 2) = col1; + *(offbuff + location + finfo.line_length + 3) = col2; + } } } else { - *(offbuff + location) = outcol; - *(offbuff + location + 1) = outcol; - *(offbuff + location + 2) = outcol; - *(offbuff + location + 3) = 0; /* should be transparency */ - if (zoom == 1) - { - *(offbuff + location + 4) = outcol; - *(offbuff + location + 5) = outcol; - *(offbuff + location + 6) = outcol; - *(offbuff + location + 7) = 0; - *(offbuff + location + finfo.line_length) = outcol; - *(offbuff + location + finfo.line_length + 1) = outcol; - *(offbuff + location + finfo.line_length + 2) = outcol; - *(offbuff + location + finfo.line_length + 3) = 0; - *(offbuff + location + finfo.line_length + 4) = outcol; - *(offbuff + location + finfo.line_length + 5) = outcol; - *(offbuff + location + finfo.line_length + 6) = outcol; - *(offbuff + location + finfo.line_length + 7) = 0; + // remap graphlcd colour representation to framebuffer rep. + colraw = ((data & 0x00FF0000) >> (16 + 8 - rlen) << roff) | // red + ((data & 0x0000FF00) >> ( 8 + 8 - glen) << goff) | // green + ((data & 0x000000FF) >> ( 0 + 8 - blen) << boff); // blue + col1 = (colraw & 0x000000FF); + col2 = (colraw & 0x0000FF00) >> 8; + col3 = (colraw & 0x00FF0000) >> 16; + + if (vinfo.bits_per_pixel == 32) { + colraw |= ((data & 0xFF000000) >> (24 + 8 - alen) << aoff); // transp. + alpha = (colraw & 0xFF000000) >> 24; + } + + if ( *(offbuff + location) != col1 || *(offbuff + location + 1) != col2 || *(offbuff + location + 2) != col3 ) { + int pos = 0; + changed = 1; + *(offbuff + location + pos++) = col1; + *(offbuff + location + pos++) = col2; + *(offbuff + location + pos++) = col3; + if (vinfo.bits_per_pixel > 24) + *(offbuff + location + pos++) = alpha; /* should be transparency */ + if (zoom == 1) + { + *(offbuff + location + pos++) = col1; + *(offbuff + location + pos++) = col2; + *(offbuff + location + pos++) = col3; + if (vinfo.bits_per_pixel > 24) + *(offbuff + location + pos++) = alpha; + pos = 0; + *(offbuff + location + finfo.line_length + pos++) = col1; + *(offbuff + location + finfo.line_length + pos++) = col2; + *(offbuff + location + finfo.line_length + pos++) = col3; + if (vinfo.bits_per_pixel > 24) + *(offbuff + location + finfo.line_length + pos++) = alpha; + *(offbuff + location + finfo.line_length + pos++) = col1; + *(offbuff + location + finfo.line_length + pos++) = col2; + *(offbuff + location + finfo.line_length + pos++) = col3; + if (vinfo.bits_per_pixel > 24) + *(offbuff + location + finfo.line_length + pos++) = alpha; + } } } + + if (changed) { + // bounding box changed? + if (x < bbox[0]) bbox[0] = x; + if (y < bbox[1]) bbox[1] = y; + if (x > bbox[2]) bbox[2] = x; + if (y > bbox[3]) bbox[3] = y; + } } void cDriverFramebuffer::Clear() { memset(offbuff, 0, screensize); + processDamage(); } #if 0 @@ -245,6 +378,69 @@ void cDriverFramebuffer::Set8Pixels(int x, int y, unsigned char data) void cDriverFramebuffer::Refresh(bool refreshAll) { memcpy(fbp, offbuff, screensize); + processDamage(); +} + +bool cDriverFramebuffer::GetDriverFeature (const std::string & Feature, int & value) { + if (offbuff) { + if (strcasecmp(Feature.c_str(), "depth") == 0) { + value = depth; + return true; + } else if (strcasecmp(Feature.c_str(), "ismonochrome") == 0) { + value = 0; + return true; + } else if (strcasecmp(Feature.c_str(), "isgreyscale") == 0 || strcasecmp(Feature.c_str(), "isgrayscale") == 0) { + value = 0; + return true; + } else if (strcasecmp(Feature.c_str(), "iscolour") == 0 || strcasecmp(Feature.c_str(), "iscolor") == 0) { + value = 1; + return true; +#if 0 + } else if (strcasecmp(Feature.c_str(), "touch") == 0 || strcasecmp(Feature.c_str(), "touchscreen") == 0) { + if (...) { + value = (...) ? 1 : 0; + } + return true; +#endif + } + } + value = 0; + return false; +} + + +/* defines for different damage processing calls needed by _update() */ +#define DLFB_IOCTL_REPORT_DAMAGE 0xAA +void cDriverFramebuffer::processDamage (void) { + switch (damage) { + case 1: // ugly + { + unsigned char buf[3] = "\n"; + write(fbfd,buf,2); + } + break; + case 2: // udlfb + { + struct fb_rect { + int x; int y; int w; int h; + } damage = {0, 0, 0, 0}; + damage.x = bbox[0] << zoom; + damage.y = bbox[1] << zoom; + damage.w = (bbox[2] - bbox[0] + 1) << zoom; + damage.h = (bbox[3] - bbox[1] + 1) << zoom; + + ioctl(fbfd, DLFB_IOCTL_REPORT_DAMAGE, &damage); + } + break; + default: // no damage reporting + break; + } + + /* reset bounding box */ + bbox[0] = width - 1; + bbox[1] = height - 1; + bbox[2] = 0; + bbox[3] = 0; } } // end of namespace diff --git a/glcddrivers/framebuffer.h b/glcddrivers/framebuffer.h index 0d94562..328b0c9 100644 --- a/glcddrivers/framebuffer.h +++ b/glcddrivers/framebuffer.h @@ -34,9 +34,16 @@ private: long int screensize; void *fbp; int zoom; + int damage; + int bbox[4]; + int depth; + uint32_t roff, boff, goff, aoff; + uint32_t rlen, blen, glen, alen; int CheckSetup(); - + void processDamage (void); +protected: + virtual bool GetDriverFeature (const std::string & Feature, int & value); public: cDriverFramebuffer(cDriverConfig * config); diff --git a/graphlcd.conf b/graphlcd.conf index bb5a0f5..bd39705 100644 --- a/graphlcd.conf +++ b/graphlcd.conf @@ -120,17 +120,32 @@ WaitPriority=0 [framebuffer] # framebuffer driver # Output goes to a framebuffer device -# Default size: 320 x 240 Driver=framebuffer -#Width=320 -#Height=240 #UpsideDown=no #Invert=no +# Device +# Framebuffer device +# Default value: /dev/fb0 +#Device=/dev/fb0 + +# Damage | ReportDamage +# Damage reporting for framebuffer devices with update problems +# Possible values: none, auto, udlfb, ugly +# none: no damage reporting +# auto: automatic determination if damage reporting is needed +# udlfb: damage reporting for udlfb-devices (displaylink) +# ugly: dirty damagereporting (a '\n' is written to the framebuffer file handle +# Default value: none +#Damage=none + # Zoom # Determines if pixels should be drawn double sized. +# If zoom is set, the actual resolution will be halved (both width and height) +# e.g.: framebuffer has resolution 800x600: this driver will report a drawing area of 400x300 # Possible values: 0, 1 +# Default value: 1 Zoom=1 ######################################################################## |