/*
* 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;
}
*/