xref: /netbsd-src/sys/dev/rcons/rcons_subr.c (revision 82357f6d420792564499086cb6440ad2fcce8410)
1 /*	$NetBSD: rcons_subr.c,v 1.18 2009/03/14 21:04:22 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.18 2009/03/14 21:04:22 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 
57 extern 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 */
67 void
rcons_init_ops(struct rconsole * rc)68 rcons_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 */
104 void
rcons_puts(struct rconsole * rc,const unsigned char * str,int n)105 rcons_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 */
174 void
rcons_pctrl(struct rconsole * rc,int c)175 rcons_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 */
217 void
rcons_esc(struct rconsole * rc,int c)218 rcons_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 */
261 void
rcons_sgresc(struct rconsole * rc,int c)262 rcons_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 */
308 void
rcons_doesc(struct rconsole * rc,int c)309 rcons_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 */
477 void
rcons_setcolor(struct rconsole * rc,int fg,int bg)478 rcons_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 */
514 void
rcons_text(struct rconsole * rc,const unsigned char * str,int n)515 rcons_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 */
535 void
rcons_cursor(struct rconsole * rc)536 rcons_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 */
548 void
rcons_invert(struct rconsole * rc,int wob)549 rcons_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 */
557 void
rcons_clear2eop(struct rconsole * rc)558 rcons_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 */
572 void
rcons_clear2eol(struct rconsole * rc)573 rcons_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 */
581 void
rcons_scroll(struct rconsole * rc,int n)582 rcons_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 */
599 void
rcons_delchar(struct rconsole * rc,int n)600 rcons_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 */
614 void
rcons_delline(struct rconsole * rc,int n)615 rcons_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 */
629 void
rcons_insertchar(struct rconsole * rc,int n)630 rcons_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 */
644 void
rcons_insertline(struct rconsole * rc,int n)645 rcons_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