/* * Copyright (c) 2019 Clementine Computing LLC. * * This file is part of PopuFare. * * PopuFare is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PopuFare 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PopuFare. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/common_defs.h" #include "fbutil.h" #include "fbfonts.h" extern fbfont smallfont; extern fbfont medfont; extern fbfont bigfont; // This include file contains the palette tables for storing the old palette to be later restored, as well as //the palette table we use for our application. It also has a byzantine and insane table where I have factored out //all of the possible permutations of painting color X over color Y with or without the blend bit set in color X, so //you can use the LOOKUP_BLEND(x, y) macro and it will return the resulting color. #include "color_tables.h" //------------------------------- named_color color_names[] = { {"white", FBCOLOR_WHITE}, {"light_gray", FBCOLOR_GRAY}, {"dark_gray", FBCOLOR_DKGRAY}, {"black", FBCOLOR_BLACK}, {"trans_white", FBCOLOR_WHITE | FBCOLOR_TRANSLUCENT}, {"trans_light_gray", FBCOLOR_GRAY | FBCOLOR_TRANSLUCENT}, {"trans_dark_gray", FBCOLOR_DKGRAY | FBCOLOR_TRANSLUCENT}, {"trans_black", FBCOLOR_BLACK | FBCOLOR_TRANSLUCENT}, {"dark_red", FBCOLOR_DKRED}, {"red", FBCOLOR_RED}, {"light_red", FBCOLOR_LTRED}, {"trans_dark_red", FBCOLOR_DKRED | FBCOLOR_TRANSLUCENT}, {"trans_red", FBCOLOR_RED | FBCOLOR_TRANSLUCENT}, {"trans_light_red", FBCOLOR_LTRED | FBCOLOR_TRANSLUCENT}, {"dark_green", FBCOLOR_DKGREEN}, {"green", FBCOLOR_GREEN}, {"light_green", FBCOLOR_LTGREEN}, {"trans_dark_green", FBCOLOR_DKGREEN | FBCOLOR_TRANSLUCENT}, {"trans_green", FBCOLOR_GREEN | FBCOLOR_TRANSLUCENT}, {"trans_light_green", FBCOLOR_LTGREEN | FBCOLOR_TRANSLUCENT}, {"dark_blue", FBCOLOR_DKBLUE}, {"blue", FBCOLOR_BLUE}, {"light_blue", FBCOLOR_LTBLUE}, {"trans_dark_blue", FBCOLOR_DKBLUE | FBCOLOR_TRANSLUCENT}, {"trans_blue", FBCOLOR_BLUE | FBCOLOR_TRANSLUCENT}, {"trans_light_blue", FBCOLOR_LTBLUE | FBCOLOR_TRANSLUCENT}, {"dark_yellow", FBCOLOR_DKYELLOW}, {"yellow", FBCOLOR_YELLOW}, {"light_yellow", FBCOLOR_LTYELLOW}, {"trans_dark_yellow", FBCOLOR_DKYELLOW | FBCOLOR_TRANSLUCENT}, {"trans_yellow", FBCOLOR_YELLOW | FBCOLOR_TRANSLUCENT}, {"trans_light_yellow", FBCOLOR_LTYELLOW | FBCOLOR_TRANSLUCENT}, {"dark_cyan", FBCOLOR_DKCYAN}, {"cyan", FBCOLOR_CYAN}, {"light_cyan", FBCOLOR_LTCYAN}, {"trans_dark_cyan", FBCOLOR_DKCYAN | FBCOLOR_TRANSLUCENT}, {"trans_cyan", FBCOLOR_CYAN | FBCOLOR_TRANSLUCENT}, {"trans_light_cyan", FBCOLOR_LTCYAN | FBCOLOR_TRANSLUCENT}, {"dark_magenta", FBCOLOR_DKMAGENTA}, {"magenta", FBCOLOR_MAGENTA}, {"light_magenta", FBCOLOR_LTMAGENTA}, {"trans_dark_magenta", FBCOLOR_DKMAGENTA | FBCOLOR_TRANSLUCENT}, {"trans_magenta", FBCOLOR_MAGENTA | FBCOLOR_TRANSLUCENT}, {"trans_light_magenta", FBCOLOR_LTMAGENTA | FBCOLOR_TRANSLUCENT}, {NULL, 0} //THIS TERMINATOR IS NEEDED SO WE DON'T WALK OFF THE END OF THE LIST! }; static inline unsigned int consume_hex_digits(char **s, int digits) { unsigned int accum = 0; int i; char *trav; if(!s) return 0; trav = *s; if(!trav) return 0; for(i = 0; i < digits; i++) { if(!*trav) break; switch(*trav) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: accum |= (*trav) - '0'; accum <<= 4; trav++; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': accum |= (*trav) - '7'; accum <<= 4; trav++; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': accum |= (*trav) - 'W'; accum <<= 4; trav++; break; default: *s = trav; return accum; break; } } *s = trav; return accum; } pixel string_to_color(char *str) { char *trav; pixel color = FBCOLOR_BLACK; int i; int r = 0, g = 0, b = 0, a = 0xFF; if(str) { trav = str; if(*trav == '#') //If we have a numeric color { trav++; i = 0; r = consume_hex_digits(&trav, 2); //Get the first hex octet if(*trav) //if there are more characters, we'll expect at least #rrggbb { g = consume_hex_digits(&trav, 2); //covert green b = consume_hex_digits(&trav, 2); //and blue if(*trav) //if there are still more characters, it may be #rrggbbaa { a = consume_hex_digits(&trav, 2); //convert alpha } color = FROM_RGBA(r,g,b,a); //make a framebuffer color from the supplied RGBA values } else //otherwise, treat the single octet as a raw framebuffer color { color = r; //if we only got one octet, use it as a raw color } } else //if there was no # to indicate a numeric color, let's scan our list of named colors { i = 0; while(1) { if(color_names[i].name == NULL) //if we've hit the end of the list { break; //give up } if(!strcasecmp(color_names[i].name, trav)) //if it matches { color = color_names[i].color; //snag the color and we're done break; } else { i++; //otherwise, try the next one } } } } return color; //report our results back } static unsigned int clip_x = 0; static unsigned int clip_y = 0; static unsigned int clip_xx = XRES; static unsigned int clip_yy = YRES; void reset_clip_rect() { clip_x = 0; clip_y = 0; clip_xx = XRES; clip_yy = YRES; } #define SWAP(a,b) {tmp = a; a = b; b = tmp;} void set_clip_rect(unsigned x, unsigned y, unsigned xx, unsigned yy) { int tmp; if(x > xx) SWAP(x, xx); if(y > yy) SWAP(y, yy); clip_x = (x <= XRES)?x:XRES; clip_xx = (xx <= XRES)?xx:XRES; clip_y = (y <= YRES)?y:YRES; clip_yy = (yy <= YRES)?yy:YRES; } void get_clip_rect(unsigned *x, unsigned *y, unsigned *xx, unsigned *yy) { if(x) *x = clip_x; if(xx) *xx = clip_xx; if(y) *y = clip_y; if(yy) *y = clip_yy; } static inline int coord_to_index(unsigned x, unsigned y) { if( (x < clip_x) || (x >= clip_xx) || (y < clip_y) || (y >= clip_yy) ) return -1; return x + (y * XRES); } static pixel fgcolor; static pixel bgcolor; static unsigned int cursor_x; static unsigned int cursor_y; static int ts_calib_left = 0x07F; static int ts_calib_right = 0x3BD; static int ts_calib_top = 0x040; static int ts_calib_bottom = 0x3AA; static int fontnum; void set_rawcolor(pixel color) { fgcolor = color; } void set_bgrawcolor(pixel color) { bgcolor = color; } static void load_ts_calib() { FILE *f; int retval; int l,r,t,b; f = fopen(TOUCHSCREEN_CALIB_FILE, "rb"); if(f) { retval = fscanf(f, "%d %d %d %d", &l, &r, &t, &b); if(retval == 4) { ts_calib_left = l; ts_calib_right = r; ts_calib_top = t; ts_calib_bottom = b; } fclose(f); } } static void save_ts_calib() { FILE *f; f = fopen(TOUCHSCREEN_CALIB_FILE, "w"); if(f) { fprintf(f, "%d %d %d %d\n", ts_calib_left, ts_calib_right, ts_calib_top, ts_calib_bottom); fclose(f); } } static enum { CS_WAIT_RELEASE, CS_WAIT_PRESS_L, CS_WAIT_RELEASE_L, CS_WAIT_PRESS_R, CS_WAIT_RELEASE_R, CS_WAIT_PRESS_T, CS_WAIT_RELEASE_T, CS_WAIT_PRESS_B, CS_WAIT_RELEASE_B, CS_DONE, } calib_state; static unsigned long long calib_sum; static unsigned long calib_count; int begin_touchscreen_calibration() { calib_state = CS_WAIT_RELEASE; calib_sum = 0; calib_count = 0; return 0; } #define CALIB_WIDGET_SIZE (32) int draw_touchscreen_calibration() { set_rawcolor(FBCOLOR_WHITE); cls(); switch(calib_state) { case CS_WAIT_RELEASE: set_rawcolor(FBCOLOR_BLACK); set_font(2); set_pos(100,100); print_string("Calibration", 0); break; case CS_WAIT_PRESS_L: case CS_WAIT_RELEASE_L: set_rawcolor(FBCOLOR_BLACK); set_pos(0,YRES / 2); line_to(CALIB_WIDGET_SIZE, YRES / 2); set_pos(CALIB_WIDGET_SIZE / 2, (YRES - CALIB_WIDGET_SIZE) / 2); line_to(CALIB_WIDGET_SIZE / 2, (YRES + CALIB_WIDGET_SIZE) / 2); break; case CS_WAIT_PRESS_R: case CS_WAIT_RELEASE_R: set_rawcolor(FBCOLOR_BLACK); set_pos(XRES - CALIB_WIDGET_SIZE, YRES / 2); line_to(XRES, YRES / 2); set_pos(XRES - (CALIB_WIDGET_SIZE / 2), (YRES - CALIB_WIDGET_SIZE) / 2); line_to(XRES - (CALIB_WIDGET_SIZE / 2), (YRES + CALIB_WIDGET_SIZE) / 2); break; case CS_WAIT_PRESS_T: case CS_WAIT_RELEASE_T: set_rawcolor(FBCOLOR_BLACK); set_pos(XRES / 2, 0); line_to(XRES / 2, CALIB_WIDGET_SIZE); set_pos((XRES - CALIB_WIDGET_SIZE) / 2, CALIB_WIDGET_SIZE / 2); line_to((XRES + CALIB_WIDGET_SIZE) / 2, CALIB_WIDGET_SIZE / 2); break; case CS_WAIT_PRESS_B: case CS_WAIT_RELEASE_B: set_rawcolor(FBCOLOR_BLACK); set_pos(XRES / 2, YRES - CALIB_WIDGET_SIZE); line_to(XRES / 2, YRES); set_pos((XRES - CALIB_WIDGET_SIZE) / 2, YRES - (CALIB_WIDGET_SIZE / 2)); line_to((XRES + CALIB_WIDGET_SIZE) / 2, YRES - (CALIB_WIDGET_SIZE / 2)); break; default: break; } present_framebuffer(); return 0; } static int tmp_l, tmp_r, tmp_t, tmp_b; int advance_touchscreen_calibration(int raw_x, int raw_y, int down) { int adj; switch(calib_state) { case CS_WAIT_RELEASE: if(!down) { calib_state++; } break; case CS_WAIT_RELEASE_L: case CS_WAIT_RELEASE_R: if(down) { calib_sum += raw_x; calib_count++; } else { if(calib_count > 16) { if(calib_state == CS_WAIT_RELEASE_L) { tmp_l = (int)(calib_sum / calib_count); } else { tmp_r = (int)(calib_sum / calib_count); } calib_sum = 0; calib_count = 0; calib_state++; } } break; case CS_WAIT_RELEASE_T: case CS_WAIT_RELEASE_B: if(down) { calib_sum += raw_y; calib_count++; } else { if(calib_count > 16) { if(calib_state == CS_WAIT_RELEASE_T) { tmp_t = (int)(calib_sum / calib_count); } else { tmp_b = (int)(calib_sum / calib_count); } calib_sum = 0; calib_count = 0; calib_state++; } } break; case CS_WAIT_PRESS_L: case CS_WAIT_PRESS_R: case CS_WAIT_PRESS_T: case CS_WAIT_PRESS_B: if(down) { calib_state++; } break; default: break; } if(calib_state == CS_DONE) { adj = ( (tmp_r * CALIB_WIDGET_SIZE / 2) - (tmp_l * CALIB_WIDGET_SIZE / 2) ) / (XRES - CALIB_WIDGET_SIZE); tmp_l -= adj; tmp_r += adj; if(tmp_l < 0) tmp_l = 0; if(tmp_l >= tmp_r) { calib_state = CS_WAIT_RELEASE; return 0; } adj = ( (tmp_b * CALIB_WIDGET_SIZE / 2) - (tmp_t * CALIB_WIDGET_SIZE / 2) ) / (YRES - CALIB_WIDGET_SIZE); tmp_t -= adj; tmp_b += adj; if(tmp_t < 0) tmp_b = 0; if(tmp_t >= tmp_b) { calib_state = CS_WAIT_RELEASE; return 0; } ts_calib_left = tmp_l; ts_calib_right = tmp_r; ts_calib_top = tmp_t; ts_calib_bottom = tmp_b; save_ts_calib(); return 1; } return 0; } int translate_ts_x(unsigned rawx) { int accum = (rawx - ts_calib_left); if(accum < 0) accum = 0; accum *= (XRES - 1); accum /= (ts_calib_right - ts_calib_left); if(accum >= XRES) { return XRES - 1; } return accum; } int translate_ts_y(unsigned rawy) { int accum = (rawy - ts_calib_top); if(accum < 0) accum = 0; accum *= (YRES - 1); accum /= (ts_calib_bottom - ts_calib_top); if(accum >= YRES) { return YRES - 1; } return accum; } static int fbfd = -1; static pixel *fb = NULL; #ifdef USE_REMOTE_UI static inline void dump_fb_shadow(pixel *src) { int fb_file; fb_file = creat(FB_SHADOW_TMP, S_IRUSR | S_IWUSR); if(fb_file >= 0) { write(fb_file, src, sizeof(pixel) * PIXELS); close(fb_file); rename(FB_SHADOW_TMP, FB_SHADOW_FILE); } } #else #define dump_fb_shadow(x) #endif // Normally, we want to do all of our drawing operations in a piece of 'mere mortal' (a.k.a. Cacheable) //system ram pulled off of the heap rather than doing that drawing (lots of little bitty read-modify-write //operations) to the framebuffer memory which is generally, while mmap()'d into our process space like normal //ram, marked by the kernel as a very special piece of non-cacheable, DMA-safe low memory, which as a result is //slow to read/write to and from as A: the CPU can't cache it, and B: even if you have a smart memory controller //and a multi-path bus, the DMA controller that scans the framebuffer spends most of its time reading this RAM //(somewhere about 25% of our SOC's total bus bandwidth(!), so we really don't want to get in its way any more //than we absolutely have to, so it's faster to write to some normal userland memory and then copy it all into //the actual framebuffer in one shot when we're done drawing. *sigh*, if we only had double buffering and a //smarter memory controller. On the other hand this runs on a glorified microcontroller, so we take what we //can get... #ifdef FRAMEBUFFER_SOFTWARE_BACKBUFFER static pixel fb_work[PIXELS] = {0}; int present_framebuffer() { int i; int n; unsigned long *s, *d; if(!fb) { return -1; } // printf("Woof!\n"); s = (unsigned long *)fb_work; //grab our source pointer (the backbuffer) as a machine-word sized type d = (unsigned long *)fb; //and our desination pointer (the framebuffer) " " " " " n = (PIXELS * sizeof(pixel)) / sizeof(unsigned long); //figure out how many machine words that really is for(i=0; i < n; i++) //Loop and copy... { *d++ = *s++; } dump_fb_shadow(fb_work); return 0; } #else //if we have selected to draw directly to the screen without using a software backbuffer static pixel *fb_work = NULL; //allocate a backbuffer pointer anyway, to later fill with the actual framebuffer int present_framebuffer() //this function becomes trivial at this point and will probably be optimized out { //by the compiler, but for interface sanity, we define it anyway... dump_fb_shadow(fb_work); return 0; } #endif int open_framebuffer_nondestructive() { pixel *foo; fb = NULL; fbfd = open(CONFIG_FRAMEBUFFER_PATH, O_RDWR); if(fbfd < 0) { fprintf(stderr, "Cannot open %s\n", CONFIG_FRAMEBUFFER_PATH); return -1; } #ifdef FRAMEBUFFER_RESTORE_PALETTE if (ioctl(fbfd, FBIOGETCMAP, &old_palette)) { fprintf(stderr,"FBIOGETCMAP failure\n"); close(fbfd); fbfd = -1; return -1; } #endif if (ioctl(fbfd, FBIOPUTCMAP, &our_palette)) { fprintf(stderr, "FBIOPUTCMAP failed\n"); close(fbfd); fbfd = -1; return -1; } foo = (pixel *)mmap(NULL, sizeof(pixel) * PIXELS, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if( (!foo) || (foo == MAP_FAILED)) { close(fbfd); fbfd = -1; fprintf(stderr, "Cannot MMAP framebuffer!\n"); return -2; } fb = foo; #ifndef FRAMEBUFFER_SOFTWARE_BACKBUFFER //If we have no software backbuffer, assign the backbuffer pointer to point to the real framebuffer fb_work = fb; #else //If we're double buffering, copy the current framebuffer to our backbuffer so we can edit its current contents. memcpy(fb_work, fb, sizeof(pixel) * PIXELS); #endif // We close the file handle in close_framebuffer(), we want to keep it around so we can //restore the palette to the state we found it in... set_bgcolor(255,255,255); set_color(0,0,0); set_pos(0,0); load_ts_calib(); return 0; } int open_framebuffer() { int retval; //First, open the framebuffer in nondestructive mode... retval = open_framebuffer_nondestructive(); //If that fails, pass the failure up the call chain if(retval) { return retval; } //Otherwise, it's time to clear the screen! set_color(255,255,255); set_bgcolor(255,255,255); cls(); set_color(0,0,0); set_pos(0,0); load_ts_calib(); return 0; } //If we're trying to close up the framebuffer, we have two things to do: void close_framebuffer() { if(fb) //Release our memory mapping { munmap(fb, sizeof(pixel) * PIXELS); fb = NULL; } if(fbfd >= 0) //and restore the color palette back to how we found it... { #ifdef FRAMEBUFFER_RESTORE_PALETTE ioctl(fbfd, FBIOPUTCMAP, &old_palette); #endif close(fbfd); fbfd = -1; } } void set_color(int r, int g, int b) { fgcolor = FROM_RGBA(r, g, b, 255); } void set_bgcolor(int r, int g, int b) { bgcolor = FROM_RGBA(r, g, b, 255); } void set_color_t(int r, int g, int b, int a) { fgcolor = FROM_RGBA(r, g, b, a); } void set_bgcolor_t(int r, int g, int b, int a) { bgcolor = FROM_RGBA(r, g, b, a); } void set_pos(int x, int y) { if(x < 0) { cursor_x = 0; } else if(x >= XRES) { cursor_x = XRES - 1; } else { cursor_x = x; } if(y < 0) { cursor_y = 0; } else if(y >= YRES) { cursor_y = YRES - 1; } else { cursor_y = y; } } //Plots a point in the current foreground color. static inline void plot(int x,int y) { int index = coord_to_index(x,y); if(index < 0) return; fb_work[index] = LOOKUP_BLEND(fgcolor, fb_work[index]); } //Plots a point using the foreground color, but forcing the transparent bit on even if it is not //normally on. This is used for font antialiasing, and probably won't ever be used for much else. static inline void plot_t(int x, int y) { int index = coord_to_index(x,y); if(index < 0) return; pixel tmpcolor = (fgcolor | ALPHA_BIT); fb_work[index] = LOOKUP_BLEND(tmpcolor, fb_work[index]); } //Plots a point in the current background color. static inline void bgplot(int x,int y) { int index = coord_to_index(x,y); if(index < 0) return; fb_work[index] = LOOKUP_BLEND(bgcolor, fb_work[index]); } void line_to(int xdest, int ydest) { int tmp; int x0 = cursor_x; int y0 = cursor_y; int x1, y1; int dx, dy, err, ys; int x, y; int steep; #ifndef FRAMEBUFFER_SOFTWARE_BACKBUFFER if(!fb_work) { return; } #endif if(xdest < 0) { xdest = 0; } else if(xdest >= XRES) { xdest = XRES - 1; } if(ydest < 0) { ydest = 0; } else if(ydest >= YRES) { ydest = YRES - 1; } x1 = xdest; y1 = ydest; steep = abs(y1 - y0) > abs(x1 - x0); if(steep) { SWAP(x0, y0); SWAP(x1, y1); } if(x0 > x1) { SWAP(x0, x1); SWAP(y0, y1); } dx = x1 - x0; err = dx / 2; dy = abs(y1 - y0); ys = (y0 < y1)?1:-1; y = y0; for(x = x0; x <= x1; x++) { if(steep) { plot(y,x); } else { plot(x,y); } err -= dy; if(err < 0) { y += ys; err += dx; } } cursor_x = xdest; cursor_y = ydest; } void draw_sprite(sprite *s) { unsigned i, j; pixel *src; unsigned int tx, ty; pixel foo; int idx; #ifndef FRAMEBUFFER_SOFTWARE_BACKBUFFER if(!fb_work) { return; } #endif if(!s) { return; } src = s->data; for(j = 0; j < s->height; j++) { ty = cursor_y + j; if(ty >= YRES) { break; } tx = cursor_x; for(i = 0; i < s->width; i++) { foo = *src++; idx = coord_to_index(tx,ty); if(idx >= 0) { fb_work[idx] = LOOKUP_BLEND(foo, fb_work[idx]); } tx++; } } } void cls() { int i, n; unsigned long pattern; unsigned long *tgt = (unsigned long *)fb_work; #ifndef FRAMEBUFFER_SOFTWARE_BACKBUFFER if(!fb_work) { return; } #endif n = sizeof(pattern) / sizeof(pixel); //figure out how many pixels in a word pattern = 0; //clear the word for(i=0; i < n; i++) //for each one { pattern <<= (sizeof(pixel) * 8); //shift the word over pattern |= (fgcolor & ~(pixel)ALPHA_BIT); //and clear the alpha bit } n = PIXELS / n; //figure out how many iterations we'll need for(i=0; i < n; i++) //splat the pattern down { *tgt++ = pattern; } } void box(int x, int y) { int tx = cursor_x; int ty = cursor_y; line_to(x, ty); line_to(x, y); line_to(tx, y); line_to(tx, ty); set_pos(x,y); } void box_fill(int x, int y) { int i,j; int t,l,r,b; #ifndef FRAMEBUFFER_SOFTWARE_BACKBUFFER if(!fb_work) { return; } #endif if(x < 0) { x = 0; } else if(x >= XRES) { x = XRES - 1; } else { x = x; } if(y < 0) { y = 0; } else if(y >= YRES) { y = YRES - 1; } else { y = y; } if(y > cursor_y) { t = cursor_y; b = y; } else { b = cursor_y; t = y; } if(x > cursor_x) { l = cursor_x; r = x; } else { r = cursor_x; l = x; } for(j = t; j <= b; j++) for(i = l; i <= r; i++) plot(i,j); cursor_x = x; cursor_y = y; } //--------------------------------------- #define SELECT_FONT(fontnum, fnt) \ switch(fontnum) \ { \ case 0: \ fnt = &smallfont; \ break; \ \ case 1: \ fnt = &medfont; \ break; \ \ case 2: \ fnt = &bigfont; \ break; \ \ default: \ fnt = &smallfont; \ break; \ } \ //--------------------------------------- void font_cell_size(int *w, int *h) { fbfont *fnt; SELECT_FONT(fontnum, fnt) if(w) { *w = fnt->cellwidth; } if(h) { *h = fnt->cellheight; } } void set_font(unsigned char font) { fontnum = font; } //This function prints the specified string at the current cursor position. //If the bkgr parameter is non-zero, background pixels (not occupied by the character itself) //will be filled in. void print_string(char *str, int bkgr) { fbfont *fnt; char *c; int glyphidx; const char *glyphdata; int i,j; int tx, ty; if(str == NULL) return; SELECT_FONT(fontnum, fnt) //Get the correct font data for the requested font number c = str; //set our travelling pointer to the beginning of the string while(*c) //while we have not hit a terminating '\0' { if( (*c < fnt->firstglyph) || (*c > fnt->lastglyph) ) //if we are being asked to draw a glyph we don't have { glyphidx = fnt->undefglyph - fnt->firstglyph; //replace it with the font's designated 'undef' glyph } else //otherwise, we have the glyph { glyphidx = *c - fnt->firstglyph; //compute its index into the glyph table } // Next, using that index, use it to compute a pointer offset into the raw font data block //(i.e. figure out how many bytes we have to index in before we get the first byte that represents //the drawing instructions for this glyph). glyphdata = fnt->data + (glyphidx * (fnt->cellwidth * fnt->cellheight)); for(j = 0; j < fnt->cellheight; j++) //For each scan line of the font { ty = cursor_y + j; //ty indexes Y of the screen location we're drawing this scan line to if(ty >= YRES) //if it's off the edge of the screen, save time and skip it { break; } for(i = 0; i < fnt->cellwidth; i++) //for each pixel in this scan line of the glyph bitmap { tx = cursor_x + i; //tx indexes X of the screen location we're drawing this //pixel of this scan line to if(tx >= XRES) //if it's off the edge of the screen, ignore it and keep counting { glyphdata++; //note that we continue rather than break so we can keep incrementing continue; //the pointer to the glyph data so we can draw the left half of the next } //scan line of a glyph that runs off the right edge of the screen switch(*(glyphdata++)) //see what character represents this pixel in the font map... { case ' ': //a ' ' (space) character is a background pixel. Don't plot, unless if(bkgr) //in background mode, in which case, plot background color. { bgplot(tx,ty); } break; case '.': //a '.' (period) character is a blended foreground pixel. If we're in if(bkgr) //background mode, we need to: (this could be more efficeint, but for clarity { //I'm keeping it simple): bgplot(tx,ty); //plot the background first, and then plot_t(tx,ty); //just plot the translucent foreground pixel } else //If we're not in background filling mode, { plot_t(tx,ty); //plot the foreground pixel in translucent mode } break; default: //Any other character counts as a solid plot... plot(tx,ty); break; } } } cursor_x += fnt->cellwidth; //Advance the cursor to the next cell if(cursor_x >= XRES) //If we somehow have run off the end of the screen { cursor_x = XRES - 1; //peg our cursor/turtle to the right edge break; //and don't bother trying to draw the rest of the string } //(as it will also be off the screen) c++; //get the next character and do it all again... } } void beep(int fd, int hz, int milis) { char buffer[64]; int n; n = sprintf(buffer, "/B:%X,%X\r", hz, milis); write(fd,buffer,n); } void set_backlight(int fd, int on) { char buffer[64]; int n; n = sprintf(buffer, "/D:%c\r", on?'0':'1'); write(fd,buffer,n); } void select_blend_mode(int mode) { switch(mode) { case BLENDMODE_LIGHT: blend_table = light_blend_table; break; case BLENDMODE_DARK: default: blend_table = dark_blend_table; break; } } void set_alpha_level(unsigned int alpha, unsigned int mode) { recompute_alpha_lookup(alpha, (mode == BLENDMODE_DARK)?dark_blend_table:light_blend_table); } /* int main() { int fd; char buffer[1024]; struct pollfd fds[2]; int retval; fd = open_rs232_device("/dev/ttyS1"); if(fd < 0) return fd; fds[0].fd = fd; fds[0].events = POLLIN; open_framebuffer(); set_color(0,0,255); set_pos(100,100); box_fill(200,200); set_color(0,255,0); box(300,300); set_pos(150,100); set_color(0,0,0); set_bgcolor(255,255,0); print_string("Hello ",0); print_string("World", 1); set_font(1); set_pos(150,240); print_string("Hello ",0); print_string("World", 1); set_pos(320,240); set_color(255,0,0); set_backlight(fd,0); sleep(1); set_backlight(fd,1); beep(fd,880,100); beep(fd,0,100); beep(fd,440,100); while(1) { retval = poll(fds, 1, 10000); if(retval < 1) { printf("Meep!\n"); continue; } retval = read(fd, buffer, sizeof(buffer) - 1); if(retval > 0) { int x,y,z; buffer[retval] = '\0'; strip_crlf(buffer); printf("Got: \"%s\"\n", buffer); if(sscanf(buffer, "/T:%x,%x,%x", &z, &x, &y) == 3) { if(z) { x = translate_ts_x(x); y = translate_ts_y(y); line_to(x,y); } } } } return 0; } */