1 /*- 2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3 * All rights reserved. 4 * 5 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer as 13 * the first lines of this file unmodified. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include "opt_syscons.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/module.h> 39 #include <sys/consio.h> 40 41 #if defined(__sparc64__) || defined(__powerpc__) 42 #include <machine/sc_machdep.h> 43 #else 44 #include <machine/pc/display.h> 45 #endif 46 47 #include <dev/syscons/syscons.h> 48 49 #include <dev/syscons/teken/teken.h> 50 51 static void scteken_revattr(unsigned char, teken_attr_t *); 52 static unsigned int scteken_attr(const teken_attr_t *); 53 54 static sc_term_init_t scteken_init; 55 static sc_term_term_t scteken_term; 56 static sc_term_puts_t scteken_puts; 57 static sc_term_ioctl_t scteken_ioctl; 58 static sc_term_default_attr_t scteken_default_attr; 59 static sc_term_clear_t scteken_clear; 60 static sc_term_input_t scteken_input; 61 static void scteken_nop(void); 62 63 typedef struct { 64 teken_t ts_teken; 65 int ts_busy; 66 } teken_stat; 67 68 static teken_stat reserved_teken_stat; 69 70 static sc_term_sw_t sc_term_scteken = { 71 { NULL, NULL }, 72 "scteken", /* emulator name */ 73 "teken terminal", /* description */ 74 "*", /* matching renderer */ 75 sizeof(teken_stat), /* softc size */ 76 0, 77 scteken_init, 78 scteken_term, 79 scteken_puts, 80 scteken_ioctl, 81 (sc_term_reset_t *)scteken_nop, 82 scteken_default_attr, 83 scteken_clear, 84 (sc_term_notify_t *)scteken_nop, 85 scteken_input, 86 }; 87 88 SCTERM_MODULE(scteken, sc_term_scteken); 89 90 static tf_bell_t scteken_bell; 91 static tf_cursor_t scteken_cursor; 92 static tf_putchar_t scteken_putchar; 93 static tf_fill_t scteken_fill; 94 static tf_copy_t scteken_copy; 95 static tf_param_t scteken_param; 96 static tf_respond_t scteken_respond; 97 98 static const teken_funcs_t scteken_funcs = { 99 .tf_bell = scteken_bell, 100 .tf_cursor = scteken_cursor, 101 .tf_putchar = scteken_putchar, 102 .tf_fill = scteken_fill, 103 .tf_copy = scteken_copy, 104 .tf_param = scteken_param, 105 .tf_respond = scteken_respond, 106 }; 107 108 static int 109 scteken_init(scr_stat *scp, void **softc, int code) 110 { 111 teken_stat *ts; 112 teken_pos_t tp; 113 114 if (*softc == NULL) { 115 if (reserved_teken_stat.ts_busy) 116 return (EINVAL); 117 *softc = &reserved_teken_stat; 118 } 119 ts = *softc; 120 121 switch (code) { 122 case SC_TE_COLD_INIT: 123 ++sc_term_scteken.te_refcount; 124 ts->ts_busy = 1; 125 /* FALLTHROUGH */ 126 case SC_TE_WARM_INIT: 127 teken_init(&ts->ts_teken, &scteken_funcs, scp); 128 129 tp.tp_row = scp->ysize; 130 tp.tp_col = scp->xsize; 131 teken_set_winsize(&ts->ts_teken, &tp); 132 133 tp.tp_row = scp->cursor_pos / scp->xsize; 134 tp.tp_col = scp->cursor_pos % scp->xsize; 135 teken_set_cursor(&ts->ts_teken, &tp); 136 break; 137 } 138 139 return (0); 140 } 141 142 static int 143 scteken_term(scr_stat *scp, void **softc) 144 { 145 146 if (*softc == &reserved_teken_stat) { 147 *softc = NULL; 148 reserved_teken_stat.ts_busy = 0; 149 } 150 --sc_term_scteken.te_refcount; 151 152 return (0); 153 } 154 155 static void 156 scteken_puts(scr_stat *scp, u_char *buf, int len, int kernel) 157 { 158 teken_stat *ts = scp->ts; 159 teken_attr_t backup, kattr; 160 161 scp->sc->write_in_progress++; 162 if (kernel) { 163 /* Use special colors for kernel messages. */ 164 backup = *teken_get_curattr(&ts->ts_teken); 165 scteken_revattr(SC_KERNEL_CONS_ATTR, &kattr); 166 teken_set_curattr(&ts->ts_teken, &kattr); 167 teken_input(&ts->ts_teken, buf, len); 168 teken_set_curattr(&ts->ts_teken, &backup); 169 } else { 170 /* Print user messages with regular colors. */ 171 teken_input(&ts->ts_teken, buf, len); 172 } 173 scp->sc->write_in_progress--; 174 } 175 176 static int 177 scteken_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, 178 struct thread *td) 179 { 180 teken_stat *ts = scp->ts; 181 vid_info_t *vi; 182 unsigned int attr; 183 184 switch (cmd) { 185 case GIO_ATTR: /* get current attributes */ 186 *(int*)data = 187 scteken_attr(teken_get_curattr(&ts->ts_teken)); 188 return (0); 189 case CONS_GETINFO: /* get current (virtual) console info */ 190 vi = (vid_info_t *)data; 191 if (vi->size != sizeof(struct vid_info)) 192 return EINVAL; 193 194 attr = scteken_attr(teken_get_defattr(&ts->ts_teken)); 195 vi->mv_norm.fore = attr & 0x0f; 196 vi->mv_norm.back = (attr >> 4) & 0x0f; 197 vi->mv_rev.fore = vi->mv_norm.back; 198 vi->mv_rev.back = vi->mv_norm.fore; 199 /* 200 * The other fields are filled by the upper routine. XXX 201 */ 202 return (ENOIOCTL); 203 } 204 205 return (ENOIOCTL); 206 } 207 208 static void 209 scteken_default_attr(scr_stat *scp, int color, int rev_color) 210 { 211 teken_stat *ts = scp->ts; 212 teken_attr_t ta; 213 214 scteken_revattr(color, &ta); 215 teken_set_defattr(&ts->ts_teken, &ta); 216 } 217 218 static void 219 scteken_clear(scr_stat *scp) 220 { 221 222 sc_move_cursor(scp, 0, 0); 223 sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); 224 mark_all(scp); 225 } 226 227 static int 228 scteken_input(scr_stat *scp, int c, struct tty *tp) 229 { 230 231 return FALSE; 232 } 233 234 static void 235 scteken_nop(void) 236 { 237 238 } 239 240 /* 241 * libteken routines. 242 */ 243 244 static const unsigned char fgcolors_normal[TC_NCOLORS] = { 245 FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, 246 FG_BLUE, FG_MAGENTA, FG_CYAN, FG_LIGHTGREY, 247 }; 248 249 static const unsigned char fgcolors_bold[TC_NCOLORS] = { 250 FG_DARKGREY, FG_LIGHTRED, FG_LIGHTGREEN, FG_YELLOW, 251 FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN, FG_WHITE, 252 }; 253 254 static const unsigned char bgcolors[TC_NCOLORS] = { 255 BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, 256 BG_BLUE, BG_MAGENTA, BG_CYAN, BG_LIGHTGREY, 257 }; 258 259 static void 260 scteken_revattr(unsigned char color, teken_attr_t *a) 261 { 262 teken_color_t fg, bg; 263 264 /* 265 * XXX: Reverse conversion of syscons to teken attributes. Not 266 * realiable. Maybe we should turn it into a 1:1 mapping one of 267 * these days? 268 */ 269 270 a->ta_format = 0; 271 a->ta_fgcolor = TC_WHITE; 272 a->ta_bgcolor = TC_BLACK; 273 274 #ifdef FG_BLINK 275 if (color & FG_BLINK) { 276 a->ta_format |= TF_BLINK; 277 color &= ~FG_BLINK; 278 } 279 #endif /* FG_BLINK */ 280 281 for (fg = 0; fg < TC_NCOLORS; fg++) { 282 for (bg = 0; bg < TC_NCOLORS; bg++) { 283 if ((fgcolors_normal[fg] | bgcolors[bg]) == color) { 284 a->ta_fgcolor = fg; 285 a->ta_bgcolor = bg; 286 return; 287 } 288 289 if ((fgcolors_bold[fg] | bgcolors[bg]) == color) { 290 a->ta_fgcolor = fg; 291 a->ta_bgcolor = bg; 292 a->ta_format |= TF_BOLD; 293 return; 294 } 295 } 296 } 297 } 298 299 static unsigned int 300 scteken_attr(const teken_attr_t *a) 301 { 302 unsigned int attr = 0; 303 304 if (a->ta_format & TF_BOLD) 305 attr |= fgcolors_bold[a->ta_fgcolor]; 306 else 307 attr |= fgcolors_normal[a->ta_fgcolor]; 308 attr |= bgcolors[a->ta_bgcolor]; 309 310 #ifdef FG_UNDERLINE 311 if (a->ta_format & TF_UNDERLINE) 312 attr |= FG_UNDERLINE; 313 #endif /* FG_UNDERLINE */ 314 #ifdef FG_BLINK 315 if (a->ta_format & TF_BLINK) 316 attr |= FG_BLINK; 317 #endif /* FG_BLINK */ 318 319 return (attr); 320 } 321 322 static void 323 scteken_bell(void *arg) 324 { 325 scr_stat *scp = arg; 326 327 sc_bell(scp, scp->bell_pitch, scp->bell_duration); 328 } 329 330 static void 331 scteken_cursor(void *arg, const teken_pos_t *p) 332 { 333 scr_stat *scp = arg; 334 335 sc_move_cursor(scp, p->tp_col, p->tp_row); 336 } 337 338 static void 339 scteken_putchar(void *arg, const teken_pos_t *tp, teken_char_t c, 340 const teken_attr_t *a) 341 { 342 scr_stat *scp = arg; 343 u_char *map; 344 u_char ch; 345 vm_offset_t p; 346 int cursor, attr; 347 348 #ifdef TEKEN_UTF8 349 if (c >= 0x80) { 350 /* XXX: Don't display UTF-8 yet. */ 351 attr = (FG_YELLOW|BG_RED) << 8; 352 ch = '?'; 353 } else 354 #endif /* TEKEN_UTF8 */ 355 { 356 attr = scteken_attr(a) << 8; 357 ch = c; 358 } 359 360 map = scp->sc->scr_map; 361 362 cursor = tp->tp_row * scp->xsize + tp->tp_col; 363 p = sc_vtb_pointer(&scp->vtb, cursor); 364 sc_vtb_putchar(&scp->vtb, p, map[ch], attr); 365 366 mark_for_update(scp, cursor); 367 /* 368 * XXX: Why do we need this? Only marking `cursor' should be 369 * enough. Without this line, we get artifacts. 370 */ 371 mark_for_update(scp, imin(cursor + 1, scp->xsize * scp->ysize - 1)); 372 } 373 374 static void 375 scteken_fill(void *arg, const teken_rect_t *r, teken_char_t c, 376 const teken_attr_t *a) 377 { 378 scr_stat *scp = arg; 379 u_char *map; 380 u_char ch; 381 unsigned int width; 382 int attr, row; 383 384 #ifdef TEKEN_UTF8 385 if (c >= 0x80) { 386 /* XXX: Don't display UTF-8 yet. */ 387 attr = (FG_YELLOW|BG_RED) << 8; 388 ch = '?'; 389 } else 390 #endif /* TEKEN_UTF8 */ 391 { 392 attr = scteken_attr(a) << 8; 393 ch = c; 394 } 395 396 map = scp->sc->scr_map; 397 398 if (r->tr_begin.tp_col == 0 && r->tr_end.tp_col == scp->xsize) { 399 /* Single contiguous region to fill. */ 400 sc_vtb_erase(&scp->vtb, r->tr_begin.tp_row * scp->xsize, 401 (r->tr_end.tp_row - r->tr_begin.tp_row) * scp->xsize, 402 map[ch], attr); 403 } else { 404 /* Fill display line by line. */ 405 width = r->tr_end.tp_col - r->tr_begin.tp_col; 406 407 for (row = r->tr_begin.tp_row; row < r->tr_end.tp_row; row++) { 408 sc_vtb_erase(&scp->vtb, r->tr_begin.tp_row * 409 scp->xsize + r->tr_begin.tp_col, 410 width, map[ch], attr); 411 } 412 } 413 414 /* Mark begin and end positions to be refreshed. */ 415 mark_for_update(scp, 416 r->tr_begin.tp_row * scp->xsize + r->tr_begin.tp_col); 417 mark_for_update(scp, 418 (r->tr_end.tp_row - 1) * scp->xsize + (r->tr_end.tp_col - 1)); 419 sc_remove_cutmarking(scp); 420 } 421 422 static void 423 scteken_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) 424 { 425 scr_stat *scp = arg; 426 unsigned int width; 427 int src, dst, end; 428 429 #ifndef SC_NO_HISTORY 430 /* 431 * We count a line of input as history if we perform a copy of 432 * one whole line upward. In other words: if a line of text gets 433 * overwritten by a rectangle that's right below it. 434 */ 435 if (scp->history != NULL && 436 r->tr_begin.tp_col == 0 && r->tr_end.tp_col == scp->xsize && 437 r->tr_begin.tp_row == p->tp_row + 1) { 438 sc_hist_save_one_line(scp, p->tp_row); 439 } 440 #endif 441 442 if (r->tr_begin.tp_col == 0 && r->tr_end.tp_col == scp->xsize) { 443 /* Single contiguous region to copy. */ 444 sc_vtb_move(&scp->vtb, r->tr_begin.tp_row * scp->xsize, 445 p->tp_row * scp->xsize, 446 (r->tr_end.tp_row - r->tr_begin.tp_row) * scp->xsize); 447 } else { 448 /* Copy line by line. */ 449 width = r->tr_end.tp_col - r->tr_begin.tp_col; 450 451 if (p->tp_row < r->tr_begin.tp_row) { 452 /* Copy from top to bottom. */ 453 src = r->tr_begin.tp_row * scp->xsize + 454 r->tr_begin.tp_col; 455 end = r->tr_end.tp_row * scp->xsize + 456 r->tr_end.tp_col; 457 dst = p->tp_row * scp->xsize + p->tp_col; 458 459 while (src < end) { 460 sc_vtb_move(&scp->vtb, src, dst, width); 461 462 src += scp->xsize; 463 dst += scp->xsize; 464 } 465 } else { 466 /* Copy from bottom to top. */ 467 src = (r->tr_end.tp_row - 1) * scp->xsize + 468 r->tr_begin.tp_col; 469 end = r->tr_begin.tp_row * scp->xsize + 470 r->tr_begin.tp_col; 471 dst = (p->tp_row + r->tr_end.tp_row - 472 r->tr_begin.tp_row - 1) * scp->xsize + p->tp_col; 473 474 while (src >= end) { 475 sc_vtb_move(&scp->vtb, src, dst, width); 476 477 src -= scp->xsize; 478 dst -= scp->xsize; 479 } 480 } 481 } 482 483 /* Mark begin and end positions to be refreshed. */ 484 mark_for_update(scp, 485 p->tp_row * scp->xsize + p->tp_col); 486 mark_for_update(scp, 487 (p->tp_row + r->tr_end.tp_row - r->tr_begin.tp_row - 1) * 488 scp->xsize + 489 (p->tp_col + r->tr_end.tp_col - r->tr_begin.tp_col - 1)); 490 sc_remove_cutmarking(scp); 491 } 492 493 static void 494 scteken_param(void *arg, int cmd, int value) 495 { 496 scr_stat *scp = arg; 497 498 switch (cmd) { 499 case TP_SHOWCURSOR: 500 if (value) { 501 sc_change_cursor_shape(scp, 502 CONS_RESET_CURSOR|CONS_LOCAL_CURSOR, -1, -1); 503 } else { 504 sc_change_cursor_shape(scp, 505 CONS_HIDDEN_CURSOR|CONS_LOCAL_CURSOR, -1, -1); 506 } 507 break; 508 case TP_SWITCHVT: 509 sc_switch_scr(scp->sc, value); 510 break; 511 } 512 } 513 514 static void 515 scteken_respond(void *arg, const void *buf, size_t len) 516 { 517 scr_stat *scp = arg; 518 519 sc_respond(scp, buf, len); 520 } 521