*** busybox-1.15.0/include/usage.h.orig 2009-08-22 14:52:14.000000000 +0200 --- busybox-1.15.0/include/usage.h 2009-09-01 08:42:18.000000000 +0200 *************** *** 156,162 **** "\n -n Start new tone" \ #define fbsplash_trivial_usage \ ! "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]" #define fbsplash_full_usage "\n\n" \ "Options:\n" \ "\n -s Image" \ --- 156,163 ---- "\n -n Start new tone" \ #define fbsplash_trivial_usage \ ! "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD] " \ ! IF_FEATURE_FBSPLASH_FONTLOAD( "[-m FONTMAPFILE]" ) #define fbsplash_full_usage "\n\n" \ "Options:\n" \ "\n -s Image" \ *************** *** 165,172 **** --- 166,178 ---- "\n -i Config file (var=value):" \ "\n BAR_LEFT,BAR_TOP,BAR_WIDTH,BAR_HEIGHT" \ "\n BAR_R,BAR_G,BAR_B" \ + "\n TEXT_LEFT,TEXT_TOP,TEXT_R,TEXT_G,TEXT_B,TEXT_SIZE" \ "\n -f Control pipe (else exit after drawing image)" \ "\n commands: 'NN' (% for progress bar) or 'exit'" \ + IF_FEATURE_FBSPLASH_FONTLOAD( \ + "\n -m Font map file" \ + "\n commands: 'NN' (% for progress bar), 'write:string to print' or 'exit'" \ + ) #define brctl_trivial_usage \ "COMMAND [BRIDGE [INTERFACE]]" *** busybox-1.15.0/miscutils/Config.in.orig 2009-08-22 17:59:16.000000000 +0200 --- busybox-1.15.0/miscutils/Config.in 2009-09-01 08:44:41.000000000 +0200 *************** *** 256,262 **** default n help Shows splash image and progress bar on framebuffer device. ! Can be used during boot phase of an embedded device. ~2kb. Usage: - use kernel option 'vga=xxx' or otherwise enable fb device. - put somewhere fbsplash.cfg file and an image in .ppm format. --- 256,263 ---- default n help Shows splash image and progress bar on framebuffer device. ! Can be used during boot phase of an embedded device. ! Code size is 2..7kb depends on enabled features. Usage: - use kernel option 'vga=xxx' or otherwise enable fb device. - put somewhere fbsplash.cfg file and an image in .ppm format. *************** *** 272,277 **** --- 273,316 ---- "NN" (ASCII decimal number) - percentage to show on progress bar "exit" - well you guessed it + config FEATURE_FBSPLASH_8BPP + bool "support 8bit depth with color mapping" + depends on FBSPLASH + help + support for low color framebuffer that's need color mapping ~350 b + + config FEATURE_FBSPLASH_SPRITES + bool "Render icons and animations" + default n + depends on FBSPLASH + help + This option adds the ability to display icons on the + image displayed by the fbsplash applet. ~1.5kb. + - command for fifo: + "load:x:y:filename" - load images from file + each sprite is one square area when image is wider than higher + before load new sprite set, old area is cleared. + "sprite:n" - show one sprite - sprite 0 is background. + "anime:n:m:t" - anime from sprite n up/down to sprite m + time between sprites is in t * 100 msec. + + config FEATURE_FBSPLASH_TEXT + bool "text rendering" + default n + depends on FBSPLASH + help + This option adds the ability to print text messages on the + image displayed by the fbsplash applet. ~2kb. + - command for fifo: + "write:string to print" - print the string after the word "write:" + + config FEATURE_FBSPLASH_FONTLOAD + bool "font loading" + depends on FEATURE_FBSPLASH_TEXT + help + The font is loaded from a standard console font file. + Supported are psf v1.x and psf v2.0 files including mapping ~1kb. + config FLASH_LOCK bool "flash_lock" default n *** busybox-1.15.0/miscutils/fbsplash.c.orig 2009-08-21 00:26:14.000000000 +0200 --- busybox-1.15.0/miscutils/fbsplash.c 2009-09-01 08:42:18.000000000 +0200 *************** *** 1,6 **** --- 1,7 ---- /* vi: set sw=4 ts=4: */ /* * Copyright (C) 2008 Michele Sanges + * Copyright (C) 2009 Jiri Gabriel * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * *************** *** 10,51 **** * - run applet: $ setsid fbsplash [params] & * -c: hide cursor * -d /dev/fbN: framebuffer device (if not /dev/fb0) ! * -s path_to_image_file (can be "-" for stdin) * -i path_to_cfg_file * -f path_to_fifo (can be "-" for stdin) * - if you want to run it only in presence of a kernel parameter * (for example fbsplash=on), use: * grep -q "fbsplash=on" /* If you want logging messages on /tmp/fbsplash.log... */ #define DEBUG 0 ! #define BYTES_PER_PIXEL 2 ! typedef unsigned short DATA; struct globals { #if DEBUG bool bdebug_messages; // enable/disable logging FILE *logfile_fd; // log file #endif ! unsigned char *addr; // pointer to framebuffer memory ! unsigned ns[7]; // n-parameters const char *image_filename; struct fb_var_screeninfo scr_var; struct fb_fix_screeninfo scr_fix; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) #define nbar_width ns[0] // progress bar width #define nbar_height ns[1] // progress bar height --- 11,198 ---- * - run applet: $ setsid fbsplash [params] & * -c: hide cursor * -d /dev/fbN: framebuffer device (if not /dev/fb0) ! * -s path_to_image_file - ppm or ppm.gz or ppm.bz2 * -i path_to_cfg_file * -f path_to_fifo (can be "-" for stdin) + * -m font map file - standard psf file (v1 and v2) can be with unumap * - if you want to run it only in presence of a kernel parameter * (for example fbsplash=on), use: * grep -q "fbsplash=on" + #if ENABLE_FEATURE_FBSPLASH_TEXT + #include + + /* if you have fonts with sequences can turn this to 1 i not find any font with this feature + ~510 bytes of code */ + #define ENABLE_FEATURE_FBSPLASH_PSFSEQS 0 + + /* if you not use text colors and need optimize size - disable text colors + ~250 bytes of code */ + #define ENABLE_FEATURE_FBSPLASH_TEXT_COLORS 1 + + #if !defined(GIO_UNIMAP) + struct unipair { + unsigned short unicode; + unsigned short fontpos; + } __atribute__ ((packed)); + + struct unimapdesc { + unsigned short entry_ct; + struct unipair *entries; + } __atribute__ ((packed)); + #endif + + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD + + enum { // uint16 + PSF1_MAGIC = 0x0436, + PSF1_SEPARATOR = 0xFFFF, + PSF1_STARTSEQ = 0xFFFE + }; + + enum { // uint32 + PSF2_MAGIC = 0x864ab572, + PSF2_HAS_UNICODE_TABLE = 0x01, /* bits used in flags */ + PSF2_MAXVERSION = 0 /* max version recognized so far */ + }; + + enum { // uint8 + PSF1_MODE512 = 0x01, + PSF1_MODEHASTAB = 0x02, + PSF1_MODEHASSEQ = 0x04, + PSF1_MAXMODE = 0x05, + PSF2_STARTSEQ = 0xFE, + PSF2_SEPARATOR = 0xFF + }; + + struct psf1_header { + uint16_t magic; /* Magic number */ + uint8_t mode; /* PSF font mode */ + uint8_t charsize; /* Character size */ + } __attribute__ ((packed)); + + struct psf2_header { + uint32_t magic; + uint32_t version; + uint32_t headersize; /* offset of bitmaps in file */ + uint32_t flags; + uint32_t length; /* number of glyphs */ + uint32_t charsize; /* number of bytes for each character */ + uint32_t height, width; /* max dimensions of glyphs */ + /* charsize = height * ((width + 7) / 8) */ + } __attribute__ ((packed)); + + #endif // FONTLOAD + struct fbfont { + uint8_t *mem1; + uint8_t *glyphs; + struct unimapdesc ud; + int height; + int width; + int glyphcount; + int charsize; + int rowbytes; + }; + + #endif /* If you want logging messages on /tmp/fbsplash.log... */ #define DEBUG 0 ! typedef uint32_t RGB; ! struct sprite { ! int w,h,bw; // width height in pixels, width in bytes ! uint8_t pixbuf[0]; ! }; ! ! struct ppmfile { ! int fd; ! int sizx,sizy; ! int bufhead,buftail; ! uint8_t buf[0]; // buf for entire pixels row ! }; struct globals { #if DEBUG bool bdebug_messages; // enable/disable logging FILE *logfile_fd; // log file #endif ! unsigned char *addr; // pointer to visible framebuffer memory ! unsigned char *mapaddr; // pointer to framebuffer memory ! #if ENABLE_FEATURE_FBSPLASH_TEXT ! #define FBCFGVALS 13 ! #else ! #define FBCFGVALS 7 ! #endif ! unsigned ns[FBCFGVALS]; // n-parameters const char *image_filename; struct fb_var_screeninfo scr_var; struct fb_fix_screeninfo scr_fix; + int bytes_per_pixel,x0,y0; // offset for centering + struct sprite *spb1,*spb2; + #if ENABLE_FEATURE_FBSPLASH_TEXT + struct fbfont *font; + struct sprite *spt; + uint8_t *unknown_glyph; + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + uint8_t combglyph[4*32]; /* max width=32 max height=32 */ + #endif + #endif }; + #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) + #if ENABLE_FEATURE_FBSPLASH_TEXT + #define GF (*(ptr_to_globals->font)) + #endif #define nbar_width ns[0] // progress bar width #define nbar_height ns[1] // progress bar height *************** *** 54,59 **** --- 201,275 ---- #define nbar_colr ns[4] // progress bar color red component #define nbar_colg ns[5] // progress bar color green component #define nbar_colb ns[6] // progress bar color blue component + #define text_posx ns[7] // text horizontal position + #define text_posy ns[8] // text vertical position + #define text_colr ns[9] // text color red component + #define text_colg ns[10] // text color green component + #define text_colb ns[11] // text color blue component + #define text_size ns[12] // text size (1 to 4) + + /** + * A function to compute pixel value + */ + static RGB FAST_FUNC makecol(uint16_t r, uint16_t g, uint16_t b) { + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + #if ENABLE_FEATURE_FBSPLASH_8BPP + if (G.bytes_per_pixel == 1) { + // mymap is 6*8*5 = 240 values on offset 16 + // (r/42.6666) + (g/32) + (b/51.2) + return 16 + (((r << 8) + r) / 13107) * 40 + + (((g << 8) + g) / 9362) * 5 + + (((b << 8) + b) / 16383); + } + #endif + return ((r >> (8 - G.scr_var.red.length )) << G.scr_var.red.offset) | + ((g >> (8 - G.scr_var.green.length)) << G.scr_var.green.offset) | + ((b >> (8 - G.scr_var.blue.length )) << G.scr_var.blue.offset); + } + + /* + * A function to put the pixel row + */ + static void fb_putpixrow(int x, int y, int w, uint8_t *rgb) { + uint8_t *a; + int realw; + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres) return; + a = G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; + realw = ((x + w) < G.scr_var.xres ? w : G.scr_var.xres - x) * G.bytes_per_pixel; + memcpy(a,rgb,realw); + return; + } + + /* + * A function to put the sprite to position or fill sprite from videoram + */ + static void fb_sprite(int x, int y, int x0, int width, struct sprite *s, int direction) { + uint8_t *a,*p; + int realw,iy; + + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres || + x0 >= s->w || x0 < 0 || width > (s->w - x0)) { + return; + } + realw = ((x + width) < G.scr_var.xres ? width : (G.scr_var.xres - x)) * G.bytes_per_pixel; + a = G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; + p = &(s->pixbuf[x0 * G.bytes_per_pixel]); + for (iy = 0; iy < s->h; iy++) { + if ((iy + y) > G.scr_var.yres) break; + if (direction == 1) { + memcpy(p,a,realw); + } else { + memcpy(a,p,realw); + } + a += G.scr_fix.line_length; + p += s->bw; + } + return; + } + #define fb_putsprite(x,y,s) fb_sprite(x,y,0,s->w,s,0) + #define fb_getsprite(x,y,s) fb_sprite(x,y,0,s->w,s,1) #if DEBUG #define DEBUG_MESSAGE(strMessage, args...) \ *************** *** 78,231 **** xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var); xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix); ! if (G.scr_var.bits_per_pixel != 16) ! bb_error_msg_and_die("only 16 bpp is supported"); // map the device in memory ! G.addr = mmap(NULL, ! G.scr_var.xres * G.scr_var.yres ! * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/ , PROT_WRITE, MAP_SHARED, fbfd, 0); ! if (G.addr == MAP_FAILED) bb_perror_msg_and_die("mmap"); close(fbfd); } /** - * Draw hollow rectangle on framebuffer - */ - static void fb_drawrectangle(void) - { - int cnt; - DATA thispix; - DATA *ptr1, *ptr2; - unsigned char nred = G.nbar_colr/2; - unsigned char ngreen = G.nbar_colg/2; - unsigned char nblue = G.nbar_colb/2; - - nred >>= 3; // 5-bit red - ngreen >>= 2; // 6-bit green - nblue >>= 3; // 5-bit blue - thispix = nblue + (ngreen << 5) + (nred << (5+6)); - - // horizontal lines - ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); - ptr2 = (DATA*)(G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); - cnt = G.nbar_width - 1; - do { - *ptr1++ = thispix; - *ptr2++ = thispix; - } while (--cnt >= 0); - - // vertical lines - ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); - ptr2 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * BYTES_PER_PIXEL); - cnt = G.nbar_height - 1 /* HUH?! G.nbar_posy + G.nbar_height - 1 - G.nbar_posy*/; - do { - *ptr1 = thispix; ptr1 += G.scr_var.xres; - *ptr2 = thispix; ptr2 += G.scr_var.xres; - } while (--cnt >= 0); - } - - - /** - * Draw filled rectangle on framebuffer - * \param nx1pos,ny1pos upper left position - * \param nx2pos,ny2pos down right position - * \param nred,ngreen,nblue rgb color - */ - static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos, - unsigned char nred, unsigned char ngreen, unsigned char nblue) - { - int cnt1, cnt2, nypos; - DATA thispix; - DATA *ptr; - - nred >>= 3; // 5-bit red - ngreen >>= 2; // 6-bit green - nblue >>= 3; // 5-bit blue - thispix = nblue + (ngreen << 5) + (nred << (5+6)); - - cnt1 = ny2pos - ny1pos; - nypos = ny1pos; - do { - ptr = (DATA*)(G.addr + (nypos * G.scr_var.xres + nx1pos) * BYTES_PER_PIXEL); - cnt2 = nx2pos - nx1pos; - do { - *ptr++ = thispix; - } while (--cnt2 >= 0); - - nypos++; - } while (--cnt1 >= 0); - } - - - /** * Draw a progress bar on framebuffer ! * \param percent percentage of loading */ ! static void fb_drawprogressbar(unsigned percent) { ! int i, left_x, top_y, width, height; ! // outer box ! left_x = G.nbar_posx; ! top_y = G.nbar_posy; ! width = G.nbar_width - 1; ! height = G.nbar_height - 1; ! if ((height | width) < 0) return; ! // NB: "width" of 1 actually makes rect with width of 2! ! fb_drawrectangle(); ! ! // inner "empty" rectangle ! left_x++; ! top_y++; ! width -= 2; ! height -= 2; ! if ((height | width) < 0) return; ! fb_drawfullrectangle( ! left_x, top_y, ! left_x + width, top_y + height, ! G.nbar_colr, G.nbar_colg, G.nbar_colb); ! ! if (percent > 0) { ! // actual progress bar ! width = width * percent / 100; ! i = height; ! if (height == 0) ! height++; // divide by 0 is bad ! while (i >= 0) { ! // draw one-line thick "rectangle" ! // top line will have gray lvl 200, bottom one 100 ! unsigned gray_level = 100 + i*100/height; ! fb_drawfullrectangle( ! left_x, top_y, left_x + width, top_y, ! gray_level, gray_level, gray_level); ! top_y++; ! i--; ! } ! } } /** ! * Draw image from PPM file */ ! static void fb_drawimage(void) ! { ! char *head, *ptr; ! FILE *theme_file; ! unsigned char *pixline; ! unsigned i, j, width, height, line_size; ! ! theme_file = xfopen_stdin(G.image_filename); ! head = xmalloc(256); ! /* parse ppm header ! * - A ppm image’s magic number is the two characters "P6". * - Whitespace (blanks, TABs, CRs, LFs). * - A width, formatted as ASCII characters in decimal. * - Whitespace. --- 294,411 ---- xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var); xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix); ! #if ENABLE_FEATURE_FBSPLASH_8BPP ! if ((G.scr_var.bits_per_pixel < 8) || ! (G.scr_fix.type != FB_TYPE_PACKED_PIXELS)) { ! bb_error_msg_and_die("only 8,15,16,24 and 32 bpp is supported"); ! } ! #else ! if ((G.scr_var.bits_per_pixel < 15) || ! (G.scr_fix.type != FB_TYPE_PACKED_PIXELS)) { ! bb_error_msg_and_die("only 15,16,24 and 32 bpp is supported"); ! } ! #endif ! switch (G.scr_fix.visual) { ! #if ENABLE_FEATURE_FBSPLASH_8BPP ! case FB_VISUAL_PSEUDOCOLOR: do { ! uint8_t *cmem; ! struct fb_cmap *cmap; ! int r,g,b,i; ! cmem = xzalloc(sizeof(struct fb_cmap) + sizeof(uint16_t) * 3 * 256); ! cmap = (struct fb_cmap *)cmem; ! cmap->len = 256; ! cmap->red = (uint16_t *)&(cmem[sizeof(struct fb_cmap)]); ! cmap->green = &(cmap->red[256]); ! cmap->blue = &(cmap->green[256]); ! xioctl(fbfd, FBIOGETCMAP, cmem); // read original palette ! // fill cmap with my 6x8x5 pallette ! for (r = 0,i = 16; r < 6; r++) { ! for (g = 0; g < 8; g++) { ! for (b = 0; b < 5; b++, i++) { ! cmap->red[i] = r * 13107; ! cmap->green[i] = g * 9362; ! cmap->blue[i] = b * 16383; ! } ! } ! } ! xioctl(fbfd, FBIOPUTCMAP, cmem); ! free(cmem); ! } while(0); ! #endif ! case FB_VISUAL_TRUECOLOR: ! break; ! default: ! bb_error_msg_and_die("only TrueColor modes is supported"); ! } + G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) / 8; // map the device in memory ! G.mapaddr = mmap(NULL, ! G.scr_var.yres_virtual * G.scr_fix.line_length , PROT_WRITE, MAP_SHARED, fbfd, 0); ! if (G.mapaddr == MAP_FAILED) bb_perror_msg_and_die("mmap"); + G.addr = G.mapaddr + G.scr_var.yoffset * G.scr_fix.line_length; close(fbfd); } /** * Draw a progress bar on framebuffer ! * \param frompct percentage on which bar starting ! * \param topct percentage where bar end */ ! static void fb_drawnbar(int frompct, int topct) { ! int w0,w1,w2; ! if (G.spb1 == NULL || G.spb2 == NULL) return; ! if (frompct < 0 || frompct > 100 || ! topct < 0 || topct > 100 || ! frompct > topct) { return; ! } ! w0 = G.spb1->w * frompct / 100; ! w0 = (w0 > G.spb1->w ? G.spb1->w : w0); ! w2 = G.spb1->w * (100 - topct) / 100; ! w2 = ((w0 + w2) > G.spb1->w ? G.spb1->w - w0 : w2); ! w1 = G.spb1->w - w0 - w2; ! if (w0 > 0) ! fb_sprite(G.nbar_posx, G.nbar_posy, 0, w0, G.spb1, 0); // left "empty" area ! if (w1 > 0) ! fb_sprite(G.nbar_posx + w0, G.nbar_posy, w0, w1, G.spb2, 0); // filled area ! if (w2 > 0) ! fb_sprite(G.nbar_posx + w0 + w1, G.nbar_posy, w0 + w1, w2, G.spb1, 0); // right "empty" area } + static struct sprite *fb_newsprite(int pw, int ph, int scrx, int scry) { + struct sprite *s; + s=xmalloc(sizeof(struct sprite) + + ph * pw * G.bytes_per_pixel); + s->w = pw; + s->h = ph; + s->bw = pw * G.bytes_per_pixel; + if (scrx >= 0) fb_getsprite(scrx, scry, s); // read + return s; + } /** ! * Open PPM file - must be binary, color maxval 255 */ ! static struct ppmfile *ppm_open(const char *filename) { ! int i,k,l,val; ! struct ppmfile *p; ! uint8_t *s; ! char *e; ! ! i = open_zipped(filename); ! if (i == -1) return NULL; ! p = (struct ppmfile *) xmalloc(sizeof(struct ppmfile) + 600); ! // initial buf 600 bytes header cannot be larger ! p->fd = i; /* parse ppm header ! * - A ppm images magic number is the two characters "P6". * - Whitespace (blanks, TABs, CRs, LFs). * - A width, formatted as ASCII characters in decimal. * - Whitespace. *************** *** 237,292 **** * - A raster of Width * Height pixels in triplets of rgb * in pure binary by 1 (or not implemented 2) bytes. */ ! while (1) { ! if (fgets(head, 256, theme_file) == NULL ! /* do not overrun the buffer */ ! || strlen(bb_common_bufsiz1) >= sizeof(bb_common_bufsiz1) - 256) ! bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); ! ! ptr = memchr(skip_whitespace(head), '#', 256); ! if (ptr != NULL) ! *ptr = 0; /* ignore comments */ ! strcat(bb_common_bufsiz1, head); ! // width, height, max_color_val ! if (sscanf(bb_common_bufsiz1, "P6 %u %u %u", &width, &height, &i) == 3 ! && i <= 255) ! break; ! /* If we do not find a signature throughout the whole file then ! we will diagnose this via EOF on read in the head of the loop. */ } ! if (ENABLE_FEATURE_CLEAN_UP) ! free(head); ! if (width != G.scr_var.xres || height != G.scr_var.yres) bb_error_msg_and_die("PPM %dx%d does not match screen %dx%d", ! width, height, G.scr_var.xres, G.scr_var.yres); ! line_size = width*3; if (width > G.scr_var.xres) width = G.scr_var.xres; if (height > G.scr_var.yres) height = G.scr_var.yres; ! pixline = xmalloc(line_size); ! for (j = 0; j < height; j++) { ! unsigned char *pixel = pixline; ! DATA *src = (DATA *)(G.addr + j * G.scr_fix.line_length); ! if (fread(pixline, 1, line_size, theme_file) != line_size) ! bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); for (i = 0; i < width; i++) { ! unsigned thispix; ! thispix = (((unsigned)pixel[0] << 8) & 0xf800) ! | (((unsigned)pixel[1] << 3) & 0x07e0) ! | (((unsigned)pixel[2] >> 3)); ! *src++ = thispix; pixel += 3; } } ! if (ENABLE_FEATURE_CLEAN_UP) ! free(pixline); ! fclose(theme_file); } /** * Parse configuration file --- 417,1159 ---- * - A raster of Width * Height pixels in triplets of rgb * in pure binary by 1 (or not implemented 2) bytes. */ ! l=read(p->fd,&(p->buf[0]),600); ! if ((l < 0) || (p->buf[0] != 'P') || (p->buf[1] != '6')) { ! goto ppmopen_end; ! } ! for (s = &(p->buf[2]), i = 0, k = 0; s < &(p->buf[l]) && (k < 3); ) { ! switch (i) { ! case 0: ! if (*s <= ' ') { ! s++; // skip whitespaces ! } else { ! i++; // next state ! } ! break; ! case 1: // num or rmks ! if (*s == '#') { ! i = 2; ! } else { ! val=bb_strtoi((char *)s,&e,10); ! if (s == (uint8_t *)e) goto ppmopen_end; // no number - error ! s=(uint8_t *)e; ! i=0; // skip whitespaces ! switch (++k) { ! case 1: p->sizx = val; break; ! case 2: p->sizy = val; break; ! case 3: ! if (val > 255) ! goto ppmopen_end; // only 1 byte per color ! s++; ! p->bufhead = s - &(p->buf[0]); ! p->buftail = l; ! break; ! } ! } ! break; ! case 2: // skip to eol ! if (*s == 10) i=0; ! s++; ! break; ! } ! } ! if (p->sizx < 1 || p->sizy < 1) goto ppmopen_end; ! if (p->sizx > 200) { ! // buf is only 600 bytes = 200 pix prealocated - need more. ! p = xrealloc(p,sizeof(struct ppmfile) + 3 * p->sizx); } + return p; ! ppmopen_end: ! close(p->fd); ! free(p); ! return NULL; ! } ! ! /** ! * Read pixelline from ppm to internal buffer and return pointer ! */ ! static uint8_t *ppm_readline(struct ppmfile *p) { ! int i; ! if (p==NULL) return NULL; ! if (p->bufhead < p->buftail) { ! // pack buffer ! for (i = 0; p->bufhead < p->buftail; i++) { ! p->buf[i]=p->buf[p->bufhead++]; ! } ! p->buftail = i; ! } else { ! p->buftail = 0; ! } ! while (p->buftail < (p->sizx * 3)) { ! i=read(p->fd, &(p->buf[p->buftail]), (p->sizx * 3) - p->buftail); ! if (i < 0) return NULL; ! p->buftail += i; ! } ! p->bufhead = p->sizx * 3; ! return &(p->buf[0]); ! } ! ! /** ! * Close ppmfile ! */ ! ! static void ppm_close(struct ppmfile *p) { ! if (p == NULL) return; ! close(p->fd); ! free(p); ! } ! ! /** ! * Draws a PPM image. ! */ ! static void fb_drawimage(void) ! { ! struct ppmfile *ppm; ! unsigned char *pixel; ! unsigned char *rgbline,*rgbpix,*sprpix; ! RGB thispix; ! unsigned i, j, width, height; ! ! ppm = ppm_open(G.image_filename); ! if (ppm == NULL) ! bb_error_msg_and_die("PPM file %s cannot be opened",G.image_filename); ! width = ppm->sizx; ! height = ppm->sizy; ! if (width > G.scr_var.xres || height > G.scr_var.yres) bb_error_msg_and_die("PPM %dx%d does not match screen %dx%d", ! width, height, G.scr_var.xres, G.scr_var.yres); if (width > G.scr_var.xres) width = G.scr_var.xres; if (height > G.scr_var.yres) height = G.scr_var.yres; ! G.x0 = (width == G.scr_var.xres ? 0 : (G.scr_var.xres - width) / 2); ! G.y0 = (height == G.scr_var.yres ? 0 : (G.scr_var.yres - height) / 2); ! rgbline = xmalloc((width * G.bytes_per_pixel) + sizeof(RGB)); ! for (j = 0; j < height; j++) { ! pixel = ppm_readline(ppm); ! rgbpix = rgbline; ! if (pixel == NULL) ! bb_error_msg_and_die("bad PPM file '%s' line %d", G.image_filename,j); ! if (G.spb2 != NULL && j >= G.nbar_posy && j < (G.nbar_posy + G.spb2->h)) { ! sprpix = &(G.spb2->pixbuf[(j - G.nbar_posy) * G.spb2->bw]); ! } else { ! sprpix = NULL; ! } for (i = 0; i < width; i++) { ! thispix = makecol(pixel[0],pixel[1],pixel[2]); ! *((RGB*)rgbpix) = thispix; ! if ((sprpix != NULL) && (i >= G.nbar_posx)) { ! if (G.nbar_colr >= 256) { // Multiple not add ! thispix = makecol((G.nbar_colr & 255) * pixel[0], (G.nbar_colg & 255) * pixel[1], (G.nbar_colb & 255) * pixel[2]); ! } else { ! thispix = makecol(G.nbar_colr + pixel[0], G.nbar_colg + pixel[1], G.nbar_colb + pixel[2]); ! } ! memcpy(sprpix,&thispix,G.bytes_per_pixel); ! if (i >= (G.nbar_posx + G.spb2->w - 1)) { ! sprpix = NULL; ! } else { ! sprpix += G.bytes_per_pixel; ! } ! } pixel += 3; + rgbpix += G.bytes_per_pixel; + } + fb_putpixrow(G.x0,j + G.y0,width,rgbline); + } + free(rgbline); + ppm_close(ppm); + // recalc cfg if centered image + G.nbar_posx += G.x0; + G.nbar_posy += G.y0; + #if ENABLE_FEATURE_FBSPLASH_TEXT + G.text_posx += G.x0; + G.text_posy += G.y0; + #endif + } + + #if ENABLE_FEATURE_FBSPLASH_SPRITES + /** + * Read rectangle sprites from ppm file + * \return pointer to array of sprites + * \return number of sprites loaded + */ + static struct sprite *fb_loadsprites(char *filename, int sx, int sy) { + struct ppmfile *ppm; + uint8_t *pixel, *rgbpix; + struct sprite *s=NULL; + int y,i; + RGB thispix; + + if ((ppm = ppm_open(filename)) == NULL) goto readsprites_out; + s = fb_newsprite(ppm->sizx + ppm->sizy, ppm->sizy, -1, -1); + fb_sprite(sx + G.x0, sy + G.y0, 0, s->h, s, 1); // load square 0 - background + rgbpix = &(s->pixbuf[0]); + for (y = 0; y < ppm->sizy; y++) { + // skip square 0 + rgbpix += s->h * G.bytes_per_pixel; + // read from file + pixel = ppm_readline(ppm); + for (i = 0; (pixel != NULL) && (i < ppm->sizx); i++) { + if (pixel[0]==0xFF && pixel[1]==0 && pixel[2]==0xFF) { + // transparent color => get pixel from square 0 + memcpy(&thispix, + &(s->pixbuf[y * s->bw + (i % s->h) * G.bytes_per_pixel]), + G.bytes_per_pixel); + } else { + thispix = makecol(pixel[0],pixel[1],pixel[2]); + } + memcpy(rgbpix,&thispix,G.bytes_per_pixel); + pixel += 3; + rgbpix += G.bytes_per_pixel; + } + } + ppm_close(ppm); + return s; + readsprites_out: + if (s != NULL) free(s); + return NULL; + } + + #define fb_putanim(x,y,n,s) fb_sprite(x + G.x0,y + G.y0,s->h * n,s->h,s,0) + #endif + + #if ENABLE_FEATURE_FBSPLASH_TEXT + + /* + * A function to put the pixel + */ + static void fb_putpixel(int x, int y, RGB rgb) { + uint8_t *a; + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres) return; + a=G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; + memcpy(a,&rgb,G.bytes_per_pixel); + /* + switch (G.scr_var.bits_per_pixel) { + case 8: + *a = rgb & 0xFF; + break; + case 15: + case 16: + *((uint16_t *)a) = rgb & 0xFFFF; + break; + case 24: + a[0] = rgb & 0xFF; + a[1] = (rgb >> 8) & 0xFF; + a[2] = (rgb >> 16) & 0xFF; + break; + case 32: + *((uint32_t *)a) = rgb; + break; + } + */ + return; + } + + /** + * UTF-8 - get one charcter and advance to next + * ??? this function may be in libbb ??? + * \return UCS4 value + */ + static uint32_t ucs4value(uint8_t **s) { + uint8_t *c; + uint32_t ucs,w; + int count; + + if (s==NULL || *s==NULL || **s==0) return 0; + c = *s; + ucs = *c++; + if (ucs >= 0xC0) { /* 0x80..0xBF is error in utf8 but i use as-is */ + for (w = ucs & 0x7F, count = 0;(w & 0x40) == 0x40; count++) { + w <<= 1; + } + ucs=ucs & (0x7E >> count); + for(; count > 0; count--) { + if ((*c & 0xC0) != 0x80) { /* error in utf8 */ + ucs=0; + break; + } + ucs = (ucs << 6) + (*c++ & 0x3F); + } + } + *s=c; + return ucs; + } + + /** + * Find in GF.ud.entries desired UTF-8 char and advance to next char + * can combine glyphs in own memory + * \param s pointer to utf8 char pointer + * \return pinter to glyph + */ + static uint8_t *getglyph(uint8_t **s) { + uint32_t ucs; + uint8_t *c,*g; + int utflen,i; + + if (s == NULL || *s == NULL || **s == 0) + return G.unknown_glyph; /* end of string - no advance */ + c = *s; + ucs = ucs4value(&c); + utflen = c - *s; + g = G.unknown_glyph; + if (GF.ud.entries == NULL) { + if (ucs < GF.glyphcount) { + g = GF.glyphs + GF.charsize * ucs; + } + } else if (ucs <= 0xFFFF) { // only ucs2 + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + int j,compose; + memset(G.combglyph,0,sizeof(G.combglyph)); + for (i = 0, compose=0; i < GF.ud.entry_ct; i++) { + if (GF.ud.entries[i].unicode == ucs) { + g=GF.glyphs + + GF.ud.entries[i].fontpos * GF.charsize; + for (j = 0; j < GF.charsize; j++) { + G.combglyph[j] |= g[j]; + } + compose++; + } + } + if (compose > 1) { // found more than one + g = &(G.combglyph[0]); + } + #else + // no sequences - first found win + for (i = 0; i < GF.ud.entry_ct ; i++) { + if (GF.ud.entries[i].unicode == ucs) { + g = GF.glyphs + GF.ud.entries[i].fontpos * GF.charsize; + break; + } + } + #endif + } + *s=c; + return g; + } + + /** + * Draws a single character on framebuffer. + * \param nXPos horizontal position from which draw the character. + * \param nYPos vertical position from which draw the character. + * \param nCharSize character scale factor [1..4]. + * \param pointer to *nChar character to draw - auto advance by utf8. + * \param RGB color + */ + static void fb_drawchar(int nXPos, int nYPos, int nCharSize, + uint8_t **nChar, RGB thispix) + { + int x,y,px; + uint8_t *glyph; + + if (G.font==NULL || GF.height==0 || GF.glyphs==NULL) return; + if (G.unknown_glyph == NULL) { + uint8_t *unknown; + const uint8_t unconst[]="?"; + unknown=(uint8_t *)unconst; + G.unknown_glyph = getglyph(&unknown); + } + + glyph=getglyph(nChar); + if (glyph==NULL) return; + glyph-=GF.rowbytes; + for (y = 0; y < (GF.height * nCharSize); y++) { + if ((y % nCharSize) == 0) glyph+=GF.rowbytes; + for (x = 0,px = -1; x < (GF.width * nCharSize); x++) { + if ((x % nCharSize) == 0) px++; + if ((glyph[px / 8] & (0x80 >> (px % 8))) != 0) { + fb_putpixel(nXPos + x, nYPos + y,thispix); + } + } + } + } + + + /** + * Draws a string on framebuffer. + * \param *strString pointer to the string. + */ + static void fb_drawstring(char *strString) + { + RGB thispix; + int nCharSize; + int x; + uint8_t *c; + + // size check + nCharSize = G.text_size; + if (nCharSize < 1) + nCharSize = 1; + else if (nCharSize > 4) + nCharSize = 4; + + if (G.font==NULL || GF.height==0 || GF.glyphs==NULL) return; + + // redraws the portion of image interested to the old write operation + fb_putsprite(G.text_posx, G.text_posy, G.spt); + + // new write operation + thispix=makecol(G.text_colr,G.text_colg,G.text_colb); + for (x = G.text_posx, c = (uint8_t *)strString; *c != 0;) { + #if ENABLE_FEATURE_FBSPLASH_TEXT_COLORS + if (*c==0x1b && c[1]=='[') { + long newcol=0,first; + char *e; + e=(char *)&(c[1]); + do { + first=newcol; + ++e; + newcol = strtol(e,&e,10); + } while (*e == ';'); + if (*e == 'm' && ((newcol == 0) || (newcol >= 30 && newcol <= 37))) { + if (newcol >= 30 && newcol <= 37) { + if (newcol == 0 && first == 1) { + thispix=makecol(0x33,0x33,0x33); + } else { + newcol -= 30; + first = (first == 1 ? 0xFF : 0xCC); + thispix=makecol((newcol & 1) ? first : 0, + (newcol & 2) ? first : 0, + (newcol & 4) ? first : 0); + } + } else { + thispix=makecol(G.text_colr,G.text_colg,G.text_colb); + } + c=(uint8_t *)(++e); + continue; + } + } + #endif + fb_drawchar(x, G.text_posy, nCharSize, &c, thispix); + x += GF.width * nCharSize; + if (x >= G.scr_var.xres) break; // too long string + } + } + + /** + * Free readed font structure with all allocated memory + */ + static void fb_freefont(void) { + if (G.font != NULL) { + if (GF.mem1 != NULL) free(GF.mem1); + if (GF.ud.entries != NULL) free(GF.ud.entries); + free(G.font); + G.font=NULL; + } + } + + /** + * Read font and mapping from console using ioctl GIO_FONT and GIO_UNIMAP + */ + static void fb_readvgafont(void) { + int fd,i; + #if (defined(KDFONTOP) && defined(KD_FONT_OP_GET)) || defined(GIO_FONTX) + #if defined(KDFONTOP) && defined(KD_FONT_OP_GET) + struct console_font_op fo; + fd = open(CURRENT_TTY,O_NONBLOCK); + // if (fd == -1) fd = open("/dev/tty0",O_NOCTTY); + // if (fd == -1) fd = open("/dev/tty1",O_NOCTTY); + if (fd == -1) return; + + fo.data = NULL; + fo.charcount = 0; + fo.height = 32; fo.width = 32; // max usable font size + fo.flags = 0; + fo.op = KD_FONT_OP_GET; + if (ioctl(fd, KDFONTOP, &fo) == 0) { + i = (fo.width + 7) / 8; + fo.data = xmalloc(fo.charcount * 32 * i); + if (ioctl(fd, KDFONTOP, &fo) == -1) { + free(fo.data); + goto fb_vgafontout; + } + } + if (G.font != NULL) fb_freefont(); + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); + GF.height = fo.height; + GF.glyphcount = fo.charcount; + GF.width = fo.width; + GF.rowbytes = (fo.width + 7) / 8; + GF.charsize = GF.height * GF.rowbytes; + GF.mem1 = (uint8_t *) xmalloc(GF.height * GF.glyphcount * GF.rowbytes); + GF.glyphs = GF.mem1; + for (i = 0; i < GF.glyphcount; i++) { + memcpy(&(GF.glyphs[i * GF.height * GF.rowbytes]), + &(fo.data[i * 32 * GF.rowbytes]), + GF.height * GF.rowbytes); + } + if (fo.data) free(fo.data); + #else // KDFONTOP / GIO_FONTX + struct consolefontdesc cfd; + int j; + fd = open(CURRENT_TTY,O_NONBLOCK); + // if (fd == -1) fd = open("/dev/tty0",O_NOCTTY); + // if (fd == -1) fd = open("/dev/tty1",O_NOCTTY); + if (fd == -1) return; + + cfd.chardata = NULL; + cfd.charcount = 0; + cfd.charheight = 0; + if (ioctl(fd, GIO_FONTX, &cfd) == 0) { + cfd.chardata = xmalloc(cfd.charcount * 32); + if (ioctl(fd, GIO_FONTX, &cfd) == -1) { + free(cfd.chardata); + goto fb_vgafontout; + } + } else { + cfd.charcount=256; + cfd.chardata = xmalloc(256 * 32); + if (ioctl(fd, GIO_FONT, cfd.chardata) != 0) { + free(cfd.chardata); + goto fb_vgafontout; + } + // compute real height + cfd.charheight=0; + for (j = 31; (j > 0) && (cfd.charheight == 0); j--) { + for (i = 0; (i < 256) && (cfd.charheight == 0); i++) { + if (cfd.chardata[i*32+j] != 0) + cfd.charheight=j; + } + } + } + if (cfd.charheight != 0) { + if (G.font != NULL) fb_freefont(); + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); + GF.charsize = cfd.charheight; + GF.height = GF.charsize; + GF.glyphcount = cfd.charcount; + GF.width = 8; + GF.rowbytes = 1; + GF.mem1 = (uint8_t *) xmalloc(GF.height * GF.glyphcount); + GF.glyphs = GF.mem1; + // pack font + for (i = 0; i < GF.glyphcount; i++) { + memcpy(&(GF.glyphs[i * GF.height]), + &(cfd.chardata[i * 32]), + GF.height); } } ! free(cfd.chardata); ! #endif // FONTOP / FONTX ! #if defined(GIO_UNIMAP) ! GF.ud.entry_ct = 0; ! ioctl(fd, GIO_UNIMAP, &(GF.ud)); ! if (errno != ENOMEM) { ! goto fb_vgafontout; ! } ! if (GF.ud.entry_ct > 0) { ! GF.ud.entries = xmalloc(GF.ud.entry_ct * sizeof(struct unipair)); ! if (ioctl(fd, GIO_UNIMAP, &(GF.ud)) == -1) { ! // error no unimap ! free(GF.ud.entries); ! GF.ud.entry_ct = 0; ! } ! } ! #endif // GIO_UNIMAP ! fb_vgafontout: ! close(fd); ! #endif // GIO_FONTX or KDFONTOP available ! return; ! } ! ! #if ENABLE_FEATURE_FBSPLASH_FONTLOAD ! ! static uint16_t nextuni(int ver, uint8_t *map, int *pos) { ! uint16_t rv; ! int i; ! i=*pos; ! if (ver==1) { ! rv = map[i + 1]; ! rv = (rv << 8) + map[i]; ! i += 2; ! } else { // ver 2 ! if (map[i] == 0xFF) rv = 0xFFFF; ! else if (map[i] == 0xFE) rv = 0xFFFE; ! else if (map[i] < 0xC0) rv = map[i]; ! else if (map[i] < 0xE0) { ! rv = map[i] & 0x1F; ! if ((map[i + 1] & 0xC0) == 0x80) ! rv = (rv << 6) + (map[++i] & 0x3F); ! } else if (map[i] < 0xF0) { ! rv = map[i] & 0x0F; ! if ((map[i + 1] & 0xC0) == 0x80) ! rv = (rv << 6) + (map[++i] & 0x3F); ! if ((map[i + 1] & 0xC0) == 0x80) ! rv = (rv << 6) + (map[++i] & 0x3F); ! } else rv = map[i]; ! i++; ! } ! *pos=i; ! return rv; } + /** + * Convert unimap from psf v1 and psf v2 to unimapdesc + */ + static void convertmap(int ver, uint8_t *map, int mapsize, struct unimapdesc *ud) { + int i,j,k,glyph,glycodes; + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + int l, seqlen=0, *seq=NULL; + #endif + uint16_t e; + ud->entry_ct=0; + for (i = 0, glycodes=0; i < mapsize; ) { + e = nextuni(ver, map, &i); + if (e == PSF1_STARTSEQ) { + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + seqlen++; + #endif + e = nextuni(ver, map, &i); + while ((e != PSF1_SEPARATOR) && (i < mapsize)) { + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + ud->entry_ct += glycodes; + #endif + e = nextuni(ver, map, &i); + } + glycodes=0; + } else if (e == PSF1_SEPARATOR) { + ud->entry_ct += glycodes; + glycodes=0; + } else { + glycodes++; + } + } + ud->entries = (struct unipair *) + xmalloc(sizeof(struct unimapdesc) * ud->entry_ct); + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + if (seqlen > 0) { + seq = xzalloc(sizeof(int) * (seqlen + 1)); + } + #endif + for (i = 0, glyph = 0, j = 0, k = 0; i < mapsize; ) { + e = nextuni(ver, map, &i); + if (e == PSF1_STARTSEQ) { + e = nextuni(ver, map, &i); + k++; // this is seqence - position saved + // remove sequence - this is not glyph + while (ud->entries[j-1].fontpos == glyph && j > 0) { + j--; + } + // skip chars in seq + while ((e != PSF1_SEPARATOR) && (i < mapsize)) { + e = nextuni(ver, map, &i); + } + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + if (k < seqlen) seq[k] = i; + #endif + } else if (e == PSF1_SEPARATOR) { + glyph++; + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + // next may be sequence + if (k < seqlen) seq[k] = i; + #endif + } else if (j < ud->entry_ct) { + ud->entries[j].unicode=e; + ud->entries[j++].fontpos=glyph; + } + } + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS + i = 0; + while (i < seqlen) { + k = seq[i]; + do { + e = nextuni(ver, map, &k); + } while (e != PSF1_STARTSEQ && e != PSF1_SEPARATOR && k < mapsize); + if (e == PSF1_STARTSEQ) { + e = nextuni(ver, map, &k); + while ((e != PSF1_SEPARATOR) && (k < mapsize)) { + // find glyph in table + glyph = 0; + while ((glyph < j) && (ud->entries[glyph].unicode != e)) { + glyph++; + } + // write glyph to table for all chars + if (glyph < j) { // found + l = seq[i]; // code pointer + e = nextuni(ver, map, &l); + while(e != PSF1_STARTSEQ && l < mapsize) { + ud->entries[j].unicode=e; + ud->entries[j++].fontpos=ud->entries[glyph].fontpos; + e = nextuni(ver, map, &l); + } + } + e = nextuni(ver, map, &k); + } + } + i++; + } + if (seqlen > 0) { + // remove unfound glyphs if any + ud->entry_ct = j; + free(seq); + } + #endif + } + + /** + * Read PSF v1 and PSF v2 fonts to own font structure + */ + static void fb_readpsf(const char *filename) { + unsigned int len; + int maplen; + uint8_t *buf; + struct psf1_header *psf1; + struct psf2_header *psf2; + + if (G.font != NULL) fb_freefont(); + len = 48*1024; // can't be larger + buf = xmalloc_open_zipped_read_close(filename, &len); + if (buf == NULL) { + bb_simple_perror_msg_and_die(filename); + } + if (len < 1024) { + goto badfmt; + } + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); + GF.mem1 = buf; + psf1 = (struct psf1_header *)buf; + psf2 = (struct psf2_header *)buf; + if ((psf1->magic == PSF1_MAGIC) && (psf1->mode <= PSF1_MAXMODE)) { + GF.glyphs = &(buf[sizeof(struct psf1_header)]); + GF.charsize = psf1->charsize; + GF.height = GF.charsize; + GF.glyphcount = ((psf1->mode & PSF1_MODE512) ? 512 : 256); + GF.width = 8; + maplen = len - sizeof(struct psf1_header) - + (GF.charsize * GF.glyphcount); + if ((maplen > 0) && ((psf1->mode & PSF1_MODEHASTAB) != 0)) { + convertmap(1,&(buf[len - maplen]),maplen,&(GF.ud)); + } else { + if (maplen < 0) goto badfmt; + } + } else if ((psf2->magic == PSF2_MAGIC) && + (psf2->version <= PSF2_MAXVERSION)) { + GF.glyphs=&(buf[psf2->headersize]); + GF.height = psf2->height; + GF.width = psf2->width; + GF.glyphcount = psf2->length; + GF.charsize = psf2->charsize; + maplen = len - psf2->headersize - + (GF.charsize * GF.glyphcount); + if ((maplen > 0) && ((psf2->flags & PSF2_HAS_UNICODE_TABLE) != 0)) { + convertmap(2,&(buf[len - maplen]),maplen,&(GF.ud)); + } else { + if (maplen < 0) goto badfmt; + } + } else { + goto badfmt; + } + GF.rowbytes = GF.charsize / GF.height; + + return; + badfmt: + if (G.font) fb_freefont(); + bb_error_msg_and_die("Cannot read file %s - unrecognized format\n",filename); + } + #endif // FONTLOAD + #endif // TEXT /** * Parse configuration file *************** *** 298,303 **** --- 1165,1173 ---- "BAR_WIDTH\0" "BAR_HEIGHT\0" "BAR_LEFT\0" "BAR_TOP\0" "BAR_R\0" "BAR_G\0" "BAR_B\0" + #if ENABLE_FEATURE_FBSPLASH_TEXT + "TEXT_LEFT\0" "TEXT_TOP\0" "TEXT_R\0" "TEXT_G\0" "TEXT_B\0" "TEXT_SIZE\0" + #endif #if DEBUG "DEBUG\0" #endif *************** *** 305,319 **** char *token[2]; parser_t *parser = config_open2(cfg_filename, xfopen_stdin); while (config_read(parser, token, 2, 2, "#=", ! (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) { unsigned val = xatoi_u(token[1]); int i = index_in_strings(param_names, token[0]); if (i < 0) bb_error_msg_and_die("syntax error: %s", token[0]); ! if (i >= 0 && i < 7) G.ns[i] = val; #if DEBUG ! if (i == 7) { G.bdebug_messages = val; if (G.bdebug_messages) G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log"); --- 1175,1189 ---- char *token[2]; parser_t *parser = config_open2(cfg_filename, xfopen_stdin); while (config_read(parser, token, 2, 2, "#=", ! (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) { unsigned val = xatoi_u(token[1]); int i = index_in_strings(param_names, token[0]); if (i < 0) bb_error_msg_and_die("syntax error: %s", token[0]); ! if (i >= 0 && i < FBCFGVALS) G.ns[i] = val; #if DEBUG ! if (i == FBCFGVALS) { G.bdebug_messages = val; if (G.bdebug_messages) G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log"); *************** *** 327,337 **** int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fbsplash_main(int argc UNUSED_PARAM, char **argv) { const char *fb_device, *cfg_filename, *fifo_filename; ! FILE *fp = fp; // for compiler ! char *num_buf; ! unsigned num; bool bCursorOff; INIT_G(); --- 1197,1220 ---- int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fbsplash_main(int argc UNUSED_PARAM, char **argv) { + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD + const char *fontmap_filename = NULL; + #endif const char *fb_device, *cfg_filename, *fifo_filename; ! char num_buf[512]; ! int num; bool bCursorOff; + struct timeval tim; + fd_set rdfds; + FILE *fp=fp; + int bounce_x=0; + #if ENABLE_FEATURE_FBSPLASH_SPRITES + int anim_x,anim_y, /* position of sprite or animation */ + anim_n,anim_m, /* min sprite, max sprite for animation */ + anim_p=0, /* actual sprite */ + anim_t=0,anim_now=0; /* time between steps */ + struct sprite *spa=NULL; + #endif INIT_G(); *************** *** 339,347 **** fb_device = "/dev/fb0"; cfg_filename = NULL; fifo_filename = NULL; bCursorOff = 1 & getopt32(argv, "cs:d:i:f:", &G.image_filename, &fb_device, &cfg_filename, &fifo_filename); ! // parse configuration file if (cfg_filename) init(cfg_filename); --- 1222,1240 ---- fb_device = "/dev/fb0"; cfg_filename = NULL; fifo_filename = NULL; + + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD + bCursorOff = 1 & getopt32(argv, "cs:d:i:f:m:", + &G.image_filename, &fb_device, &cfg_filename, &fifo_filename, &fontmap_filename); + + if (fontmap_filename) fb_readpsf(fontmap_filename); + #else bCursorOff = 1 & getopt32(argv, "cs:d:i:f:", &G.image_filename, &fb_device, &cfg_filename, &fifo_filename); ! #endif ! #if ENABLE_FEATURE_FBSPLASH_TEXT ! if (G.font==NULL) fb_readvgafont(); ! #endif // parse configuration file if (cfg_filename) init(cfg_filename); *************** *** 357,369 **** full_write(STDOUT_FILENO, "\x1b" "[?25l", 6); } fb_drawimage(); if (!fifo_filename) return EXIT_SUCCESS; fp = xfopen_stdin(fifo_filename); ! if (fp != stdin) { // For named pipes, we want to support this: // mkfifo cmd_pipe // fbsplash -f cmd_pipe .... & --- 1250,1274 ---- full_write(STDOUT_FILENO, "\x1b" "[?25l", 6); } + if (fifo_filename != NULL) { + G.spb2 = fb_newsprite(G.nbar_width, G.nbar_height, -1, -1); + } + fb_drawimage(); + G.spb1 = fb_newsprite(G.nbar_width, G.nbar_height, G.nbar_posx, G.nbar_posy); + #if ENABLE_FEATURE_FBSPLASH_TEXT + if (G.font != NULL) + G.spt = fb_newsprite(G.scr_var.xres - G.text_posx, + GF.height * G.text_size, + G.text_posx, G.text_posy); + #endif + if (!fifo_filename) return EXIT_SUCCESS; fp = xfopen_stdin(fifo_filename); ! if (fp != stdin) { // For named pipes, we want to support this: // mkfifo cmd_pipe // fbsplash -f cmd_pipe .... & *************** *** 375,407 **** // when last writer closes input end. // The simplest way is to open fifo for writing too // and become an additional writer :) ! open(fifo_filename, O_WRONLY); // errors are ignored ! } - fb_drawprogressbar(0); // Block on read, waiting for some input. // Use of style I/O allows to correctly // handle a case when we have many buffered lines // already in the pipe ! while ((num_buf = xmalloc_fgetline(fp)) != NULL) { ! if (strncmp(num_buf, "exit", 4) == 0) { ! DEBUG_MESSAGE("exit"); ! break; ! } ! num = atoi(num_buf); ! if (isdigit(num_buf[0]) && (num <= 100)) { #if DEBUG ! char strVal[10]; ! sprintf(strVal, "%d", num); ! DEBUG_MESSAGE(strVal); #endif - fb_drawprogressbar(num); } - free(num_buf); } ! if (bCursorOff) // restore cursor full_write(STDOUT_FILENO, "\x1b" "[?25h", 6); return EXIT_SUCCESS; } --- 1280,1416 ---- // when last writer closes input end. // The simplest way is to open fifo for writing too // and become an additional writer :) ! open(fifo_filename, O_WRONLY | O_NDELAY); // errors are ignored ! } // Block on read, waiting for some input. // Use of style I/O allows to correctly // handle a case when we have many buffered lines // already in the pipe ! // select and fgets work just in time only in nobuf mode ! setvbuf(fp, NULL, _IONBF, 0); ! tim.tv_sec = 0; ! tim.tv_usec = 100000; ! while (1) { ! FD_ZERO(&rdfds); ! FD_SET(fileno(fp),&rdfds); ! if ((num=select(fileno(fp) + 1,&rdfds,NULL,NULL,&tim))==-1) ! break; // select error ! if (num != 0 && fgets(num_buf,sizeof(num_buf) - 1,fp) != NULL) { ! num_buf[sizeof(num_buf) - 1]=0; ! num=strlen(num_buf) - 1; ! if (num >= 0 && num_buf[num]==10) num_buf[num]=0; ! if (strncmp(num_buf, "exit", 4) == 0) { ! DEBUG_MESSAGE("exit"); ! break; ! } else if (strncmp(num_buf, "bounce", 6) == 0) { ! bounce_x = 0; ! #if ENABLE_FEATURE_FBSPLASH_TEXT ! } else if (strncmp(num_buf, "write:", 6) == 0) { ! if (G.font!=NULL) fb_drawstring(num_buf + 6); ! DEBUG_MESSAGE(num_buf); ! #endif ! #if ENABLE_FEATURE_FBSPLASH_SPRITES ! } else if (strncmp(num_buf, "load:", 5) == 0) { ! if (spa) { ! fb_putanim(anim_x,anim_y,0,spa); // hide old ! free(spa); ! spa=NULL; ! } ! if (sscanf(&(num_buf[5]),"%d:%d:", ! &anim_x,&anim_y) == 2) { ! // skip x:y ! for (num=5;num_buf[num] != ':';) num++; ! for (num++;num_buf[num] != ':';) num++; ! spa=fb_loadsprites(&(num_buf[num + 1]),anim_x,anim_y); ! anim_t = anim_now = 0; // stop animation ! } ! } else if (strncmp(num_buf, "sprite:", 7) == 0) { ! if (spa != NULL) { ! // "sprite:n" ! anim_t = anim_now = 0; // stop animation ! anim_p = atoi(&(num_buf[7])); ! if (anim_p >= 0) { ! fb_putanim(anim_x,anim_y,anim_p,spa); ! } ! } ! } else if (strncmp(num_buf, "anime:", 6) == 0) { ! if (spa != NULL) { ! // "anime:n:m:t" ! if ((sscanf(&(num_buf[6]),"%d:%d:%d", ! &anim_n,&anim_m,&anim_t) == 3) && ! (anim_n >= 0) && (anim_m >= 0)) { ! anim_p = anim_n; // first frame ! fb_putanim(anim_x,anim_y,anim_p,spa); ! anim_now = anim_t; // next sprite after time t ! } else { ! anim_now = anim_t = 0; // stop animation ! } ! } ! #endif ! } else { ! num = atoi(num_buf); ! if ((isdigit(num_buf[0]) || (num_buf[0]=='-')) && ! (num >= -100 && num <= 100)) { #if DEBUG ! char strVal[10]; ! sprintf(strVal, "%d", num); ! DEBUG_MESSAGE(strVal); ! #endif ! if (num < 0) { ! fb_drawnbar(-1 * num,100); ! } else { ! fb_drawnbar(0,num); ! } ! bounce_x = -1; // stop bounce ! } ! } ! } else { ! if (fp == stdin && feof(fp)) break; // stdin closed END ! } ! if (tim.tv_usec == 0) { // timeout - anime ! tim.tv_usec = 100000; ! if (bounce_x >= 0) { ! bounce_x += 5; ! if (bounce_x > 160) bounce_x = 0; ! if (bounce_x > 80) ! fb_drawnbar(160 - bounce_x,180 - bounce_x); ! else ! fb_drawnbar(bounce_x, bounce_x + 20); ! } ! #if ENABLE_FEATURE_FBSPLASH_SPRITES ! if (spa != NULL && anim_t != 0) { ! --anim_now; ! if (anim_now <= 0) { ! anim_now = anim_t; // wait ! if (anim_n < anim_m) { ! anim_p++; ! if (anim_p > anim_m) anim_p = anim_n; ! } else { ! anim_p--; ! if (anim_p < anim_m) anim_p = anim_n; ! } ! fb_putanim(anim_x,anim_y,anim_p,spa); ! } ! } #endif } } ! if (G.spb1 != NULL) free(G.spb1); ! G.spb1 = NULL; ! if (G.spb2 != NULL) free(G.spb2); ! G.spb2 = NULL; ! #if ENABLE_FEATURE_FBSPLASH_TEXT ! if (G.font != NULL) fb_freefont(); ! if (G.spt != NULL) free(G.spt); ! G.spt = NULL; ! #endif ! #if ENABLE_FEATURE_FBSPLASH_SPRITES ! if (spa != NULL) free(spa); ! #endif if (bCursorOff) // restore cursor full_write(STDOUT_FILENO, "\x1b" "[?25h", 6); return EXIT_SUCCESS; } + #endif *** busybox-1.15.0/miscutils/fbsplash.cfg.orig 2009-08-21 00:26:14.000000000 +0200 --- busybox-1.15.0/miscutils/fbsplash.cfg 2009-09-01 08:42:18.000000000 +0200 *************** *** 7,9 **** --- 7,19 ---- BAR_R=80 BAR_G=80 BAR_B=130 + # the below settings are active only if you enable the option FBSPLASH_TEXT_RENDERING + # text position + #TEXT_LEFT=100 + #TEXT_TOP=350 + ## text color + #TEXT_R=80 + #TEXT_G=80 + #TEXT_B=130 + ## text size (1 to 4) + #TEXT_SIZE=2