shithub: olednews

ref: 77eb1803c4620ff1362e03faa8a8d674dbe11b2f
dir: /textimg.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>

void
usage(void)
{
	sysfatal("usage: %s [-f subfont] [-p reformfs/] [-m ms] [-h y] [-w x] < text", argv0);
}

// Stolen from `iconv -u`. 
void	writeuncompressed(int fd, Memimage *m);


// Scroll text in two lines across the MNT Reform (v2) keyboard OLED display. 
void
main(int argc, char **argv)
{
	char *s, *path, *bufbot, *buftop;
	Memsubfont *f;
	Point p;
	int fd, i, w, h, ms, lineh;
	Biobufhdr *in;	
	int ncharspline;
	long n;

	w = 126;
	h = 32;
	ms = 100;

	s = "/lib/font/bit/vga/vga.0000-007F";
	f = nil;
	path = "/mnt/reform/kbdoled";
	in = Bfdopen(0, OREAD);

	ARGBEGIN{
	case 'f':
		s = EARGF(usage());
		break;
	case 'p':
		path = EARGF(usage());
		break;
	case 'm':
		ms = atoi(EARGF(usage()));
		break;
	case 'w':
		w = atoi(EARGF(usage()));
		break;
	case 'h':
		h = atoi(EARGF(usage()));
		break;
	}ARGEND;

	// I'm not convinced this is mandatory. 
	if(memimageinit())
		sysfatal("memimageinit failed: %r");
	 
	if(s)
		f = openmemsubfont(s);
	if(!f){
		fprint(2, "cannot load subfont. Falling back to memdraw default.\n");
		f = getmemdefont();
	}

	/* 
		Read text in - one OLED line width at a time. 
		2 lines on the OLED display @ height 8 font, like vga. 
		p.x = max(?) width of one character in pixels. 
		126 x 32 oled. 
		Q is arbitrary. 
	*/
	p = memsubfontwidth(f, "Q");
	if (p.x == 0)
		sysfatal("font has no width, not wide enough to ride");

	// The height of the text line ≡ the font's height
	lineh = f->height;

	// Number of characters that can be on a line. 
	ncharspline = w / p.x;

	// Initialize our buffers. 
	bufbot = calloc(ncharspline, sizeof (char));
	n = Bread(in, bufbot, ncharspline-1);
	if(n <= 0)
		sysfatal("no bread in the bread box");
	bufbot[n] = '\0';
	for(i = 0; i < n; i++)
		if(bufbot[i] == '\n' || bufbot[i] == '\r')
			bufbot[i] = ' ';

	buftop = calloc(ncharspline, sizeof (char));
	memset(buftop, ' ', ncharspline*sizeof (char));
	buftop[n] = '\0';

	// Primary loop routine. 
	for(;;){
		Memimage *img;
		char c;

		// Not entirely sure if open/close every time is necessary. 
		fd = open(path, OWRITE);
		if(fd < 0){
			sysfatal("could not open kbdoled file → %r");
		}
		
		// White background for the whole OLED. 
		// This might be incorrect, but it works cosmetically. 
		img = allocmemimage(Rect(0, 0, p.x*ncharspline, f->height+lineh), GREY1);
		if (!img)
			sysfatal("cannot allocate memimage: %r");
		memfillcolor(img, DWhite);
		
		// Top line
		memimagestring(img, Pt(0, 0), memblack, ZP, f, buftop);

		// Bottom line
		memimagestring(img, Pt(0, h/2), memblack, ZP, f, bufbot);

		// Reform/pm requires an uncompressed plan9 bitmap image
		writeuncompressed(fd, img);
	
		close(fd);

		// Read one character at a time in for 'text scroll' effect. 
		c = Bgetc(in);

		// EOF
		if(c <= 0)
			break;

		// Don't print whitespace symbols other than spaces. 
		if(c == '\n' || c == '\r')
			c = ' ';

		// Shift the top values in from the bottom row. 
		for(i = 0; i < n-1; i++)
			buftop[i] = buftop[i+1];
		
		buftop[n-1] = bufbot[0];

		// Shift the bottom values over and the new char in. 
		for(i = 0; i < n-1; i++)
			bufbot[i] = bufbot[i+1];
		
		bufbot[n-1] = c;
		freememimage(img);

		sleep(ms);
	}

	free(bufbot);
	free(buftop);
}


void
writeuncompressed(int fd, Memimage *m)
{
	char chanstr[32];
	int bpl, y, j;
	uchar *buf;

	if(chantostr(chanstr, m->chan) == nil)
		sysfatal("can't convert channel descriptor: %r");
	fprint(fd, "%11s %11d %11d %11d %11d ",
		chanstr, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);

	bpl = bytesperline(m->r, m->depth);
	buf = malloc(bpl+1);
	if(buf == nil)
		sysfatal("malloc failed: %r");
	for(y=m->r.min.y; y<m->r.max.y; y++){
		j = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), buf, bpl);
		if(j != bpl)
			sysfatal("image unload failed: %r");
		if(write(fd, buf, bpl) != bpl)
			sysfatal("write failed: %r");
	}
	free(buf);
}