1/*	$NetBSD: rcons_subr.c,v 1.17 2009/03/14 15:36:20 dsl Exp $ */
2
3/*
4 * Copyright (c) 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
10 *
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 *	This product includes software developed by the University of
14 *	California, Lawrence Berkeley Laboratory.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 *    notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 *    notice, this list of conditions and the following disclaimer in the
23 *    documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 *	@(#)rcons_subr.c	8.1 (Berkeley) 6/11/93
41 */
42
43#include <sys/cdefs.h>
44__KERNEL_RCSID(0, "$NetBSD: rcons_subr.c,v 1.17 2009/03/14 15:36:20 dsl Exp $");
45
46#include <sys/param.h>
47#ifdef _KERNEL
48#include <sys/device.h>
49#include <sys/systm.h>
50#else
51#include "myfbdevice.h"
52#endif
53
54#include <dev/rcons/rcons.h>
55#include <dev/wscons/wsdisplayvar.h>
56
57extern void rcons_bell(struct rconsole *);
58
59#if 0
60#define RCONS_ISPRINT(c) ((((c) >= ' ') && ((c) <= '~')) || ((c) > 160))
61#else
62#define RCONS_ISPRINT(c) (((((c) >= ' ') && ((c) <= '~'))) || ((c) > 127))
63#endif
64#define RCONS_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
65
66/* Initialize our operations set */
67void
68rcons_init_ops(struct rconsole *rc)
69{
70	long tmp;
71	int i, m;
72
73	m = sizeof(rc->rc_charmap) / sizeof(rc->rc_charmap[0]);
74
75	for (i = 0; i < m; i++)
76		rc->rc_ops->mapchar(rc->rc_cookie, i, rc->rc_charmap + i);
77
78	/* Determine which attributes the device supports. */
79#ifdef RASTERCONSOLE_FGCOL
80	rc->rc_deffgcolor = RASTERCONSOLE_FGCOL;
81#endif
82#ifdef RASTERCONSOLE_BGCOL
83	rc->rc_defbgcolor = RASTERCONSOLE_BGCOL;
84#endif
85	rc->rc_fgcolor = rc->rc_deffgcolor;
86	rc->rc_bgcolor = rc->rc_defbgcolor;
87	rc->rc_supwsflg = 0;
88
89	for (i = 1; i < 256; i <<= 1)
90		if (rc->rc_ops->allocattr(rc->rc_cookie, 0, 0, i, &tmp) == 0)
91			rc->rc_supwsflg |= i;
92
93	/* Allocate kernel output attribute */
94	rc->rc_wsflg = WSATTR_HILIT;
95	rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
96	rc->rc_kern_attr = rc->rc_attr;
97
98	rc->rc_wsflg = 0;
99	rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
100	rc->rc_defattr = rc->rc_attr;
101}
102
103/* Output (or at least handle) a string sent to the console */
104void
105rcons_puts(struct rconsole *rc, const unsigned char *str, int n)
106{
107	int c, i, j;
108	const unsigned char *cp;
109
110	/* Jump scroll */
111	/* XXX maybe this should be an option? */
112	if ((rc->rc_bits & FB_INESC) == 0) {
113		/* Count newlines up to an escape sequence */
114		i = 0;
115		j = 0;
116		for (cp = str; j++ < n && *cp != '\033'; ++cp) {
117			if (*cp == '\n')
118				++i;
119			else if (*cp == '\013')
120				--i;
121		}
122
123		/* Only jump scroll two or more rows */
124		if (rc->rc_row + i > rc->rc_maxrow + 1) {
125			/* Erase the cursor (if necessary) */
126			if (rc->rc_bits & FB_CURSOR)
127				rcons_cursor(rc);
128
129			rcons_scroll(rc, i);
130		}
131	}
132
133	/* Process characters */
134	while (--n >= 0) {
135		c = *str;
136		if (c == '\033') {
137			/* Start an escape (perhaps aborting one in progress) */
138			rc->rc_bits |= FB_INESC | FB_P0_DEFAULT | FB_P1_DEFAULT;
139			rc->rc_bits &= ~(FB_P0 | FB_P1);
140
141			/* Most parameters default to 1 */
142			rc->rc_p0 = rc->rc_p1 = 1;
143		} else if (rc->rc_bits & FB_INESC) {
144			rcons_esc(rc, c);
145		} else {
146			/* Erase the cursor (if necessary) */
147			if (rc->rc_bits & FB_CURSOR)
148				rcons_cursor(rc);
149
150			/* Display the character */
151			if (RCONS_ISPRINT(c)) {
152				/* Try to output as much as possible */
153				j = rc->rc_maxcol - rc->rc_col;
154				if (j > n)
155					j = n;
156				for (i = 1; i < j && RCONS_ISPRINT(str[i]); ++i)
157					continue;
158				rcons_text(rc, str, i);
159				--i;
160				str += i;
161				n -= i;
162			} else
163				rcons_pctrl(rc, c);
164		}
165		++str;
166	}
167	/* Redraw the cursor (if necessary) */
168	if ((rc->rc_bits & FB_CURSOR) == 0)
169		rcons_cursor(rc);
170}
171
172
173/* Handle a control character sent to the console */
174void
175rcons_pctrl(struct rconsole *rc, int c)
176{
177
178	switch (c) {
179	case '\r':	/* Carriage return */
180		rc->rc_col = 0;
181		break;
182
183	case '\b':	/* Backspace */
184		if (rc->rc_col > 0)
185			(rc->rc_col)--;
186		break;
187
188	case '\v':	/* Vertical tab */
189		if (rc->rc_row > 0)
190			(rc->rc_row)--;
191		break;
192
193	case '\f':	/* Formfeed */
194		rc->rc_row = rc->rc_col = 0;
195		rcons_clear2eop(rc);
196		break;
197
198	case '\n':	/* Linefeed */
199		(rc->rc_row)++;
200		if (rc->rc_row >= rc->rc_maxrow)
201			rcons_scroll(rc, 1);
202		break;
203
204	case '\a':	/* Bell */
205		rcons_bell(rc);
206		break;
207
208	case '\t':	/* Horizontal tab */
209		rc->rc_col = (rc->rc_col + 8) & ~7;
210		if (rc->rc_col >= rc->rc_maxcol)
211			rc->rc_col = rc->rc_maxcol;
212		break;
213	}
214}
215
216/* Handle the next character in an escape sequence */
217void
218rcons_esc(struct rconsole *rc, int c)
219{
220
221	if (c == '[') {
222		/* Parameter 0 */
223		rc->rc_bits &= ~FB_P1;
224		rc->rc_bits |= FB_P0;
225	} else if (c == ';') {
226		/* Parameter 1 */
227		rc->rc_bits &= ~FB_P0;
228		rc->rc_bits |= FB_P1;
229	} else if (RCONS_ISDIGIT(c)) {
230		/* Add a digit to a parameter */
231		if (rc->rc_bits & FB_P0) {
232			/* Parameter 0 */
233			if (rc->rc_bits & FB_P0_DEFAULT) {
234				rc->rc_bits &= ~FB_P0_DEFAULT;
235				rc->rc_p0 = 0;
236			}
237			rc->rc_p0 *= 10;
238			rc->rc_p0 += c - '0';
239		} else if (rc->rc_bits & FB_P1) {
240			/* Parameter 1 */
241			if (rc->rc_bits & FB_P1_DEFAULT) {
242				rc->rc_bits &= ~FB_P1_DEFAULT;
243				rc->rc_p1 = 0;
244			}
245			rc->rc_p1 *= 10;
246			rc->rc_p1 += c - '0';
247		}
248	} else {
249		/* Erase the cursor (if necessary) */
250		if (rc->rc_bits & FB_CURSOR)
251			rcons_cursor(rc);
252
253		/* Process the completed escape sequence */
254		rcons_doesc(rc, c);
255		rc->rc_bits &= ~FB_INESC;
256	}
257}
258
259
260/* Handle an SGR (Select Graphic Rendition) escape */
261void
262rcons_sgresc(struct rconsole *rc, int c)
263{
264
265	switch (c) {
266	/* Clear all attributes || End underline */
267	case 0:
268		rc->rc_wsflg = 0;
269		rc->rc_fgcolor = rc->rc_deffgcolor;
270		rc->rc_bgcolor = rc->rc_defbgcolor;
271		rc->rc_attr = rc->rc_defattr;
272		break;
273
274	/* ANSI foreground color */
275	case 30: case 31: case 32: case 33:
276	case 34: case 35: case 36: case 37:
277		rcons_setcolor(rc, c - 30, rc->rc_bgcolor);
278		break;
279
280	/* ANSI background color */
281	case 40: case 41: case 42: case 43:
282	case 44: case 45: case 46: case 47:
283		rcons_setcolor(rc, rc->rc_fgcolor, c - 40);
284		break;
285
286	/* Begin reverse */
287	case 7:
288		rc->rc_wsflg |= WSATTR_REVERSE;
289		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
290		break;
291
292	/* Begin bold */
293	case 1:
294		rc->rc_wsflg |= WSATTR_HILIT;
295		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
296		break;
297
298	/* Begin underline */
299	case 4:
300		rc->rc_wsflg |= WSATTR_UNDERLINE;
301		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
302		break;
303	}
304}
305
306
307/* Process a complete escape sequence */
308void
309rcons_doesc(struct rconsole *rc, int c)
310{
311
312#ifdef notdef
313	/* XXX add escape sequence to enable visual (and audible) bell */
314	rc->rc_bits = FB_VISBELL;
315#endif
316
317	switch (c) {
318
319	case '@':
320		/* Insert Character (ICH) */
321		rcons_insertchar(rc, rc->rc_p0);
322		break;
323
324	case 'A':
325		/* Cursor Up (CUU) */
326		if (rc->rc_row >= rc->rc_p0)
327			rc->rc_row -= rc->rc_p0;
328		else
329			rc->rc_row = 0;
330		break;
331
332	case 'B':
333		/* Cursor Down (CUD) */
334		rc->rc_row += rc->rc_p0;
335		if (rc->rc_row >= rc->rc_maxrow)
336			rc->rc_row = rc->rc_maxrow - 1;
337		break;
338
339	case 'C':
340		/* Cursor Forward (CUF) */
341		rc->rc_col += rc->rc_p0;
342		if (rc->rc_col >= rc->rc_maxcol)
343			rc->rc_col = rc->rc_maxcol - 1;
344		break;
345
346	case 'D':
347		/* Cursor Backward (CUB) */
348		if (rc->rc_col >= rc->rc_p0)
349			rc->rc_col -= rc->rc_p0;
350		else
351			rc->rc_col = 0;
352		break;
353
354	case 'E':
355		/* Cursor Next Line (CNL) */
356		rc->rc_col = 0;
357		rc->rc_row += rc->rc_p0;
358		if (rc->rc_row >= rc->rc_maxrow)
359			rc->rc_row = rc->rc_maxrow - 1;
360		break;
361
362	case 'f':
363		/* Horizontal And Vertical Position (HVP) */
364	case 'H':
365		/* Cursor Position (CUP) */
366		rc->rc_col = MIN(MAX(rc->rc_p1, 1), rc->rc_maxcol) - 1;
367		rc->rc_row = MIN(MAX(rc->rc_p0, 1), rc->rc_maxrow) - 1;
368		break;
369
370	case 'J':
371		/* Erase in Display (ED) */
372		rcons_clear2eop(rc);
373		break;
374
375	case 'K':
376		/* Erase in Line (EL) */
377		rcons_clear2eol(rc);
378		break;
379
380	case 'L':
381		/* Insert Line (IL) */
382		rcons_insertline(rc, rc->rc_p0);
383		break;
384
385	case 'M':
386		/* Delete Line (DL) */
387		rcons_delline(rc, rc->rc_p0);
388		break;
389
390	case 'P':
391		/* Delete Character (DCH) */
392		rcons_delchar(rc, rc->rc_p0);
393		break;
394
395	case 'm':
396		/* Select Graphic Rendition (SGR) */
397		/* (defaults to zero) */
398		if (rc->rc_bits & FB_P0_DEFAULT)
399			rc->rc_p0 = 0;
400
401		if (rc->rc_bits & FB_P1_DEFAULT)
402			rc->rc_p1 = 0;
403
404		rcons_sgresc(rc, rc->rc_p0);
405
406		if (rc->rc_bits & FB_P1)
407			rcons_sgresc(rc, rc->rc_p1);
408
409		break;
410
411	/*
412	 * XXX: setting SUNBOW and SUNWOB should probably affect
413	 * deffgcolor, defbgcolor and defattr too.
414	 */
415	case 'p':
416		/* Black On White (SUNBOW) */
417		rcons_setcolor(rc, WSCOL_BLACK, WSCOL_WHITE);
418		break;
419
420	case 'q':
421		/* White On Black (SUNWOB) */
422		rcons_setcolor(rc, WSCOL_WHITE, WSCOL_BLACK);
423		break;
424
425	case 'r':
426		/* Set scrolling (SUNSCRL) */
427		/* (defaults to zero) */
428		if (rc->rc_bits & FB_P0_DEFAULT)
429			rc->rc_p0 = 0;
430		/* XXX not implemented yet */
431		rc->rc_scroll = rc->rc_p0;
432		break;
433
434	case 's':
435		/* Reset terminal emulator (SUNRESET) */
436		rc->rc_wsflg = 0;
437		rc->rc_scroll = 0;
438		rc->rc_bits &= ~FB_NO_CURSOR;
439		rc->rc_fgcolor = rc->rc_deffgcolor;
440		rc->rc_bgcolor = rc->rc_defbgcolor;
441		rc->rc_attr = rc->rc_defattr;
442
443		if (rc->rc_bits & FB_INVERT)
444			rcons_invert(rc, 0);
445		break;
446#ifdef notyet
447	/*
448	 * XXX following two read \E[?25h and \E[?25l. rcons
449	 * can't currently handle the '?'.
450	 */
451	case 'h':
452		/* Normal/very visible cursor */
453		if (rc->rc_p0 == 25) {
454			rc->rc_bits &= ~FB_NO_CURSOR;
455
456			if (rc->rc_bits & FB_CURSOR) {
457				rc->rc_bits ^= FB_CURSOR;
458				rcons_cursor(rc);
459			}
460		}
461		break;
462
463	case 'l':
464		/* Invisible cursor */
465		if (rc->rc_p0 == 25 && (rc->rc_bits & FB_NO_CURSOR) == 0) {
466			if (rc->rc_bits & FB_CURSOR)
467				rcons_cursor(rc);
468
469			rc->rc_bits |= FB_NO_CURSOR;
470		}
471		break;
472#endif
473	}
474}
475
476/* Set ANSI colors */
477void
478rcons_setcolor(struct rconsole *rc, int fg, int bg)
479{
480	int flg;
481
482	if (fg > WSCOL_WHITE || fg < 0)
483		return;
484
485	if (bg > WSCOL_WHITE || bg < 0)
486		return;
487
488#ifdef RASTERCONS_WONB
489	flg = bg;
490	bg = fg;
491	fg = flg;
492#endif
493
494	/* Emulate WSATTR_REVERSE attribute if it's not supported */
495	if ((rc->rc_wsflg & WSATTR_REVERSE) &&
496	    !(rc->rc_supwsflg & WSATTR_REVERSE)) {
497		flg = bg;
498		bg = fg;
499		fg = flg;
500	}
501
502	/*
503	 * Mask out unsupported flags and get attribute
504	 * XXX - always ask for WSCOLORS if supported (why shouldn't we?)
505	 */
506	flg = (rc->rc_wsflg | WSATTR_WSCOLORS) & rc->rc_supwsflg;
507	rc->rc_bgcolor = bg;
508	rc->rc_fgcolor = fg;
509	rc->rc_ops->allocattr(rc->rc_cookie, fg, bg, flg, &rc->rc_attr);
510}
511
512
513/* Actually write a string to the frame buffer */
514void
515rcons_text(struct rconsole *rc, const unsigned char *str, int n)
516{
517	u_int uc;
518
519	while (n--) {
520		uc = rc->rc_charmap[*str++ & 255];
521		rc->rc_ops->putchar(rc->rc_cookie, rc->rc_row, rc->rc_col++,
522		    uc, rc->rc_attr);
523	}
524
525	if (rc->rc_col >= rc->rc_maxcol) {
526		rc->rc_col = 0;
527		rc->rc_row++;
528	}
529
530	if (rc->rc_row >= rc->rc_maxrow)
531		rcons_scroll(rc, 1);
532}
533
534/* Paint (or unpaint) the cursor */
535void
536rcons_cursor(struct rconsole *rc)
537{
538	rc->rc_bits ^= FB_CURSOR;
539
540	if (rc->rc_bits & FB_NO_CURSOR)
541		return;
542
543	rc->rc_ops->cursor(rc->rc_cookie, rc->rc_bits & FB_CURSOR,
544	    rc->rc_row, rc->rc_col);
545}
546
547/* Possibly change to SUNWOB or SUNBOW mode */
548void
549rcons_invert(struct rconsole *rc, int wob)
550{
551
552	rc->rc_bits ^= FB_INVERT;
553	/* XXX how do we do we invert the framebuffer?? */
554}
555
556/* Clear to the end of the page */
557void
558rcons_clear2eop(struct rconsole *rc)
559{
560	if (rc->rc_col || rc->rc_row) {
561		rcons_clear2eol(rc);
562
563		if (rc->rc_row < (rc->rc_maxrow - 1))
564			rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row + 1,
565			    rc->rc_maxrow, rc->rc_attr);
566	} else
567		rc->rc_ops->eraserows(rc->rc_cookie, 0, rc->rc_maxrow,
568		    rc->rc_attr);
569}
570
571/* Clear to the end of the line */
572void
573rcons_clear2eol(struct rconsole *rc)
574{
575	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
576	    rc->rc_maxcol - rc->rc_col, rc->rc_attr);
577}
578
579
580/* Scroll up */
581void
582rcons_scroll(struct rconsole *rc, int n)
583{
584	/* Can't scroll more than the whole screen */
585	if (n > rc->rc_maxrow)
586		n = rc->rc_maxrow;
587
588	/* Calculate new row */
589	if (rc->rc_row >= n)
590		rc->rc_row -= n;
591	else
592		rc->rc_row = 0;
593
594	rc->rc_ops->copyrows(rc->rc_cookie, n, 0, rc->rc_maxrow - n);
595	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,  rc->rc_attr);
596}
597
598/* Delete characters */
599void
600rcons_delchar(struct rconsole *rc, int n)
601{
602	/* Can't delete more chars than there are */
603	if (n > rc->rc_maxcol - rc->rc_col)
604		n = rc->rc_maxcol - rc->rc_col;
605
606	rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col + n,
607	    rc->rc_col, rc->rc_maxcol - rc->rc_col - n);
608
609	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row,
610	    rc->rc_maxcol - n, n, rc->rc_attr);
611}
612
613/* Delete a number of lines */
614void
615rcons_delline(struct rconsole *rc, int n)
616{
617	/* Can't delete more lines than there are */
618	if (n > rc->rc_maxrow - rc->rc_row)
619		n = rc->rc_maxrow - rc->rc_row;
620
621	rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row + n, rc->rc_row,
622	    rc->rc_maxrow - rc->rc_row - n);
623
624	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,
625	    rc->rc_attr);
626}
627
628/* Insert some characters */
629void
630rcons_insertchar(struct rconsole *rc, int n)
631{
632	/* Can't insert more chars than can fit */
633	if (n > rc->rc_maxcol - rc->rc_col)
634		n = rc->rc_maxcol - rc->rc_col - 1;
635
636	rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col,
637	    rc->rc_col + n, rc->rc_maxcol - rc->rc_col - n - 1);
638
639	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
640	    n, rc->rc_attr);
641}
642
643/* Insert some lines */
644void
645rcons_insertline(struct rconsole *rc, int n)
646{
647	/* Can't insert more lines than can fit */
648	if (n > rc->rc_maxrow - rc->rc_row)
649		n = rc->rc_maxrow - rc->rc_row;
650
651	rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row, rc->rc_row + n,
652	    rc->rc_maxrow - rc->rc_row - n);
653
654	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row, n,
655	    rc->rc_attr);
656}
657
658/* end of rcons_subr.c */
659