1 /*
2 * Fonteditfs - A full-screen console font editor.
3 * Copyright (C) 2002, 2003 Uri Shaked <uri@keves.org>.
4 * Portions of the code were donated by amir shalem <amir@boom.org.il>.
5 * Homepage: http://fonteditfs.sourceforge.net/
6 *
7 * Licensed under the BSD license:
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id: fnteditfs.c,v 1.4 2003/09/20 15:17:34 uri Exp $
29 * Main source file.
30 */
31
32 #include <string.h>
33 #include <signal.h>
34 #include <curses.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <errno.h>
41 #include "font.h"
42 #include "stack.h"
43
44 #define VERSION "1.2"
45
46 char *helptext[] = {
47 "Left/Up/Right/Down - move",
48 "Space - Toggle",
49 "W - Wide editing mode",
50 "L - Load file",
51 "S - Save file",
52 "C - Save font to console",
53 "D - Load font from console",
54 "Q - Quit",
55 "R - redraw screen",
56 "P - Push current -> stack",
57 "O - pOp stack -> current",
58 "PgUp - previous char",
59 "PgDown - next char",
60 "# - jump to specific char"
61 };
62 const int helplines = 14;
63
64 char *title = "fnteditfs "VERSION" - A Full-Screen Console Font Editor";
65
66 int charnum = 65;
67 int editx = 0, edity = 0;
68 int wide_editing = 0;
69
70 WINDOW *helpwin, *editwin, *statusln;
71 char fontbuf[4096];
72 char fnamebuf[_POSIX_PATH_MAX+1];
73 Stack *char_stack;
74
75 #define edit_line_refresh \
76 prefresh(pad, 0, x / (width - 10) * (width - 10) - 10, top, left, top, left + width)
77
edit_line(char * buf,int bufsize,int top,int left,int width,int numeric)78 char *edit_line (char *buf, int bufsize, int top, int left, int width, int numeric) {
79 WINDOW *pad = newpad(1, bufsize);
80 int ch, buflen = strlen(buf);
81 int insertmode = 1;
82 int y = 0, x = buflen;
83 if (!pad)
84 return (char*)0;
85 keypad(pad, 1);
86 wattrset(pad, A_BOLD);
87 wclear(pad);
88 waddstr(pad, buf);
89 edit_line_refresh;
90 while (1) {
91 getyx(pad, y, x);
92 ch = wgetch(pad);
93 switch (ch) {
94 case -1:
95 return NULL;
96 case KEY_IC: // insert
97 insertmode = !insertmode;
98 break;
99 case KEY_DC: // delete
100 if (buflen == 0) {
101 beep();
102 edit_line_refresh;
103 break;
104 }
105 if (x == buflen)
106 wmove(pad, y, --x);
107 memcpy(&buf[x], &buf[x+1], buflen - x + 1);
108 buf[--buflen] = (char)0;
109 waddstr(pad, &buf[x]);
110 wclrtoeol(pad);
111 wmove(pad, y, x);
112 edit_line_refresh;
113 break;
114 case KEY_BACKSPACE: // backspace
115 if (buflen == 0 || x == 0) {
116 beep();
117 edit_line_refresh;
118 break;
119 }
120 if (x == buflen) {
121 wmove(pad, y, --x);
122 waddch(pad, ' ');
123 wmove(pad, y, x);
124 buflen--;
125 } else {
126 wmove(pad, y, --x);
127 memcpy(&buf[x], &buf[x+1], buflen - x + 1);
128 buf[--buflen] = (char)0;
129 waddstr(pad, &buf[x]);
130 wclrtoeol(pad);
131 wmove(pad, y, x);
132 }
133 edit_line_refresh;
134 break;
135 case KEY_HOME: // home
136 x = 0;
137 wmove(pad, y, x);
138 edit_line_refresh;
139 break;
140 case KEY_END: // end
141 x = buflen;
142 wmove(pad, y, x);
143 edit_line_refresh;
144 break;
145 case KEY_LEFT: // left arrow
146 if (!x) {
147 beep();
148 edit_line_refresh;
149 break;
150 }
151 x--;
152 wmove(pad, y, x);
153 edit_line_refresh;
154 break;
155 case KEY_RIGHT: // right arrow
156 if (x == buflen) {
157 beep();
158 edit_line_refresh;
159 break;
160 }
161 x++;
162 wmove(pad, y, x);
163 edit_line_refresh;
164 break;
165 case 12: // ^L char
166 redrawwin(pad);
167 edit_line_refresh;
168 break;
169 case '\r': // CR
170 case '\n': // LF (line feed)
171 wattrset(pad, 0);
172 wmove(pad, 0, 0);
173 wclear(pad);
174 edit_line_refresh;
175 delwin(pad);
176 buf[buflen] = (char)0;
177 return (char*)buf;
178 case 21: // ^U char
179 x = 0;
180 buflen = 0;
181 wclear(pad);
182 edit_line_refresh;
183 break;
184 case 27: // ESC
185 wattrset(pad, 0);
186 wmove(pad, 0, 0);
187 wclear(pad);
188 edit_line_refresh;
189 delwin(pad);
190 return (char*) 0;
191 default: // any other key
192 if ((ch > 255 || ch < 32) || (numeric && (ch < '0' || ch > '9'))) {
193 beep();
194 edit_line_refresh;
195 break;
196 }
197 if (buflen >= bufsize - 1) { // end of buffer
198 beep();
199 edit_line_refresh;
200 break;
201 }
202 if (x == buflen) {
203 buf[x] = ch;
204 waddch(pad, ch);
205 buflen++;
206 } else {
207 if (insertmode) {
208 memcpy(&buf[x+1], &buf[x], buflen - x);
209 buf[++buflen] = (char)0;
210 buf[x] = ch;
211 waddstr(pad, &buf[x]);
212 wmove(pad, 0, ++x);
213 } else {
214 buf[x] = ch;
215 waddch(pad, ch);
216 }
217 }
218 edit_line_refresh;
219 }
220 }
221 }
222
draw_char(WINDOW * win,int num,char buf[])223 void draw_char (WINDOW *win, int num, char buf[]) {
224 int i, ch;
225 wmove(win, 0, 12 / 2 - 5);
226 wprintw(win, wide_editing ? " Char %03d [WIDE] " : " Char %03d ", charnum);
227 for (i = 0; i < 128; i++) {
228 if (i % 8 == 0)
229 wmove(win, i / 8 + 1, 2);
230 ch = buf[i / 8] & (1 << (7 - i % 8)) ? ACS_BLOCK : ' ';
231 waddch(win, ch);
232 if (wide_editing)
233 waddch(win, ch);
234 }
235 wmove(win, edity + 1, editx * (wide_editing + 1) + 2);
236 }
237
toggle_char_bit(WINDOW * win,int num,char * buf,int y,int x,int chwidth)238 void toggle_char_bit(WINDOW *win, int num, char *buf, int y, int x, int chwidth) {
239 int i;
240 buf[num * 16 + y] ^= (1 << (7 - x));
241 wmove(win, y + 1, x * chwidth + 2);
242 for (i = 0; i < chwidth; i++)
243 waddch(win, buf[num * 16 + y] & (1 << (7 - x)) ? ACS_BLOCK : ' ');
244 wmove(win, y + 1, x * chwidth + 2);
245 }
246
draw_screen()247 void draw_screen () {
248 int i, len = strlen(title);
249 attrset(A_STANDOUT);
250 move(0, 0);
251 for (i = 0; i < (80 - len) / 2; i++)
252 addch(' ');
253 addstr(title);
254 for (i = (80 - len) / 2 + len; i < COLS; i++)
255 addch(' ');
256 helpwin = newwin(helplines + 2, 30, 5, 5);
257 box(helpwin, ACS_VLINE, ACS_HLINE);
258 wmove(helpwin, 0, 30 / 2 - 3);
259 waddstr(helpwin, " Keys ");
260 for (i = 0; i < helplines; i++) {
261 wmove(helpwin, 1 + i, 2);
262 waddstr(helpwin, helptext[i]);
263 }
264 editwin = newwin(18, 12, 5, 45);
265 box(editwin, ACS_VLINE, ACS_HLINE);
266 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
267 refresh();
268 statusln = newwin(1, COLS, LINES - 1, 0);
269 wrefresh(helpwin);
270 wrefresh(editwin);
271 }
272
ask_move_char()273 void ask_move_char () {
274 char buf[4];
275 buf[0] = 0;
276 wclear(statusln);
277 mvwaddstr(statusln, 0, 0, "New character #:");
278 wrefresh(statusln);
279 if (edit_line(buf, sizeof(buf), LINES - 1, 17, 4, 1)) {
280 charnum = atol(buf) % 256;
281 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
282 wrefresh(editwin);
283 }
284 wclear(statusln);
285 wrefresh(statusln);
286 wrefresh(editwin);
287 }
288
interactive_load_file()289 void interactive_load_file () {
290 char buf[sizeof(fnamebuf)];
291 int rc;
292 strcpy(buf, fnamebuf);
293 wclear(statusln);
294 mvwaddstr(statusln, 0, 0, "Load from file:");
295 wrefresh(statusln);
296 if (!edit_line(buf, sizeof(buf), LINES - 1, 16, COLS - 16, 0)) {
297 wclear(statusln);
298 mvwaddstr(statusln, 0, 0, "Load canceled.");
299 wrefresh(statusln);
300 wrefresh(editwin);
301 return;
302 }
303 strcpy(fnamebuf, buf);
304 // first try to load as uuencoded font
305 rc = font_uuload(buf, fontbuf, sizeof(fontbuf));
306 if (rc == -1) // otherwise, try normal load
307 rc = font_load(buf, fontbuf, sizeof(fontbuf));
308 if (rc) {
309 wclear(statusln);
310 mvwprintw(statusln, 0, 0, "Load error: %s", font_error);
311 wrefresh(statusln);
312 wrefresh(editwin);
313 return;
314 }
315 wclear(statusln);
316 mvwaddstr(statusln, 0, 0, "Font loaded.");
317 wrefresh(statusln);
318 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
319 wrefresh(editwin);
320 }
321
interactive_save_file()322 void interactive_save_file () {
323 char buf[sizeof(fnamebuf)];
324 int rc;
325 strcpy(buf, fnamebuf);
326 wclear(statusln);
327 mvwaddstr(statusln, 0, 0, "Save to file:");
328 wrefresh(statusln);
329 if (!edit_line(buf, sizeof(buf), LINES - 1, 14, COLS - 14, 0)) {
330 wclear(statusln);
331 mvwaddstr(statusln, 0, 0, "Save canceled.");
332 wrefresh(statusln);
333 wrefresh(editwin);
334 return;
335 }
336 strcpy(fnamebuf, buf);
337 rc = font_save(buf, fontbuf, sizeof(fontbuf));
338 if (rc) {
339 wclear(statusln);
340 mvwprintw(statusln, 0, 0, "Save error: %s", font_error);
341 wrefresh(statusln);
342 wrefresh(editwin);
343 return;
344 }
345 wclear(statusln);
346 mvwaddstr(statusln, 0, 0, "Font saved.");
347 wrefresh(statusln);
348 wrefresh(editwin);
349 }
350
351 /**********************************/
352 /* operation system depended code */
353 /**********************************/
354
355 #if defined(__FreeBSD__)
356
357 #include <sys/ioctl.h>
358 #include <sys/consio.h>
359
do_ioctl(const char * header,unsigned long request,char * buf)360 int do_ioctl(const char *header, unsigned long request, char *buf) {
361 int fd;
362
363 if (ioctl(0, request, buf) < 0) {
364 fd = open("/dev/console", O_RDONLY, 0);
365 if (fd < 0) {
366 wclear(statusln);
367 mvwprintw(statusln, 0, 0, "%s failed to open /dev/console: %s", header, strerror(errno));
368 wrefresh(statusln);
369 wrefresh(editwin);
370 return -1;
371 }
372 if (ioctl(fd, request, buf) < 0) {
373 wclear(statusln);
374 mvwprintw(statusln, 0, 0, "%s failed to ioctl /dev/console: %s", header, strerror(errno));
375 wrefresh(statusln);
376 wrefresh(editwin);
377 close(fd);
378 return -1;
379 }
380 }
381 return 0;
382 }
383
save_font_to_console()384 void save_font_to_console () {
385 if (do_ioctl("Save error:", PIO_FONT8x16, fontbuf) < 0)
386 return;
387
388 wclear(statusln);
389 mvwaddstr(statusln, 0, 0, "Font saved to console.");
390 wrefresh(statusln);
391 wrefresh(editwin);
392 }
393
load_font_from_console()394 void load_font_from_console () {
395 if (do_ioctl("Load error:", GIO_FONT8x16, fontbuf) < 0)
396 return;
397
398 wclear(statusln);
399 mvwaddstr(statusln, 0, 0, "Font loaded from console.");
400 wrefresh(statusln);
401 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
402 wrefresh(editwin);
403 }
404
405 #else
406
save_font_to_console()407 void save_font_to_console () {
408 wclear(statusln);
409 mvwaddstr(statusln, 0, 0, "No console interaction is supported under this OS");
410 wrefresh(statusln);
411 wrefresh(editwin);
412 }
413
load_font_from_console()414 void load_font_from_console () {
415 save_font_to_console();
416 }
417
418 #endif /* OS is not supported */
419
420 /*****************************************/
421 /* end of operation system depended code */
422 /*****************************************/
423
input_loop()424 void input_loop () {
425 char *tmp;
426 int c;
427 while (1) {
428 c = getch();
429 switch (c) {
430 case KEY_UP:
431 edity ? edity -- : (edity = 15);
432 wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
433 wrefresh(editwin);
434 break;
435 case KEY_LEFT:
436 editx ? editx -- : (editx = 7);
437 wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
438 wrefresh(editwin);
439 break;
440 case KEY_DOWN:
441 edity ++; edity %= 16;
442 wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
443 wrefresh(editwin);
444 break;
445 case KEY_RIGHT:
446 editx ++; editx %= 8;
447 wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
448 wrefresh(editwin);
449 break;
450 case KEY_PPAGE:
451 charnum ? charnum-- : (charnum = 255);
452 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
453 wrefresh(editwin);
454 break;
455 case KEY_NPAGE:
456 charnum++;
457 charnum %= 256;
458 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
459 wrefresh(editwin);
460 break;
461 case '#':
462 ask_move_char();
463 break;
464 case ' ':
465 toggle_char_bit(editwin, charnum, fontbuf, edity, editx, wide_editing + 1);
466 wrefresh(editwin);
467 break;
468 case 'l': case 'L':
469 interactive_load_file();
470 break;
471 case 's': case 'S':
472 interactive_save_file();
473 break;
474 case 'c': case 'C':
475 save_font_to_console();
476 break;
477 case 'd': case 'D':
478 load_font_from_console();
479 break;
480 case 'p': case 'P':
481 tmp = (char*)malloc(16);
482 memcpy(tmp, &fontbuf[charnum * 16], 16);
483 stack_push(char_stack, tmp);
484 wclear(statusln);
485 mvwprintw(statusln, 0, 0, "Character %d pushed to stack (new stack size %d).",
486 charnum, stack_size(char_stack));
487 wrefresh(statusln);
488 break;
489 case 'o': case 'O':
490 tmp = (char*)stack_pop(char_stack);
491 wclear(statusln);
492 if (!tmp) {
493 mvwprintw(statusln, 0, 0, "Stack is already empty, can't pop.",
494 charnum, stack_size(char_stack));
495 wrefresh(statusln);
496 break;
497 }
498 memcpy(&fontbuf[charnum * 16], tmp, 16);
499 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
500 wrefresh(editwin);
501 mvwprintw(statusln, 0, 0, "Character %d poped from stack (new stack size %d).",
502 charnum, stack_size(char_stack));
503 wrefresh(statusln);
504 break;
505 case 'w': case 'W':
506 wide_editing = !wide_editing;
507 delwin(editwin);
508 editwin = newwin(18, wide_editing ? 20 : 12, 5, 45);
509 box(editwin, ACS_VLINE, ACS_HLINE);
510 draw_char(editwin, charnum, &fontbuf[charnum * 16]);
511 if (wide_editing) {
512 wrefresh(editwin);
513 break;
514 }
515 case 'r': case 'R':
516 redrawwin(stdscr);
517 redrawwin(helpwin);
518 redrawwin(editwin);
519 refresh();
520 wrefresh(helpwin);
521 wrefresh(editwin);
522 break;
523 case 'q': case 'Q':
524 return;
525 }
526 }
527 }
528
print_usage(char * argv[])529 void print_usage (char *argv[]) {
530 printf("Full screen font editor v" VERSION " by Uri Shaked <uri@keves.org>\n");
531 printf("Usage: %s fontfile\n", argv[0]);
532 exit(1);
533 }
534
main(int argc,char * argv[])535 int main (int argc, char *argv[]) {
536 int rc;
537 if (argc > 2)
538 print_usage(argv);
539 memset(fontbuf, 0, sizeof(fontbuf));
540 if (argc == 2) {
541 strcpy(fnamebuf, argv[1]);
542 rc = font_uuload(argv[1], fontbuf, sizeof(fontbuf));
543 if (rc == -1)
544 rc = font_load(argv[1], fontbuf, sizeof(fontbuf));
545 if (rc) {
546 printf("Load font: %s: %s", argv[1], font_error);
547 return 1;
548 }
549 } else
550 fnamebuf[0] = 0;
551 initscr();
552 cbreak();
553 noecho();
554 intrflush(stdscr,FALSE);
555 keypad(stdscr, TRUE);
556 nonl();
557 char_stack = stack_init();
558 draw_screen();
559 // if no font file loaded, try loading console font.
560 if (fnamebuf[0] == 0)
561 load_font_from_console();
562 input_loop();
563 endwin();
564 return 0;
565 }
566