#include <string.h>
#include <signal.h>
#include <curses.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#define VERSION "1.0"
char *helptext[] = {
"Left/Up/Right/Down - move",
"Space - Toggle",
"L - Load file",
"S - Save file",
"C - Save font to console",
"D - Load font from console",
"Q - Quit",
"R - redraw screen",
"PgUp - previous char",
"PgDown - next char",
"# - jump to specific char"
};
const int helplines = 11;
char *title = "fnteditfs "VERSION" - A Full-Screen Console Font Editor";
int charnum = 65;
int editx = 0, edity = 0;
WINDOW *helpwin, *editwin, *statusln;
char fontbuf[4096];
char fnamebuf[_POSIX_PATH_MAX+1];
#define edit_line_refresh \
prefresh(pad, 0, x / (width - 10) * (width - 10) - 10, top, left, top, left + width)
char *edit_line (char *buf, int bufsize, int top, int left, int width, int numeric) {
WINDOW *pad = newpad(1, bufsize);
int ch, buflen = strlen(buf);
int insertmode = 1;
int y = 0, x = buflen;
if (!pad)
return (char*)0;
keypad(pad, 1);
wattrset(pad, A_BOLD);
wclear(pad);
waddstr(pad, buf);
edit_line_refresh;
while (1) {
getyx(pad, y, x);
ch = wgetch(pad);
switch (ch) {
case -1:
return NULL;
case KEY_IC: // insert
insertmode = !insertmode;
break;
case KEY_DC: // delete
if (buflen == 0) {
beep();
edit_line_refresh;
break;
}
if (x == buflen)
wmove(pad, y, --x);
memcpy(&buf[x], &buf[x+1], buflen - x + 1);
buf[--buflen] = (char)0;
waddstr(pad, &buf[x]);
wclrtoeol(pad);
wmove(pad, y, x);
edit_line_refresh;
break;
case KEY_BACKSPACE: // backspace
if (buflen == 0 || x == 0) {
beep();
edit_line_refresh;
break;
}
if (x == buflen) {
wmove(pad, y, --x);
waddch(pad, ' ');
wmove(pad, y, x);
buflen--;
} else {
wmove(pad, y, --x);
memcpy(&buf[x], &buf[x+1], buflen - x + 1);
buf[--buflen] = (char)0;
waddstr(pad, &buf[x]);
wclrtoeol(pad);
wmove(pad, y, x);
}
edit_line_refresh;
break;
case KEY_HOME: // home
x = 0;
wmove(pad, y, x);
edit_line_refresh;
break;
case KEY_END: // end
x = buflen;
wmove(pad, y, x);
edit_line_refresh;
break;
case KEY_LEFT: // left arrow
if (!x) {
beep();
edit_line_refresh;
break;
}
x--;
wmove(pad, y, x);
edit_line_refresh;
break;
case KEY_RIGHT: // right arrow
if (x == buflen) {
beep();
edit_line_refresh;
break;
}
x++;
wmove(pad, y, x);
edit_line_refresh;
break;
case 12: // ^L char
redrawwin(pad);
edit_line_refresh;
break;
case '\r': // CR
case '\n': // LF (line feed)
wattrset(pad, 0);
wmove(pad, 0, 0);
wclear(pad);
edit_line_refresh;
delwin(pad);
buf[buflen] = (char)0;
return (char*)buf;
case 21: // ^U char
x = 0;
buflen = 0;
wclear(pad);
edit_line_refresh;
break;
case 27: // ESC
wattrset(pad, 0);
wmove(pad, 0, 0);
wclear(pad);
edit_line_refresh;
delwin(pad);
return (char*) 0;
default: // any other key
if ((ch > 255 || ch < 32) || (numeric && (ch < '0' || ch > '9'))) {
beep();
edit_line_refresh;
break;
}
if (buflen >= bufsize - 1) { // end of buffer
beep();
edit_line_refresh;
break;
}
if (x == buflen) {
buf[x] = ch;
waddch(pad, ch);
buflen++;
} else {
if (insertmode) {
memcpy(&buf[x+1], &buf[x], buflen - x);
buf[++buflen] = (char)0;
buf[x] = ch;
waddstr(pad, &buf[x]);
wmove(pad, 0, ++x);
} else {
buf[x] = ch;
waddch(pad, ch);
}
}
edit_line_refresh;
}
}
}
void draw_char (WINDOW *win, int num, char buf[]) {
int i;
wmove(win, 0, 12 / 2 - 5);
wprintw(win, " Char %03d ", charnum);
for (i = 0; i < 128; i++) {
if (i % 8 == 0)
wmove(win, i / 8 + 1, 2);
waddch(win, buf[i / 8] & (1 << (7 - i % 8)) ? ACS_BLOCK : ' ');
}
wmove(win, edity + 1, editx + 2);
}
void toggle_char_bit(WINDOW *win, int num, char *buf, int y, int x) {
buf[num * 16 + y] ^= (1 << (7 - x));
wmove(win, y + 1, x + 2);
waddch(win, buf[num * 16 + y] & (1 << (7 - x)) ? ACS_BLOCK : ' ');
wmove(win, y + 1, x + 2);
}
void draw_screen () {
int i, len = strlen(title);
attrset(A_STANDOUT);
move(0, 0);
for (i = 0; i < (80 - len) / 2; i++)
addch(' ');
addstr(title);
for (i = (80 - len) / 2 + len; i < COLS; i++)
addch(' ');
helpwin = newwin(helplines + 2, 30, 5, 5);
box(helpwin, ACS_VLINE, ACS_HLINE);
wmove(helpwin, 0, 30 / 2 - 3);
waddstr(helpwin, " Keys ");
for (i = 0; i < helplines; i++) {
wmove(helpwin, 1 + i, 2);
waddstr(helpwin, helptext[i]);
}
editwin = newwin(18, 12, 5, 59);
box(editwin, ACS_VLINE, ACS_HLINE);
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
refresh();
statusln = newwin(1, COLS, LINES - 1, 0);
wrefresh(helpwin);
wrefresh(editwin);
}
void ask_move_char () {
char buf[4];
buf[0] = 0;
wclear(statusln);
mvwaddstr(statusln, 0, 0, "New character #:");
wrefresh(statusln);
if (edit_line(buf, sizeof(buf), LINES - 1, 17, 4, 1)) {
charnum = atol(buf) % 256;
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
}
wclear(statusln);
wrefresh(statusln);
wrefresh(editwin);
}
void interactive_load_file () {
char buf[sizeof(fnamebuf)];
int fd, rc;
strcpy(buf, fnamebuf);
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Load from file:");
wrefresh(statusln);
if (!edit_line(buf, sizeof(buf), LINES - 1, 16, COLS - 16, 0)) {
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Load canceled.");
wrefresh(statusln);
wrefresh(editwin);
return;
}
strcpy(fnamebuf, buf);
fd = open(buf, O_RDONLY);
if (fd < 0) {
wclear(statusln);
mvwprintw(statusln, 0, 0, "Load error: open: %s", strerror(errno));
wrefresh(statusln);
wrefresh(editwin);
return;
}
rc = read(fd, fontbuf, sizeof(fontbuf));
if (rc < sizeof(fontbuf)) {
wclear(statusln);
if (rc < 0)
mvwprintw(statusln, 0, 0, "Load error: read: %s", strerror(errno));
else if (rc == 0)
mvwaddstr(statusln, 0, 0, "Load error: Font is empty");
else
mvwaddstr(statusln, 0, 0, "Load warning: Font is too short; partially loaded");
wrefresh(statusln);
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
close(fd);
return;
}
close(fd);
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Font loaded.");
wrefresh(statusln);
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
}
void interactive_save_file () {
char buf[sizeof(fnamebuf)];
int fd, rc;
strcpy(buf, fnamebuf);
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Save to file:");
wrefresh(statusln);
if (!edit_line(buf, sizeof(buf), LINES - 1, 14, COLS - 14, 0)) {
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Save canceled.");
wrefresh(statusln);
wrefresh(editwin);
return;
}
strcpy(fnamebuf, buf);
fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
wclear(statusln);
mvwprintw(statusln, 0, 0, "Save error: open: %s", strerror(errno));
wrefresh(statusln);
wrefresh(editwin);
return;
}
rc = write(fd, fontbuf, sizeof(fontbuf));
if (rc < sizeof(fontbuf)) {
wclear(statusln);
mvwprintw(statusln, 0, 0, "Save error: write: %s", strerror(errno));
wrefresh(statusln);
wrefresh(editwin);
close(fd);
return;
}
close(fd);
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Font saved.");
wrefresh(statusln);
wrefresh(editwin);
}
/**********************************/
/* operation system depended code */
/**********************************/
#if defined(__FreeBSD__)
#include <sys/ioctl.h>
#include <sys/consio.h>
int do_ioctl(const char *header, unsigned long request, char *buf) {
int fd;
if (ioctl(0, request, buf) < 0) {
fd = open("/dev/console", O_RDONLY, 0);
if (fd < 0) {
wclear(statusln);
mvwprintw(statusln, 0, 0, "%s failed to open /dev/console: %s", header, strerror(errno));
wrefresh(statusln);
wrefresh(editwin);
return -1;
}
if (ioctl(fd, request, buf) < 0) {
wclear(statusln);
mvwprintw(statusln, 0, 0, "%s failed to ioctl /dev/console: %s", header, strerror(errno));
wrefresh(statusln);
wrefresh(editwin);
close(fd);
return -1;
}
}
return 0;
}
void save_font_to_console () {
if (do_ioctl("Save error:", PIO_FONT8x16, fontbuf) < 0)
return;
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Font saved to console.");
wrefresh(statusln);
wrefresh(editwin);
}
void load_font_from_console () {
if (do_ioctl("Load error:", GIO_FONT8x16, fontbuf) < 0)
return;
wclear(statusln);
mvwaddstr(statusln, 0, 0, "Font loaded from console.");
wrefresh(statusln);
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
}
#else
void save_font_to_console () {
wclear(statusln);
mvwaddstr(statusln, 0, 0, "No console interaction is supported under this OS");
wrefresh(statusln);
wrefresh(editwin);
}
void load_font_from_console () {
save_font_to_console();
}
#endif /* OS is not supported */
/*****************************************/
/* end of operation system depended code */
/*****************************************/
void input_loop () {
int c;
while (1) {
c = getch();
switch (c) {
case KEY_UP:
edity ? edity -- : (edity = 15);
wmove(editwin, edity+1, editx+2);
wrefresh(editwin);
break;
case KEY_LEFT:
editx ? editx -- : (editx = 7);
wmove(editwin, edity+1, editx+2);
wrefresh(editwin);
break;
case KEY_DOWN:
edity ++; edity %= 16;
wmove(editwin, edity+1, editx+2);
wrefresh(editwin);
break;
case KEY_RIGHT:
editx ++; editx %= 8;
wmove(editwin, edity+1, editx+2);
wrefresh(editwin);
break;
case KEY_PPAGE:
charnum ? charnum-- : (charnum = 255);
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
break;
case KEY_NPAGE:
charnum++;
charnum %= 256;
draw_char(editwin, charnum, &fontbuf[charnum * 16]);
wrefresh(editwin);
break;
case '#':
ask_move_char();
break;
case ' ':
toggle_char_bit(editwin, charnum, fontbuf, edity, editx);
wrefresh(editwin);
break;
case 'l': case 'L':
interactive_load_file();
break;
case 's': case 'S':
interactive_save_file();
break;
case 'c': case 'C':
save_font_to_console();
break;
case 'd': case 'D':
load_font_from_console();
break;
case 'r': case 'R':
redrawwin(stdscr);
redrawwin(helpwin);
redrawwin(editwin);
refresh();
wrefresh(helpwin);
wrefresh(editwin);
break;
case 'q': case 'Q':
return;
}
}
}
void print_usage (char *argv[]) {
printf("Full screen font editor v" VERSION " by Uri Shaked <uri@keves.org>\n");
printf("Usage: %s fontfile\n", argv[0]);
exit(1);
}
int main (int argc, char *argv[]) {
int fd, rc;
if (argc > 2)
print_usage(argv);
memset(fontbuf, 0, sizeof(fontbuf));
if (argc == 2) {
strcpy(fnamebuf, argv[1]);
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
rc = read(fd, fontbuf, sizeof(fontbuf));
if (rc == -1) {
perror("read");
return 1;
}
close(fd);
} else
fnamebuf[0] = 0;
initscr();
cbreak();
noecho();
intrflush(stdscr,FALSE);
keypad(stdscr, TRUE);
nonl();
draw_screen();
// if no font file loaded, try loading console font.
if (fnamebuf[0] == 0)
load_font_from_console();
input_loop();
endwin();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1