Index: head/sys/dev/syscons/scvgarndr.c =================================================================== --- head/sys/dev/syscons/scvgarndr.c (revision 322877) +++ head/sys/dev/syscons/scvgarndr.c (revision 322878) @@ -1,1355 +1,1359 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_syscons.h" #include "opt_vga.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SC_RENDER_DEBUG #define SC_RENDER_DEBUG 0 #endif static vr_clear_t vga_txtclear; static vr_draw_border_t vga_txtborder; static vr_draw_t vga_txtdraw; static vr_set_cursor_t vga_txtcursor_shape; static vr_draw_cursor_t vga_txtcursor; static vr_blink_cursor_t vga_txtblink; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_txtmouse; #else #define vga_txtmouse (vr_draw_mouse_t *)vga_nop #endif #ifdef SC_PIXEL_MODE static vr_init_t vga_rndrinit; static vr_clear_t vga_pxlclear_direct; static vr_clear_t vga_pxlclear_planar; static vr_draw_border_t vga_pxlborder_direct; static vr_draw_border_t vga_pxlborder_planar; static vr_draw_t vga_vgadraw_direct; static vr_draw_t vga_vgadraw_planar; static vr_set_cursor_t vga_pxlcursor_shape; static vr_draw_cursor_t vga_pxlcursor_direct; static vr_draw_cursor_t vga_pxlcursor_planar; static vr_blink_cursor_t vga_pxlblink_direct; static vr_blink_cursor_t vga_pxlblink_planar; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_pxlmouse_direct; static vr_draw_mouse_t vga_pxlmouse_planar; #else #define vga_pxlmouse_direct (vr_draw_mouse_t *)vga_nop #define vga_pxlmouse_planar (vr_draw_mouse_t *)vga_nop #endif #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static vr_draw_border_t vga_grborder; #endif static void vga_nop(scr_stat *scp); static sc_rndr_sw_t txtrndrsw = { (vr_init_t *)vga_nop, vga_txtclear, vga_txtborder, vga_txtdraw, vga_txtcursor_shape, vga_txtcursor, vga_txtblink, (vr_set_mouse_t *)vga_nop, vga_txtmouse, }; RENDERER(mda, 0, txtrndrsw, vga_set); RENDERER(cga, 0, txtrndrsw, vga_set); RENDERER(ega, 0, txtrndrsw, vga_set); RENDERER(vga, 0, txtrndrsw, vga_set); #ifdef SC_PIXEL_MODE static sc_rndr_sw_t vgarndrsw = { vga_rndrinit, (vr_clear_t *)vga_nop, (vr_draw_border_t *)vga_nop, (vr_draw_t *)vga_nop, vga_pxlcursor_shape, (vr_draw_cursor_t *)vga_nop, (vr_blink_cursor_t *)vga_nop, (vr_set_mouse_t *)vga_nop, (vr_draw_mouse_t *)vga_nop, }; RENDERER(ega, PIXEL_MODE, vgarndrsw, vga_set); RENDERER(vga, PIXEL_MODE, vgarndrsw, vga_set); #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static sc_rndr_sw_t grrndrsw = { (vr_init_t *)vga_nop, (vr_clear_t *)vga_nop, vga_grborder, (vr_draw_t *)vga_nop, (vr_set_cursor_t *)vga_nop, (vr_draw_cursor_t *)vga_nop, (vr_blink_cursor_t *)vga_nop, (vr_set_mouse_t *)vga_nop, (vr_draw_mouse_t *)vga_nop, }; RENDERER(cga, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(ega, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(vga, GRAPHICS_MODE, grrndrsw, vga_set); #endif /* SC_NO_MODE_CHANGE */ RENDERER_MODULE(vga, vga_set); #ifndef SC_NO_CUTPASTE #if !defined(SC_ALT_MOUSE_IMAGE) || defined(SC_PIXEL_MODE) struct mousedata { u_short md_border[16]; u_short md_interior[16]; u_char md_width; u_char md_height; u_char md_baspect; u_char md_iaspect; const char *md_name; }; static const struct mousedata mouse10x16_50 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8200, 0x8400, 0x8400, 0x8400, 0x9200, 0xB200, 0xA900, 0xC900, 0x8600, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7C00, 0x7800, 0x7800, 0x7800, 0x6C00, 0x4C00, 0x4600, 0x0600, 0x0000, }, 10, 16, 49, 52, "mouse10x16_50", }; static const struct mousedata mouse8x14_67 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8700, 0x8400, 0x9200, 0xB200, 0xA900, 0xC900, 0x0600, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7800, 0x7800, 0x6C00, 0x4C00, 0x4600, 0x0600, 0x0000, 0x0000, 0x0000, }, 8, 14, 64, 65, "mouse8x14_67", }; static const struct mousedata mouse8x13_75 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8600, 0x8400, 0xB200, 0xD200, 0x0900, 0x0900, 0x0600, 0x0000, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7800, 0x7800, 0x4C00, 0x0C00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, }, 8, 13, 75, 80, "mouse8x13_75", }; static const struct mousedata mouse10x16_75 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8700, 0x8400, 0x9200, 0xB200, 0xC900, 0x0900, 0x0480, 0x0480, 0x0300, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7800, 0x7800, 0x6C00, 0x4C00, 0x0600, 0x0600, 0x0300, 0x0300, 0x0000, }, 10, 16, 72, 75, "mouse10x16_75", }; static const struct mousedata mouse9x13_90 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8780, 0x9200, 0xB200, 0xD900, 0x8900, 0x0600, 0x0000, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7800, 0x6C00, 0x4C00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, }, 9, 13, 89, 89, "mouse9x13_90", }; static const struct mousedata mouse10x16_90 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x8040, 0x83E0, 0x8200, 0x9900, 0xA900, 0xC480, 0x8480, 0x0300, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x7F80, 0x7C00, 0x7C00, 0x6600, 0x4600, 0x0300, 0x0300, 0x0000, }, 10, 16, 89, 89, "mouse10x16_90", }; static const struct mousedata mouse9x13_100 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8780, 0xB200, 0xD200, 0x8900, 0x0900, 0x0600, 0x0000, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7800, 0x4C00, 0x0C00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, }, 9, 13, 106, 113, "mouse9x13_100", }; static const struct mousedata mouse10x16_100 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x8040, 0x83C0, 0x9200, 0xA900, 0xC900, 0x0480, 0x0480, 0x0300, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x7F80, 0x7C00, 0x6C00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0000, }, 10, 16, 96, 106, "mouse10x16_100", }; static const struct mousedata mouse10x14_120 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x97C0, 0xB200, 0xF200, 0xC900, 0x8900, 0x0600, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x6800, 0x4C00, 0x0C00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, }, 10, 14, 120, 124, "mouse10x14_120", }; static const struct mousedata mouse10x16_120 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x97C0, 0xB200, 0xF200, 0xC900, 0x8900, 0x0480, 0x0480, 0x0300, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x6800, 0x4C00, 0x0C00, 0x0600, 0x0600, 0x0300, 0x0300, 0x0000, }, 10, 16, 120, 124, "mouse10x16_120", }; static const struct mousedata mouse9x13_133 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x9780, 0xB200, 0xC900, 0x0900, 0x0600, 0x0000, 0x0000, 0x0000, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x6800, 0x4C00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, }, 9, 13, 142, 124, "mouse9x13_133", }; static const struct mousedata mouse10x16_133 = { { 0xC000, 0xA000, 0x9000, 0x8800, 0x8400, 0x8200, 0x8100, 0x8080, 0x8040, 0x93E0, 0xB200, 0xC900, 0x8900, 0x0480, 0x0480, 0x0300, }, { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, 0x7F80, 0x6C00, 0x4C00, 0x0600, 0x0600, 0x0300, 0x0300, 0x0000, }, 10, 16, 120, 133, "mouse10x16_133", }; static const struct mousedata mouse14x10_240 = { { 0xF800, 0xCE00, 0xC380, 0xC0E0, 0xC038, 0xC1FC, 0xDCC0, 0xF660, 0xC330, 0x01E0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }, { 0x0000, 0x3000, 0x3C00, 0x3F00, 0x3FC0, 0x3E00, 0x2300, 0x0180, 0x00C0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }, 14, 10, 189, 189, "mouse14x10_240", }; static const struct mousedata * const mouselarge[] = { &mouse10x16_50, &mouse8x14_67, &mouse10x16_75, &mouse10x16_90, &mouse10x16_100, &mouse10x16_120, &mouse10x16_133, &mouse14x10_240, }; static const struct mousedata * const mousesmall[] = { &mouse8x14_67, &mouse8x13_75, &mouse9x13_90, &mouse9x13_100, &mouse10x14_120, &mouse9x13_133, &mouse14x10_240, }; #endif #endif #ifdef SC_PIXEL_MODE #define GET_PIXEL(scp, pos, x, w) \ ({ \ (scp)->sc->adp->va_window + \ (x) * (scp)->xoff + \ (scp)->yoff * (scp)->font_size * (w) + \ (x) * ((pos) % (scp)->xsize) + \ (scp)->font_size * (w) * ((pos) / (scp)->xsize); \ }) #define DRAW_PIXEL(scp, pos, color) do { \ switch ((scp)->sc->adp->va_info.vi_depth) { \ case 32: \ writel((pos), vga_palette32[color]); \ break; \ case 24: \ if (((pos) & 1) == 0) { \ writew((pos), vga_palette32[color]); \ writeb((pos) + 2, vga_palette32[color] >> 16); \ } else { \ writeb((pos), vga_palette32[color]); \ writew((pos) + 1, vga_palette32[color] >> 8); \ } \ break; \ case 16: \ if ((scp)->sc->adp->va_info.vi_pixel_fsizes[1] == 5) \ writew((pos), vga_palette15[color]); \ else \ writew((pos), vga_palette16[color]); \ break; \ case 15: \ writew((pos), vga_palette15[color]); \ break; \ case 8: \ writeb((pos), (uint8_t)(color)); \ } \ } while (0) static uint32_t vga_palette32[16] = { 0x000000, 0x0000ad, 0x00ad00, 0x00adad, 0xad0000, 0xad00ad, 0xad5200, 0xadadad, 0x525252, 0x5252ff, 0x52ff52, 0x52ffff, 0xff5252, 0xff52ff, 0xffff52, 0xffffff }; static uint16_t vga_palette16[16] = { 0x0000, 0x0016, 0x0560, 0x0576, 0xb000, 0xb016, 0xb2a0, 0xb576, 0x52aa, 0x52bf, 0x57ea, 0x57ff, 0xfaaa, 0xfabf, 0xffea, 0xffff }; static uint16_t vga_palette15[16] = { 0x0000, 0x0016, 0x02c0, 0x02d6, 0x5800, 0x5816, 0x5940, 0x5ad6, 0x294a, 0x295f, 0x2bea, 0x2bff, 0x7d4a, 0x7d5f, 0x7fea, 0x7fff }; #endif static int vga_aspect_scale= 100; SYSCTL_INT(_machdep, OID_AUTO, vga_aspect_scale, CTLFLAG_RW, &vga_aspect_scale, 0, "Aspect scale ratio (3:4):actual times 100"); static u_short vga_flipattr(u_short a, int blink) { if (blink) a = (a & 0x8800) | ((a & 0x7000) >> 4) | ((a & 0x0700) << 4); else a = ((a & 0xf000) >> 4) | ((a & 0x0f00) << 4); return (a); } static u_short -vga_cursorattr_adj(u_short a, int blink) +vga_cursorattr_adj(scr_stat *scp, u_short a, int blink) { - /* - * !blink means pixel mode, and the cursor attribute in that case - * is simplistic reverse video. - */ - if (!blink) - return (vga_flipattr(a, blink)); + int i; + u_short bg, bgmask, fg, newbg; /* * The cursor attribute is usually that of the underlying char - * with the bg changed to white. If the bg is already white, - * then the bg is changed to black. The fg is usually not - * changed, but if it is the same as the new bg then it is - * changed to the inverse of the new bg. + * with only the bg changed, to the first preferred color that + * differs from both the fg and bg. If there is no such color, + * use reverse video. */ - if ((a & 0x7000) == 0x7000) { - a &= 0x8f00; - if ((a & 0x0700) == 0) - a |= 0x0700; - } else { - a |= 0x7000; - if ((a & 0x0700) == 0x0700) - a &= 0xf000; + bgmask = blink ? 0x7000 : 0xf000; + bg = a & bgmask; + fg = a & 0x0f00; + for (i = 0; i < nitems(scp->curs_attr.bg); i++) { + newbg = (scp->curs_attr.bg[i] << 12) & bgmask; + if (newbg != bg && newbg != (fg << 4)) + break; } - return (a); + if (i == nitems(scp->curs_attr.bg)) + return (vga_flipattr(a, blink)); + return (fg | newbg | (blink ? a & 0x8000 : 0)); } static void vga_setmdp(scr_stat *scp) { #if !defined(SC_NO_CUTPASTE) && \ (!defined(SC_ALT_MOUSE_IMAGE) || defined(SC_PIXEL_MODE)) const struct mousedata *mdp; const struct mousedata * const *mdpp; int aspect, best_i, best_v, i, n, v, wb, wi, xpixel, ypixel; xpixel = scp->xpixel; ypixel = scp->ypixel; if (scp->sc->adp->va_flags & V_ADP_CWIDTH9) xpixel = xpixel * 9 / 8; /* If 16:9 +-1%, assume square pixels, else scale to 4:3 or full. */ aspect = xpixel * 900 / ypixel / 16; if (aspect < 99 || aspect > 100) aspect = xpixel * 300 / ypixel / 4 * vga_aspect_scale / 100; /* * Use 10x16 cursors except even with 8x8 fonts except in ~200- * line modes where pixels are very large and in text mode where * even 13 pixels high is really 4 too many. Clipping a 16-high * cursor at 9-high gives a variable tail which looks better than * a smaller cursor with a constant tail. * * XXX: the IS*SC() macros don't work when this is called at the * end of a mode switch since UNKNOWN_SC is still set. */ if (scp->font_size <= 8 && (ypixel < 300 || !(scp->status & PIXEL_MODE))) { mdpp = &mousesmall[0]; n = nitems(mousesmall); } else { mdpp = &mouselarge[0]; n = nitems(mouselarge); } if (scp->status & PIXEL_MODE) { wb = 1024; wi = 256; } else { wb = 256; wi = 1024; } best_i = 0; best_v = 0x7fffffff; for (i = 0; i < n; i++) { v = (wb * abs(mdpp[i]->md_baspect - aspect) + wi * abs(mdpp[i]->md_iaspect - aspect)) / aspect; if (best_v > v) { best_v = v; best_i = i; } } mdp = mdpp[best_i]; scp->mouse_data = mdp; #endif /* !SC_NO_CUTPASTE && (!SC_ALT_MOUSE_IMAGE || SC_PIXEL_MODE) */ } static void vga_nop(scr_stat *scp) { } /* text mode renderer */ static void vga_txtclear(scr_stat *scp, int c, int attr) { sc_vtb_clear(&scp->scr, c, attr); } static void vga_txtborder(scr_stat *scp, int color) { vidd_set_border(scp->sc->adp, color); } static void vga_txtdraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t p; int c; int a; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; if (flip) { for (p = sc_vtb_pointer(&scp->scr, from); count-- > 0; ++from) { c = sc_vtb_getc(&scp->vtb, from); a = sc_vtb_geta(&scp->vtb, from); a = vga_flipattr(a, TRUE); p = sc_vtb_putchar(&scp->scr, p, c, a); } } else { sc_vtb_copy(&scp->vtb, from, &scp->scr, from, count); } } static void vga_txtcursor_shape(scr_stat *scp, int base, int height, int blink) { vga_setmdp(scp); if (base < 0 || base >= scp->font_size) return; /* the caller may set height <= 0 in order to disable the cursor */ vidd_set_hw_cursor_shape(scp->sc->adp, base, height, scp->font_size, blink); } static void draw_txtcharcursor(scr_stat *scp, int at, u_short c, u_short a, int flip) { sc_softc_t *sc; sc = scp->sc; #ifndef SC_NO_FONT_LOADING if (scp->curs_attr.flags & CONS_CHAR_CURSOR) { unsigned char *font; int h; int i; if (scp->font_size < 14) { font = sc->font_8; h = 8; } else if (scp->font_size >= 16) { font = sc->font_16; h = 16; } else { font = sc->font_14; h = 14; } if (scp->curs_attr.base >= h) return; if (flip) a = vga_flipattr(a, TRUE); + /* + * This clause handles partial-block cursors in text mode. + * We want to change the attribute only under the partial + * block, but in text mode we can only change full blocks. + * Use reverse video instead. + */ bcopy(font + c*h, font + sc->cursor_char*h, h); font = font + sc->cursor_char*h; for (i = imax(h - scp->curs_attr.base - scp->curs_attr.height, 0); i < h - scp->curs_attr.base; ++i) { font[i] ^= 0xff; } /* XXX */ vidd_load_font(sc->adp, 0, h, 8, font, sc->cursor_char, 1); sc_vtb_putc(&scp->scr, at, sc->cursor_char, a); } else #endif /* SC_NO_FONT_LOADING */ { if (flip) a = vga_flipattr(a, TRUE); - a = vga_cursorattr_adj(a, TRUE); + a = vga_cursorattr_adj(scp, a, TRUE); sc_vtb_putc(&scp->scr, at, c, a); } } static void vga_txtcursor(scr_stat *scp, int at, int blink, int on, int flip) { video_adapter_t *adp; int cursor_attr; if (scp->curs_attr.height <= 0) /* the text cursor is disabled */ return; adp = scp->sc->adp; if (blink) { scp->status |= VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize); } else { if (scp->status & VR_CURSOR_ON) vidd_set_hw_cursor(adp, -1, -1); scp->status &= ~VR_CURSOR_ON; } } else { scp->status &= ~VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; draw_txtcharcursor(scp, at, sc_vtb_getc(&scp->vtb, at), sc_vtb_geta(&scp->vtb, at), flip); } else { cursor_attr = sc_vtb_geta(&scp->vtb, at); if (flip) cursor_attr = vga_flipattr(cursor_attr, TRUE); if (scp->status & VR_CURSOR_ON) sc_vtb_putc(&scp->scr, at, sc_vtb_getc(&scp->vtb, at), cursor_attr); scp->status &= ~VR_CURSOR_ON; } } } static void vga_txtblink(scr_stat *scp, int at, int flip) { } int sc_txtmouse_no_retrace_wait; #ifndef SC_NO_CUTPASTE static void draw_txtmouse(scr_stat *scp, int x, int y) { #ifndef SC_ALT_MOUSE_IMAGE if (ISMOUSEAVAIL(scp->sc->adp->va_flags)) { const struct mousedata *mdp; uint32_t border, interior; u_char font_buf[128]; u_short cursor[32]; u_char c; int pos; int xoffset, yoffset; int crtc_addr; int i; mdp = scp->mouse_data; /* prepare mousepointer char's bitmaps */ pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; bcopy(scp->font + sc_vtb_getc(&scp->scr, pos)*scp->font_size, &font_buf[0], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->scr, pos + 1)*scp->font_size, &font_buf[32], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->scr, pos + scp->xsize)*scp->font_size, &font_buf[64], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->scr, pos + scp->xsize + 1)*scp->font_size, &font_buf[96], scp->font_size); for (i = 0; i < scp->font_size; ++i) { cursor[i] = font_buf[i]<<8 | font_buf[i+32]; cursor[i + scp->font_size] = font_buf[i+64]<<8 | font_buf[i+96]; } /* now and-or in the mousepointer image */ xoffset = x%8; yoffset = y%scp->font_size; for (i = 0; i < 16; ++i) { border = mdp->md_border[i] << 8; /* avoid right shifting out */ interior = mdp->md_interior[i] << 8; border >>= xoffset; /* normalize */ interior >>= xoffset; if (scp->sc->adp->va_flags & V_ADP_CWIDTH9) { /* skip gaps between characters */ border = (border & 0xff0000) | (border & 0x007f80) << 1 | (border & 0x00003f) << 2; interior = (interior & 0xff0000) | (interior & 0x007f80) << 1 | (interior & 0x00003f) << 2; } border >>= 8; /* back to normal position */ interior >>= 8; cursor[i + yoffset] = (cursor[i + yoffset] & ~border) | interior; } for (i = 0; i < scp->font_size; ++i) { font_buf[i] = (cursor[i] & 0xff00) >> 8; font_buf[i + 32] = cursor[i] & 0xff; font_buf[i + 64] = (cursor[i + scp->font_size] & 0xff00) >> 8; font_buf[i + 96] = cursor[i + scp->font_size] & 0xff; } #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ crtc_addr = scp->sc->adp->va_crtc_addr; while (!sc_txtmouse_no_retrace_wait && !(inb(crtc_addr + 6) & 0x08)) /* idle */ ; #endif c = scp->sc->mouse_char; vidd_load_font(scp->sc->adp, 0, 32, 8, font_buf, c, 4); sc_vtb_putc(&scp->scr, pos, c, sc_vtb_geta(&scp->scr, pos)); /* FIXME: may be out of range! */ sc_vtb_putc(&scp->scr, pos + scp->xsize, c + 2, sc_vtb_geta(&scp->scr, pos + scp->xsize)); if (x < (scp->xsize - 1)*8) { sc_vtb_putc(&scp->scr, pos + 1, c + 1, sc_vtb_geta(&scp->scr, pos + 1)); sc_vtb_putc(&scp->scr, pos + scp->xsize + 1, c + 3, sc_vtb_geta(&scp->scr, pos + scp->xsize + 1)); } } else #endif /* SC_ALT_MOUSE_IMAGE */ { /* Red, magenta and brown are mapped to green to to keep it readable */ static const int col_conv[16] = { 6, 6, 6, 6, 2, 2, 2, 6, 14, 14, 14, 14, 10, 10, 10, 14 }; int pos; int color; int a; pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; a = sc_vtb_geta(&scp->scr, pos); if (scp->sc->adp->va_flags & V_ADP_COLOR) color = (col_conv[(a & 0xf000) >> 12] << 12) | ((a & 0x0f00) | 0x0800); else color = ((a & 0xf000) >> 4) | ((a & 0x0f00) << 4); sc_vtb_putc(&scp->scr, pos, sc_vtb_getc(&scp->scr, pos), color); } } static void remove_txtmouse(scr_stat *scp, int x, int y) { } static void vga_txtmouse(scr_stat *scp, int x, int y, int on) { if (on) draw_txtmouse(scp, x, y); else remove_txtmouse(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #ifdef SC_PIXEL_MODE /* pixel (raster text) mode renderer */ static void vga_rndrinit(scr_stat *scp) { if (scp->sc->adp->va_info.vi_mem_model == V_INFO_MM_PLANAR) { scp->rndr->clear = vga_pxlclear_planar; scp->rndr->draw_border = vga_pxlborder_planar; scp->rndr->draw = vga_vgadraw_planar; scp->rndr->draw_cursor = vga_pxlcursor_planar; scp->rndr->blink_cursor = vga_pxlblink_planar; scp->rndr->draw_mouse = vga_pxlmouse_planar; } else if (scp->sc->adp->va_info.vi_mem_model == V_INFO_MM_DIRECT || scp->sc->adp->va_info.vi_mem_model == V_INFO_MM_PACKED) { scp->rndr->clear = vga_pxlclear_direct; scp->rndr->draw_border = vga_pxlborder_direct; scp->rndr->draw = vga_vgadraw_direct; scp->rndr->draw_cursor = vga_pxlcursor_direct; scp->rndr->blink_cursor = vga_pxlblink_direct; scp->rndr->draw_mouse = vga_pxlmouse_direct; } } static void vga_pxlclear_direct(scr_stat *scp, int c, int attr) { vm_offset_t p; int line_width; int pixel_size; int lines; int i; line_width = scp->sc->adp->va_line_width; pixel_size = scp->sc->adp->va_info.vi_pixel_size; lines = scp->ysize * scp->font_size; p = scp->sc->adp->va_window + line_width * scp->yoff * scp->font_size + scp->xoff * 8 * pixel_size; for (i = 0; i < lines; ++i) { bzero_io((void *)p, scp->xsize * 8 * pixel_size); p += line_width; } } static void vga_pxlclear_planar(scr_stat *scp, int c, int attr) { vm_offset_t p; int line_width; int lines; int i; /* XXX: we are just filling the screen with the background color... */ outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, ((attr & 0xf000) >> 4) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; lines = scp->ysize*scp->font_size; p = scp->sc->adp->va_window + line_width*scp->yoff*scp->font_size + scp->xoff; for (i = 0; i < lines; ++i) { bzero_io((void *)p, scp->xsize); p += line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlborder_direct(scr_stat *scp, int color) { vm_offset_t s; vm_offset_t e; vm_offset_t f; int line_width; int pixel_size; int x; int y; int i; line_width = scp->sc->adp->va_line_width; pixel_size = scp->sc->adp->va_info.vi_pixel_size; if (scp->yoff > 0) { s = scp->sc->adp->va_window; e = s + line_width * scp->yoff * scp->font_size; for (f = s; f < e; f += pixel_size) DRAW_PIXEL(scp, f, color); } y = (scp->yoff + scp->ysize) * scp->font_size; if (scp->ypixel > y) { s = scp->sc->adp->va_window + line_width * y; e = s + line_width * (scp->ypixel - y); for (f = s; f < e; f += pixel_size) DRAW_PIXEL(scp, f, color); } y = scp->yoff * scp->font_size; x = scp->xpixel / 8 - scp->xoff - scp->xsize; for (i = 0; i < scp->ysize * scp->font_size; ++i) { if (scp->xoff > 0) { s = scp->sc->adp->va_window + line_width * (y + i); e = s + scp->xoff * 8 * pixel_size; for (f = s; f < e; f += pixel_size) DRAW_PIXEL(scp, f, color); } if (x > 0) { s = scp->sc->adp->va_window + line_width * (y + i) + scp->xoff * 8 * pixel_size + scp->xsize * 8 * pixel_size; e = s + x * 8 * pixel_size; for (f = s; f < e; f += pixel_size) DRAW_PIXEL(scp, f, color); } } } static void vga_pxlborder_planar(scr_stat *scp, int color) { vm_offset_t p; int line_width; int x; int y; int i; vidd_set_border(scp->sc->adp, color); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (color << 8) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; p = scp->sc->adp->va_window; if (scp->yoff > 0) bzero_io((void *)p, line_width*scp->yoff*scp->font_size); y = (scp->yoff + scp->ysize)*scp->font_size; if (scp->ypixel > y) bzero_io((void *)(p + line_width*y), line_width*(scp->ypixel - y)); y = scp->yoff*scp->font_size; x = scp->xpixel/8 - scp->xoff - scp->xsize; for (i = 0; i < scp->ysize*scp->font_size; ++i) { if (scp->xoff > 0) bzero_io((void *)(p + line_width*(y + i)), scp->xoff); if (x > 0) bzero_io((void *)(p + line_width*(y + i) + scp->xoff + scp->xsize), x); } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_vgadraw_direct(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short col1, col2, color; int line_width, pixel_size; int i, j, k; int a; line_width = scp->sc->adp->va_line_width; pixel_size = scp->sc->adp->va_info.vi_pixel_size; d = GET_PIXEL(scp, from, 8 * pixel_size, line_width); if (from + count > scp->xsize * scp->ysize) count = scp->xsize * scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) a = vga_flipattr(a, FALSE); col1 = (a & 0x0f00) >> 8; col2 = (a & 0xf000) >> 12; e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i) * scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { for (k = 0; k < 8; ++k) { color = *f & (1 << (7 - k)) ? col1 : col2; DRAW_PIXEL(scp, e + pixel_size * k, color); } e += line_width; } d += 8 * pixel_size; if ((i % scp->xsize) == scp->xsize - 1) d += scp->font_size * line_width - scp->xsize * 8 * pixel_size; } } static void vga_vgadraw_planar(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short bg, fg; u_short col1, col2; int line_width; int i, j; int a; u_char c; line_width = scp->sc->adp->va_line_width; d = GET_PIXEL(scp, from, 1, line_width); if (scp->sc->adp->va_type == KD_VGA) { outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ outw(GDCIDX, 0xff08); /* bit mask */ } else outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ fg = bg = -1; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) a = vga_flipattr(a, FALSE); col1 = a & 0x0f00; col2 = (a & 0xf000) >> 4; /* set background color in EGA/VGA latch */ if (bg != col2) { bg = col2; fg = -1; outw(GDCIDX, bg | 0x00); /* set/reset */ if (scp->sc->adp->va_type != KD_VGA) outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0xff); c = readb(d); /* set bg color in the latch */ } /* foreground color */ if (fg != col1) { fg = col1; outw(GDCIDX, col1 | 0x00); /* set/reset */ } e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i)*scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { if (scp->sc->adp->va_type == KD_VGA) writeb(e, *f); else { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(e, 0); } e += line_width; } ++d; if ((i % scp->xsize) == scp->xsize - 1) d += scp->font_size * line_width - scp->xsize; } if (scp->sc->adp->va_type == KD_VGA) outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ else outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlcursor_shape(scr_stat *scp, int base, int height, int blink) { vga_setmdp(scp); } static void draw_pxlcursor_direct(scr_stat *scp, int at, int on, int flip) { vm_offset_t d; u_char *f; int line_width, pixel_size; int height; int col1, col2, color; int a; int i, j; line_width = scp->sc->adp->va_line_width; pixel_size = scp->sc->adp->va_info.vi_pixel_size; d = GET_PIXEL(scp, at, 8 * pixel_size, line_width) + (scp->font_size - scp->curs_attr.base - 1) * line_width; a = sc_vtb_geta(&scp->vtb, at); if (flip) a = vga_flipattr(a, FALSE); if (on) - a = vga_cursorattr_adj(a, FALSE); + a = vga_cursorattr_adj(scp, a, FALSE); col1 = (a & 0x0f00) >> 8; col2 = a >> 12; f = &(scp->font[sc_vtb_getc(&scp->vtb, at) * scp->font_size + scp->font_size - scp->curs_attr.base - 1]); height = imin(scp->curs_attr.height, scp->font_size); for (i = 0; i < height; ++i, --f) { for (j = 0; j < 8; ++j) { color = *f & (1 << (7 - j)) ? col1 : col2; DRAW_PIXEL(scp, d + pixel_size * j, color); } d -= line_width; } } static void draw_pxlcursor_planar(scr_stat *scp, int at, int on, int flip) { vm_offset_t d; u_char *f; int line_width; int height; int col; int a; int i; u_char c; line_width = scp->sc->adp->va_line_width; d = GET_PIXEL(scp, at, 1, line_width) + (scp->font_size - scp->curs_attr.base - 1) * line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ /* set background color in EGA/VGA latch */ a = sc_vtb_geta(&scp->vtb, at); if (flip) a = vga_flipattr(a, FALSE); if (on) - a = vga_cursorattr_adj(a, FALSE); + a = vga_cursorattr_adj(scp, a, FALSE); col = (a & 0xf000) >> 4; outw(GDCIDX, col | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ /* foreground color */ col = a & 0x0f00; outw(GDCIDX, col | 0x00); /* set/reset */ f = &(scp->font[sc_vtb_getc(&scp->vtb, at)*scp->font_size + scp->font_size - scp->curs_attr.base - 1]); height = imin(scp->curs_attr.height, scp->font_size); for (i = 0; i < height; ++i, --f) { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(d, 0); d -= line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ } static int pxlblinkrate = 0; static void vga_pxlcursor_direct(scr_stat *scp, int at, int blink, int on, int flip) { if (scp->curs_attr.height <= 0) /* the text cursor is disabled */ return; if (on) { if (!blink) { scp->status |= VR_CURSOR_ON; draw_pxlcursor_direct(scp, at, on, flip); } else if (++pxlblinkrate & 4) { pxlblinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor_direct(scp, at, scp->status & VR_CURSOR_ON, flip); } } else { if (scp->status & VR_CURSOR_ON) draw_pxlcursor_direct(scp, at, on, flip); scp->status &= ~VR_CURSOR_ON; } if (blink) scp->status |= VR_CURSOR_BLINK; else scp->status &= ~VR_CURSOR_BLINK; } static void vga_pxlcursor_planar(scr_stat *scp, int at, int blink, int on, int flip) { if (scp->curs_attr.height <= 0) /* the text cursor is disabled */ return; if (on) { if (!blink) { scp->status |= VR_CURSOR_ON; draw_pxlcursor_planar(scp, at, on, flip); } else if (++pxlblinkrate & 4) { pxlblinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor_planar(scp, at, scp->status & VR_CURSOR_ON, flip); } } else { if (scp->status & VR_CURSOR_ON) draw_pxlcursor_planar(scp, at, on, flip); scp->status &= ~VR_CURSOR_ON; } if (blink) scp->status |= VR_CURSOR_BLINK; else scp->status &= ~VR_CURSOR_BLINK; } static void vga_pxlblink_direct(scr_stat *scp, int at, int flip) { if (!(scp->status & VR_CURSOR_BLINK)) return; if (!(++pxlblinkrate & 4)) return; pxlblinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor_direct(scp, at, scp->status & VR_CURSOR_ON, flip); } static void vga_pxlblink_planar(scr_stat *scp, int at, int flip) { if (!(scp->status & VR_CURSOR_BLINK)) return; if (!(++pxlblinkrate & 4)) return; pxlblinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor_planar(scp, at, scp->status & VR_CURSOR_ON, flip); } #ifndef SC_NO_CUTPASTE static void draw_pxlmouse_planar(scr_stat *scp, int x, int y) { const struct mousedata *mdp; vm_offset_t p; int line_width; int xoff, yoff; int ymax; uint32_t m; int i, j, k; uint8_t m1; mdp = scp->mouse_data; line_width = scp->sc->adp->va_line_width; xoff = (x - scp->xoff*8)%8; yoff = y - rounddown(y, line_width); ymax = imin(y + mdp->md_height, scp->ypixel); if (scp->sc->adp->va_type == KD_VGA) { outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ outw(GDCIDX, 0xff08); /* bit mask */ } else outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ - outw(GDCIDX, (0 << 8) | 0x00); /* set/reset */ + outw(GDCIDX, (scp->curs_attr.mouse_ba << 8) | 0x00); /* set/reset */ p = scp->sc->adp->va_window + line_width*y + x/8; for (i = y, j = 0; i < ymax; ++i, ++j) { m = mdp->md_border[j] << 8 >> xoff; for (k = 0; k < 3; ++k) { m1 = m >> (8 * (2 - k)); if (m1 != 0 && x + 8 * k < scp->xpixel) { readb(p + k); if (scp->sc->adp->va_type == KD_VGA) writeb(p + k, m1); else { /* bit mask: */ outw(GDCIDX, (m1 << 8) | 0x08); writeb(p + k, 0); } } } p += line_width; } - outw(GDCIDX, (15 << 8) | 0x00); /* set/reset */ + outw(GDCIDX, (scp->curs_attr.mouse_ia << 8) | 0x00); /* set/reset */ p = scp->sc->adp->va_window + line_width*y + x/8; for (i = y, j = 0; i < ymax; ++i, ++j) { m = mdp->md_interior[j] << 8 >> xoff; for (k = 0; k < 3; ++k) { m1 = m >> (8 * (2 - k)); if (m1 != 0 && x + 8 * k < scp->xpixel) { readb(p + k); if (scp->sc->adp->va_type == KD_VGA) writeb(p + k, m1); else { /* bit mask: */ outw(GDCIDX, (m1 << 8) | 0x08); writeb(p + k, 0); } } } p += line_width; } if (scp->sc->adp->va_type == KD_VGA) outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ else outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void remove_pxlmouse_planar(scr_stat *scp, int x, int y) { const struct mousedata *mdp; vm_offset_t p; int bx, by, i, line_width, xend, xoff, yend, yoff; mdp = scp->mouse_data; /* * It is only necessary to remove the mouse image where it overlaps * the border. Determine the overlap, and do nothing if it is empty. */ bx = (scp->xoff + scp->xsize) * 8; by = (scp->yoff + scp->ysize) * scp->font_size; xend = imin(x + mdp->md_width, scp->xpixel); yend = imin(y + mdp->md_height, scp->ypixel); if (xend <= bx && yend <= by) return; /* Repaint the non-empty overlap. */ line_width = scp->sc->adp->va_line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (scp->border << 8) | 0x00); /* set/reset */ for (i = x / 8, xoff = i * 8; xoff < xend; ++i, xoff += 8) { yoff = (xoff >= bx) ? y : by; p = scp->sc->adp->va_window + yoff * line_width + i; for (; yoff < yend; ++yoff, p += line_width) writeb(p, 0); } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlmouse_direct(scr_stat *scp, int x, int y, int on) { const struct mousedata *mdp; vm_offset_t p; int line_width, pixel_size; int xend, yend; int i, j; mdp = scp->mouse_data; /* * Determine overlap with the border and then if removing, do nothing * if the overlap is empty. */ xend = imin(x + mdp->md_width, scp->xpixel); yend = imin(y + mdp->md_height, scp->ypixel); if (!on && xend <= (scp->xoff + scp->xsize) * 8 && yend <= (scp->yoff + scp->ysize) * scp->font_size) return; line_width = scp->sc->adp->va_line_width; pixel_size = scp->sc->adp->va_info.vi_pixel_size; if (on) goto do_on; /* Repaint overlap with the border (mess up the corner a little). */ p = scp->sc->adp->va_window + y * line_width + x * pixel_size; for (i = 0; i < yend - y; i++, p += line_width) for (j = xend - x - 1; j >= 0; j--) DRAW_PIXEL(scp, p + j * pixel_size, scp->border); return; do_on: p = scp->sc->adp->va_window + y * line_width + x * pixel_size; for (i = 0; i < yend - y; i++, p += line_width) for (j = xend - x - 1; j >= 0; j--) if (mdp->md_interior[i] & (1 << (15 - j))) - DRAW_PIXEL(scp, p + j * pixel_size, 15); + DRAW_PIXEL(scp, p + j * pixel_size, + scp->curs_attr.mouse_ia); else if (mdp->md_border[i] & (1 << (15 - j))) - DRAW_PIXEL(scp, p + j * pixel_size, 0); + DRAW_PIXEL(scp, p + j * pixel_size, + scp->curs_attr.mouse_ba); } static void vga_pxlmouse_planar(scr_stat *scp, int x, int y, int on) { if (on) draw_pxlmouse_planar(scp, x, y); else remove_pxlmouse_planar(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE /* graphics mode renderer */ static void vga_grborder(scr_stat *scp, int color) { vidd_set_border(scp->sc->adp, color); } #endif Index: head/sys/dev/syscons/syscons.c =================================================================== --- head/sys/dev/syscons/syscons.c (revision 322877) +++ head/sys/dev/syscons/syscons.c (revision 322878) @@ -1,4224 +1,4242 @@ /*- * Copyright (c) 1992-1998 Søren Schmidt * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_syscons.h" #include "opt_splash.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__arm__) || defined(__mips__) || \ defined(__powerpc__) || defined(__sparc64__) #include #else #include #endif #if defined( __i386__) || defined(__amd64__) #include #include #endif #include #include #include #include #include #define COLD 0 #define WARM 1 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ #define KEYCODE_BS 0x0e /* "<-- Backspace" key, XXX */ /* NULL-safe version of "tty_opened()" */ #define tty_opened_ns(tp) ((tp) != NULL && tty_opened(tp)) static u_char sc_kattrtab[MAXCPU]; static int sc_console_unit = -1; static int sc_saver_keyb_only = 1; static scr_stat *sc_console; static struct consdev *sc_consptr; static void *sc_kts[MAXCPU]; static struct sc_term_sw *sc_ktsw; static scr_stat main_console; static struct tty *main_devs[MAXCONS]; static char init_done = COLD; static int shutdown_in_progress = FALSE; static int suspend_in_progress = FALSE; static char sc_malloc = FALSE; static int saver_mode = CONS_NO_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static int enable_bell = TRUE; /* enable beeper */ #ifndef SC_DISABLE_REBOOT static int enable_reboot = TRUE; /* enable keyboard reboot */ #endif #ifndef SC_DISABLE_KDBKEY static int enable_kdbkey = TRUE; /* enable keyboard debug */ #endif static long scrn_blank_time = 0; /* screen saver timeout value */ #ifdef DEV_SPLASH static int scrn_blanked; /* # of blanked screen */ static int sticky_splash = FALSE; static void none_saver(sc_softc_t *sc, int blank) { } static void (*current_saver)(sc_softc_t *, int) = none_saver; #endif #ifdef SC_NO_SUSPEND_VTYSWITCH static int sc_no_suspend_vtswitch = 1; #else static int sc_no_suspend_vtswitch = 0; #endif static int sc_susp_scr; static SYSCTL_NODE(_hw, OID_AUTO, syscons, CTLFLAG_RD, 0, "syscons"); SYSCTL_OPAQUE(_hw_syscons, OID_AUTO, kattr, CTLFLAG_RW, &sc_kattrtab, sizeof(sc_kattrtab), "CU", "kernel console attributes"); static SYSCTL_NODE(_hw_syscons, OID_AUTO, saver, CTLFLAG_RD, 0, "saver"); SYSCTL_INT(_hw_syscons_saver, OID_AUTO, keybonly, CTLFLAG_RW, &sc_saver_keyb_only, 0, "screen saver interrupted by input only"); SYSCTL_INT(_hw_syscons, OID_AUTO, bell, CTLFLAG_RW, &enable_bell, 0, "enable bell"); #ifndef SC_DISABLE_REBOOT SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_reboot, CTLFLAG_RW|CTLFLAG_SECURE, &enable_reboot, 0, "enable keyboard reboot"); #endif #ifndef SC_DISABLE_KDBKEY SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_debug, CTLFLAG_RW|CTLFLAG_SECURE, &enable_kdbkey, 0, "enable keyboard debug"); #endif SYSCTL_INT(_hw_syscons, OID_AUTO, sc_no_suspend_vtswitch, CTLFLAG_RWTUN, &sc_no_suspend_vtswitch, 0, "Disable VT switch before suspend."); #if !defined(SC_NO_FONT_LOADING) && defined(SC_DFLT_FONT) #include "font.h" #endif tsw_ioctl_t *sc_user_ioctl; static bios_values_t bios_value; static int enable_panic_key; SYSCTL_INT(_machdep, OID_AUTO, enable_panic_key, CTLFLAG_RW, &enable_panic_key, 0, "Enable panic via keypress specified in kbdmap(5)"); #define SC_CONSOLECTL 255 #define VTY_WCHAN(sc, vty) (&SC_DEV(sc, vty)) /* prototypes */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit); static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scmeminit(void *arg); static int scdevtounit(struct tty *tp); static kbd_callback_func_t sckbdevent; static void scinit(int unit, int flags); static scr_stat *sc_get_stat(struct tty *tp); static void scterm(int unit, int flags); static void scshutdown(void *, int); static void scsuspend(void *); static void scresume(void *); static u_int scgetc(sc_softc_t *sc, u_int flags, struct sc_cnstate *sp); static void sc_puts(scr_stat *scp, u_char *buf, int len); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static void sccnupdate(scr_stat *scp); static scr_stat *alloc_scp(sc_softc_t *sc, int vty); static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp); static timeout_t scrn_timer; static int and_region(int *s1, int *e1, int s2, int e2); static void scrn_update(scr_stat *scp, int show_cursor); #ifdef DEV_SPLASH static int scsplash_callback(int event, void *arg); static void scsplash_saver(sc_softc_t *sc, int show); static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border); static int restore_scrn_saver_mode(scr_stat *scp, int changemode); static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)); static int wait_scrn_saver_stop(sc_softc_t *sc); #define scsplash_stick(stick) (sticky_splash = (stick)) #else /* !DEV_SPLASH */ #define scsplash_stick(stick) #endif /* DEV_SPLASH */ static int do_switch_scr(sc_softc_t *sc, int s); static int vt_proc_alive(scr_stat *scp); static int signal_vt_rel(scr_stat *scp); static int signal_vt_acq(scr_stat *scp); static int finish_vt_rel(scr_stat *scp, int release, int *s); static int finish_vt_acq(scr_stat *scp); static void exchange_scr(sc_softc_t *sc); static void update_cursor_image(scr_stat *scp); static void change_cursor_shape(scr_stat *scp, int flags, int base, int height); static void update_font(scr_stat *); static int save_kbd_state(scr_stat *scp); static int update_kbd_state(scr_stat *scp, int state, int mask); static int update_kbd_leds(scr_stat *scp, int which); static int sc_kattr(void); static timeout_t blink_screen; static struct tty *sc_alloc_tty(int, int); static cn_probe_t sc_cnprobe; static cn_init_t sc_cninit; static cn_term_t sc_cnterm; static cn_getc_t sc_cngetc; static cn_putc_t sc_cnputc; static cn_grab_t sc_cngrab; static cn_ungrab_t sc_cnungrab; CONSOLE_DRIVER(sc); static tsw_open_t sctty_open; static tsw_close_t sctty_close; static tsw_outwakeup_t sctty_outwakeup; static tsw_ioctl_t sctty_ioctl; static tsw_mmap_t sctty_mmap; static struct ttydevsw sc_ttydevsw = { .tsw_open = sctty_open, .tsw_close = sctty_close, .tsw_outwakeup = sctty_outwakeup, .tsw_ioctl = sctty_ioctl, .tsw_mmap = sctty_mmap, }; static d_ioctl_t consolectl_ioctl; static d_close_t consolectl_close; static struct cdevsw consolectl_devsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT | D_TRACKCLOSE, .d_ioctl = consolectl_ioctl, .d_close = consolectl_close, .d_name = "consolectl", }; /* ec -- emergency console. */ static u_int ec_scroffset; static void ec_putc(int c) { uintptr_t fb; u_short *scrptr; u_int ind; int attr, column, mysize, width, xsize, yborder, ysize; if (c < 0 || c > 0xff || c == '\a') return; if (sc_console == NULL) { #if !defined(__amd64__) && !defined(__i386__) return; #endif /* * This is enough for ec_putc() to work very early on x86 * if the kernel starts in normal color text mode. */ fb = 0xb8000; xsize = 80; ysize = 25; } else { if (main_console.status & GRAPHICS_MODE) return; fb = main_console.sc->adp->va_window; xsize = main_console.xsize; ysize = main_console.ysize; } yborder = ysize / 5; scrptr = (u_short *)(void *)fb + xsize * yborder; mysize = xsize * (ysize - 2 * yborder); do { ind = ec_scroffset; column = ind % xsize; width = (c == '\b' ? -1 : c == '\t' ? (column + 8) & ~7 : c == '\r' ? -column : c == '\n' ? xsize - column : 1); if (width == 0 || (width < 0 && ind < -width)) return; } while (atomic_cmpset_rel_int(&ec_scroffset, ind, ind + width) == 0); if (c == '\b' || c == '\r') return; if (c == '\n') ind += xsize; /* XXX clearing from new pos is not atomic */ attr = sc_kattr(); if (c == '\t' || c == '\n') c = ' '; do scrptr[ind++ % mysize] = (attr << 8) | c; while (--width != 0); } int sc_probe_unit(int unit, int flags) { if (!vty_enabled(VTY_SC)) return ENXIO; if (!scvidprobe(unit, flags, FALSE)) { if (bootverbose) printf("%s%d: no video adapter found.\n", SC_DRIVER_NAME, unit); return ENXIO; } /* syscons will be attached even when there is no keyboard */ sckbdprobe(unit, flags, FALSE); return 0; } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags, int cons) { /* * Access the video adapter driver through the back door! * Video adapter drivers need to be configured before syscons. * However, when syscons is being probed as the low-level console, * they have not been initialized yet. We force them to initialize * themselves here. XXX */ vid_configure(cons ? VIO_PROBE_ONLY : 0); return (vid_find_adapter("*", unit) >= 0); } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags, int cons) { /* access the keyboard driver through the backdoor! */ kbd_configure(cons ? KB_CONF_PROBE_ONLY : 0); return (kbd_find_keyboard("*", unit) >= 0); } static char *adapter_name(video_adapter_t *adp) { static struct { int type; char *name[2]; } names[] = { { KD_MONO, { "MDA", "MDA" } }, { KD_HERCULES, { "Hercules", "Hercules" } }, { KD_CGA, { "CGA", "CGA" } }, { KD_EGA, { "EGA", "EGA (mono)" } }, { KD_VGA, { "VGA", "VGA (mono)" } }, { KD_TGA, { "TGA", "TGA" } }, { -1, { "Unknown", "Unknown" } }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == adp->va_type) break; return names[i].name[(adp->va_flags & V_ADP_COLOR) ? 0 : 1]; } static void sctty_outwakeup(struct tty *tp) { size_t len; u_char buf[PCBURST]; scr_stat *scp = sc_get_stat(tp); if (scp->status & SLKED || (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) return; for (;;) { len = ttydisc_getc(tp, buf, sizeof buf); if (len == 0) break; SC_VIDEO_LOCK(scp->sc); sc_puts(scp, buf, len); SC_VIDEO_UNLOCK(scp->sc); } } static struct tty * sc_alloc_tty(int index, int devnum) { struct sc_ttysoftc *stc; struct tty *tp; /* Allocate TTY object and softc to store unit number. */ stc = malloc(sizeof(struct sc_ttysoftc), M_DEVBUF, M_WAITOK); stc->st_index = index; stc->st_stat = NULL; tp = tty_alloc_mutex(&sc_ttydevsw, stc, &Giant); /* Create device node. */ tty_makedev(tp, NULL, "v%r", devnum); return (tp); } #ifdef SC_PIXEL_MODE static void sc_set_vesa_mode(scr_stat *scp, sc_softc_t *sc, int unit) { video_info_t info; u_char *font; int depth; int fontsize; int i; int vmode; vmode = 0; (void)resource_int_value("sc", unit, "vesa_mode", &vmode); if (vmode < M_VESA_BASE || vmode > M_VESA_MODE_MAX || vidd_get_info(sc->adp, vmode, &info) != 0 || !sc_support_pixel_mode(&info)) vmode = 0; /* * If the mode is unset or unsupported, search for an available * 800x600 graphics mode with the highest color depth. */ if (vmode == 0) { for (depth = 0, i = M_VESA_BASE; i <= M_VESA_MODE_MAX; i++) if (vidd_get_info(sc->adp, i, &info) == 0 && info.vi_width == 800 && info.vi_height == 600 && sc_support_pixel_mode(&info) && info.vi_depth > depth) { vmode = i; depth = info.vi_depth; } if (vmode == 0) return; vidd_get_info(sc->adp, vmode, &info); } #if !defined(SC_NO_FONT_LOADING) && defined(SC_DFLT_FONT) fontsize = info.vi_cheight; #else fontsize = scp->font_size; #endif if (fontsize < 14) fontsize = 8; else if (fontsize >= 16) fontsize = 16; else fontsize = 14; #ifndef SC_NO_FONT_LOADING switch (fontsize) { case 8: if ((sc->fonts_loaded & FONT_8) == 0) return; font = sc->font_8; break; case 14: if ((sc->fonts_loaded & FONT_14) == 0) return; font = sc->font_14; break; case 16: if ((sc->fonts_loaded & FONT_16) == 0) return; font = sc->font_16; break; } #else font = NULL; #endif #ifdef DEV_SPLASH if ((sc->flags & SC_SPLASH_SCRN) != 0) splash_term(sc->adp); #endif #ifndef SC_NO_HISTORY if (scp->history != NULL) { sc_vtb_append(&scp->vtb, 0, scp->history, scp->ypos * scp->xsize + scp->xpos); scp->history_pos = sc_vtb_tail(scp->history); } #endif vidd_set_mode(sc->adp, vmode); scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE); scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->xsize = scp->xpixel / 8; scp->ysize = scp->ypixel / fontsize; scp->xpos = 0; scp->ypos = scp->ysize - 1; scp->xoff = scp->yoff = 0; scp->font = font; scp->font_size = fontsize; scp->font_width = 8; scp->start = scp->xsize * scp->ysize - 1; scp->end = 0; scp->cursor_pos = scp->cursor_oldpos = scp->xsize * scp->xsize; scp->mode = sc->initial_mode = vmode; #ifndef __sparc64__ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)sc->adp->va_window, FALSE); #endif sc_alloc_scr_buffer(scp, FALSE, FALSE); sc_init_emulator(scp, NULL); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, FALSE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, 0, FALSE); #endif sc_set_border(scp, scp->border); sc_set_cursor_image(scp); scp->status &= ~UNKNOWN_MODE; #ifdef DEV_SPLASH if ((sc->flags & SC_SPLASH_SCRN) != 0) splash_init(sc->adp, scsplash_callback, sc); #endif } #endif int sc_attach_unit(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; struct cdev *dev; void *oldts, *ts; int i, vc; if (!vty_enabled(VTY_SC)) return ENXIO; flags &= ~SC_KERNEL_CONSOLE; if (sc_console_unit == unit) { /* * If this unit is being used as the system console, we need to * adjust some variables and buffers before and after scinit(). */ /* assert(sc_console != NULL) */ flags |= SC_KERNEL_CONSOLE; scmeminit(NULL); scinit(unit, flags); if (sc_console->tsw->te_size > 0) { sc_ktsw = sc_console->tsw; /* assert(sc_console->ts != NULL); */ oldts = sc_console->ts; for (i = 0; i <= mp_maxid; i++) { ts = malloc(sc_console->tsw->te_size, M_DEVBUF, M_WAITOK | M_ZERO); (*sc_console->tsw->te_init)(sc_console, &ts, SC_TE_COLD_INIT); sc_console->ts = ts; (*sc_console->tsw->te_default_attr)(sc_console, sc_kattrtab[i], SC_KERNEL_CONS_REV_ATTR); sc_kts[i] = ts; } sc_console->ts = oldts; (*sc_console->tsw->te_default_attr)(sc_console, SC_NORM_ATTR, SC_NORM_REV_ATTR); } } else { scinit(unit, flags); } sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); sc->config = flags; callout_init(&sc->ctimeout, 0); callout_init(&sc->cblink, 0); scp = sc_get_stat(sc->dev[0]); if (sc_console == NULL) /* sc_console_unit < 0 */ sc_console = scp; #ifdef SC_PIXEL_MODE if ((sc->config & SC_VESAMODE) != 0) sc_set_vesa_mode(scp, sc, unit); #endif /* SC_PIXEL_MODE */ /* initialize cursor */ if (!ISGRAPHSC(scp)) update_cursor_image(scp); /* get screen update going */ scrn_timer(sc); /* set up the keyboard */ (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); printf("%s%d: %s <%d virtual consoles, flags=0x%x>\n", SC_DRIVER_NAME, unit, adapter_name(sc->adp), sc->vtys, sc->config); if (bootverbose) { printf("%s%d:", SC_DRIVER_NAME, unit); if (sc->adapter >= 0) printf(" fb%d", sc->adapter); if (sc->keyboard >= 0) printf(", kbd%d", sc->keyboard); if (scp->tsw) printf(", terminal emulator: %s (%s)", scp->tsw->te_name, scp->tsw->te_desc); printf("\n"); } /* Register suspend/resume/shutdown callbacks for the kernel console. */ if (sc_console_unit == unit) { EVENTHANDLER_REGISTER(power_suspend_early, scsuspend, NULL, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(power_resume, scresume, NULL, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(shutdown_pre_sync, scshutdown, NULL, SHUTDOWN_PRI_DEFAULT); } for (vc = 0; vc < sc->vtys; vc++) { if (sc->dev[vc] == NULL) { sc->dev[vc] = sc_alloc_tty(vc, vc + unit * MAXCONS); if (vc == 0 && sc->dev == main_devs) SC_STAT(sc->dev[0]) = &main_console; } /* * The first vty already has struct tty and scr_stat initialized * in scinit(). The other vtys will have these structs when * first opened. */ } dev = make_dev(&consolectl_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "consolectl"); dev->si_drv1 = sc->dev[0]; return 0; } static void scmeminit(void *arg) { if (!vty_enabled(VTY_SC)) return; if (sc_malloc) return; sc_malloc = TRUE; /* * As soon as malloc() becomes functional, we had better allocate * various buffers for the kernel console. */ if (sc_console_unit < 0) /* sc_console == NULL */ return; /* copy the temporary buffer to the final buffer */ sc_alloc_scr_buffer(sc_console, FALSE, FALSE); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(sc_console, FALSE); #endif #ifndef SC_NO_HISTORY /* initialize history buffer & pointers */ sc_alloc_history_buffer(sc_console, 0, 0, FALSE); #endif } /* XXX */ SYSINIT(sc_mem, SI_SUB_KMEM, SI_ORDER_ANY, scmeminit, NULL); static int scdevtounit(struct tty *tp) { int vty = SC_VTY(tp); if (vty == SC_CONSOLECTL) return ((sc_console != NULL) ? sc_console->sc->unit : -1); else if ((vty < 0) || (vty >= MAXCONS*sc_max_unit())) return -1; else return vty/MAXCONS; } static int sctty_open(struct tty *tp) { int unit = scdevtounit(tp); sc_softc_t *sc; scr_stat *scp; #ifndef __sparc64__ keyarg_t key; #endif DPRINTF(5, ("scopen: dev:%s, unit:%d, vty:%d\n", devtoname(tp->t_dev), unit, SC_VTY(tp))); sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc == NULL) return ENXIO; if (!tty_opened(tp)) { /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ #ifndef __sparc64__ if (sc->kbd != NULL) { key.keynum = KEYCODE_BS; (void)kbdd_ioctl(sc->kbd, GIO_KEYMAPENT, (caddr_t)&key); tp->t_termios.c_cc[VERASE] = key.key.map[0]; } #endif } scp = sc_get_stat(tp); if (scp == NULL) { scp = SC_STAT(tp) = alloc_scp(sc, SC_VTY(tp)); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, 0, 0, 16, 8); } if (!tp->t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; } return (0); } static void sctty_close(struct tty *tp) { scr_stat *scp; int s; if (SC_VTY(tp) != SC_CONSOLECTL) { scp = sc_get_stat(tp); /* were we in the middle of the VT switching process? */ DPRINTF(5, ("sc%d: scclose(), ", scp->sc->unit)); s = spltty(); if ((scp == scp->sc->cur_scp) && (scp->sc->unit == sc_console_unit)) cnavailable(sc_consptr, TRUE); if (finish_vt_rel(scp, TRUE, &s) == 0) /* force release */ DPRINTF(5, ("reset WAIT_REL, ")); if (finish_vt_acq(scp) == 0) /* force acknowledge */ DPRINTF(5, ("reset WAIT_ACQ, ")); #ifdef not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { sc_vtb_destroy(&scp->vtb); #ifndef __sparc64__ sc_vtb_destroy(&scp->scr); #endif sc_free_history_buffer(scp, scp->ysize); SC_STAT(tp) = NULL; free(scp, M_DEVBUF); } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif scp->kbd_mode = K_XLATE; if (scp == scp->sc->cur_scp) (void)kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); DPRINTF(5, ("done.\n")); } } #if 0 /* XXX mpsafetty: fix screensaver. What about outwakeup? */ static int scread(struct cdev *dev, struct uio *uio, int flag) { if (!sc_saver_keyb_only) sc_touch_scrn_saver(); return ttyread(dev, uio, flag); } #endif static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { sc_softc_t *sc; struct tty *cur_tty; int c, error = 0; size_t len; const u_char *cp; sc = (sc_softc_t *)arg; /* assert(thiskbd == sc->kbd) */ mtx_lock(&Giant); switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: sc->kbd = NULL; sc->keyboard = -1; kbd_release(thiskbd, (void *)&sc->keyboard); goto done; default: error = EINVAL; goto done; } /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(sc, SCGETC_NONBLOCK, NULL)) != NOKEY) { cur_tty = SC_DEV(sc, sc->cur_scp->index); if (!tty_opened_ns(cur_tty)) continue; if ((*sc->cur_scp->tsw->te_input)(sc->cur_scp, c, cur_tty)) continue; switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case FKEY: /* function key, return string */ cp = (*sc->cur_scp->tsw->te_fkeystr)(sc->cur_scp, c); if (cp != NULL) { ttydisc_rint_simple(cur_tty, cp, strlen(cp)); break; } cp = kbdd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) ttydisc_rint_simple(cur_tty, cp, len); break; case MKEY: /* meta is active, prepend ESC */ ttydisc_rint(cur_tty, 0x1b, 0); ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ ttydisc_rint_simple(cur_tty, "\x1B[Z", 3); break; } ttydisc_rint_done(cur_tty); } sc->cur_scp->status |= MOUSE_HIDDEN; done: mtx_unlock(&Giant); return (error); } static int sctty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { int error; int i; struct cursor_attr *cap; sc_softc_t *sc; scr_stat *scp; int s; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { error = (*sc_user_ioctl)(tp, cmd, data, td); if (error != ENOIOCTL) return error; } error = sc_vid_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #ifndef SC_NO_HISTORY error = sc_hist_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif #ifndef SC_NO_SYSMOUSE error = sc_mouse_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif scp = sc_get_stat(tp); /* assert(scp != NULL) */ /* scp is sc_console, if SC_VTY(dev) == SC_CONSOLECTL. */ sc = scp->sc; if (scp->tsw) { error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, td); if (error != ENOIOCTL) return error; } switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ /* this ioctl is not processed here, but in the terminal emulator */ return ENOTTY; case GIO_COLOR: /* is this a color console ? */ *(int *)data = (sc->adp->va_flags & V_ADP_COLOR) ? 1 : 0; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type (old interface + HIDDEN) */ s = spltty(); *(int *)data &= CONS_CURSOR_ATTRS; sc_change_cursor_shape(scp, *(int *)data, -1, -1); splx(s); return 0; case CONS_GETCURSORSHAPE: /* get cursor shape (new interface) */ switch (((int *)data)[0] & (CONS_DEFAULT_CURSOR | CONS_LOCAL_CURSOR)) { case 0: cap = &sc->curs_attr; break; case CONS_LOCAL_CURSOR: cap = &scp->base_curs_attr; break; case CONS_DEFAULT_CURSOR: cap = &sc->dflt_curs_attr; break; case CONS_DEFAULT_CURSOR | CONS_LOCAL_CURSOR: cap = &scp->dflt_curs_attr; break; } - ((int *)data)[1] = cap->base; - ((int *)data)[2] = cap->height; + if (((int *)data)[0] & CONS_CHARCURSOR_COLORS) { + ((int *)data)[1] = cap->bg[0]; + ((int *)data)[2] = cap->bg[1]; + } else if (((int *)data)[0] & CONS_MOUSECURSOR_COLORS) { + ((int *)data)[1] = cap->mouse_ba; + ((int *)data)[2] = cap->mouse_ia; + } else { + ((int *)data)[1] = cap->base; + ((int *)data)[2] = cap->height; + } ((int *)data)[0] = cap->flags; return 0; case CONS_SETCURSORSHAPE: /* set cursor shape (new interface) */ s = spltty(); sc_change_cursor_shape(scp, ((int *)data)[0], ((int *)data)[1], ((int *)data)[2]); splx(s); return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & CONS_VISUAL_BELL) sc->flags |= SC_VISUAL_BELL; else sc->flags &= ~SC_VISUAL_BELL; if ((*(int *)data) & CONS_QUIET_BELL) sc->flags |= SC_QUIET_BELL; else sc->flags &= ~SC_QUIET_BELL; return 0; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = sc->cur_scp->index; ptr->font_size = scp->font_size; ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_hsz = (scp->history != NULL) ? scp->history->vtb_rows : 0; /* * The following fields are filled by the terminal emulator. XXX * * ptr->mv_norm.fore * ptr->mv_norm.back * ptr->mv_rev.fore * ptr->mv_rev.back */ ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; if (scp == sc->cur_scp) save_kbd_state(scp); ptr->mk_keylock = scp->status & LOCK_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ /* * When the screen is in the GRAPHICS_MODE or UNKNOWN_MODE, * the user process may have been writing something on the * screen and syscons is not aware of it. Declare the screen * is NOT idle if it is in one of these modes. But there is * an exception to it; if a screen saver is running in the * graphics mode in the current screen, we should say that the * screen has been idle. */ *(int *)data = (sc->flags & SC_SCRN_IDLE) && (!ISGRAPHSC(sc->cur_scp) || (sc->cur_scp->status & SAVER_RUNNING)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_NO_SAVER: case CONS_USR_SAVER: /* if a LKM screen saver is running, stop it first. */ scsplash_stick(FALSE); saver_mode = *(int *)data; s = spltty(); #ifdef DEV_SPLASH if ((error = wait_scrn_saver_stop(NULL))) { splx(s); return error; } #endif run_scrn_saver = TRUE; if (saver_mode == CONS_USR_SAVER) scp->status |= SAVER_RUNNING; else scp->status &= ~SAVER_RUNNING; scsplash_stick(TRUE); splx(s); break; case CONS_LKM_SAVER: s = spltty(); if ((saver_mode == CONS_USR_SAVER) && (scp->status & SAVER_RUNNING)) scp->status &= ~SAVER_RUNNING; saver_mode = *(int *)data; splx(s); break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) sc->scrn_time_stamp -= scrn_blank_time; splx(s); return 0; case CONS_SCRSHOT: /* get a screen shot */ { int retval, hist_rsz; size_t lsize, csize; vm_offset_t frbp, hstp; unsigned lnum; scrshot_t *ptr = (scrshot_t *)data; void *outp = ptr->buf; if (ptr->x < 0 || ptr->y < 0 || ptr->xsize < 0 || ptr->ysize < 0) return EINVAL; s = spltty(); if (ISGRAPHSC(scp)) { splx(s); return EOPNOTSUPP; } hist_rsz = (scp->history != NULL) ? scp->history->vtb_rows : 0; if (((u_int)ptr->x + ptr->xsize) > scp->xsize || ((u_int)ptr->y + ptr->ysize) > (scp->ysize + hist_rsz)) { splx(s); return EINVAL; } lsize = scp->xsize * sizeof(u_int16_t); csize = ptr->xsize * sizeof(u_int16_t); /* Pointer to the last line of framebuffer */ frbp = scp->vtb.vtb_buffer + scp->ysize * lsize + ptr->x * sizeof(u_int16_t); /* Pointer to the last line of target buffer */ outp = (char *)outp + ptr->ysize * csize; /* Pointer to the last line of history buffer */ if (scp->history != NULL) hstp = scp->history->vtb_buffer + sc_vtb_tail(scp->history) * sizeof(u_int16_t) + ptr->x * sizeof(u_int16_t); else hstp = 0; retval = 0; for (lnum = 0; lnum < (ptr->y + ptr->ysize); lnum++) { if (lnum < scp->ysize) { frbp -= lsize; } else { hstp -= lsize; if (hstp < scp->history->vtb_buffer) hstp += scp->history->vtb_rows * lsize; frbp = hstp; } if (lnum < ptr->y) continue; outp = (char *)outp - csize; retval = copyout((void *)frbp, outp, csize); if (retval != 0) break; } splx(s); return retval; } case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; struct proc *p1; mode = (struct vt_mode *)data; DPRINTF(5, ("%s%d: VT_SETMODE ", SC_DRIVER_NAME, sc->unit)); if (scp->smode.mode == VT_PROCESS) { p1 = pfind(scp->pid); if (scp->proc == p1 && scp->proc != td->td_proc) { if (p1) PROC_UNLOCK(p1); DPRINTF(5, ("error EPERM\n")); return EPERM; } if (p1) PROC_UNLOCK(p1); } s = spltty(); if (mode->mode == VT_AUTO) { scp->smode.mode = VT_AUTO; scp->proc = NULL; scp->pid = 0; DPRINTF(5, ("VT_AUTO, ")); if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cnavailable(sc_consptr, TRUE); /* were we in the middle of the vty switching process? */ if (finish_vt_rel(scp, TRUE, &s) == 0) DPRINTF(5, ("reset WAIT_REL, ")); if (finish_vt_acq(scp) == 0) DPRINTF(5, ("reset WAIT_ACQ, ")); } else { if (!ISSIGVALID(mode->relsig) || !ISSIGVALID(mode->acqsig) || !ISSIGVALID(mode->frsig)) { splx(s); DPRINTF(5, ("error EINVAL\n")); return EINVAL; } DPRINTF(5, ("VT_PROCESS %d, ", td->td_proc->p_pid)); bcopy(data, &scp->smode, sizeof(struct vt_mode)); scp->proc = td->td_proc; scp->pid = scp->proc->p_pid; if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cnavailable(sc_consptr, FALSE); } splx(s); DPRINTF(5, ("\n")); return 0; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 4): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_RELDISP: /* screen switcher ioctl */ s = spltty(); /* * This must be the current vty which is in the VT_PROCESS * switching mode... */ if ((scp != sc->cur_scp) || (scp->smode.mode != VT_PROCESS)) { splx(s); return EINVAL; } /* ...and this process is controlling it. */ if (scp->proc != td->td_proc) { splx(s); return EPERM; } error = EINVAL; switch(*(int *)data) { case VT_FALSE: /* user refuses to release screen, abort */ if ((error = finish_vt_rel(scp, FALSE, &s)) == 0) DPRINTF(5, ("%s%d: VT_FALSE\n", SC_DRIVER_NAME, sc->unit)); break; case VT_TRUE: /* user has released screen, go on */ if ((error = finish_vt_rel(scp, TRUE, &s)) == 0) DPRINTF(5, ("%s%d: VT_TRUE\n", SC_DRIVER_NAME, sc->unit)); break; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if ((error = finish_vt_acq(scp)) == 0) DPRINTF(5, ("%s%d: VT_ACKACQ\n", SC_DRIVER_NAME, sc->unit)); break; default: break; } splx(s); return error; case VT_OPENQRY: /* return free virtual console */ for (i = sc->first_vty; i < sc->first_vty + sc->vtys; i++) { tp = SC_DEV(sc, i); if (!tty_opened_ns(tp)) { *(int *)data = i + 1; return 0; } } return EINVAL; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 5): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_ACTIVATE: /* switch to screen *data */ i = (*(int *)data == 0) ? scp->index : (*(int *)data - 1); s = spltty(); error = sc_clean_up(sc->cur_scp); splx(s); if (error) return error; error = sc_switch_scr(sc, i); return (error); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 6): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_WAITACTIVE: /* wait for switch to occur */ i = (*(int *)data == 0) ? scp->index : (*(int *)data - 1); if ((i < sc->first_vty) || (i >= sc->first_vty + sc->vtys)) return EINVAL; if (i == sc->cur_scp->index) return 0; error = tsleep(VTY_WCHAN(sc, i), (PZERO + 1) | PCATCH, "waitvt", 0); return error; case VT_GETACTIVE: /* get active vty # */ *(int *)data = sc->cur_scp->index + 1; return 0; case VT_GETINDEX: /* get this vty # */ *(int *)data = scp->index + 1; return 0; case VT_LOCKSWITCH: /* prevent vty switching */ if ((*(int *)data) & 0x01) sc->flags |= SC_SCRN_VTYLOCK; else sc->flags &= ~SC_SCRN_VTYLOCK; return 0; case KDENABIO: /* allow io operations */ error = priv_check(td, PRIV_IO); if (error != 0) return error; error = securelevel_gt(td->td_ucred, 0); if (error != 0) return error; #ifdef __i386__ td->td_frame->tf_eflags |= PSL_IOPL; #elif defined(__amd64__) td->td_frame->tf_rflags |= PSL_IOPL; #endif return 0; case KDDISABIO: /* disallow io operations (default) */ #ifdef __i386__ td->td_frame->tf_eflags &= ~PSL_IOPL; #elif defined(__amd64__) td->td_frame->tf_rflags &= ~PSL_IOPL; #endif return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set keyboard state (locks) */ if (*(int *)data & ~LOCK_MASK) return EINVAL; scp->status &= ~LOCK_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_state(scp, scp->status, LOCK_MASK); return 0; case KDGKBSTATE: /* get keyboard state (locks) */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LOCK_MASK; return 0; case KDGETREPEAT: /* get keyboard repeat & delay rates */ case KDSETREPEAT: /* set keyboard repeat & delay rates (new) */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat & delay rates (old) */ if (*(int *)data & ~0x7f) return EINVAL; error = kbdd_ioctl(sc->kbd, KDSETRAD, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)data) { case K_XLATE: /* switch to XLT ascii mode */ case K_RAW: /* switch to RAW scancode mode */ case K_CODE: /* switch to CODE mode */ scp->kbd_mode = *(int *)data; if (scp == sc->cur_scp) (void)kbdd_ioctl(sc->kbd, KDSKBMODE, data); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *(int *)data = scp->kbd_mode; return 0; case KDGKBINFO: error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 8): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDMKTONE: /* sound the bell */ if (*(int*)data) sc_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else sc_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 63): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KIOCSOUND: /* make tone (*data) hz */ if (scp == sc->cur_scp) { if (*(int *)data) return sc_tone(*(int *)data); else return sc_tone(0); } return 0; case KDGKBTYPE: /* get keyboard type */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) { /* always return something? XXX */ *(int *)data = 0; } return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED status */ if (*(int *)data & ~LED_MASK) /* FIXME: LOCK_MASK? */ return EINVAL; scp->status &= ~LED_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_leds(scp, scp->status); return 0; case KDGETLED: /* get keyboard LED status */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LED_MASK; return 0; case KBADDKBD: /* add/remove keyboard to/from mux */ case KBRELKBD: error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('c', 110): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case CONS_SETKBD: /* set the new keyboard */ { keyboard_t *newkbd; s = spltty(); newkbd = kbd_get_keyboard(*(int *)data); if (newkbd == NULL) { splx(s); return EINVAL; } error = 0; if (sc->kbd != newkbd) { i = kbd_allocate(newkbd->kb_name, newkbd->kb_unit, (void *)&sc->keyboard, sckbdevent, sc); /* i == newkbd->kb_index */ if (i >= 0) { if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); kbd_release(sc->kbd, (void *)&sc->keyboard); } sc->kbd = kbd_get_keyboard(i); /* sc->kbd == newkbd */ sc->keyboard = i; (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } else { error = EPERM; /* XXX */ } } splx(s); return error; } case CONS_RELKBD: /* release the current keyboard */ s = spltty(); error = 0; if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); error = kbd_release(sc->kbd, (void *)&sc->keyboard); if (error == 0) { sc->kbd = NULL; sc->keyboard = -1; } } splx(s); return error; case CONS_GETTERM: /* get the current terminal emulator info */ { sc_term_sw_t *sw; if (((term_info_t *)data)->ti_index == 0) { sw = scp->tsw; } else { sw = sc_term_match_by_number(((term_info_t *)data)->ti_index); } if (sw != NULL) { strncpy(((term_info_t *)data)->ti_name, sw->te_name, sizeof(((term_info_t *)data)->ti_name)); strncpy(((term_info_t *)data)->ti_desc, sw->te_desc, sizeof(((term_info_t *)data)->ti_desc)); ((term_info_t *)data)->ti_flags = 0; return 0; } else { ((term_info_t *)data)->ti_name[0] = '\0'; ((term_info_t *)data)->ti_desc[0] = '\0'; ((term_info_t *)data)->ti_flags = 0; return EINVAL; } } case CONS_SETTERM: /* set the current terminal emulator */ s = spltty(); error = sc_init_emulator(scp, ((term_info_t *)data)->ti_name); /* FIXME: what if scp == sc_console! XXX */ splx(s); return error; case GIO_SCRNMAP: /* get output translation table */ bcopy(&sc->scr_map, data, sizeof(sc->scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &sc->scr_map, sizeof(sc->scr_map)); for (i=0; iscr_map); i++) { sc->scr_rmap[sc->scr_map[i]] = i; } return 0; case GIO_KEYMAP: /* get keyboard translation table */ case PIO_KEYMAP: /* set keyboard translation table */ case OGIO_KEYMAP: /* get keyboard translation table (compat) */ case OPIO_KEYMAP: /* set keyboard translation table (compat) */ case GIO_DEADKEYMAP: /* get accent key translation table */ case PIO_DEADKEYMAP: /* set accent key translation table */ case GETFKEY: /* get function key string */ case SETFKEY: /* set function key string */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #ifndef SC_NO_FONT_LOADING case PIO_FONT8x8: /* set 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_8, 8*256); sc->fonts_loaded |= FONT_8; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x8. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size < 14)) sc_load_font(sc->cur_scp, 0, 8, 8, sc->font_8, 0, 256); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_8) { bcopy(sc->font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_14, 14*256); sc->fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 14) && (sc->cur_scp->font_size < 16)) sc_load_font(sc->cur_scp, 0, 14, 8, sc->font_14, 0, 256); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_14) { bcopy(sc->font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_16, 16*256); sc->fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 16)) sc_load_font(sc->cur_scp, 0, 16, 8, sc->font_16, 0, 256); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_16) { bcopy(sc->font_16, data, 16*256); return 0; } else return ENXIO; #endif /* SC_NO_FONT_LOADING */ default: break; } return (ENOIOCTL); } static int consolectl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { return sctty_ioctl(dev->si_drv1, cmd, data, td); } static int consolectl_close(struct cdev *dev, int flags, int mode, struct thread *td) { #ifndef SC_NO_SYSMOUSE mouse_info_t info; memset(&info, 0, sizeof(info)); info.operation = MOUSE_ACTION; /* * Make sure all buttons are released when moused and other * console daemons exit, so that no buttons are left pressed. */ (void) sctty_ioctl(dev->si_drv1, CONS_MOUSECTL, (caddr_t)&info, td); #endif return (0); } static void sc_cnprobe(struct consdev *cp) { int unit; int flags; if (!vty_enabled(VTY_SC)) { cp->cn_pri = CN_DEAD; return; } cp->cn_pri = sc_get_cons_priority(&unit, &flags); /* a video card is always required */ if (!scvidprobe(unit, flags, TRUE)) cp->cn_pri = CN_DEAD; /* syscons will become console even when there is no keyboard */ sckbdprobe(unit, flags, TRUE); if (cp->cn_pri == CN_DEAD) return; /* initialize required fields */ strcpy(cp->cn_name, "ttyv0"); } static void sc_cninit(struct consdev *cp) { int unit; int flags; sc_get_cons_priority(&unit, &flags); scinit(unit, flags | SC_KERNEL_CONSOLE); sc_console_unit = unit; sc_console = sc_get_stat(sc_get_softc(unit, SC_KERNEL_CONSOLE)->dev[0]); sc_consptr = cp; } static void sc_cnterm(struct consdev *cp) { void *ts; int i; /* we are not the kernel console any more, release everything */ if (sc_console_unit < 0) return; /* shouldn't happen */ #if 0 /* XXX */ sc_clear_screen(sc_console); sccnupdate(sc_console); #endif if (sc_ktsw != NULL) { for (i = 0; i <= mp_maxid; i++) { ts = sc_kts[i]; sc_kts[i] = NULL; (*sc_ktsw->te_term)(sc_console, &ts); free(ts, M_DEVBUF); } sc_ktsw = NULL; } scterm(sc_console_unit, SC_KERNEL_CONSOLE); sc_console_unit = -1; sc_console = NULL; } static void sccnclose(sc_softc_t *sc, struct sc_cnstate *sp); static int sc_cngetc_locked(struct sc_cnstate *sp); static void sccnkbdlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnkbdunlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags); static void sccnscrlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnscrunlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnkbdlock(sc_softc_t *sc, struct sc_cnstate *sp) { /* * Locking method: hope for the best. * The keyboard is supposed to be Giant locked. We can't handle that * in general. The kdb_active case here is not safe, and we will * proceed without the lock in all cases. */ sp->kbd_locked = !kdb_active && mtx_trylock(&Giant); } static void sccnkbdunlock(sc_softc_t *sc, struct sc_cnstate *sp) { if (sp->kbd_locked) mtx_unlock(&Giant); sp->kbd_locked = FALSE; } static void sccnscrlock(sc_softc_t *sc, struct sc_cnstate *sp) { int retries; /** * Locking method: * - if kdb_active and video_mtx is not owned by anyone, then lock * by kdb remaining active * - if !kdb_active, try to acquire video_mtx without blocking or * recursing; if we get it then it works normally. * Note that video_mtx is especially unusable if we already own it, * since then it is protecting something and syscons is not reentrant * enough to ignore the protection even in the kdb_active case. */ if (kdb_active) { sp->kdb_locked = sc->video_mtx.mtx_lock == MTX_UNOWNED || panicstr; sp->mtx_locked = FALSE; } else { sp->kdb_locked = FALSE; for (retries = 0; retries < 1000; retries++) { sp->mtx_locked = mtx_trylock_spin_flags(&sc->video_mtx, MTX_QUIET) != 0 || panicstr; if (sp->mtx_locked) break; DELAY(1); } } } static void sccnscrunlock(sc_softc_t *sc, struct sc_cnstate *sp) { if (sp->mtx_locked) mtx_unlock_spin(&sc->video_mtx); sp->mtx_locked = sp->kdb_locked = FALSE; } static void sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags) { int kbd_mode; /* assert(sc_console_unit >= 0) */ sp->kbd_opened = FALSE; sp->scr_opened = FALSE; sp->kbd_locked = FALSE; /* Opening the keyboard is optional. */ if (!(flags & 1) || sc->kbd == NULL) goto over_keyboard; sccnkbdlock(sc, sp); /* * Make sure the keyboard is accessible even when the kbd device * driver is disabled. */ kbdd_enable(sc->kbd); /* Switch the keyboard to console mode (K_XLATE, polled) on all scp's. */ kbd_mode = K_XLATE; (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&kbd_mode); sc->kbd_open_level++; kbdd_poll(sc->kbd, TRUE); sp->kbd_opened = TRUE; over_keyboard: ; /* The screen is opened iff locking it succeeds. */ sccnscrlock(sc, sp); if (!sp->kdb_locked && !sp->mtx_locked) return; sp->scr_opened = TRUE; /* The screen switch is optional. */ if (!(flags & 2)) return; /* try to switch to the kernel console screen */ if (!cold && sc->cur_scp->index != sc_console->index && sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc, sc_console->index); } static void sccnclose(sc_softc_t *sc, struct sc_cnstate *sp) { sp->scr_opened = FALSE; sccnscrunlock(sc, sp); if (!sp->kbd_opened) return; /* Restore keyboard mode (for the current, possibly-changed scp). */ kbdd_poll(sc->kbd, FALSE); if (--sc->kbd_open_level == 0) (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); kbdd_disable(sc->kbd); sp->kbd_opened = FALSE; sccnkbdunlock(sc, sp); } /* * Grabbing switches the screen and keyboard focus to sc_console and the * keyboard mode to (K_XLATE, polled). Only switching to polled mode is * essential (for preventing the interrupt handler from eating input * between polls). Focus is part of the UI, and the other switches are * work just was well when they are done on every entry and exit. * * Screen switches while grabbed are supported, and to maintain focus for * this ungrabbing and closing only restore the polling state and then * the keyboard mode if on the original screen. */ static void sc_cngrab(struct consdev *cp) { sc_softc_t *sc; int lev; sc = sc_console->sc; lev = atomic_fetchadd_int(&sc->grab_level, 1); if (lev >= 0 && lev < 2) { sccnopen(sc, &sc->grab_state[lev], 1 | 2); sccnscrunlock(sc, &sc->grab_state[lev]); sccnkbdunlock(sc, &sc->grab_state[lev]); } } static void sc_cnungrab(struct consdev *cp) { sc_softc_t *sc; int lev; sc = sc_console->sc; lev = atomic_load_acq_int(&sc->grab_level) - 1; if (lev >= 0 && lev < 2) { sccnkbdlock(sc, &sc->grab_state[lev]); sccnscrlock(sc, &sc->grab_state[lev]); sccnclose(sc, &sc->grab_state[lev]); } atomic_add_int(&sc->grab_level, -1); } static char sc_cnputc_log[0x1000]; static u_int sc_cnputc_loghead; static u_int sc_cnputc_logtail; static void sc_cnputc(struct consdev *cd, int c) { struct sc_cnstate st; u_char buf[1]; scr_stat *scp = sc_console; void *oldts, *ts; struct sc_term_sw *oldtsw; #ifndef SC_NO_HISTORY #if 0 struct tty *tp; #endif #endif /* !SC_NO_HISTORY */ u_int head; int s; /* assert(sc_console != NULL) */ sccnopen(scp->sc, &st, 0); /* * Log the output. * * In the unlocked case, the logging is intentionally only * perfectly atomic for the indexes. */ head = atomic_fetchadd_int(&sc_cnputc_loghead, 1); sc_cnputc_log[head % sizeof(sc_cnputc_log)] = c; /* * If we couldn't open, do special reentrant output and return to defer * normal output. */ if (!st.scr_opened) { ec_putc(c); return; } #ifndef SC_NO_HISTORY if (scp == scp->sc->cur_scp && scp->status & SLKED) { scp->status &= ~SLKED; update_kbd_state(scp, scp->status, SLKED); if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } #if 0 /* * XXX: Now that TTY's have their own locks, we cannot process * any data after disabling scroll lock. cnputs already holds a * spinlock. */ tp = SC_DEV(scp->sc, scp->index); /* XXX "tp" can be NULL */ tty_lock(tp); if (tty_opened(tp)) sctty_outwakeup(tp); tty_unlock(tp); #endif } #endif /* !SC_NO_HISTORY */ /* Play any output still in the log (our char may already be done). */ while (sc_cnputc_logtail != atomic_load_acq_int(&sc_cnputc_loghead)) { buf[0] = sc_cnputc_log[sc_cnputc_logtail++ % sizeof(sc_cnputc_log)]; if (atomic_load_acq_int(&sc_cnputc_loghead) - sc_cnputc_logtail >= sizeof(sc_cnputc_log)) continue; /* Console output has a per-CPU "input" state. Switch for it. */ oldtsw = scp->tsw; oldts = scp->ts; ts = sc_kts[PCPU_GET(cpuid)]; if (ts != NULL) { scp->tsw = sc_ktsw; scp->ts = ts; (*scp->tsw->te_sync)(scp); } sc_puts(scp, buf, 1); scp->tsw = oldtsw; scp->ts = oldts; (*scp->tsw->te_sync)(scp); } s = spltty(); /* block sckbdevent and scrn_timer */ sccnupdate(scp); splx(s); sccnclose(scp->sc, &st); } static int sc_cngetc(struct consdev *cd) { struct sc_cnstate st; int c, s; /* assert(sc_console != NULL) */ sccnopen(sc_console->sc, &st, 1); s = spltty(); /* block sckbdevent and scrn_timer while we poll */ if (!st.kbd_opened) { splx(s); sccnclose(sc_console->sc, &st); return -1; /* means no keyboard since we fudged the locking */ } c = sc_cngetc_locked(&st); splx(s); sccnclose(sc_console->sc, &st); return c; } static int sc_cngetc_locked(struct sc_cnstate *sp) { static struct fkeytab fkey; static int fkeycp; scr_stat *scp; const u_char *p; int c; /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ if (sp->scr_opened) sc_touch_scrn_saver(); scp = sc_console->sc->cur_scp; /* XXX */ if (sp->scr_opened) sccnupdate(scp); if (fkeycp < fkey.len) return fkey.str[fkeycp++]; c = scgetc(scp->sc, SCGETC_CN | SCGETC_NONBLOCK, sp); switch (KEYFLAGS(c)) { case 0: /* normal char */ return KEYCHAR(c); case FKEY: /* function key */ p = (*scp->tsw->te_fkeystr)(scp, c); if (p != NULL) { fkey.len = strlen(p); bcopy(p, fkey.str, fkey.len); fkeycp = 1; return fkey.str[0]; } p = kbdd_get_fkeystr(scp->sc->kbd, KEYCHAR(c), (size_t *)&fkeycp); fkey.len = fkeycp; if ((p != NULL) && (fkey.len > 0)) { bcopy(p, fkey.str, fkey.len); fkeycp = 1; return fkey.str[0]; } return c; /* XXX */ case NOKEY: case ERRKEY: default: return -1; } /* NOT REACHED */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (suspend_in_progress || scp->sc->font_loading_in_progress) return; if (kdb_active || panicstr || shutdown_in_progress) { sc_touch_scrn_saver(); } else if (scp != scp->sc->cur_scp) { return; } if (!run_scrn_saver) scp->sc->flags &= ~SC_SCRN_IDLE; #ifdef DEV_SPLASH if ((saver_mode != CONS_LKM_SAVER) || !(scp->sc->flags & SC_SCRN_IDLE)) if (scp->sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(scp->sc, current_saver); #endif if (scp != scp->sc->cur_scp || scp->sc->blink_in_progress || scp->sc->switch_in_progress) return; /* * FIXME: unlike scrn_timer(), we call scrn_update() from here even * when write_in_progress is non-zero. XXX */ if (!ISGRAPHSC(scp) && !(scp->sc->flags & SC_SCRN_BLANKED)) scrn_update(scp, TRUE); } static void scrn_timer(void *arg) { static time_t kbd_time_stamp = 0; sc_softc_t *sc; scr_stat *scp; int again, rate; again = (arg != NULL); if (arg != NULL) sc = (sc_softc_t *)arg; else if (sc_console != NULL) sc = sc_console->sc; else return; /* find the vty to update */ scp = sc->cur_scp; /* don't do anything when we are performing some I/O operations */ if (suspend_in_progress || sc->font_loading_in_progress) goto done; if ((sc->kbd == NULL) && (sc->config & SC_AUTODETECT_KBD)) { /* try to allocate a keyboard automatically */ if (kbd_time_stamp != time_uptime) { kbd_time_stamp = time_uptime; sc->keyboard = sc_allocate_keyboard(sc, -1); if (sc->keyboard >= 0) { sc->kbd = kbd_get_keyboard(sc->keyboard); (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } } } /* should we stop the screen saver? */ if (kdb_active || panicstr || shutdown_in_progress) sc_touch_scrn_saver(); if (run_scrn_saver) { if (time_uptime > sc->scrn_time_stamp + scrn_blank_time) sc->flags |= SC_SCRN_IDLE; else sc->flags &= ~SC_SCRN_IDLE; } else { sc->scrn_time_stamp = time_uptime; sc->flags &= ~SC_SCRN_IDLE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } #ifdef DEV_SPLASH if ((saver_mode != CONS_LKM_SAVER) || !(sc->flags & SC_SCRN_IDLE)) if (sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(sc, current_saver); #endif /* should we just return ? */ if (sc->blink_in_progress || sc->switch_in_progress || sc->write_in_progress) goto done; /* Update the screen */ scp = sc->cur_scp; /* cur_scp may have changed... */ if (!ISGRAPHSC(scp) && !(sc->flags & SC_SCRN_BLANKED)) scrn_update(scp, TRUE); #ifdef DEV_SPLASH /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && (sc->flags & SC_SCRN_IDLE)) if (!ISGRAPHSC(scp) || (sc->flags & SC_SCRN_BLANKED)) (*current_saver)(sc, TRUE); #endif done: if (again) { /* * Use reduced "refresh" rate if we are in graphics and that is not a * graphical screen saver. In such case we just have nothing to do. */ if (ISGRAPHSC(scp) && !(sc->flags & SC_SCRN_BLANKED)) rate = 2; else rate = 30; callout_reset_sbt(&sc->ctimeout, SBT_1S / rate, 0, scrn_timer, sc, C_PREL(1)); } } static int and_region(int *s1, int *e1, int s2, int e2) { if (*e1 < s2 || e2 < *s1) return FALSE; *s1 = imax(*s1, s2); *e1 = imin(*e1, e2); return TRUE; } static void scrn_update(scr_stat *scp, int show_cursor) { int start; int end; int s; int e; /* assert(scp == scp->sc->cur_scp) */ SC_VIDEO_LOCK(scp->sc); #ifndef SC_NO_CUTPASTE /* remove the previous mouse pointer image if necessary */ if (scp->status & MOUSE_VISIBLE) { s = scp->mouse_pos; e = scp->mouse_pos + scp->xsize + 1; if ((scp->status & (MOUSE_MOVED | MOUSE_HIDDEN)) || and_region(&s, &e, scp->start, scp->end) || ((scp->status & CURSOR_ENABLED) && (scp->cursor_pos != scp->cursor_oldpos) && (and_region(&s, &e, scp->cursor_pos, scp->cursor_pos) || and_region(&s, &e, scp->cursor_oldpos, scp->cursor_oldpos)))) { sc_remove_mouse_image(scp); if (scp->end >= scp->xsize*scp->ysize) scp->end = scp->xsize*scp->ysize - 1; } } #endif /* !SC_NO_CUTPASTE */ #if 1 /* debug: XXX */ if (scp->end >= scp->xsize*scp->ysize) { printf("scrn_update(): scp->end %d > size_of_screen!!\n", scp->end); scp->end = scp->xsize*scp->ysize - 1; } if (scp->start < 0) { printf("scrn_update(): scp->start %d < 0\n", scp->start); scp->start = 0; } #endif /* update screen image */ if (scp->start <= scp->end) { if (scp->mouse_cut_end >= 0) { /* there is a marked region for cut & paste */ if (scp->mouse_cut_start <= scp->mouse_cut_end) { start = scp->mouse_cut_start; end = scp->mouse_cut_end; } else { start = scp->mouse_cut_end; end = scp->mouse_cut_start - 1; } s = start; e = end; /* does the cut-mark region overlap with the update region? */ if (and_region(&s, &e, scp->start, scp->end)) { (*scp->rndr->draw)(scp, s, e - s + 1, TRUE); s = 0; e = start - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); s = end + 1; e = scp->xsize*scp->ysize - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; SC_VIDEO_UNLOCK(scp->sc); return; } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { s = scp->start; e = scp->end; /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if (!and_region(&s, &e, scp->cursor_oldpos, scp->cursor_oldpos)) sc_remove_cursor_image(scp); sc_draw_cursor_image(scp); } else { if (and_region(&s, &e, scp->cursor_pos, scp->cursor_pos)) /* cursor didn't move, but has been overwritten */ sc_draw_cursor_image(scp); else if (scp->curs_attr.flags & CONS_BLINK_CURSOR) /* if it's a blinking cursor, update it */ (*scp->rndr->blink_cursor)(scp, scp->cursor_pos, sc_inside_cutmark(scp, scp->cursor_pos)); } } #ifndef SC_NO_CUTPASTE /* update "pseudo" mouse pointer image */ if (scp->sc->flags & SC_MOUSE_ENABLED) { if (!(scp->status & (MOUSE_VISIBLE | MOUSE_HIDDEN))) { scp->status &= ~MOUSE_MOVED; sc_draw_mouse_image(scp); } } #endif /* SC_NO_CUTPASTE */ scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; SC_VIDEO_UNLOCK(scp->sc); } #ifdef DEV_SPLASH static int scsplash_callback(int event, void *arg) { sc_softc_t *sc; int error; sc = (sc_softc_t *)arg; switch (event) { case SPLASH_INIT: if (add_scrn_saver(scsplash_saver) == 0) { sc->flags &= ~SC_SAVER_FAILED; run_scrn_saver = TRUE; if (cold && !(boothowto & RB_VERBOSE)) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } return 0; case SPLASH_TERM: if (current_saver == scsplash_saver) { scsplash_stick(FALSE); error = remove_scrn_saver(scsplash_saver); if (error) return error; } return 0; default: return EINVAL; } } static void scsplash_saver(sc_softc_t *sc, int show) { static int busy = FALSE; scr_stat *scp; if (busy) return; busy = TRUE; scp = sc->cur_scp; if (show) { if (!(sc->flags & SC_SAVER_FAILED)) { if (!(sc->flags & SC_SCRN_BLANKED)) set_scrn_saver_mode(scp, -1, NULL, 0); switch (splash(sc->adp, TRUE)) { case 0: /* succeeded */ break; case EAGAIN: /* try later */ restore_scrn_saver_mode(scp, FALSE); sc_touch_scrn_saver(); /* XXX */ break; default: sc->flags |= SC_SAVER_FAILED; scsplash_stick(FALSE); restore_scrn_saver_mode(scp, TRUE); printf("scsplash_saver(): failed to put up the image\n"); break; } } } else if (!sticky_splash) { if ((sc->flags & SC_SCRN_BLANKED) && (splash(sc->adp, FALSE) == 0)) restore_scrn_saver_mode(scp, TRUE); } busy = FALSE; } static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { #if 0 int error; if (current_saver != none_saver) { error = remove_scrn_saver(current_saver); if (error) return error; } #endif if (current_saver != none_saver) return EBUSY; run_scrn_saver = FALSE; saver_mode = CONS_LKM_SAVER; current_saver = this_saver; return 0; } static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { if (current_saver != this_saver) return EINVAL; #if 0 /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked) stop_scrn_saver(this_saver); #endif /* unblank all blanked screens */ wait_scrn_saver_stop(NULL); if (scrn_blanked) return EBUSY; current_saver = none_saver; return 0; } static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border) { int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); if (!ISGRAPHSC(scp)) sc_remove_cursor_image(scp); scp->splash_save_mode = scp->mode; scp->splash_save_status = scp->status & (GRAPHICS_MODE | PIXEL_MODE); scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE); scp->status |= (UNKNOWN_MODE | SAVER_RUNNING); scp->sc->flags |= SC_SCRN_BLANKED; ++scrn_blanked; splx(s); if (mode < 0) return 0; scp->mode = mode; if (set_mode(scp) == 0) { if (scp->sc->adp->va_info.vi_flags & V_INFO_GRAPHICS) scp->status |= GRAPHICS_MODE; #ifndef SC_NO_PALETTE_LOADING if (pal != NULL) vidd_load_palette(scp->sc->adp, pal); #endif sc_set_border(scp, border); return 0; } else { s = spltty(); scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; splx(s); return 1; } } static int restore_scrn_saver_mode(scr_stat *scp, int changemode) { int mode; int status; int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); mode = scp->mode; status = scp->status; scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; scp->sc->flags &= ~SC_SCRN_BLANKED; if (!changemode) { if (!ISGRAPHSC(scp)) sc_draw_cursor_image(scp); --scrn_blanked; splx(s); return 0; } if (set_mode(scp) == 0) { #ifndef SC_NO_PALETTE_LOADING #ifdef SC_PIXEL_MODE if (scp->sc->adp->va_info.vi_mem_model == V_INFO_MM_DIRECT) vidd_load_palette(scp->sc->adp, scp->sc->palette2); else #endif vidd_load_palette(scp->sc->adp, scp->sc->palette); #endif --scrn_blanked; splx(s); return 0; } else { scp->mode = mode; scp->status = status; splx(s); return 1; } } static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)) { (*saver)(sc, FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (sc->flags & SC_SCRN_BLANKED) return; mark_all(sc->cur_scp); if (sc->delayed_next_scr) sc_switch_scr(sc, sc->delayed_next_scr - 1); if (!kdb_active) wakeup(&scrn_blanked); } static int wait_scrn_saver_stop(sc_softc_t *sc) { int error = 0; while (scrn_blanked > 0) { run_scrn_saver = FALSE; if (sc && !(sc->flags & SC_SCRN_BLANKED)) { error = 0; break; } error = tsleep(&scrn_blanked, PZERO | PCATCH, "scrsav", 0); if ((error != 0) && (error != ERESTART)) break; } run_scrn_saver = FALSE; return error; } #endif /* DEV_SPLASH */ void sc_touch_scrn_saver(void) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } int sc_switch_scr(sc_softc_t *sc, u_int next_scr) { scr_stat *cur_scp; struct tty *tp; struct proc *p; int s; DPRINTF(5, ("sc0: sc_switch_scr() %d ", next_scr + 1)); if (sc->cur_scp == NULL) return (0); /* prevent switch if previously requested */ if (sc->flags & SC_SCRN_VTYLOCK) { sc_bell(sc->cur_scp, sc->cur_scp->bell_pitch, sc->cur_scp->bell_duration); return EPERM; } /* delay switch if the screen is blanked or being updated */ if ((sc->flags & SC_SCRN_BLANKED) || sc->write_in_progress || sc->blink_in_progress) { sc->delayed_next_scr = next_scr + 1; sc_touch_scrn_saver(); DPRINTF(5, ("switch delayed\n")); return 0; } sc->delayed_next_scr = 0; s = spltty(); cur_scp = sc->cur_scp; /* we are in the middle of the vty switching process... */ if (sc->switch_in_progress && (cur_scp->smode.mode == VT_PROCESS) && cur_scp->proc) { p = pfind(cur_scp->pid); if (cur_scp->proc != p) { if (p) PROC_UNLOCK(p); /* * The controlling process has died!!. Do some clean up. * NOTE:`cur_scp->proc' and `cur_scp->smode.mode' * are not reset here yet; they will be cleared later. */ DPRINTF(5, ("cur_scp controlling process %d died, ", cur_scp->pid)); if (cur_scp->status & SWITCH_WAIT_REL) { /* * Force the previous switch to finish, but return now * with error. */ DPRINTF(5, ("reset WAIT_REL, ")); finish_vt_rel(cur_scp, TRUE, &s); splx(s); DPRINTF(5, ("finishing previous switch\n")); return EINVAL; } else if (cur_scp->status & SWITCH_WAIT_ACQ) { /* let's assume screen switch has been completed. */ DPRINTF(5, ("reset WAIT_ACQ, ")); finish_vt_acq(cur_scp); } else { /* * We are in between screen release and acquisition, and * reached here via scgetc() or scrn_timer() which has * interrupted exchange_scr(). Don't do anything stupid. */ DPRINTF(5, ("waiting nothing, ")); } } else { if (p) PROC_UNLOCK(p); /* * The controlling process is alive, but not responding... * It is either buggy or it may be just taking time. * The following code is a gross kludge to cope with this * problem for which there is no clean solution. XXX */ if (cur_scp->status & SWITCH_WAIT_REL) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending relsig again, ")); signal_vt_rel(cur_scp); break; case 3: break; case 4: default: /* * Act as if the controlling program returned * VT_FALSE. */ DPRINTF(5, ("force reset WAIT_REL, ")); finish_vt_rel(cur_scp, FALSE, &s); splx(s); DPRINTF(5, ("act as if VT_FALSE was seen\n")); return EINVAL; } } else if (cur_scp->status & SWITCH_WAIT_ACQ) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending acqsig again, ")); signal_vt_acq(cur_scp); break; case 3: break; case 4: default: /* clear the flag and finish the previous switch */ DPRINTF(5, ("force reset WAIT_ACQ, ")); finish_vt_acq(cur_scp); break; } } } } /* * Return error if an invalid argument is given, or vty switch * is still in progress. */ if ((next_scr < sc->first_vty) || (next_scr >= sc->first_vty + sc->vtys) || sc->switch_in_progress) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 1\n")); return EINVAL; } /* * Don't allow switching away from the graphics mode vty * if the switch mode is VT_AUTO, unless the next vty is the same * as the current or the current vty has been closed (but showing). */ tp = SC_DEV(sc, cur_scp->index); if ((cur_scp->index != next_scr) && tty_opened_ns(tp) && (cur_scp->smode.mode == VT_AUTO) && ISGRAPHSC(cur_scp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error, graphics mode\n")); return EINVAL; } /* * Is the wanted vty open? Don't allow switching to a closed vty. * If we are in DDB, don't switch to a vty in the VT_PROCESS mode. * Note that we always allow the user to switch to the kernel * console even if it is closed. */ if ((sc_console == NULL) || (next_scr != sc_console->index)) { tp = SC_DEV(sc, next_scr); if (!tty_opened_ns(tp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 2, requested vty isn't open!\n")); return EINVAL; } if (kdb_active && SC_STAT(tp)->smode.mode == VT_PROCESS) { splx(s); DPRINTF(5, ("error 3, requested vty is in the VT_PROCESS mode\n")); return EINVAL; } } /* this is the start of vty switching process... */ ++sc->switch_in_progress; sc->old_scp = cur_scp; sc->new_scp = sc_get_stat(SC_DEV(sc, next_scr)); if (sc->new_scp == sc->old_scp) { sc->switch_in_progress = 0; /* * XXX wakeup() locks the scheduler lock which will hang if * the lock is in an in-between state, e.g., when we stop at * a breakpoint at fork_exit. It has always been wrong to call * wakeup() when the debugger is active. In RELENG_4, wakeup() * is supposed to be locked by splhigh(), but the debugger may * be invoked at splhigh(). */ if (!kdb_active) wakeup(VTY_WCHAN(sc,next_scr)); splx(s); DPRINTF(5, ("switch done (new == old)\n")); return 0; } /* has controlling process died? */ vt_proc_alive(sc->old_scp); vt_proc_alive(sc->new_scp); /* wait for the controlling process to release the screen, if necessary */ if (signal_vt_rel(sc->old_scp)) { splx(s); return 0; } /* go set up the new vty screen */ splx(s); exchange_scr(sc); s = spltty(); /* wake up processes waiting for this vty */ if (!kdb_active) wakeup(VTY_WCHAN(sc,next_scr)); /* wait for the controlling process to acknowledge, if necessary */ if (signal_vt_acq(sc->cur_scp)) { splx(s); return 0; } sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cnavailable(sc_consptr, TRUE); splx(s); DPRINTF(5, ("switch done\n")); return 0; } static int do_switch_scr(sc_softc_t *sc, int s) { vt_proc_alive(sc->new_scp); splx(s); exchange_scr(sc); s = spltty(); /* sc->cur_scp == sc->new_scp */ wakeup(VTY_WCHAN(sc,sc->cur_scp->index)); /* wait for the controlling process to acknowledge, if necessary */ if (!signal_vt_acq(sc->cur_scp)) { sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cnavailable(sc_consptr, TRUE); } return s; } static int vt_proc_alive(scr_stat *scp) { struct proc *p; if (scp->proc) { if ((p = pfind(scp->pid)) != NULL) PROC_UNLOCK(p); if (scp->proc == p) return TRUE; scp->proc = NULL; scp->smode.mode = VT_AUTO; DPRINTF(5, ("vt controlling process %d died\n", scp->pid)); } return FALSE; } static int signal_vt_rel(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; scp->status |= SWITCH_WAIT_REL; PROC_LOCK(scp->proc); kern_psignal(scp->proc, scp->smode.relsig); PROC_UNLOCK(scp->proc); DPRINTF(5, ("sending relsig to %d\n", scp->pid)); return TRUE; } static int signal_vt_acq(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; if (scp->sc->unit == sc_console_unit) cnavailable(sc_consptr, FALSE); scp->status |= SWITCH_WAIT_ACQ; PROC_LOCK(scp->proc); kern_psignal(scp->proc, scp->smode.acqsig); PROC_UNLOCK(scp->proc); DPRINTF(5, ("sending acqsig to %d\n", scp->pid)); return TRUE; } static int finish_vt_rel(scr_stat *scp, int release, int *s) { if (scp == scp->sc->old_scp && scp->status & SWITCH_WAIT_REL) { scp->status &= ~SWITCH_WAIT_REL; if (release) *s = do_switch_scr(scp->sc, *s); else scp->sc->switch_in_progress = 0; return 0; } return EINVAL; } static int finish_vt_acq(scr_stat *scp) { if (scp == scp->sc->new_scp && scp->status & SWITCH_WAIT_ACQ) { scp->status &= ~SWITCH_WAIT_ACQ; scp->sc->switch_in_progress = 0; return 0; } return EINVAL; } static void exchange_scr(sc_softc_t *sc) { scr_stat *scp; /* save the current state of video and keyboard */ sc_move_cursor(sc->old_scp, sc->old_scp->xpos, sc->old_scp->ypos); if (!ISGRAPHSC(sc->old_scp)) sc_remove_cursor_image(sc->old_scp); if (sc->old_scp->kbd_mode == K_XLATE) save_kbd_state(sc->old_scp); /* set up the video for the new screen */ scp = sc->cur_scp = sc->new_scp; if (sc->old_scp->mode != scp->mode || ISUNKNOWNSC(sc->old_scp)) set_mode(scp); #ifndef __sparc64__ else sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)sc->adp->va_window, FALSE); #endif scp->status |= MOUSE_HIDDEN; sc_move_cursor(scp, scp->xpos, scp->ypos); if (!ISGRAPHSC(scp)) sc_set_cursor_image(scp); #ifndef SC_NO_PALETTE_LOADING if (ISGRAPHSC(sc->old_scp)) { #ifdef SC_PIXEL_MODE if (sc->adp->va_info.vi_mem_model == V_INFO_MM_DIRECT) vidd_load_palette(sc->adp, sc->palette2); else #endif vidd_load_palette(sc->adp, sc->palette); } #endif sc_set_border(scp, scp->border); /* set up the keyboard for the new screen */ if (sc->kbd_open_level == 0 && sc->old_scp->kbd_mode != scp->kbd_mode) (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); mark_all(scp); } static void sc_puts(scr_stat *scp, u_char *buf, int len) { #ifdef DEV_SPLASH /* make screensaver happy */ if (!sticky_splash && scp == scp->sc->cur_scp && !sc_saver_keyb_only) run_scrn_saver = FALSE; #endif if (scp->tsw) (*scp->tsw->te_puts)(scp, buf, len); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } void sc_draw_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_cursor)(scp, scp->cursor_pos, scp->curs_attr.flags & CONS_BLINK_CURSOR, TRUE, sc_inside_cutmark(scp, scp->cursor_pos)); scp->cursor_oldpos = scp->cursor_pos; SC_VIDEO_UNLOCK(scp->sc); } void sc_remove_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_cursor)(scp, scp->cursor_oldpos, scp->curs_attr.flags & CONS_BLINK_CURSOR, FALSE, sc_inside_cutmark(scp, scp->cursor_oldpos)); SC_VIDEO_UNLOCK(scp->sc); } static void update_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ sc_remove_cursor_image(scp); sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } void sc_set_cursor_image(scr_stat *scp) { scp->curs_attr = scp->base_curs_attr; if (scp->curs_attr.flags & CONS_HIDDEN_CURSOR) { /* hidden cursor is internally represented as zero-height underline */ scp->curs_attr.flags = CONS_CHAR_CURSOR; scp->curs_attr.base = scp->curs_attr.height = 0; } else if (scp->curs_attr.flags & CONS_CHAR_CURSOR) { scp->curs_attr.base = imin(scp->base_curs_attr.base, scp->font_size - 1); scp->curs_attr.height = imin(scp->base_curs_attr.height, scp->font_size - scp->curs_attr.base); } else { /* block cursor */ scp->curs_attr.base = 0; scp->curs_attr.height = scp->font_size; } /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->set_cursor)(scp, scp->curs_attr.base, scp->curs_attr.height, scp->curs_attr.flags & CONS_BLINK_CURSOR); SC_VIDEO_UNLOCK(scp->sc); } static void sc_adjust_ca(struct cursor_attr *cap, int flags, int base, int height) { - if (0) { - /* Dummy clause to avoid changing indentation later. */ + if (flags & CONS_CHARCURSOR_COLORS) { + cap->bg[0] = base & 0xff; + cap->bg[1] = height & 0xff; + } else if (flags & CONS_MOUSECURSOR_COLORS) { + cap->mouse_ba = base & 0xff; + cap->mouse_ia = height & 0xff; } else { if (base >= 0) cap->base = base; if (height >= 0) cap->height = height; if (!(flags & CONS_SHAPEONLY_CURSOR)) cap->flags = flags & CONS_CURSOR_ATTRS; } } static void change_cursor_shape(scr_stat *scp, int flags, int base, int height) { if ((scp == scp->sc->cur_scp) && !ISGRAPHSC(scp)) sc_remove_cursor_image(scp); if (flags & CONS_RESET_CURSOR) scp->base_curs_attr = scp->dflt_curs_attr; else if (flags & CONS_DEFAULT_CURSOR) { sc_adjust_ca(&scp->dflt_curs_attr, flags, base, height); scp->base_curs_attr = scp->dflt_curs_attr; } else sc_adjust_ca(&scp->base_curs_attr, flags, base, height); if ((scp == scp->sc->cur_scp) && !ISGRAPHSC(scp)) { sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } } void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) { sc_softc_t *sc; struct tty *tp; int s; int i; if (flags == -1) flags = CONS_SHAPEONLY_CURSOR; s = spltty(); if (flags & CONS_LOCAL_CURSOR) { /* local (per vty) change */ change_cursor_shape(scp, flags, base, height); splx(s); return; } /* global change */ sc = scp->sc; if (flags & CONS_RESET_CURSOR) sc->curs_attr = sc->dflt_curs_attr; else if (flags & CONS_DEFAULT_CURSOR) { sc_adjust_ca(&sc->dflt_curs_attr, flags, base, height); sc->curs_attr = sc->dflt_curs_attr; } else sc_adjust_ca(&sc->curs_attr, flags, base, height); for (i = sc->first_vty; i < sc->first_vty + sc->vtys; ++i) { if ((tp = SC_DEV(sc, i)) == NULL) continue; if ((scp = sc_get_stat(tp)) == NULL) continue; scp->dflt_curs_attr = sc->curs_attr; change_cursor_shape(scp, CONS_RESET_CURSOR, -1, -1); } splx(s); } static void scinit(int unit, int flags) { /* * When syscons is being initialized as the kernel console, malloc() * is not yet functional, because various kernel structures has not been * fully initialized yet. Therefore, we need to declare the following * static buffers for the console. This is less than ideal, * but is necessry evil for the time being. XXX */ static u_short sc_buffer[ROW*COL]; /* XXX */ #ifndef SC_NO_FONT_LOADING static u_char font_8[256*8]; static u_char font_14[256*14]; static u_char font_16[256*16]; #endif sc_softc_t *sc; scr_stat *scp; video_adapter_t *adp; int col; int row; int i; /* one time initialization */ if (init_done == COLD) { sc_get_bios_values(&bios_value); for (i = 0; i < nitems(sc_kattrtab); i++) { #if SC_KERNEL_CONS_ATTR == FG_WHITE sc_kattrtab[i] = 8 + (i + FG_WHITE) % 8U; #else sc_kattrtab[i] = SC_KERNEL_CONS_ATTR; #endif } } init_done = WARM; /* * Allocate resources. Even if we are being called for the second * time, we must allocate them again, because they might have * disappeared... */ sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); if ((sc->flags & SC_INIT_DONE) == 0) SC_VIDEO_LOCKINIT(sc); adp = NULL; if (sc->adapter >= 0) { vid_release(sc->adp, (void *)&sc->adapter); adp = sc->adp; sc->adp = NULL; } if (sc->keyboard >= 0) { DPRINTF(5, ("sc%d: releasing kbd%d\n", unit, sc->keyboard)); i = kbd_release(sc->kbd, (void *)&sc->keyboard); DPRINTF(5, ("sc%d: kbd_release returned %d\n", unit, i)); if (sc->kbd != NULL) { DPRINTF(5, ("sc%d: kbd != NULL!, index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } sc->kbd = NULL; } sc->adapter = vid_allocate("*", unit, (void *)&sc->adapter); sc->adp = vid_get_adapter(sc->adapter); /* assert((sc->adapter >= 0) && (sc->adp != NULL)) */ sc->keyboard = sc_allocate_keyboard(sc, unit); DPRINTF(1, ("sc%d: keyboard %d\n", unit, sc->keyboard)); sc->kbd = kbd_get_keyboard(sc->keyboard); if (sc->kbd != NULL) { DPRINTF(1, ("sc%d: kbd index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } if (!(sc->flags & SC_INIT_DONE) || (adp != sc->adp)) { sc->initial_mode = sc->adp->va_initial_mode; #ifndef SC_NO_FONT_LOADING if (flags & SC_KERNEL_CONSOLE) { sc->font_8 = font_8; sc->font_14 = font_14; sc->font_16 = font_16; } else if (sc->font_8 == NULL) { /* assert(sc_malloc) */ sc->font_8 = malloc(sizeof(font_8), M_DEVBUF, M_WAITOK); sc->font_14 = malloc(sizeof(font_14), M_DEVBUF, M_WAITOK); sc->font_16 = malloc(sizeof(font_16), M_DEVBUF, M_WAITOK); } #endif /* extract the hardware cursor location and hide the cursor for now */ vidd_read_hw_cursor(sc->adp, &col, &row); vidd_set_hw_cursor(sc->adp, -1, -1); /* set up the first console */ sc->first_vty = unit*MAXCONS; sc->vtys = MAXCONS; /* XXX: should be configurable */ if (flags & SC_KERNEL_CONSOLE) { /* * Set up devs structure but don't use it yet, calling make_dev() * might panic kernel. Wait for sc_attach_unit() to actually * create the devices. */ sc->dev = main_devs; scp = &main_console; init_scp(sc, sc->first_vty, scp); sc_vtb_init(&scp->vtb, VTB_MEMORY, scp->xsize, scp->ysize, (void *)sc_buffer, FALSE); if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); (*scp->tsw->te_default_attr)(scp, SC_KERNEL_CONS_ATTR, SC_KERNEL_CONS_REV_ATTR); } else { /* assert(sc_malloc) */ sc->dev = malloc(sizeof(struct tty *)*sc->vtys, M_DEVBUF, M_WAITOK|M_ZERO); sc->dev[0] = sc_alloc_tty(0, unit * MAXCONS); scp = alloc_scp(sc, sc->first_vty); SC_STAT(sc->dev[0]) = scp; } sc->cur_scp = scp; #ifndef __sparc64__ /* copy screen to temporary buffer */ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); if (ISTEXTSC(scp)) sc_vtb_copy(&scp->scr, 0, &scp->vtb, 0, scp->xsize*scp->ysize); #endif /* Sync h/w cursor position to s/w (sc and teken). */ if (col >= scp->xsize) col = 0; if (row >= scp->ysize) row = scp->ysize - 1; scp->xpos = col; scp->ypos = row; scp->cursor_pos = scp->cursor_oldpos = row*scp->xsize + col; (*scp->tsw->te_sync)(scp); sc->dflt_curs_attr.base = 0; sc->dflt_curs_attr.height = howmany(scp->font_size, 8); sc->dflt_curs_attr.flags = 0; + sc->dflt_curs_attr.bg[0] = FG_RED; + sc->dflt_curs_attr.bg[1] = FG_LIGHTGREY; + sc->dflt_curs_attr.bg[2] = FG_BLUE; + sc->dflt_curs_attr.mouse_ba = FG_WHITE; + sc->dflt_curs_attr.mouse_ia = FG_RED; sc->curs_attr = sc->dflt_curs_attr; scp->base_curs_attr = scp->dflt_curs_attr = sc->curs_attr; + scp->curs_attr = scp->base_curs_attr; #ifndef SC_NO_SYSMOUSE sc_mouse_move(scp, scp->xpixel/2, scp->ypixel/2); #endif if (!ISGRAPHSC(scp)) { sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } /* save font and palette */ #ifndef SC_NO_FONT_LOADING sc->fonts_loaded = 0; if (ISFONTAVAIL(sc->adp->va_flags)) { #ifdef SC_DFLT_FONT bcopy(dflt_font_8, sc->font_8, sizeof(dflt_font_8)); bcopy(dflt_font_14, sc->font_14, sizeof(dflt_font_14)); bcopy(dflt_font_16, sc->font_16, sizeof(dflt_font_16)); sc->fonts_loaded = FONT_16 | FONT_14 | FONT_8; if (scp->font_size < 14) { sc_load_font(scp, 0, 8, 8, sc->font_8, 0, 256); } else if (scp->font_size >= 16) { sc_load_font(scp, 0, 16, 8, sc->font_16, 0, 256); } else { sc_load_font(scp, 0, 14, 8, sc->font_14, 0, 256); } #else /* !SC_DFLT_FONT */ if (scp->font_size < 14) { sc_save_font(scp, 0, 8, 8, sc->font_8, 0, 256); sc->fonts_loaded = FONT_8; } else if (scp->font_size >= 16) { sc_save_font(scp, 0, 16, 8, sc->font_16, 0, 256); sc->fonts_loaded = FONT_16; } else { sc_save_font(scp, 0, 14, 8, sc->font_14, 0, 256); sc->fonts_loaded = FONT_14; } #endif /* SC_DFLT_FONT */ /* FONT KLUDGE: always use the font page #0. XXX */ sc_show_font(scp, 0); } #endif /* !SC_NO_FONT_LOADING */ #ifndef SC_NO_PALETTE_LOADING vidd_save_palette(sc->adp, sc->palette); #ifdef SC_PIXEL_MODE for (i = 0; i < sizeof(sc->palette2); i++) sc->palette2[i] = i / 3; #endif #endif #ifdef DEV_SPLASH if (!(sc->flags & SC_SPLASH_SCRN)) { /* we are ready to put up the splash image! */ splash_init(sc->adp, scsplash_callback, sc); sc->flags |= SC_SPLASH_SCRN; } #endif } /* the rest is not necessary, if we have done it once */ if (sc->flags & SC_INIT_DONE) return; /* initialize mapscrn arrays to a one to one map */ for (i = 0; i < sizeof(sc->scr_map); i++) sc->scr_map[i] = sc->scr_rmap[i] = i; sc->flags |= SC_INIT_DONE; } static void scterm(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); if (sc == NULL) return; /* shouldn't happen */ #ifdef DEV_SPLASH /* this console is no longer available for the splash screen */ if (sc->flags & SC_SPLASH_SCRN) { splash_term(sc->adp); sc->flags &= ~SC_SPLASH_SCRN; } #endif #if 0 /* XXX */ /* move the hardware cursor to the upper-left corner */ vidd_set_hw_cursor(sc->adp, 0, 0); #endif /* release the keyboard and the video card */ if (sc->keyboard >= 0) kbd_release(sc->kbd, &sc->keyboard); if (sc->adapter >= 0) vid_release(sc->adp, &sc->adapter); /* stop the terminal emulator, if any */ scp = sc_get_stat(sc->dev[0]); if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); mtx_destroy(&sc->video_mtx); /* clear the structure */ if (!(flags & SC_KERNEL_CONSOLE)) { free(scp->ts, M_DEVBUF); /* XXX: We need delete_dev() for this */ free(sc->dev, M_DEVBUF); #if 0 /* XXX: We need a ttyunregister for this */ free(sc->tty, M_DEVBUF); #endif #ifndef SC_NO_FONT_LOADING free(sc->font_8, M_DEVBUF); free(sc->font_14, M_DEVBUF); free(sc->font_16, M_DEVBUF); #endif /* XXX vtb, history */ } bzero(sc, sizeof(*sc)); sc->keyboard = -1; sc->adapter = -1; } static void scshutdown(__unused void *arg, __unused int howto) { KASSERT(sc_console != NULL, ("sc_console != NULL")); KASSERT(sc_console->sc != NULL, ("sc_console->sc != NULL")); KASSERT(sc_console->sc->cur_scp != NULL, ("sc_console->sc->cur_scp != NULL")); sc_touch_scrn_saver(); if (!cold && sc_console->sc->cur_scp->index != sc_console->index && sc_console->sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc_console->sc, sc_console->index); shutdown_in_progress = TRUE; } static void scsuspend(__unused void *arg) { int retry; KASSERT(sc_console != NULL, ("sc_console != NULL")); KASSERT(sc_console->sc != NULL, ("sc_console->sc != NULL")); KASSERT(sc_console->sc->cur_scp != NULL, ("sc_console->sc->cur_scp != NULL")); sc_susp_scr = sc_console->sc->cur_scp->index; if (sc_no_suspend_vtswitch || sc_susp_scr == sc_console->index) { sc_touch_scrn_saver(); sc_susp_scr = -1; return; } for (retry = 0; retry < 10; retry++) { sc_switch_scr(sc_console->sc, sc_console->index); if (!sc_console->sc->switch_in_progress) break; pause("scsuspend", hz); } suspend_in_progress = TRUE; } static void scresume(__unused void *arg) { KASSERT(sc_console != NULL, ("sc_console != NULL")); KASSERT(sc_console->sc != NULL, ("sc_console->sc != NULL")); KASSERT(sc_console->sc->cur_scp != NULL, ("sc_console->sc->cur_scp != NULL")); suspend_in_progress = FALSE; if (sc_susp_scr < 0) { update_font(sc_console->sc->cur_scp); return; } sc_switch_scr(sc_console->sc, sc_susp_scr); } int sc_clean_up(scr_stat *scp) { #ifdef DEV_SPLASH int error; #endif if (scp->sc->flags & SC_SCRN_BLANKED) { sc_touch_scrn_saver(); #ifdef DEV_SPLASH if ((error = wait_scrn_saver_stop(scp->sc))) return error; #endif } scp->status |= MOUSE_HIDDEN; sc_remove_mouse_image(scp); sc_remove_cutmarking(scp); return 0; } void sc_alloc_scr_buffer(scr_stat *scp, int wait, int discard) { sc_vtb_t new; sc_vtb_t old; old = scp->vtb; sc_vtb_init(&new, VTB_MEMORY, scp->xsize, scp->ysize, NULL, wait); if (!discard && (old.vtb_flags & VTB_VALID)) { /* retain the current cursor position and buffer contants */ scp->cursor_oldpos = scp->cursor_pos; /* * This works only if the old buffer has the same size as or larger * than the new one. XXX */ sc_vtb_copy(&old, 0, &new, 0, scp->xsize*scp->ysize); scp->vtb = new; } else { scp->vtb = new; sc_vtb_destroy(&old); } #ifndef SC_NO_SYSMOUSE /* move the mouse cursor at the center of the screen */ sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); #endif } static scr_stat *alloc_scp(sc_softc_t *sc, int vty) { scr_stat *scp; /* assert(sc_malloc) */ scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(sc, vty, scp); sc_alloc_scr_buffer(scp, TRUE, TRUE); if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, TRUE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, 0, TRUE); #endif return scp; } static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp) { video_info_t info; bzero(scp, sizeof(*scp)); scp->index = vty; scp->sc = sc; scp->status = 0; scp->mode = sc->initial_mode; vidd_get_info(sc->adp, scp->mode, &info); if (info.vi_flags & V_INFO_GRAPHICS) { scp->status |= GRAPHICS_MODE; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->xsize = info.vi_width/info.vi_cwidth; scp->ysize = info.vi_height/info.vi_cheight; scp->font_size = 0; scp->font = NULL; } else { scp->xsize = info.vi_width; scp->ysize = info.vi_height; scp->xpixel = scp->xsize*info.vi_cwidth; scp->ypixel = scp->ysize*info.vi_cheight; } scp->font_size = info.vi_cheight; scp->font_width = info.vi_cwidth; #ifndef SC_NO_FONT_LOADING if (info.vi_cheight < 14) scp->font = sc->font_8; else if (info.vi_cheight >= 16) scp->font = sc->font_16; else scp->font = sc->font_14; #else scp->font = NULL; #endif sc_vtb_init(&scp->vtb, VTB_MEMORY, 0, 0, NULL, FALSE); #ifndef __sparc64__ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, 0, 0, NULL, FALSE); #endif scp->xoff = scp->yoff = 0; scp->xpos = scp->ypos = 0; scp->start = scp->xsize * scp->ysize - 1; scp->end = 0; scp->tsw = NULL; scp->ts = NULL; scp->rndr = NULL; scp->border = (SC_NORM_ATTR >> 4) & 0x0f; scp->base_curs_attr = scp->dflt_curs_attr = sc->curs_attr; scp->mouse_cut_start = scp->xsize*scp->ysize; scp->mouse_cut_end = -1; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->kbd_mode = K_XLATE; scp->bell_pitch = bios_value.bell_pitch; scp->bell_duration = BELL_DURATION; scp->status |= (bios_value.shift_state & NLKED); scp->status |= CURSOR_ENABLED | MOUSE_HIDDEN; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history = NULL; scp->history_pos = 0; scp->history_size = 0; } int sc_init_emulator(scr_stat *scp, char *name) { sc_term_sw_t *sw; sc_rndr_sw_t *rndr; void *p; int error; if (name == NULL) /* if no name is given, use the current emulator */ sw = scp->tsw; else /* ...otherwise find the named emulator */ sw = sc_term_match(name); if (sw == NULL) return EINVAL; rndr = NULL; if (strcmp(sw->te_renderer, "*") != 0) { rndr = sc_render_match(scp, sw->te_renderer, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); } if (rndr == NULL) { rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); if (rndr == NULL) return ENODEV; } if (sw == scp->tsw) { error = (*sw->te_init)(scp, &scp->ts, SC_TE_WARM_INIT); scp->rndr = rndr; scp->rndr->init(scp); sc_clear_screen(scp); /* assert(error == 0); */ return error; } if (sc_malloc && (sw->te_size > 0)) p = malloc(sw->te_size, M_DEVBUF, M_NOWAIT); else p = NULL; error = (*sw->te_init)(scp, &p, SC_TE_COLD_INIT); if (error) return error; if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); if (scp->ts != NULL) free(scp->ts, M_DEVBUF); scp->tsw = sw; scp->ts = p; scp->rndr = rndr; scp->rndr->init(scp); (*sw->te_default_attr)(scp, SC_NORM_ATTR, SC_NORM_REV_ATTR); sc_clear_screen(scp); return 0; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(sc_softc_t *sc, u_int flags, struct sc_cnstate *sp) { scr_stat *scp; #ifndef SC_NO_HISTORY struct tty *tp; #endif u_int c; int this_scr; int f; int i; if (sc->kbd == NULL) return NOKEY; next_code: #if 1 /* I don't like this, but... XXX */ if (flags & SCGETC_CN) sccnupdate(sc->cur_scp); #endif scp = sc->cur_scp; /* first see if there is something in the keyboard port */ for (;;) { if (flags & SCGETC_CN) sccnscrunlock(sc, sp); c = kbdd_read_char(sc->kbd, !(flags & SCGETC_NONBLOCK)); if (flags & SCGETC_CN) sccnscrlock(sc, sp); if (c == ERRKEY) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); } else if (c == NOKEY) return c; else break; } /* make screensaver happy */ if (!(c & RELKEY)) sc_touch_scrn_saver(); if (!(flags & SCGETC_CN)) random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD); if (sc->kbd_open_level == 0 && scp->kbd_mode != K_XLATE) return KEYCHAR(c); /* if scroll-lock pressed allow history browsing */ if (!ISGRAPHSC(scp) && scp->history && scp->status & SLKED) { scp->status &= ~CURSOR_ENABLED; sc_remove_cursor_image(scp); #ifndef SC_NO_HISTORY if (!(scp->status & BUFFER_SAVED)) { scp->status |= BUFFER_SAVED; sc_hist_save(scp); } switch (c) { /* FIXME: key codes */ case SPCLKEY | FKEY | F(49): /* home key */ sc_remove_cutmarking(scp); sc_hist_home(scp); goto next_code; case SPCLKEY | FKEY | F(57): /* end key */ sc_remove_cutmarking(scp); sc_hist_end(scp); goto next_code; case SPCLKEY | FKEY | F(50): /* up arrow key */ sc_remove_cutmarking(scp); if (sc_hist_up_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(58): /* down arrow key */ sc_remove_cutmarking(scp); if (sc_hist_down_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(51): /* page up key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_up_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; case SPCLKEY | FKEY | F(59): /* page down key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_down_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; } #endif /* SC_NO_HISTORY */ } /* * Process and consume special keys here. Return a plain char code * or a char code with the META flag or a function key code. */ if (c & RELKEY) { /* key released */ /* goto next_code */ } else { /* key pressed */ if (c & SPCLKEY) { c &= ~SPCLKEY; switch (KEYCHAR(c)) { /* LOCKING KEYS */ case NLK: case CLK: case ALK: break; case SLK: (void)kbdd_ioctl(sc->kbd, KDGKBSTATE, (caddr_t)&f); if (f & SLKED) { scp->status |= SLKED; } else { if (scp->status & SLKED) { scp->status &= ~SLKED; #ifndef SC_NO_HISTORY if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } /* Only safe in Giant-locked context. */ tp = SC_DEV(sc, scp->index); if (!(flags & SCGETC_CN) && tty_opened_ns(tp)) sctty_outwakeup(tp); #endif } } break; case PASTE: #ifndef SC_NO_CUTPASTE sc_mouse_paste(scp); #endif break; /* NON-LOCKING KEYS */ case NOP: case LSH: case RSH: case LCTR: case RCTR: case LALT: case RALT: case ASH: case META: break; case BTAB: if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; case SPSC: #ifdef DEV_SPLASH /* force activatation/deactivation of the screen saver */ if (!(sc->flags & SC_SCRN_BLANKED)) { run_scrn_saver = TRUE; sc->scrn_time_stamp -= scrn_blank_time; } if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explicitly. XXX */ if (sc->flags & SC_SCRN_BLANKED) { scsplash_stick(FALSE); stop_scrn_saver(sc, current_saver); } else { if (!ISGRAPHSC(scp)) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } } #endif /* DEV_SPLASH */ break; case RBT: #ifndef SC_DISABLE_REBOOT if (enable_reboot && !(flags & SCGETC_CN)) shutdown_nice(0); #endif break; case HALT: #ifndef SC_DISABLE_REBOOT if (enable_reboot && !(flags & SCGETC_CN)) shutdown_nice(RB_HALT); #endif break; case PDWN: #ifndef SC_DISABLE_REBOOT if (enable_reboot && !(flags & SCGETC_CN)) shutdown_nice(RB_HALT|RB_POWEROFF); #endif break; case SUSP: power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); break; case STBY: power_pm_suspend(POWER_SLEEP_STATE_STANDBY); break; case DBG: #ifndef SC_DISABLE_KDBKEY if (enable_kdbkey) kdb_break(); #endif break; case PNC: if (enable_panic_key) panic("Forced by the panic key"); break; case NEXT: this_scr = scp->index; for (i = (this_scr - sc->first_vty + 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + 1)%sc->vtys) { struct tty *tp = SC_DEV(sc, sc->first_vty + i); if (tty_opened_ns(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; case PREV: this_scr = scp->index; for (i = (this_scr - sc->first_vty + sc->vtys - 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + sc->vtys - 1)%sc->vtys) { struct tty *tp = SC_DEV(sc, sc->first_vty + i); if (tty_opened_ns(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; default: if (KEYCHAR(c) >= F_SCR && KEYCHAR(c) <= L_SCR) { sc_switch_scr(scp->sc, sc->first_vty + KEYCHAR(c) - F_SCR); break; } /* assert(c & FKEY) */ if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; } /* goto next_code */ } else { /* regular keys (maybe MKEY is set) */ #if !defined(SC_DISABLE_KDBKEY) && defined(KDB) if (enable_kdbkey) kdb_alt_break(c, &sc->sc_altbrk); #endif if (!(sc->flags & SC_SCRN_BLANKED)) return c; } } goto next_code; } static int sctty_mmap(struct tty *tp, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { scr_stat *scp; scp = sc_get_stat(tp); if (scp != scp->sc->cur_scp) return -1; return vidd_mmap(scp->sc->adp, offset, paddr, nprot, memattr); } static void update_font(scr_stat *scp) { #ifndef SC_NO_FONT_LOADING /* load appropriate font */ if (!(scp->status & GRAPHICS_MODE)) { if (!(scp->status & PIXEL_MODE) && ISFONTAVAIL(scp->sc->adp->va_flags)) { if (scp->font_size < 14) { if (scp->sc->fonts_loaded & FONT_8) sc_load_font(scp, 0, 8, 8, scp->sc->font_8, 0, 256); } else if (scp->font_size >= 16) { if (scp->sc->fonts_loaded & FONT_16) sc_load_font(scp, 0, 16, 8, scp->sc->font_16, 0, 256); } else { if (scp->sc->fonts_loaded & FONT_14) sc_load_font(scp, 0, 14, 8, scp->sc->font_14, 0, 256); } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ sc_show_font(scp, 0); } mark_all(scp); } #endif /* !SC_NO_FONT_LOADING */ } static int save_kbd_state(scr_stat *scp) { int state; int error; error = kbdd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error == 0) { scp->status &= ~LOCK_MASK; scp->status |= state; } return error; } static int update_kbd_state(scr_stat *scp, int new_bits, int mask) { int state; int error; if (mask != LOCK_MASK) { error = kbdd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error) return error; state &= ~mask; state |= new_bits & mask; } else { state = new_bits & LOCK_MASK; } error = kbdd_ioctl(scp->sc->kbd, KDSKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; return error; } static int update_kbd_leds(scr_stat *scp, int which) { int error; which &= LOCK_MASK; error = kbdd_ioctl(scp->sc->kbd, KDSETLED, (caddr_t)&which); if (error == ENOIOCTL) error = ENODEV; return error; } int set_mode(scr_stat *scp) { video_info_t info; /* reject unsupported mode */ if (vidd_get_info(scp->sc->adp, scp->mode, &info)) return 1; /* if this vty is not currently showing, do nothing */ if (scp != scp->sc->cur_scp) return 0; /* setup video hardware for the given mode */ vidd_set_mode(scp->sc->adp, scp->mode); scp->rndr->init(scp); #ifndef __sparc64__ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); #endif update_font(scp); sc_set_border(scp, scp->border); sc_set_cursor_image(scp); return 0; } void sc_set_border(scr_stat *scp, int color) { SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_border)(scp, color); SC_VIDEO_UNLOCK(scp->sc); } #ifndef SC_NO_FONT_LOADING void sc_load_font(scr_stat *scp, int page, int size, int width, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; vidd_load_font(sc->adp, page, size, width, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_save_font(scr_stat *scp, int page, int size, int width, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; vidd_save_font(sc->adp, page, size, width, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_show_font(scr_stat *scp, int page) { vidd_show_font(scp->sc->adp, page); } #endif /* !SC_NO_FONT_LOADING */ void sc_paste(scr_stat *scp, const u_char *p, int count) { struct tty *tp; u_char *rmap; tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened_ns(tp)) return; rmap = scp->sc->scr_rmap; for (; count > 0; --count) ttydisc_rint(tp, rmap[*p++], 0); ttydisc_rint_done(tp); } void sc_respond(scr_stat *scp, const u_char *p, int count, int wakeup) { struct tty *tp; tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened_ns(tp)) return; ttydisc_rint_simple(tp, p, count); if (wakeup) { /* XXX: we can't always call ttydisc_rint_done() here! */ ttydisc_rint_done(tp); } } void sc_bell(scr_stat *scp, int pitch, int duration) { if (cold || kdb_active || shutdown_in_progress || !enable_bell) return; if (scp != scp->sc->cur_scp && (scp->sc->flags & SC_QUIET_BELL)) return; if (scp->sc->flags & SC_VISUAL_BELL) { if (scp->sc->blink_in_progress) return; scp->sc->blink_in_progress = 3; if (scp != scp->sc->cur_scp) scp->sc->blink_in_progress += 2; blink_screen(scp->sc->cur_scp); } else if (duration != 0 && pitch != 0) { if (scp != scp->sc->cur_scp) pitch *= 2; sysbeep(1193182 / pitch, duration); } } static int sc_kattr(void) { if (sc_console == NULL) return (SC_KERNEL_CONS_ATTR); return (sc_kattrtab[PCPU_GET(cpuid) % nitems(sc_kattrtab)]); } static void blink_screen(void *arg) { scr_stat *scp = arg; struct tty *tp; if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) { scp->sc->blink_in_progress = 0; mark_all(scp); tp = SC_DEV(scp->sc, scp->index); if (tty_opened_ns(tp)) sctty_outwakeup(tp); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } else { (*scp->rndr->draw)(scp, 0, scp->xsize*scp->ysize, scp->sc->blink_in_progress & 1); scp->sc->blink_in_progress--; callout_reset_sbt(&scp->sc->cblink, SBT_1S / 15, 0, blink_screen, scp, C_PREL(0)); } } /* * Until sc_attach_unit() gets called no dev structures will be available * to store the per-screen current status. This is the case when the * kernel is initially booting and needs access to its console. During * this early phase of booting the console's current status is kept in * one statically defined scr_stat structure, and any pointers to the * dev structures will be NULL. */ static scr_stat * sc_get_stat(struct tty *tp) { if (tp == NULL) return (&main_console); return (SC_STAT(tp)); } /* * Allocate active keyboard. Try to allocate "kbdmux" keyboard first, and, * if found, add all non-busy keyboards to "kbdmux". Otherwise look for * any keyboard. */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit) { int idx0, idx; keyboard_t *k0, *k; keyboard_info_t ki; idx0 = kbd_allocate("kbdmux", -1, (void *)&sc->keyboard, sckbdevent, sc); if (idx0 != -1) { k0 = kbd_get_keyboard(idx0); for (idx = kbd_find_keyboard2("*", -1, 0); idx != -1; idx = kbd_find_keyboard2("*", -1, idx + 1)) { k = kbd_get_keyboard(idx); if (idx == idx0 || KBD_IS_BUSY(k)) continue; bzero(&ki, sizeof(ki)); strcpy(ki.kb_name, k->kb_name); ki.kb_unit = k->kb_unit; (void)kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki); } } else idx0 = kbd_allocate("*", unit, (void *)&sc->keyboard, sckbdevent, sc); return (idx0); } Index: head/sys/dev/syscons/syscons.h =================================================================== --- head/sys/dev/syscons/syscons.h (revision 322877) +++ head/sys/dev/syscons/syscons.h (revision 322878) @@ -1,688 +1,691 @@ /*- * Copyright (c) 1995-1998 Søren Schmidt * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_SYSCONS_SYSCONS_H_ #define _DEV_SYSCONS_SYSCONS_H_ #include /* XXX */ #include #include /* default values for configuration options */ #ifndef MAXCONS #define MAXCONS 16 #endif #ifdef SC_NO_SYSMOUSE #undef SC_NO_CUTPASTE #define SC_NO_CUTPASTE 1 #endif #ifdef SC_NO_MODE_CHANGE #undef SC_PIXEL_MODE #endif /* Always load font data if the pixel (raster text) mode is to be used. */ #ifdef SC_PIXEL_MODE #undef SC_NO_FONT_LOADING #endif /* * If font data is not available, the `arrow'-shaped mouse cursor cannot * be drawn. Use the alternative drawing method. */ #ifdef SC_NO_FONT_LOADING #undef SC_ALT_MOUSE_IMAGE #define SC_ALT_MOUSE_IMAGE 1 #endif #ifndef SC_CURSOR_CHAR #define SC_CURSOR_CHAR 7 #endif #ifndef SC_MOUSE_CHAR #define SC_MOUSE_CHAR 8 #endif #if SC_MOUSE_CHAR <= SC_CURSOR_CHAR && SC_CURSOR_CHAR < (SC_MOUSE_CHAR + 4) #undef SC_CURSOR_CHAR #define SC_CURSOR_CHAR (SC_MOUSE_CHAR + 4) #endif #ifndef SC_DEBUG_LEVEL #define SC_DEBUG_LEVEL 0 #endif #define DPRINTF(l, p) if (SC_DEBUG_LEVEL >= (l)) printf p #ifndef __sparc64__ #define SC_DRIVER_NAME "sc" #else /* * Use a different driver name on sparc64 so it does not get confused * with the system controller devices which are also termed 'sc' in OFW. */ #define SC_DRIVER_NAME "syscons" #endif #define SC_VTY(dev) (((sc_ttysoftc *)tty_softc(tp))->st_index) #define SC_DEV(sc, vty) ((sc)->dev[(vty) - (sc)->first_vty]) #define SC_STAT(tp) (*((scr_stat **)&((sc_ttysoftc *)tty_softc(tp))->st_stat)) /* printable chars */ #ifndef PRINTABLE #define PRINTABLE(ch) ((ch) > 0x1b || ((ch) > 0x0d && (ch) < 0x1b) \ || (ch) < 0x07) #endif /* macros for "intelligent" screen update */ #define mark_for_update(scp, x) {\ if ((x) < scp->start) scp->start = (x);\ else if ((x) > scp->end) scp->end = (x);\ } #define mark_all(scp) {\ scp->start = 0;\ scp->end = scp->xsize * scp->ysize - 1;\ } /* vty status flags (scp->status) */ #define UNKNOWN_MODE 0x00010 /* unknown video mode */ #define SWITCH_WAIT_REL 0x00080 /* waiting for vty release */ #define SWITCH_WAIT_ACQ 0x00100 /* waiting for vty ack */ #define BUFFER_SAVED 0x00200 /* vty buffer is saved */ #define CURSOR_ENABLED 0x00400 /* text cursor is enabled */ #define MOUSE_MOVED 0x01000 /* mouse cursor has moved */ #define MOUSE_CUTTING 0x02000 /* mouse cursor is cutting text */ #define MOUSE_VISIBLE 0x04000 /* mouse cursor is showing */ #define GRAPHICS_MODE 0x08000 /* vty is in a graphics mode */ #define PIXEL_MODE 0x10000 /* vty is in a raster text mode */ #define SAVER_RUNNING 0x20000 /* screen saver is running */ #define VR_CURSOR_BLINK 0x40000 /* blinking text cursor */ #define VR_CURSOR_ON 0x80000 /* text cursor is on */ #define MOUSE_HIDDEN 0x100000 /* mouse cursor is temporarily hidden */ /* misc defines */ #define FALSE 0 #define TRUE 1 /* The following #defines are hard-coded for a maximum text resolution corresponding to a maximum framebuffer resolution of 1920x1200 with an 8x8 font... */ #define COL 240 #define ROW 150 #define PCBURST 128 #ifndef BELL_DURATION #define BELL_DURATION ((5 * hz + 99) / 100) #define BELL_PITCH 800 #endif /* virtual terminal buffer */ typedef struct sc_vtb { int vtb_flags; #define VTB_VALID (1 << 0) #define VTB_ALLOCED (1 << 1) int vtb_type; #define VTB_INVALID 0 #define VTB_MEMORY 1 #define VTB_FRAMEBUFFER 2 #define VTB_RINGBUFFER 3 int vtb_cols; int vtb_rows; int vtb_size; vm_offset_t vtb_buffer; int vtb_tail; /* valid for VTB_RINGBUFFER only */ } sc_vtb_t; -/* text cursor attributes */ +/* text and some mouse cursor attributes */ struct cursor_attr { - int flags; - int base; - int height; + u_char flags; + u_char base; + u_char height; + u_char bg[3]; + u_char mouse_ba; + u_char mouse_ia; }; /* softc */ struct keyboard; struct video_adapter; struct scr_stat; struct tty; struct sc_cnstate { u_char kbd_locked; u_char kdb_locked; u_char mtx_locked; u_char kbd_opened; u_char scr_opened; }; typedef struct sc_softc { int unit; /* unit # */ int config; /* configuration flags */ #define SC_VESAMODE (1 << 7) #define SC_AUTODETECT_KBD (1 << 8) #define SC_KERNEL_CONSOLE (1 << 9) int flags; /* status flags */ #define SC_VISUAL_BELL (1 << 0) #define SC_QUIET_BELL (1 << 1) #if 0 /* not used anymore */ #define SC_BLINK_CURSOR (1 << 2) #define SC_CHAR_CURSOR (1 << 3) #endif #define SC_MOUSE_ENABLED (1 << 4) #define SC_SCRN_IDLE (1 << 5) #define SC_SCRN_BLANKED (1 << 6) #define SC_SAVER_FAILED (1 << 7) #define SC_SCRN_VTYLOCK (1 << 8) #define SC_INIT_DONE (1 << 16) #define SC_SPLASH_SCRN (1 << 17) int keyboard; /* -1 if unavailable */ struct keyboard *kbd; int adapter; struct video_adapter *adp; int initial_mode; /* initial video mode */ int first_vty; int vtys; struct tty **dev; struct scr_stat *cur_scp; struct scr_stat *new_scp; struct scr_stat *old_scp; int delayed_next_scr; char font_loading_in_progress; char switch_in_progress; char write_in_progress; char blink_in_progress; int grab_level; /* 2 is just enough for kdb to grab for stepping normal grabbing: */ struct sc_cnstate grab_state[2]; int kbd_open_level; struct mtx video_mtx; long scrn_time_stamp; struct cursor_attr dflt_curs_attr; struct cursor_attr curs_attr; u_char scr_map[256]; u_char scr_rmap[256]; #ifdef _SC_MD_SOFTC_DECLARED_ sc_md_softc_t md; /* machine dependent vars */ #endif #ifndef SC_NO_PALETTE_LOADING u_char palette[256 * 3]; #ifdef SC_PIXEL_MODE u_char palette2[256 * 3]; #endif #endif #ifndef SC_NO_FONT_LOADING int fonts_loaded; #define FONT_8 2 #define FONT_14 4 #define FONT_16 8 #define FONT_22 8 u_char *font_8; u_char *font_14; u_char *font_16; u_char *font_22; #endif u_char cursor_char; u_char mouse_char; #ifdef KDB int sc_altbrk; #endif struct callout ctimeout; struct callout cblink; } sc_softc_t; /* virtual screen */ typedef struct scr_stat { int index; /* index of this vty */ struct sc_softc *sc; /* pointer to softc */ struct sc_rndr_sw *rndr; /* renderer */ #ifndef __sparc64__ sc_vtb_t scr; #endif sc_vtb_t vtb; int xpos; /* current X position */ int ypos; /* current Y position */ int xsize; /* X text size */ int ysize; /* Y text size */ int xpixel; /* X graphics size */ int ypixel; /* Y graphics size */ int xoff; /* X offset in pixel mode */ int yoff; /* Y offset in pixel mode */ u_char *font; /* current font */ int font_size; /* fontsize in Y direction */ int font_width; /* fontsize in X direction */ int start; /* modified area start */ int end; /* modified area end */ struct sc_term_sw *tsw; void *ts; int status; /* status (bitfield) */ int kbd_mode; /* keyboard I/O mode */ int cursor_pos; /* cursor buffer position */ int cursor_oldpos; /* cursor old buffer position */ struct cursor_attr dflt_curs_attr; struct cursor_attr base_curs_attr; struct cursor_attr curs_attr; int mouse_pos; /* mouse buffer position */ int mouse_oldpos; /* mouse old buffer position */ short mouse_xpos; /* mouse x coordinate */ short mouse_ypos; /* mouse y coordinate */ short mouse_oldxpos; /* mouse previous x coordinate */ short mouse_oldypos; /* mouse previous y coordinate */ short mouse_buttons; /* mouse buttons */ int mouse_cut_start; /* mouse cut start pos */ int mouse_cut_end; /* mouse cut end pos */ int mouse_level; /* xterm mouse protocol */ struct proc *mouse_proc; /* proc* of controlling proc */ pid_t mouse_pid; /* pid of controlling proc */ int mouse_signal; /* signal # to report with */ const void *mouse_data; /* renderer (pixmap) data */ u_short bell_duration; u_short bell_pitch; u_char border; /* border color */ int mode; /* mode */ pid_t pid; /* pid of controlling proc */ struct proc *proc; /* proc* of controlling proc */ struct vt_mode smode; /* switch mode */ sc_vtb_t *history; /* circular history buffer */ int history_pos; /* position shown on screen */ int history_size; /* size of history buffer */ int splash_save_mode; /* saved mode for splash screen */ int splash_save_status; /* saved status for splash screen */ #ifdef _SCR_MD_STAT_DECLARED_ scr_md_stat_t md; /* machine dependent vars */ #endif } scr_stat; /* TTY softc. */ typedef struct sc_ttysoftc { int st_index; scr_stat *st_stat; } sc_ttysoftc; #ifndef SC_NORM_ATTR #define SC_NORM_ATTR (FG_LIGHTGREY | BG_BLACK) #endif #ifndef SC_NORM_REV_ATTR #define SC_NORM_REV_ATTR (FG_BLACK | BG_LIGHTGREY) #endif #ifndef SC_KERNEL_CONS_ATTR #define SC_KERNEL_CONS_ATTR (FG_WHITE | BG_BLACK) #endif #ifndef SC_KERNEL_CONS_REV_ATTR #define SC_KERNEL_CONS_REV_ATTR (FG_BLACK | BG_LIGHTGREY) #endif /* terminal emulator */ #ifndef SC_DFLT_TERM #define SC_DFLT_TERM "*" /* any */ #endif typedef int sc_term_init_t(scr_stat *scp, void **tcp, int code); #define SC_TE_COLD_INIT 0 #define SC_TE_WARM_INIT 1 typedef int sc_term_term_t(scr_stat *scp, void **tcp); typedef void sc_term_puts_t(scr_stat *scp, u_char *buf, int len); typedef int sc_term_ioctl_t(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, struct thread *td); typedef int sc_term_reset_t(scr_stat *scp, int code); #define SC_TE_HARD_RESET 0 #define SC_TE_SOFT_RESET 1 typedef void sc_term_default_attr_t(scr_stat *scp, int norm, int rev); typedef void sc_term_clear_t(scr_stat *scp); typedef void sc_term_notify_t(scr_stat *scp, int event); #define SC_TE_NOTIFY_VTSWITCH_IN 0 #define SC_TE_NOTIFY_VTSWITCH_OUT 1 typedef int sc_term_input_t(scr_stat *scp, int c, struct tty *tp); typedef const char *sc_term_fkeystr_t(scr_stat *scp, int c); typedef void sc_term_sync_t(scr_stat *scp); typedef struct sc_term_sw { LIST_ENTRY(sc_term_sw) link; char *te_name; /* name of the emulator */ char *te_desc; /* description */ char *te_renderer; /* matching renderer */ size_t te_size; /* size of internal buffer */ int te_refcount; /* reference counter */ sc_term_init_t *te_init; sc_term_term_t *te_term; sc_term_puts_t *te_puts; sc_term_ioctl_t *te_ioctl; sc_term_reset_t *te_reset; sc_term_default_attr_t *te_default_attr; sc_term_clear_t *te_clear; sc_term_notify_t *te_notify; sc_term_input_t *te_input; sc_term_fkeystr_t *te_fkeystr; sc_term_sync_t *te_sync; } sc_term_sw_t; #define SCTERM_MODULE(name, sw) \ DATA_SET(scterm_set, sw); \ static int \ scterm_##name##_event(module_t mod, int type, void *data) \ { \ switch (type) { \ case MOD_LOAD: \ return sc_term_add(&sw); \ case MOD_UNLOAD: \ if (sw.te_refcount > 0) \ return EBUSY; \ return sc_term_remove(&sw); \ default: \ return EOPNOTSUPP; \ break; \ } \ return 0; \ } \ static moduledata_t scterm_##name##_mod = { \ "scterm-" #name, \ scterm_##name##_event, \ NULL, \ }; \ DECLARE_MODULE(scterm_##name, scterm_##name##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) /* renderer function table */ typedef void vr_init_t(scr_stat *scp); typedef void vr_clear_t(scr_stat *scp, int c, int attr); typedef void vr_draw_border_t(scr_stat *scp, int color); typedef void vr_draw_t(scr_stat *scp, int from, int count, int flip); typedef void vr_set_cursor_t(scr_stat *scp, int base, int height, int blink); typedef void vr_draw_cursor_t(scr_stat *scp, int at, int blink, int on, int flip); typedef void vr_blink_cursor_t(scr_stat *scp, int at, int flip); typedef void vr_set_mouse_t(scr_stat *scp); typedef void vr_draw_mouse_t(scr_stat *scp, int x, int y, int on); typedef struct sc_rndr_sw { vr_init_t *init; vr_clear_t *clear; vr_draw_border_t *draw_border; vr_draw_t *draw; vr_set_cursor_t *set_cursor; vr_draw_cursor_t *draw_cursor; vr_blink_cursor_t *blink_cursor; vr_set_mouse_t *set_mouse; vr_draw_mouse_t *draw_mouse; } sc_rndr_sw_t; typedef struct sc_renderer { char *name; int mode; sc_rndr_sw_t *rndrsw; LIST_ENTRY(sc_renderer) link; } sc_renderer_t; #define RENDERER(name, mode, sw, set) \ static struct sc_renderer scrndr_##name##_##mode = { \ #name, mode, &sw \ }; \ DATA_SET(scrndr_set, scrndr_##name##_##mode); \ DATA_SET(set, scrndr_##name##_##mode) #define RENDERER_MODULE(name, set) \ SET_DECLARE(set, sc_renderer_t); \ static int \ scrndr_##name##_event(module_t mod, int type, void *data) \ { \ sc_renderer_t **list; \ int error = 0; \ switch (type) { \ case MOD_LOAD: \ SET_FOREACH(list, set) { \ error = sc_render_add(*list); \ if (error) \ break; \ } \ break; \ case MOD_UNLOAD: \ SET_FOREACH(list, set) { \ error = sc_render_remove(*list);\ if (error) \ break; \ } \ break; \ default: \ return EOPNOTSUPP; \ break; \ } \ return error; \ } \ static moduledata_t scrndr_##name##_mod = { \ "scrndr-" #name, \ scrndr_##name##_event, \ NULL, \ }; \ DECLARE_MODULE(scrndr_##name, scrndr_##name##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) typedef struct { int shift_state; int bell_pitch; } bios_values_t; /* other macros */ #define ISTEXTSC(scp) (!((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE | PIXEL_MODE))) #define ISGRAPHSC(scp) (((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE))) #define ISPIXELSC(scp) (((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE | PIXEL_MODE))\ == PIXEL_MODE) #define ISUNKNOWNSC(scp) ((scp)->status & UNKNOWN_MODE) #define ISMOUSEAVAIL(af) ((af) & V_ADP_FONT) #define ISFONTAVAIL(af) ((af) & V_ADP_FONT) #define ISPALAVAIL(af) ((af) & V_ADP_PALETTE) #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) #define SC_VIDEO_LOCKINIT(sc) \ mtx_init(&(sc)->video_mtx, "syscons video lock", NULL, \ MTX_SPIN | MTX_RECURSE); #define SC_VIDEO_LOCK(sc) \ do { \ if (!kdb_active) \ mtx_lock_spin(&(sc)->video_mtx); \ } while(0) #define SC_VIDEO_UNLOCK(sc) \ do { \ if (!kdb_active) \ mtx_unlock_spin(&(sc)->video_mtx); \ } while(0) /* syscons.c */ extern int (*sc_user_ioctl)(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_probe_unit(int unit, int flags); int sc_attach_unit(int unit, int flags); int set_mode(scr_stat *scp); void sc_set_border(scr_stat *scp, int color); void sc_load_font(scr_stat *scp, int page, int size, int width, u_char *font, int base, int count); void sc_save_font(scr_stat *scp, int page, int size, int width, u_char *font, int base, int count); void sc_show_font(scr_stat *scp, int page); void sc_touch_scrn_saver(void); void sc_draw_cursor_image(scr_stat *scp); void sc_remove_cursor_image(scr_stat *scp); void sc_set_cursor_image(scr_stat *scp); void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height); int sc_clean_up(scr_stat *scp); int sc_switch_scr(sc_softc_t *sc, u_int next_scr); void sc_alloc_scr_buffer(scr_stat *scp, int wait, int discard); int sc_init_emulator(scr_stat *scp, char *name); void sc_paste(scr_stat *scp, const u_char *p, int count); void sc_respond(scr_stat *scp, const u_char *p, int count, int wakeup); void sc_bell(scr_stat *scp, int pitch, int duration); /* schistory.c */ #ifndef SC_NO_HISTORY int sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait); void sc_free_history_buffer(scr_stat *scp, int prev_ysize); void sc_hist_save(scr_stat *scp); #define sc_hist_save_one_line(scp, from) \ sc_vtb_append(&(scp)->vtb, (from), (scp)->history, (scp)->xsize) int sc_hist_restore(scr_stat *scp); void sc_hist_home(scr_stat *scp); void sc_hist_end(scr_stat *scp); int sc_hist_up_line(scr_stat *scp); int sc_hist_down_line(scr_stat *scp); int sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); #endif /* SC_NO_HISTORY */ /* scmouse.c */ #ifndef SC_NO_CUTPASTE void sc_alloc_cut_buffer(scr_stat *scp, int wait); void sc_draw_mouse_image(scr_stat *scp); void sc_remove_mouse_image(scr_stat *scp); int sc_inside_cutmark(scr_stat *scp, int pos); void sc_remove_cutmarking(scr_stat *scp); void sc_remove_all_cutmarkings(sc_softc_t *scp); void sc_remove_all_mouse(sc_softc_t *scp); void sc_mouse_paste(scr_stat *scp); #else #define sc_draw_mouse_image(scp) #define sc_remove_mouse_image(scp) #define sc_inside_cutmark(scp, pos) FALSE #define sc_remove_cutmarking(scp) #define sc_remove_all_cutmarkings(scp) #define sc_remove_all_mouse(scp) #define sc_mouse_paste(scp) #endif /* SC_NO_CUTPASTE */ #ifndef SC_NO_SYSMOUSE void sc_mouse_move(scr_stat *scp, int x, int y); int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); #endif /* SC_NO_SYSMOUSE */ /* scvidctl.c */ int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, int fontsize, int font_width); int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode); int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize, int font_width); int sc_support_pixel_mode(void *arg); int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_render_add(sc_renderer_t *rndr); int sc_render_remove(sc_renderer_t *rndr); sc_rndr_sw_t *sc_render_match(scr_stat *scp, char *name, int mode); /* scvtb.c */ void sc_vtb_init(sc_vtb_t *vtb, int type, int cols, int rows, void *buffer, int wait); void sc_vtb_destroy(sc_vtb_t *vtb); size_t sc_vtb_size(int cols, int rows); void sc_vtb_clear(sc_vtb_t *vtb, int c, int attr); int sc_vtb_getc(sc_vtb_t *vtb, int at); int sc_vtb_geta(sc_vtb_t *vtb, int at); void sc_vtb_putc(sc_vtb_t *vtb, int at, int c, int a); vm_offset_t sc_vtb_putchar(sc_vtb_t *vtb, vm_offset_t p, int c, int a); vm_offset_t sc_vtb_pointer(sc_vtb_t *vtb, int at); int sc_vtb_pos(sc_vtb_t *vtb, int pos, int offset); #define sc_vtb_tail(vtb) ((vtb)->vtb_tail) #define sc_vtb_rows(vtb) ((vtb)->vtb_rows) #define sc_vtb_cols(vtb) ((vtb)->vtb_cols) void sc_vtb_copy(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int to, int count); void sc_vtb_append(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int count); void sc_vtb_seek(sc_vtb_t *vtb, int pos); void sc_vtb_erase(sc_vtb_t *vtb, int at, int count, int c, int attr); void sc_vtb_move(sc_vtb_t *vtb, int from, int to, int count); void sc_vtb_delete(sc_vtb_t *vtb, int at, int count, int c, int attr); void sc_vtb_ins(sc_vtb_t *vtb, int at, int count, int c, int attr); /* sysmouse.c */ int sysmouse_event(mouse_info_t *info); /* scterm.c */ void sc_move_cursor(scr_stat *scp, int x, int y); void sc_clear_screen(scr_stat *scp); int sc_term_add(sc_term_sw_t *sw); int sc_term_remove(sc_term_sw_t *sw); sc_term_sw_t *sc_term_match(char *name); sc_term_sw_t *sc_term_match_by_number(int index); /* machine dependent functions */ int sc_max_unit(void); sc_softc_t *sc_get_softc(int unit, int flags); sc_softc_t *sc_find_softc(struct video_adapter *adp, struct keyboard *kbd); int sc_get_cons_priority(int *unit, int *flags); void sc_get_bios_values(bios_values_t *values); int sc_tone(int herz); #endif /* !_DEV_SYSCONS_SYSCONS_H_ */ Index: head/sys/sys/consio.h =================================================================== --- head/sys/sys/consio.h (revision 322877) +++ head/sys/sys/consio.h (revision 322878) @@ -1,464 +1,466 @@ /*- * Copyright (c) 1991-1996 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_CONSIO_H_ #define _SYS_CONSIO_H_ #ifndef _KERNEL #include #endif #include /* * Console ioctl commands. Some commands are named as KDXXXX, GIO_XXX, and * PIO_XXX, rather than CONS_XXX, for historical and compatibility reasons. * Some other CONS_XXX commands are works as wrapper around frame buffer * ioctl commands FBIO_XXX. Do not try to change all these commands, * otherwise we shall have compatibility problems. */ /* get/set video mode */ #define KD_TEXT 0 /* set text mode restore fonts */ #define KD_TEXT0 0 /* ditto */ #define KD_GRAPHICS 1 /* set graphics mode */ #define KD_TEXT1 2 /* set text mode !restore fonts */ #define KD_PIXEL 3 /* set pixel mode */ #define KDGETMODE _IOR('K', 9, int) #define KDSETMODE _IOWINT('K', 10) /* set border color */ #define KDSBORDER _IOWINT('K', 13) /* set up raster(pixel) text mode */ struct _scr_size { int scr_size[3]; }; typedef struct _scr_size scr_size_t; #define KDRASTER _IOW('K', 100, scr_size_t) /* get/set screen char map */ struct _scrmap { char scrmap[256]; }; typedef struct _scrmap scrmap_t; #define GIO_SCRNMAP _IOR('k', 2, scrmap_t) #define PIO_SCRNMAP _IOW('k', 3, scrmap_t) /* get the current text attribute */ #define GIO_ATTR _IOR('a', 0, int) /* get the current text color */ #define GIO_COLOR _IOR('c', 0, int) /* get the adapter type (equivalent to FBIO_ADPTYPE) */ #define CONS_CURRENT _IOR('c', 1, int) /* get the current video mode (equivalent to FBIO_GETMODE) */ #define CONS_GET _IOR('c', 2, int) /* not supported? */ #define CONS_IO _IO('c', 3) /* set blank time interval */ #define CONS_BLANKTIME _IOW('c', 4, int) /* set/get the screen saver (these ioctls are current noop) */ struct ssaver { #define MAXSSAVER 16 char name[MAXSSAVER]; int num; long time; }; typedef struct ssaver ssaver_t; #define CONS_SSAVER _IOW('c', 5, ssaver_t) #define CONS_GSAVER _IOWR('c', 6, ssaver_t) /* * Set the text cursor type. * * This is an old interface extended to support the CONS_HIDDEN_CURSOR bit. * New code should use CONS_CURSORSHAPE. CONS_CURSOR_ATTRS gives the 3 * bits supported by the (extended) old interface. The old interface is * especially unusable for hiding the cursor (even with its extension) * since it changes the cursor on all vtys. */ #define CONS_CURSORTYPE _IOW('c', 7, int) /* set the bell type to audible or visual */ #define CONS_VISUAL_BELL (1 << 0) #define CONS_QUIET_BELL (1 << 1) #define CONS_BELLTYPE _IOW('c', 8, int) /* set the history (scroll back) buffer size (in lines) */ #define CONS_HISTORY _IOW('c', 9, int) /* clear the history (scroll back) buffer */ #define CONS_CLRHIST _IO('c', 10) /* mouse cursor ioctl */ struct mouse_data { int x; int y; int z; int buttons; }; typedef struct mouse_data mouse_data_t; struct mouse_mode { int mode; int signal; }; typedef struct mouse_mode mouse_mode_t; struct mouse_event { int id; /* one based */ int value; }; typedef struct mouse_event mouse_event_t; struct mouse_info { int operation; #define MOUSE_SHOW 0x01 #define MOUSE_HIDE 0x02 #define MOUSE_MOVEABS 0x03 #define MOUSE_MOVEREL 0x04 #define MOUSE_GETINFO 0x05 #define MOUSE_MODE 0x06 #define MOUSE_ACTION 0x07 #define MOUSE_MOTION_EVENT 0x08 #define MOUSE_BUTTON_EVENT 0x09 #define MOUSE_MOUSECHAR 0x0a union { mouse_data_t data; mouse_mode_t mode; mouse_event_t event; int mouse_char; } u; }; typedef struct mouse_info mouse_info_t; #define CONS_MOUSECTL _IOWR('c', 10, mouse_info_t) /* see if the vty has been idle */ #define CONS_IDLE _IOR('c', 11, int) /* set the screen saver mode */ #define CONS_NO_SAVER (-1) #define CONS_LKM_SAVER 0 #define CONS_USR_SAVER 1 #define CONS_SAVERMODE _IOW('c', 12, int) /* start the screen saver */ #define CONS_SAVERSTART _IOW('c', 13, int) /* set the text cursor shape (see also CONS_CURSORTYPE above) */ #define CONS_BLINK_CURSOR (1 << 0) #define CONS_CHAR_CURSOR (1 << 1) #define CONS_HIDDEN_CURSOR (1 << 2) #define CONS_CURSOR_ATTRS (CONS_BLINK_CURSOR | CONS_CHAR_CURSOR | \ CONS_HIDDEN_CURSOR) +#define CONS_CHARCURSOR_COLORS (1 << 26) +#define CONS_MOUSECURSOR_COLORS (1 << 27) #define CONS_DEFAULT_CURSOR (1 << 28) #define CONS_SHAPEONLY_CURSOR (1 << 29) #define CONS_RESET_CURSOR (1 << 30) #define CONS_LOCAL_CURSOR (1U << 31) struct cshape { /* shape[0]: flags, shape[1]: base, shape[2]: height */ int shape[3]; }; #define CONS_GETCURSORSHAPE _IOWR('c', 14, struct cshape) #define CONS_SETCURSORSHAPE _IOW('c', 15, struct cshape) /* set/get font data */ struct fnt8 { char fnt8x8[8*256]; }; typedef struct fnt8 fnt8_t; struct fnt14 { char fnt8x14[14*256]; }; typedef struct fnt14 fnt14_t; struct fnt16 { char fnt8x16[16*256]; }; typedef struct fnt16 fnt16_t; struct vfnt_map { uint32_t src; uint16_t dst; uint16_t len; }; typedef struct vfnt_map vfnt_map_t; #define VFNT_MAP_NORMAL 0 #define VFNT_MAP_NORMAL_RIGHT 1 #define VFNT_MAP_BOLD 2 #define VFNT_MAP_BOLD_RIGHT 3 #define VFNT_MAPS 4 struct vfnt { vfnt_map_t *map[VFNT_MAPS]; uint8_t *glyphs; unsigned int map_count[VFNT_MAPS]; unsigned int glyph_count; unsigned int width; unsigned int height; }; typedef struct vfnt vfnt_t; #define PIO_FONT8x8 _IOW('c', 64, fnt8_t) #define GIO_FONT8x8 _IOR('c', 65, fnt8_t) #define PIO_FONT8x14 _IOW('c', 66, fnt14_t) #define GIO_FONT8x14 _IOR('c', 67, fnt14_t) #define PIO_FONT8x16 _IOW('c', 68, fnt16_t) #define GIO_FONT8x16 _IOR('c', 69, fnt16_t) #define PIO_VFONT _IOW('c', 70, vfnt_t) #define GIO_VFONT _IOR('c', 71, vfnt_t) #define PIO_VFONT_DEFAULT _IO('c', 72) /* get video mode information */ struct colors { char fore; char back; }; struct vid_info { short size; short m_num; u_short font_size; u_short mv_row, mv_col; u_short mv_rsz, mv_csz; u_short mv_hsz; struct colors mv_norm, mv_rev, mv_grfc; u_char mv_ovscan; u_char mk_keylock; }; typedef struct vid_info vid_info_t; #define CONS_GETINFO _IOWR('c', 73, vid_info_t) /* get version */ #define CONS_GETVERS _IOR('c', 74, int) /* get the video adapter index (equivalent to FBIO_ADAPTER) */ #define CONS_CURRENTADP _IOR('c', 100, int) /* get the video adapter information (equivalent to FBIO_ADPINFO) */ #define CONS_ADPINFO _IOWR('c', 101, video_adapter_info_t) /* get the video mode information (equivalent to FBIO_MODEINFO) */ #define CONS_MODEINFO _IOWR('c', 102, video_info_t) /* find a video mode (equivalent to FBIO_FINDMODE) */ #define CONS_FINDMODE _IOWR('c', 103, video_info_t) /* set the frame buffer window origin (equivalent to FBIO_SETWINORG) */ #define CONS_SETWINORG _IOWINT('c', 104) /* use the specified keyboard */ #define CONS_SETKBD _IOWINT('c', 110) /* release the current keyboard */ #define CONS_RELKBD _IO('c', 111) struct scrshot { int x; int y; int xsize; int ysize; u_int16_t* buf; }; typedef struct scrshot scrshot_t; /* Snapshot the current video buffer */ #define CONS_SCRSHOT _IOWR('c', 105, scrshot_t) /* get/set the current terminal emulator info. */ #define TI_NAME_LEN 32 #define TI_DESC_LEN 64 struct term_info { int ti_index; int ti_flags; u_char ti_name[TI_NAME_LEN]; u_char ti_desc[TI_DESC_LEN]; }; typedef struct term_info term_info_t; #define CONS_GETTERM _IOWR('c', 112, term_info_t) #define CONS_SETTERM _IOW('c', 113, term_info_t) /* * Vty switching ioctl commands. */ /* get the next available vty */ #define VT_OPENQRY _IOR('v', 1, int) /* set/get vty switching mode */ #ifndef _VT_MODE_DECLARED #define _VT_MODE_DECLARED struct vt_mode { char mode; #define VT_AUTO 0 /* switching is automatic */ #define VT_PROCESS 1 /* switching controlled by prog */ #define VT_KERNEL 255 /* switching controlled in kernel */ char waitv; /* not implemented yet SOS */ short relsig; short acqsig; short frsig; /* not implemented yet SOS */ }; typedef struct vt_mode vtmode_t; #endif /* !_VT_MODE_DECLARED */ #define VT_SETMODE _IOW('v', 2, vtmode_t) #define VT_GETMODE _IOR('v', 3, vtmode_t) /* acknowledge release or acquisition of a vty */ #define VT_FALSE 0 #define VT_TRUE 1 #define VT_ACKACQ 2 #define VT_RELDISP _IOWINT('v', 4) /* activate the specified vty */ #define VT_ACTIVATE _IOWINT('v', 5) /* wait until the specified vty is activate */ #define VT_WAITACTIVE _IOWINT('v', 6) /* get the currently active vty */ #define VT_GETACTIVE _IOR('v', 7, int) /* get the index of the vty */ #define VT_GETINDEX _IOR('v', 8, int) /* prevent switching vtys */ #define VT_LOCKSWITCH _IOW('v', 9, int) /* * Video mode switching ioctl. See sys/fbio.h for mode numbers. */ #define SW_B40x25 _IO('S', M_B40x25) #define SW_C40x25 _IO('S', M_C40x25) #define SW_B80x25 _IO('S', M_B80x25) #define SW_C80x25 _IO('S', M_C80x25) #define SW_BG320 _IO('S', M_BG320) #define SW_CG320 _IO('S', M_CG320) #define SW_BG640 _IO('S', M_BG640) #define SW_EGAMONO80x25 _IO('S', M_EGAMONO80x25) #define SW_CG320_D _IO('S', M_CG320_D) #define SW_CG640_E _IO('S', M_CG640_E) #define SW_EGAMONOAPA _IO('S', M_EGAMONOAPA) #define SW_CG640x350 _IO('S', M_CG640x350) #define SW_ENH_MONOAPA2 _IO('S', M_ENHMONOAPA2) #define SW_ENH_CG640 _IO('S', M_ENH_CG640) #define SW_ENH_B40x25 _IO('S', M_ENH_B40x25) #define SW_ENH_C40x25 _IO('S', M_ENH_C40x25) #define SW_ENH_B80x25 _IO('S', M_ENH_B80x25) #define SW_ENH_C80x25 _IO('S', M_ENH_C80x25) #define SW_ENH_B80x43 _IO('S', M_ENH_B80x43) #define SW_ENH_C80x43 _IO('S', M_ENH_C80x43) #define SW_MCAMODE _IO('S', M_MCA_MODE) #define SW_VGA_C40x25 _IO('S', M_VGA_C40x25) #define SW_VGA_C80x25 _IO('S', M_VGA_C80x25) #define SW_VGA_C80x30 _IO('S', M_VGA_C80x30) #define SW_VGA_C80x50 _IO('S', M_VGA_C80x50) #define SW_VGA_C80x60 _IO('S', M_VGA_C80x60) #define SW_VGA_M80x25 _IO('S', M_VGA_M80x25) #define SW_VGA_M80x30 _IO('S', M_VGA_M80x30) #define SW_VGA_M80x50 _IO('S', M_VGA_M80x50) #define SW_VGA_M80x60 _IO('S', M_VGA_M80x60) #define SW_VGA11 _IO('S', M_VGA11) #define SW_BG640x480 _IO('S', M_VGA11) #define SW_VGA12 _IO('S', M_VGA12) #define SW_CG640x480 _IO('S', M_VGA12) #define SW_VGA13 _IO('S', M_VGA13) #define SW_VGA_CG320 _IO('S', M_VGA13) #define SW_VGA_CG640 _IO('S', M_VGA_CG640) #define SW_VGA_MODEX _IO('S', M_VGA_MODEX) #define SW_VGA_C90x25 _IO('S', M_VGA_C90x25) #define SW_VGA_M90x25 _IO('S', M_VGA_M90x25) #define SW_VGA_C90x30 _IO('S', M_VGA_C90x30) #define SW_VGA_M90x30 _IO('S', M_VGA_M90x30) #define SW_VGA_C90x43 _IO('S', M_VGA_C90x43) #define SW_VGA_M90x43 _IO('S', M_VGA_M90x43) #define SW_VGA_C90x50 _IO('S', M_VGA_C90x50) #define SW_VGA_M90x50 _IO('S', M_VGA_M90x50) #define SW_VGA_C90x60 _IO('S', M_VGA_C90x60) #define SW_VGA_M90x60 _IO('S', M_VGA_M90x60) #define SW_TEXT_80x25 _IO('S', M_TEXT_80x25) #define SW_TEXT_80x30 _IO('S', M_TEXT_80x30) #define SW_TEXT_80x43 _IO('S', M_TEXT_80x43) #define SW_TEXT_80x50 _IO('S', M_TEXT_80x50) #define SW_TEXT_80x60 _IO('S', M_TEXT_80x60) #define SW_TEXT_132x25 _IO('S', M_TEXT_132x25) #define SW_TEXT_132x30 _IO('S', M_TEXT_132x30) #define SW_TEXT_132x43 _IO('S', M_TEXT_132x43) #define SW_TEXT_132x50 _IO('S', M_TEXT_132x50) #define SW_TEXT_132x60 _IO('S', M_TEXT_132x60) #define SW_VESA_CG640x400 _IO('V', M_VESA_CG640x400 - M_VESA_BASE) #define SW_VESA_CG640x480 _IO('V', M_VESA_CG640x480 - M_VESA_BASE) #define SW_VESA_800x600 _IO('V', M_VESA_800x600 - M_VESA_BASE) #define SW_VESA_CG800x600 _IO('V', M_VESA_CG800x600 - M_VESA_BASE) #define SW_VESA_1024x768 _IO('V', M_VESA_1024x768 - M_VESA_BASE) #define SW_VESA_CG1024x768 _IO('V', M_VESA_CG1024x768 - M_VESA_BASE) #define SW_VESA_1280x1024 _IO('V', M_VESA_1280x1024 - M_VESA_BASE) #define SW_VESA_CG1280x1024 _IO('V', M_VESA_CG1280x1024 - M_VESA_BASE) #define SW_VESA_C80x60 _IO('V', M_VESA_C80x60 - M_VESA_BASE) #define SW_VESA_C132x25 _IO('V', M_VESA_C132x25 - M_VESA_BASE) #define SW_VESA_C132x43 _IO('V', M_VESA_C132x43 - M_VESA_BASE) #define SW_VESA_C132x50 _IO('V', M_VESA_C132x50 - M_VESA_BASE) #define SW_VESA_C132x60 _IO('V', M_VESA_C132x60 - M_VESA_BASE) #define SW_VESA_32K_320 _IO('V', M_VESA_32K_320 - M_VESA_BASE) #define SW_VESA_64K_320 _IO('V', M_VESA_64K_320 - M_VESA_BASE) #define SW_VESA_FULL_320 _IO('V', M_VESA_FULL_320 - M_VESA_BASE) #define SW_VESA_32K_640 _IO('V', M_VESA_32K_640 - M_VESA_BASE) #define SW_VESA_64K_640 _IO('V', M_VESA_64K_640 - M_VESA_BASE) #define SW_VESA_FULL_640 _IO('V', M_VESA_FULL_640 - M_VESA_BASE) #define SW_VESA_32K_800 _IO('V', M_VESA_32K_800 - M_VESA_BASE) #define SW_VESA_64K_800 _IO('V', M_VESA_64K_800 - M_VESA_BASE) #define SW_VESA_FULL_800 _IO('V', M_VESA_FULL_800 - M_VESA_BASE) #define SW_VESA_32K_1024 _IO('V', M_VESA_32K_1024 - M_VESA_BASE) #define SW_VESA_64K_1024 _IO('V', M_VESA_64K_1024 - M_VESA_BASE) #define SW_VESA_FULL_1024 _IO('V', M_VESA_FULL_1024 - M_VESA_BASE) #define SW_VESA_32K_1280 _IO('V', M_VESA_32K_1280 - M_VESA_BASE) #define SW_VESA_64K_1280 _IO('V', M_VESA_64K_1280 - M_VESA_BASE) #define SW_VESA_FULL_1280 _IO('V', M_VESA_FULL_1280 - M_VESA_BASE) #endif /* !_SYS_CONSIO_H_ */ Index: head/usr.sbin/vidcontrol/vidcontrol.1 =================================================================== --- head/usr.sbin/vidcontrol/vidcontrol.1 (revision 322877) +++ head/usr.sbin/vidcontrol/vidcontrol.1 (revision 322878) @@ -1,686 +1,728 @@ .\" .\" vidcontrol - a utility for manipulating the syscons or vt video driver .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" @(#)vidcontrol.1 .\" $FreeBSD$ .\" .Dd January 19, 2016 .Dt VIDCONTROL 1 .Os .Sh NAME .Nm vidcontrol .Nd system console control and configuration utility .Sh SYNOPSIS .Nm .Op Fl CdLHPpx .Op Fl b Ar color .Op Fl c Ar appearance .Oo .Fl f .Oo .Op Ar size .Ar file .Oc .Oc .Op Fl g Ar geometry .Op Fl h Ar size .Op Fl i Cm active | adapter | mode .Op Fl l Ar screen_map .Op Fl M Ar char .Op Fl m Cm on | off .Op Fl r Ar foreground Ar background .Op Fl S Cm on | off .Op Fl s Ar number .Op Fl T Cm xterm | cons25 .Op Fl t Ar N | Cm off .Op Ar mode .Op Ar foreground Op Ar background .Op Cm show .Sh DESCRIPTION The .Nm utility is used to set various options for the .Xr syscons 4 or .Xr vt 4 console driver, such as video mode, colors, cursor shape, screen output map, font and screen saver timeout. Only a small subset of options is supported by .Xr vt 4 . Unsupported options lead to error messages, typically including the text "Inappropriate ioctl for device". .Pp The following command line options are supported: .Bl -tag -width indent .It Ar mode Select a new video mode. The modes currently recognized are: .Ar 80x25 , .Ar 80x30 , .Ar 80x43 , .Ar 80x50 , .Ar 80x60 , .Ar 132x25 , .Ar 132x30 , .Ar 132x43 , .Ar 132x50 , .Ar 132x60 , .Ar VGA_40x25 , .Ar VGA_80x25 , .Ar VGA_80x30 , .Ar VGA_80x50 , .Ar VGA_80x60 , .Ar VGA_90x25 , .Ar VGA_90x30 , .Ar VGA_90x43 , .Ar VGA_90x50 , .Ar VGA_90x60 , .Ar EGA_80x25 , .Ar EGA_80x43 , .Ar VESA_132x25 , .Ar VESA_132x43 , .Ar VESA_132x50 , .Ar VESA_132x60 . .\"The graphic mode .\".Ar VGA_320x200 .\"and The raster text mode .Ar VESA_800x600 can also be chosen. Alternatively, a mode can be specified with its number by using a mode name of the form .Li MODE_ Ns Aq Ar NUMBER . A list of valid mode numbers can be obtained with the .Fl i Cm mode option. See .Sx Video Mode Support below. .It Ar foreground Op Ar background Change colors when displaying text. Specify the foreground color (e.g.\& .Dq vidcontrol white ) , or both a foreground and background colors (e.g.\& .Dq vidcontrol yellow blue ) . Use the .Cm show command below to see available colors. .It Cm show See the supported colors on a given platform. .It Fl b Ar color Set border color to .Ar color . This option may not be always supported by the video driver. .It Fl C Clear the history buffer. .It Fl c Ar setting Ns Op , Ns Ar setting ... Change the cursor appearance. The change is specified by a non-empty comma-separated list of .Cm setting Ns s . Each .Cm setting overrides or modifies previous ones in left to right order. .Pp The following override .Cm setting Ns s are available: .Bl -tag -width indent .It Cm normal Set to a block covering 1 character cell, with a configuration-dependent coloring that should be at worst inverse video. .It Cm destructive Set to a blinking sub-block with .Cm height scanlines starting at .Cm base . The name .Dq destructive is bad for backwards compatibility. This .Cm setting should not force destructiveness, and it now only gives destructiveness in some configurations (typically for hardware cursors in text mode). Blinking limits destructiveness. This .Cm setting should now be spelled .Cm normal , Ns Cm blink , Ns Cm noblock . A non-blinking destructive cursor would be unusable, so old versions of .Nm didn't support it, and this version doesn't have an override for it. .It Cm base Ns = Ns Ar value, Cm height Ns = Ns Ar value Set the specified scanline parameters. These parameters are only active in .Cm noblock mode. .Cm value is an integer in any base supported by .Xr strtol 3 . Setting .Cm height to 0 turns off the cursor in .Cm noblock mode. Negative .Ar value Ns s are silently ignored. Positive .Ar value Ns s are clamped to fit in the character cell when the cursor is drawn. .El .Pp The following modifier .Cm setting Ns s are available: .Bl -tag -width indent .It Cm blink , noblink Set or clear the blinking attribute. This is not quite backwards compatible. In old versions of .Nm , Cm blink was an override to a blinking block. .It Cm block , noblock Set or clear the .Cm block attribute. This attribute is the inverse of the flag .Dv CONS_CHAR_CURSOR in the implementation. It deactivates the scanline parameters, and expresses a preference for using a simpler method of implementation. Its inverse does the opposite. When the scanline parameters give a full block, this attribute reduces to a method selection bit. The .Cm block method tends to give better coloring. .It Cm hidden , nohidden Set or clear the hidden attribute. .El .Pp The following (non-sticky) flags control application of the .Cm setting Ns s : .Bl -tag -width indent +.It Cm charcolors +Apply +.Cm base +and +.Cm height +to the (character) cursor's list of preferred colors instead of its shape. +Beware that the color numbers are raw VGA palette indexes, +not ANSI color numbers. +The indexes are reduced mod 8, 16 or 256, +or ignored, +depending on the video mode and renderer. +.It Cm mousecolors +Colors for the mouse cursor in graphics mode. +Like +.Cm charcolors , +except there is no preference or sequence; +.Cm base +gives the mouse border color and +.Cm height +gives the mouse interior color. +Together with +.Cm charcolors , +this gives 2 selection bits which select between +only 3 of 4 sub-destinations of the 4 destinations selected by +.Cm default +and +.Cm local +(by ignoring +.Cm mousecolors +if +.Cm charcolors +is also set). +.It Cm default +Apply the changes to the default settings and then to the active settings, +instead of only to the active settings. +Together with +.Cm local , +this gives 2 selection bits which select between 4 destinations. +.It Cm shapeonly +Ignore any changes to the +.Cm block +and +.Cm hidden +attributes. .It Cm local Apply the changes to the current vty. The default is to apply them to a global place and copy from there to all vtys. .It Cm reset Reset everything. The default is to not reset. When the .Cm local parameter is specified, the current local settings are reset to default local settings. Otherwise, the current global settings are reset to default global settings and then copied to the current and default settings for all vtys. -The global defaults are decided (not quite right) at boot time -and cannot be fixed up. -The local defaults are obtained as above and cannot be fixed up -locally. +.It Cm show +Show the current changes. .El .It Fl d Print out current output screen map. .It Xo .Fl f .Oo .Op Ar size .Ar file .Oc .Xc Load font .Ar file for .Ar size (currently, only .Cm 8x8 , .Cm 8x14 or .Cm 8x16 ) . The font file can be either uuencoded or in raw binary format. You can also use the menu-driven .Xr vidfont 1 command to load the font of your choice. .Pp .Ar Size may be omitted, in this case .Nm will try to guess it from the size of font file. .Pp When using .Xr vt 4 both .Ar size and .Ar font can be omitted, and the default font will be loaded. .Pp Note that older video cards, such as MDA and CGA, do not support software font. See also .Sx Video Mode Support and .Sx EXAMPLES below and the man page for either .Xr syscons 4 or .Xr vt 4 (depending on which driver you use). .It Fl g Ar geometry Set the .Ar geometry of the text mode for the modes with selectable geometry. Currently only raster modes, such as .Ar VESA_800x600 , support this option. See also .Sx Video Mode Support and .Sx EXAMPLES below. .It Fl h Ar size Set the size of the history (scrollback) buffer to .Ar size lines. .It Fl i Cm active Shows the active vty number. .It Fl i Cm adapter Shows info about the current video adapter. .It Fl i Cm mode Shows the possible video modes with the current video hardware. .It Fl l Ar screen_map Install screen output map file from .Ar screen_map . See also .Xr syscons 4 or .Xr vt 4 (depending on which driver you use). .It Fl L Install default screen output map. .It Fl M Ar char Sets the base character used to render the mouse pointer to .Ar char . .It Fl m Cm on | off Switch the mouse pointer .Cm on or .Cm off . Used together with the .Xr moused 8 daemon for text mode cut & paste functionality. .It Fl p Capture the current contents of the video buffer corresponding to the terminal device referred to by standard input. The .Nm utility writes contents of the video buffer to the standard output in a raw binary format. For details about that format see .Sx Format of Video Buffer Dump below. .It Fl P Same as .Fl p , but dump contents of the video buffer in a plain text format ignoring nonprintable characters and information about text attributes. .It Fl H When used with .Fl p or .Fl P , it instructs .Nm to dump full history buffer instead of visible portion of the video buffer only. .It Fl r Ar foreground background Change reverse mode colors to .Ar foreground and .Ar background . .It Fl S Cm on | off Turn vty switching on or off. When vty switching is off, attempts to switch to a different virtual terminal will fail. (The default is to permit vty switching.) This protection can be easily bypassed when the kernel is compiled with the .Dv DDB option. However, you probably should not compile the kernel debugger on a box which is supposed to be physically secure. .It Fl s Ar number Set the active vty to .Ar number . .It Fl T Cm xterm | cons25 Switch between xterm and cons25 style terminal emulation. .It Fl t Ar N | Cm off Set the screensaver timeout to .Ar N seconds, or turns it .Cm off . .It Fl x Use hexadecimal digits for output. .El .Ss Video Mode Support Note that not all modes listed above may be supported by the video hardware. You can verify which mode is supported by the video hardware, using the .Fl i Cm mode option. .Pp The VESA BIOS support must be linked to the kernel or loaded as a KLD module if you wish to use VESA video modes or 132 column modes (see .Xr vga 4 ) . .Pp You need to compile your kernel with the .Ar VGA_WIDTH90 option if you wish to use VGA 90 column modes (see .Xr vga 4 ) . .Pp Video modes other than 25 and 30 line modes may require specific size of font. Use .Fl f option above to load a font file to the kernel. If the required size of font has not been loaded to the kernel, .Nm will fail if the user attempts to set a new video mode. .Pp .Bl -column "25 line modes" "8x16 (VGA), 8x14 (EGA)" -compact .Sy Modes Ta Sy Font size .No 25 line modes Ta 8x16 (VGA), 8x14 (EGA) .No 30 line modes Ta 8x16 .No 43 line modes Ta 8x8 .No 50 line modes Ta 8x8 .No 60 line modes Ta 8x8 .El .Pp It is better to always load all three sizes (8x8, 8x14 and 8x16) of the same font. .Pp You may set variables in .Pa /etc/rc.conf or .Pa /etc/rc.conf.local so that desired font files will be automatically loaded when the system starts up. See below. .Pp If you want to use any of the raster text modes you need to recompile your kernel with the .Dv SC_PIXEL_MODE option. See .Xr syscons 4 or .Xr vt 4 (depending on which driver you use) for more details on this kernel option. .Ss Format of Video Buffer Dump The .Nm utility uses the .Xr syscons 4 .\" is it supported on vt(4)??? or .Xr vt 4 .Dv CONS_SCRSHOT .Xr ioctl 2 to capture the current contents of the video buffer. The .Nm utility writes version and additional information to the standard output, followed by the contents of the video buffer. .Pp VGA video memory is typically arranged in two byte tuples, one per character position. In each tuple, the first byte will be the character code, and the second byte is the character's color attribute. .Pp The VGA color attribute byte looks like this: .Bl -column "X:X" "<00000000>" "width" "bright foreground color" .Sy "bits# width meaning" .Li "7 1 character blinking" .Li "6:4 <0XXX0000> 3 background color" .Li "3 <0000X000> 1 bright foreground color" .Li "2:0 <00000XXX> 3 foreground color" .El .Pp Here is a list of the three bit wide base colors: .Pp .Bl -hang -offset indent -compact .It 0 Black .It 1 Blue .It 2 Green .It 3 Cyan .It 4 Red .It 5 Magenta .It 6 Brown .It 7 Light Grey .El .Pp Base colors with bit 3 (the bright foreground flag) set: .Pp .Bl -hang -offset indent -compact .It 0 Dark Grey .It 1 Light Blue .It 2 Light Green .It 3 Light Cyan .It 4 Light Red .It 5 Light Magenta .It 6 Yellow .It 7 White .El .Pp For example, the two bytes .Pp .Dl "65 158" .Pp specify an uppercase A (character code 65), blinking (bit 7 set) in yellow (bits 3:0) on a blue background (bits 6:4). .Pp The .Nm output contains a small header which includes additional information which may be useful to utilities processing the output. .Pp The first 10 bytes are always arranged as follows: .Bl -column "Byte range" "Contents" -offset indent .It Sy "Byte Range Contents" .It "1 thru 8 Literal text" Dq Li SCRSHOT_ .It "9 File format version number" .It "10 Remaining number of bytes in the header" .El .Pp Subsequent bytes depend on the version number. .Bl -column "Version" "13 and up" -offset indent .It Sy "Version Byte Meaning" .It "1 11 Terminal width, in characters" .It " 12 Terminal depth, in characters" .It " 13 and up The snapshot data" .El .Pp So a dump of an 80x25 screen would start (in hex) .Bd -literal -offset indent 53 43 52 53 48 4f 54 5f 01 02 50 19 ----------------------- -- -- -- -- | | | | ` 25 decimal | | | `--- 80 decimal | | `------ 2 remaining bytes of header data | `--------- File format version 1 `------------------------ Literal "SCRSHOT_" .Ed .Sh VIDEO OUTPUT CONFIGURATION .Ss Boot Time Configuration You may set the following variables in .Pa /etc/rc.conf or .Pa /etc/rc.conf.local in order to configure the video output at boot time. .Pp .Bl -tag -width foo_bar_var -compact .It Ar blanktime Sets the timeout value for the .Fl t option. .It Ar font8x16 , font8x14 , font8x8 Specifies font files for the .Fl f option. .It Ar scrnmap Specifies a screen output map file for the .Fl l option. .El .Pp See .Xr rc.conf 5 for more details. .Ss Driver Configuration The video card driver may let you change default configuration options, such as the default font, so that you do not need to set up the options at boot time. See video card driver manuals, (e.g.\& .Xr vga 4 ) for details. .Sh FILES .Bl -tag -width /usr/share/syscons/scrnmaps/foo-bar -compact .It Pa /usr/share/syscons/fonts/* .It Pa /usr/share/vt/fonts/* font files. .It Pa /usr/share/syscons/scrnmaps/* screen output map files (relevant for .Xr syscons 4 only). .El .Sh EXAMPLES If you want to load .Pa /usr/share/syscons/fonts/iso-8x16.fnt to the kernel, run .Nm as: .Pp .Dl vidcontrol -f 8x16 /usr/share/syscons/fonts/iso-8x16.fnt .Pp So long as the font file is in .Pa /usr/share/syscons/fonts (if using syscons) or .Pa /usr/share/vt/fonts (if using vt), you may abbreviate the file name as .Pa iso-8x16 : .Pp .Dl vidcontrol -f 8x16 iso-8x16 .Pp Furthermore, you can also omit font size .Dq Li 8x16 : .Pp .Dl vidcontrol -f iso-8x16 .Pp Moreover, the suffix specifying the font size can be also omitted; in this case, .Nm will use the size of the currently displayed font to construct the suffix: .Pp .Dl vidcontrol -f iso .Pp Likewise, you can also abbreviate the screen output map file name for the .Fl l option if the file is found in .Pa /usr/share/syscons/scrnmaps . .Pp .Dl vidcontrol -l iso-8859-1_to_cp437 .Pp The above command will load .Pa /usr/share/syscons/scrnmaps/iso-8859-1_to_cp437.scm . .Pp The following command will set-up a 100x37 raster text mode (useful for some LCD models): .Pp .Dl vidcontrol -g 100x37 VESA_800x600 .Pp The following command will capture the contents of the first virtual terminal video buffer, and redirect the output to the .Pa shot.scr file: .Pp .Dl vidcontrol -p < /dev/ttyv0 > shot.scr .Pp The following command will dump contents of the fourth virtual terminal video buffer to the standard output in the human readable format: .Pp .Dl vidcontrol -P < /dev/ttyv3 .Sh SEE ALSO .Xr kbdcontrol 1 , .Xr vidfont 1 , .Xr keyboard 4 , .Xr screen 4 , .Xr syscons 4 , .Xr vga 4 , .Xr vt 4 , .Xr rc.conf 5 , .Xr kldload 8 , .Xr moused 8 , .Xr watch 8 .Pp The various .Pa scr2* utilities in the .Pa graphics and .Pa textproc categories of the .Em "Ports Collection" . .Sh AUTHORS .An S\(/oren Schmidt Aq Mt sos@FreeBSD.org .An Sascha Wildner Aq Mt saw@online.de .Sh CONTRIBUTORS .An -split .An Maxim Sobolev Aq Mt sobomax@FreeBSD.org .An Nik Clayton Aq Mt nik@FreeBSD.org Index: head/usr.sbin/vidcontrol/vidcontrol.c =================================================================== --- head/usr.sbin/vidcontrol/vidcontrol.c (revision 322877) +++ head/usr.sbin/vidcontrol/vidcontrol.c (revision 322878) @@ -1,1518 +1,1531 @@ /*- * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * * Portions of this software are based in part on the work of * Sascha Wildner contributed to The DragonFly Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $DragonFly: src/usr.sbin/vidcontrol/vidcontrol.c,v 1.10 2005/03/02 06:08:29 joerg Exp $ */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "path.h" #include "decode.h" #define DATASIZE(x) ((x).w * (x).h * 256 / 8) /* Screen dump modes */ #define DUMP_FMT_RAW 1 #define DUMP_FMT_TXT 2 /* Screen dump options */ #define DUMP_FBF 0 #define DUMP_ALL 1 /* Screen dump file format revision */ #define DUMP_FMT_REV 1 static const char *legal_colors[16] = { "black", "blue", "green", "cyan", "red", "magenta", "brown", "white", "grey", "lightblue", "lightgreen", "lightcyan", "lightred", "lightmagenta", "yellow", "lightwhite" }; static struct { int active_vty; vid_info_t console_info; unsigned char screen_map[256]; int video_mode_number; struct video_info video_mode_info; } cur_info; struct vt4font_header { uint8_t magic[8]; uint8_t width; uint8_t height; uint16_t pad; uint32_t glyph_count; uint32_t map_count[4]; } __packed; static int hex = 0; static int vesa_cols; static int vesa_rows; static int font_height; static int vt4_mode = 0; static int video_mode_changed; static struct video_info new_mode_info; /* * Initialize revert data. * * NOTE: the following parameters are not yet saved/restored: * * screen saver timeout * cursor type * mouse character and mouse show/hide state * vty switching on/off state * history buffer size * history contents * font maps */ static void init(void) { if (ioctl(0, VT_GETACTIVE, &cur_info.active_vty) == -1) err(1, "getting active vty"); cur_info.console_info.size = sizeof(cur_info.console_info); if (ioctl(0, CONS_GETINFO, &cur_info.console_info) == -1) err(1, "getting console information"); /* vt(4) use unicode, so no screen mapping required. */ if (vt4_mode == 0 && ioctl(0, GIO_SCRNMAP, &cur_info.screen_map) == -1) err(1, "getting screen map"); if (ioctl(0, CONS_GET, &cur_info.video_mode_number) == -1) err(1, "getting video mode number"); cur_info.video_mode_info.vi_mode = cur_info.video_mode_number; if (ioctl(0, CONS_MODEINFO, &cur_info.video_mode_info) == -1) err(1, "getting video mode parameters"); } /* * If something goes wrong along the way we call revert() to go back to the * console state we came from (which is assumed to be working). * * NOTE: please also read the comments of init(). */ static void revert(void) { int save_errno, size[3]; save_errno = errno; ioctl(0, VT_ACTIVATE, cur_info.active_vty); ioctl(0, KDSBORDER, cur_info.console_info.mv_ovscan); fprintf(stderr, "\033[=%dH", cur_info.console_info.mv_rev.fore); fprintf(stderr, "\033[=%dI", cur_info.console_info.mv_rev.back); if (vt4_mode == 0) ioctl(0, PIO_SCRNMAP, &cur_info.screen_map); if (video_mode_changed) { if (cur_info.video_mode_number >= M_VESA_BASE) ioctl(0, _IO('V', cur_info.video_mode_number - M_VESA_BASE), NULL); else ioctl(0, _IO('S', cur_info.video_mode_number), NULL); if (cur_info.video_mode_info.vi_flags & V_INFO_GRAPHICS) { size[0] = cur_info.video_mode_info.vi_width / 8; size[1] = cur_info.video_mode_info.vi_height / cur_info.console_info.font_size; size[2] = cur_info.console_info.font_size; ioctl(0, KDRASTER, size); } } /* Restore some colors last since mode setting forgets some. */ fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore); fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back); errno = save_errno; } /* * Print a short usage string describing all options, then exit. */ static void usage(void) { if (vt4_mode) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", "usage: vidcontrol [-CHPpx] [-b color] [-c appearance] [-f [[size] file]]", " [-g geometry] [-h size] [-i active | adapter | mode]", " [-M char] [-m on | off]", " [-r foreground background] [-S on | off] [-s number]", " [-T xterm | cons25] [-t N | off] [mode]", " [foreground [background]] [show]"); else fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", "usage: vidcontrol [-CdHLPpx] [-b color] [-c appearance] [-f [size] file]", " [-g geometry] [-h size] [-i active | adapter | mode]", " [-l screen_map] [-M char] [-m on | off]", " [-r foreground background] [-S on | off] [-s number]", " [-T xterm | cons25] [-t N | off] [mode]", " [foreground [background]] [show]"); exit(1); } /* Detect presence of vt(4). */ static int is_vt4(void) { char vty_name[4] = ""; size_t len = sizeof(vty_name); if (sysctlbyname("kern.vty", vty_name, &len, NULL, 0) != 0) return (0); return (strcmp(vty_name, "vt") == 0); } /* * Retrieve the next argument from the command line (for options that require * more than one argument). */ static char * nextarg(int ac, char **av, int *indp, int oc, int strict) { if (*indp < ac) return(av[(*indp)++]); if (strict != 0) { revert(); errx(1, "option requires two arguments -- %c", oc); } return(NULL); } /* * Guess which file to open. Try to open each combination of a specified set * of file name components. */ static FILE * openguess(const char *a[], const char *b[], const char *c[], const char *d[], char **name) { FILE *f; int i, j, k, l; for (i = 0; a[i] != NULL; i++) { for (j = 0; b[j] != NULL; j++) { for (k = 0; c[k] != NULL; k++) { for (l = 0; d[l] != NULL; l++) { asprintf(name, "%s%s%s%s", a[i], b[j], c[k], d[l]); f = fopen(*name, "r"); if (f != NULL) return (f); free(*name); } } } } return (NULL); } /* * Load a screenmap from a file and set it. */ static void load_scrnmap(const char *filename) { FILE *fd; int size; char *name; scrmap_t scrnmap; const char *a[] = {"", SCRNMAP_PATH, NULL}; const char *b[] = {filename, NULL}; const char *c[] = {"", ".scm", NULL}; const char *d[] = {"", NULL}; fd = openguess(a, b, c, d, &name); if (fd == NULL) { revert(); errx(1, "screenmap file not found"); } size = sizeof(scrnmap); if (decode(fd, (char *)&scrnmap, size) != size) { rewind(fd); if (fread(&scrnmap, 1, size, fd) != (size_t)size) { fclose(fd); revert(); errx(1, "bad screenmap file"); } } if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) { revert(); err(1, "loading screenmap"); } fclose(fd); } /* * Set the default screenmap. */ static void load_default_scrnmap(void) { scrmap_t scrnmap; int i; for (i=0; i<256; i++) *((char*)&scrnmap + i) = i; if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) { revert(); err(1, "loading default screenmap"); } } /* * Print the current screenmap to stdout. */ static void print_scrnmap(void) { unsigned char map[256]; size_t i; if (ioctl(0, GIO_SCRNMAP, &map) == -1) { revert(); err(1, "getting screenmap"); } for (i=0; ishape[0]; while ((word = strsep(¶m, ",")) != NULL) { if (strcmp(word, "normal") == 0) type = 0; else if (strcmp(word, "destructive") == 0) type = CONS_BLINK_CURSOR | CONS_CHAR_CURSOR; else if (strcmp(word, "blink") == 0) type |= CONS_BLINK_CURSOR; else if (strcmp(word, "noblink") == 0) type &= ~CONS_BLINK_CURSOR; else if (strcmp(word, "block") == 0) type &= ~CONS_CHAR_CURSOR; else if (strcmp(word, "noblock") == 0) type |= CONS_CHAR_CURSOR; else if (strcmp(word, "hidden") == 0) type |= CONS_HIDDEN_CURSOR; else if (strcmp(word, "nohidden") == 0) type &= ~CONS_HIDDEN_CURSOR; else if (strncmp(word, "base=", 5) == 0) shape->shape[1] = strtol(word + 5, NULL, 0); else if (strncmp(word, "height=", 7) == 0) shape->shape[2] = strtol(word + 7, NULL, 0); + else if (strcmp(word, "charcolors") == 0) + type |= CONS_CHARCURSOR_COLORS; + else if (strcmp(word, "mousecolors") == 0) + type |= CONS_MOUSECURSOR_COLORS; + else if (strcmp(word, "default") == 0) + type |= CONS_DEFAULT_CURSOR; + else if (strcmp(word, "shapeonly") == 0) + type |= CONS_SHAPEONLY_CURSOR; else if (strcmp(word, "local") == 0) type |= CONS_LOCAL_CURSOR; else if (strcmp(word, "reset") == 0) type |= CONS_RESET_CURSOR; + else if (strcmp(word, "show") == 0) + printf("flags %#x, base %d, height %d\n", + type, shape->shape[1], shape->shape[2]); else { revert(); errx(1, "invalid parameters for -c starting at '%s%s%s'", word, param != NULL ? "," : "", param != NULL ? param : ""); } } free(dupparam); shape->shape[0] = type; } /* * Set the cursor's shape/type. */ static void set_cursor_type(char *param) { struct cshape shape; - /* Determine if the new setting is local (default to non-local). */ + /* Dry run to determine color, default and local flags. */ shape.shape[0] = 0; + shape.shape[1] = -1; + shape.shape[2] = -1; parse_cursor_params(param, &shape); - /* Get the relevant shape (the local flag is the only input arg). */ + /* Get the relevant old setting. */ if (ioctl(0, CONS_GETCURSORSHAPE, &shape) != 0) { revert(); err(1, "ioctl(CONS_GETCURSORSHAPE)"); } parse_cursor_params(param, &shape); if (ioctl(0, CONS_SETCURSORSHAPE, &shape) != 0) { revert(); err(1, "ioctl(CONS_SETCURSORSHAPE)"); } } /* * Set the video mode. */ static void video_mode(int argc, char **argv, int *mode_index) { static struct { const char *name; unsigned long mode; unsigned long mode_num; } modes[] = { { "80x25", SW_TEXT_80x25, M_TEXT_80x25 }, { "80x30", SW_TEXT_80x30, M_TEXT_80x30 }, { "80x43", SW_TEXT_80x43, M_TEXT_80x43 }, { "80x50", SW_TEXT_80x50, M_TEXT_80x50 }, { "80x60", SW_TEXT_80x60, M_TEXT_80x60 }, { "132x25", SW_TEXT_132x25, M_TEXT_132x25 }, { "132x30", SW_TEXT_132x30, M_TEXT_132x30 }, { "132x43", SW_TEXT_132x43, M_TEXT_132x43 }, { "132x50", SW_TEXT_132x50, M_TEXT_132x50 }, { "132x60", SW_TEXT_132x60, M_TEXT_132x60 }, { "VGA_40x25", SW_VGA_C40x25, M_VGA_C40x25 }, { "VGA_80x25", SW_VGA_C80x25, M_VGA_C80x25 }, { "VGA_80x30", SW_VGA_C80x30, M_VGA_C80x30 }, { "VGA_80x50", SW_VGA_C80x50, M_VGA_C80x50 }, { "VGA_80x60", SW_VGA_C80x60, M_VGA_C80x60 }, #ifdef SW_VGA_C90x25 { "VGA_90x25", SW_VGA_C90x25, M_VGA_C90x25 }, { "VGA_90x30", SW_VGA_C90x30, M_VGA_C90x30 }, { "VGA_90x43", SW_VGA_C90x43, M_VGA_C90x43 }, { "VGA_90x50", SW_VGA_C90x50, M_VGA_C90x50 }, { "VGA_90x60", SW_VGA_C90x60, M_VGA_C90x60 }, #endif { "VGA_320x200", SW_VGA_CG320, M_CG320 }, { "EGA_80x25", SW_ENH_C80x25, M_ENH_C80x25 }, { "EGA_80x43", SW_ENH_C80x43, M_ENH_C80x43 }, { "VESA_132x25", SW_VESA_C132x25,M_VESA_C132x25 }, { "VESA_132x43", SW_VESA_C132x43,M_VESA_C132x43 }, { "VESA_132x50", SW_VESA_C132x50,M_VESA_C132x50 }, { "VESA_132x60", SW_VESA_C132x60,M_VESA_C132x60 }, { "VESA_800x600", SW_VESA_800x600,M_VESA_800x600 }, { NULL, 0, 0 }, }; int new_mode_num = 0; unsigned long mode = 0; int cur_mode; int save_errno; int size[3]; int i; if (ioctl(0, CONS_GET, &cur_mode) < 0) err(1, "cannot get the current video mode"); /* * Parse the video mode argument... */ if (*mode_index < argc) { if (!strncmp(argv[*mode_index], "MODE_", 5)) { if (!isdigit(argv[*mode_index][5])) errx(1, "invalid video mode number"); new_mode_num = atoi(&argv[*mode_index][5]); } else { for (i = 0; modes[i].name != NULL; ++i) { if (!strcmp(argv[*mode_index], modes[i].name)) { mode = modes[i].mode; new_mode_num = modes[i].mode_num; break; } } if (modes[i].name == NULL) return; if (ioctl(0, mode, NULL) < 0) { revert(); err(1, "cannot set videomode"); } video_mode_changed = 1; } /* * Collect enough information about the new video mode... */ new_mode_info.vi_mode = new_mode_num; if (ioctl(0, CONS_MODEINFO, &new_mode_info) == -1) { revert(); err(1, "obtaining new video mode parameters"); } if (mode == 0) { if (new_mode_num >= M_VESA_BASE) mode = _IO('V', new_mode_num - M_VESA_BASE); else mode = _IO('S', new_mode_num); } /* * Try setting the new mode. */ if (ioctl(0, mode, NULL) == -1) { revert(); err(1, "setting video mode"); } video_mode_changed = 1; /* * For raster modes it's not enough to just set the mode. * We also need to explicitly set the raster mode. */ if (new_mode_info.vi_flags & V_INFO_GRAPHICS) { /* font size */ if (font_height == 0) font_height = cur_info.console_info.font_size; size[2] = font_height; /* adjust columns */ if ((vesa_cols * 8 > new_mode_info.vi_width) || (vesa_cols <= 0)) { size[0] = new_mode_info.vi_width / 8; } else { size[0] = vesa_cols; } /* adjust rows */ if ((vesa_rows * font_height > new_mode_info.vi_height) || (vesa_rows <= 0)) { size[1] = new_mode_info.vi_height / font_height; } else { size[1] = vesa_rows; } /* set raster mode */ if (ioctl(0, KDRASTER, size)) { save_errno = errno; if (cur_mode >= M_VESA_BASE) ioctl(0, _IO('V', cur_mode - M_VESA_BASE), NULL); else ioctl(0, _IO('S', cur_mode), NULL); revert(); errno = save_errno; err(1, "cannot activate raster display"); } } /* Recover from mode setting forgetting colors. */ fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore); fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back); (*mode_index)++; } } /* * Return the number for a specified color name. */ static int get_color_number(char *color) { int i; for (i=0; i<16; i++) { if (!strcmp(color, legal_colors[i])) return i; } return -1; } /* * Set normal text and background colors. */ static void set_normal_colors(int argc, char **argv, int *_index) { int color; if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dF", color); if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dG", color); } } } /* * Set reverse text and background colors. */ static void set_reverse_colors(int argc, char **argv, int *_index) { int color; if ((color = get_color_number(argv[*(_index)-1])) != -1) { fprintf(stderr, "\033[=%dH", color); if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dI", color); } } } /* * Switch to virtual terminal #arg. */ static void set_console(char *arg) { int n; if(!arg || strspn(arg,"0123456789") != strlen(arg)) { revert(); errx(1, "bad console number"); } n = atoi(arg); if (n < 1 || n > 16) { revert(); errx(1, "console number out of range"); } else if (ioctl(0, VT_ACTIVATE, n) == -1) { revert(); err(1, "switching vty"); } } /* * Sets the border color. */ static void set_border_color(char *arg) { int color; color = get_color_number(arg); if (color == -1) { revert(); errx(1, "invalid color '%s'", arg); } if (ioctl(0, KDSBORDER, color) != 0) { revert(); err(1, "ioctl(KD_SBORDER)"); } } static void set_mouse_char(char *arg) { struct mouse_info mouse; long l; l = strtol(arg, NULL, 0); if ((l < 0) || (l > UCHAR_MAX - 3)) { revert(); warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3); return; } mouse.operation = MOUSE_MOUSECHAR; mouse.u.mouse_char = (int)l; if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) { revert(); err(1, "setting mouse character"); } } /* * Show/hide the mouse. */ static void set_mouse(char *arg) { struct mouse_info mouse; if (!strcmp(arg, "on")) { mouse.operation = MOUSE_SHOW; } else if (!strcmp(arg, "off")) { mouse.operation = MOUSE_HIDE; } else { revert(); errx(1, "argument to -m must be either on or off"); } if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) { revert(); err(1, "%sing the mouse", mouse.operation == MOUSE_SHOW ? "show" : "hid"); } } static void set_lockswitch(char *arg) { int data; if (!strcmp(arg, "off")) { data = 0x01; } else if (!strcmp(arg, "on")) { data = 0x02; } else { revert(); errx(1, "argument to -S must be either on or off"); } if (ioctl(0, VT_LOCKSWITCH, &data) == -1) { revert(); err(1, "turning %s vty switching", data == 0x01 ? "off" : "on"); } } /* * Return the adapter name for a specified type. */ static const char *adapter_name(int type) { static struct { int type; const char *name; } names[] = { { KD_MONO, "MDA" }, { KD_HERCULES, "Hercules" }, { KD_CGA, "CGA" }, { KD_EGA, "EGA" }, { KD_VGA, "VGA" }, { KD_TGA, "TGA" }, { -1, "Unknown" }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == type) break; return names[i].name; } /* * Show active VTY, ie current console number. */ static void show_active_info(void) { printf("%d\n", cur_info.active_vty); } /* * Show graphics adapter information. */ static void show_adapter_info(void) { struct video_adapter_info ad; ad.va_index = 0; if (ioctl(0, CONS_ADPINFO, &ad) == -1) { revert(); err(1, "obtaining adapter information"); } printf("fb%d:\n", ad.va_index); printf(" %.*s%d, type:%s%s (%d), flags:0x%x\n", (int)sizeof(ad.va_name), ad.va_name, ad.va_unit, (ad.va_flags & V_ADP_VESA) ? "VESA " : "", adapter_name(ad.va_type), ad.va_type, ad.va_flags); printf(" initial mode:%d, current mode:%d, BIOS mode:%d\n", ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode); printf(" frame buffer window:0x%zx, buffer size:0x%zx\n", ad.va_window, ad.va_buffer_size); printf(" window size:0x%zx, origin:0x%x\n", ad.va_window_size, ad.va_window_orig); printf(" display start address (%d, %d), scan line width:%d\n", ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width); printf(" reserved:0x%zx\n", ad.va_unused0); } /* * Show video mode information. */ static void show_mode_info(void) { char buf[80]; struct video_info info; int c; int mm; int mode; printf(" mode# flags type size " "font window linear buffer\n"); printf("---------------------------------------" "---------------------------------------\n"); memset(&info, 0, sizeof(info)); for (mode = 0; mode <= M_VESA_MODE_MAX; ++mode) { info.vi_mode = mode; if (ioctl(0, CONS_MODEINFO, &info)) continue; if (info.vi_mode != mode) continue; if (info.vi_width == 0 && info.vi_height == 0 && info.vi_cwidth == 0 && info.vi_cheight == 0) continue; printf("%3d (0x%03x)", mode, mode); printf(" 0x%08x", info.vi_flags); if (info.vi_flags & V_INFO_GRAPHICS) { c = 'G'; if (info.vi_mem_model == V_INFO_MM_PLANAR) snprintf(buf, sizeof(buf), "%dx%dx%d %d", info.vi_width, info.vi_height, info.vi_depth, info.vi_planes); else { switch (info.vi_mem_model) { case V_INFO_MM_PACKED: mm = 'P'; break; case V_INFO_MM_DIRECT: mm = 'D'; break; case V_INFO_MM_CGA: mm = 'C'; break; case V_INFO_MM_HGC: mm = 'H'; break; case V_INFO_MM_VGAX: mm = 'V'; break; default: mm = ' '; break; } snprintf(buf, sizeof(buf), "%dx%dx%d %c", info.vi_width, info.vi_height, info.vi_depth, mm); } } else { c = 'T'; snprintf(buf, sizeof(buf), "%dx%d", info.vi_width, info.vi_height); } printf(" %c %-15s", c, buf); snprintf(buf, sizeof(buf), "%dx%d", info.vi_cwidth, info.vi_cheight); printf(" %-5s", buf); printf(" 0x%05zx %2dk %2dk", info.vi_window, (int)info.vi_window_size/1024, (int)info.vi_window_gran/1024); printf(" 0x%08zx %dk\n", info.vi_buffer, (int)info.vi_buffer_size/1024); } } static void show_info(char *arg) { if (!strcmp(arg, "active")) { show_active_info(); } else if (!strcmp(arg, "adapter")) { show_adapter_info(); } else if (!strcmp(arg, "mode")) { show_mode_info(); } else { revert(); errx(1, "argument to -i must be active, adapter, or mode"); } } static void test_frame(void) { vid_info_t info; const char *bg, *sep; int i, fore; info.size = sizeof(info); if (ioctl(0, CONS_GETINFO, &info) == -1) err(1, "getting console information"); fore = 15; if (info.mv_csz < 80) { bg = "BG"; sep = " "; } else { bg = "BACKGROUND"; sep = " "; } fprintf(stdout, "\033[=0G\n\n"); for (i=0; i<8; i++) { fprintf(stdout, "\033[=%dF\033[=0G%2d \033[=%dF%-7s%s" "\033[=%dF\033[=0G%2d \033[=%dF%-12s%s" "\033[=%dF%2d \033[=%dG%s\033[=0G%s" "\033[=%dF%2d \033[=%dG%s\033[=0G\n", fore, i, i, legal_colors[i], sep, fore, i + 8, i + 8, legal_colors[i + 8], sep, fore, i, i, bg, sep, fore, i + 8, i + 8, bg); } fprintf(stdout, "\033[=%dF\033[=%dG\033[=%dH\033[=%dI\n", info.mv_norm.fore, info.mv_norm.back, info.mv_rev.fore, info.mv_rev.back); } /* * Snapshot the video memory of that terminal, using the CONS_SCRSHOT * ioctl, and writes the results to stdout either in the special * binary format (see manual page for details), or in the plain * text format. */ static void dump_screen(int mode, int opt) { scrshot_t shot; vid_info_t info; info.size = sizeof(info); if (ioctl(0, CONS_GETINFO, &info) == -1) { revert(); err(1, "getting console information"); } shot.x = shot.y = 0; shot.xsize = info.mv_csz; shot.ysize = info.mv_rsz; if (opt == DUMP_ALL) shot.ysize += info.mv_hsz; shot.buf = alloca(shot.xsize * shot.ysize * sizeof(u_int16_t)); if (shot.buf == NULL) { revert(); errx(1, "failed to allocate memory for dump"); } if (ioctl(0, CONS_SCRSHOT, &shot) == -1) { revert(); err(1, "dumping screen"); } if (mode == DUMP_FMT_RAW) { printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2, shot.xsize, shot.ysize); fflush(stdout); write(STDOUT_FILENO, shot.buf, shot.xsize * shot.ysize * sizeof(u_int16_t)); } else { char *line; int x, y; u_int16_t ch; line = alloca(shot.xsize + 1); if (line == NULL) { revert(); errx(1, "failed to allocate memory for line buffer"); } for (y = 0; y < shot.ysize; y++) { for (x = 0; x < shot.xsize; x++) { ch = shot.buf[x + (y * shot.xsize)]; ch &= 0xff; if (isprint(ch) == 0) ch = ' '; line[x] = (char)ch; } /* Trim trailing spaces */ do { line[x--] = '\0'; } while (line[x] == ' ' && x != 0); puts(line); } fflush(stdout); } } /* * Set the console history buffer size. */ static void set_history(char *opt) { int size; size = atoi(opt); if ((*opt == '\0') || size < 0) { revert(); errx(1, "argument must be a positive number"); } if (ioctl(0, CONS_HISTORY, &size) == -1) { revert(); err(1, "setting history buffer size"); } } /* * Clear the console history buffer. */ static void clear_history(void) { if (ioctl(0, CONS_CLRHIST) == -1) { revert(); err(1, "clearing history buffer"); } } static void set_terminal_mode(char *arg) { if (strcmp(arg, "xterm") == 0) fprintf(stderr, "\033[=T"); else if (strcmp(arg, "cons25") == 0) fprintf(stderr, "\033[=1T"); } int main(int argc, char **argv) { char *font, *type, *termmode; const char *opts; int dumpmod, dumpopt, opt; vt4_mode = is_vt4(); init(); dumpmod = 0; dumpopt = DUMP_FBF; termmode = NULL; if (vt4_mode) opts = "b:Cc:fg:h:Hi:M:m:pPr:S:s:T:t:x"; else opts = "b:Cc:dfg:h:Hi:l:LM:m:pPr:S:s:T:t:x"; while ((opt = getopt(argc, argv, opts)) != -1) switch(opt) { case 'b': set_border_color(optarg); break; case 'C': clear_history(); break; case 'c': set_cursor_type(optarg); break; case 'd': if (vt4_mode) break; print_scrnmap(); break; case 'f': optarg = nextarg(argc, argv, &optind, 'f', 0); if (optarg != NULL) { font = nextarg(argc, argv, &optind, 'f', 0); if (font == NULL) { type = NULL; font = optarg; } else type = optarg; load_font(type, font); } else { if (!vt4_mode) usage(); /* Switch syscons to ROM? */ load_default_vt4font(); } break; case 'g': if (sscanf(optarg, "%dx%d", &vesa_cols, &vesa_rows) != 2) { revert(); warnx("incorrect geometry: %s", optarg); usage(); } break; case 'h': set_history(optarg); break; case 'H': dumpopt = DUMP_ALL; break; case 'i': show_info(optarg); break; case 'l': if (vt4_mode) break; load_scrnmap(optarg); break; case 'L': if (vt4_mode) break; load_default_scrnmap(); break; case 'M': set_mouse_char(optarg); break; case 'm': set_mouse(optarg); break; case 'p': dumpmod = DUMP_FMT_RAW; break; case 'P': dumpmod = DUMP_FMT_TXT; break; case 'r': set_reverse_colors(argc, argv, &optind); break; case 'S': set_lockswitch(optarg); break; case 's': set_console(optarg); break; case 'T': if (strcmp(optarg, "xterm") != 0 && strcmp(optarg, "cons25") != 0) usage(); termmode = optarg; break; case 't': set_screensaver_timeout(optarg); break; case 'x': hex = 1; break; default: usage(); } if (dumpmod != 0) dump_screen(dumpmod, dumpopt); video_mode(argc, argv, &optind); set_normal_colors(argc, argv, &optind); if (optind < argc && !strcmp(argv[optind], "show")) { test_frame(); optind++; } if (termmode != NULL) set_terminal_mode(termmode); if ((optind != argc) || (argc == 1)) usage(); return (0); }