Index: head/sys/dev/vt/hw/vga/vt_vga.c =================================================================== --- head/sys/dev/vt/hw/vga/vt_vga.c (revision 270330) +++ head/sys/dev/vt/hw/vga/vt_vga.c (revision 270331) @@ -1,1137 +1,1109 @@ /*- * Copyright (c) 2005 Marcel Moolenaar * All rights reserved. * * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Ed Schouten * under sponsorship from the FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 #include #include #include #include #include #if defined(__amd64__) || defined(__i386__) #include #include #include #include #endif /* __amd64__ || __i386__ */ struct vga_softc { bus_space_tag_t vga_fb_tag; bus_space_handle_t vga_fb_handle; bus_space_tag_t vga_reg_tag; bus_space_handle_t vga_reg_handle; term_color_t vga_curfg, vga_curbg; }; /* Convenience macros. */ #define MEM_READ1(sc, ofs) \ bus_space_read_1(sc->vga_fb_tag, sc->vga_fb_handle, ofs) #define MEM_WRITE1(sc, ofs, val) \ bus_space_write_1(sc->vga_fb_tag, sc->vga_fb_handle, ofs, val) #define REG_READ1(sc, reg) \ bus_space_read_1(sc->vga_reg_tag, sc->vga_reg_handle, reg) #define REG_WRITE1(sc, reg, val) \ bus_space_write_1(sc->vga_reg_tag, sc->vga_reg_handle, reg, val) #define VT_VGA_WIDTH 640 #define VT_VGA_HEIGHT 480 #define VT_VGA_MEMSIZE (VT_VGA_WIDTH * VT_VGA_HEIGHT / 8) /* * VGA is designed to handle 8 pixels at a time (8 pixels in one byte of * memory). */ #define VT_VGA_PIXELS_BLOCK 8 /* * We use an off-screen addresses to: * o store the background color; * o store pixels pattern. * Those addresses are then loaded in the latches once. */ #define VT_VGA_BGCOLOR_OFFSET VT_VGA_MEMSIZE static vd_probe_t vga_probe; static vd_init_t vga_init; static vd_blank_t vga_blank; static vd_bitblt_text_t vga_bitblt_text; static vd_drawrect_t vga_drawrect; static vd_setpixel_t vga_setpixel; static vd_postswitch_t vga_postswitch; static const struct vt_driver vt_vga_driver = { .vd_name = "vga", .vd_probe = vga_probe, .vd_init = vga_init, .vd_blank = vga_blank, .vd_bitblt_text = vga_bitblt_text, .vd_drawrect = vga_drawrect, .vd_setpixel = vga_setpixel, .vd_postswitch = vga_postswitch, .vd_priority = VD_PRIORITY_GENERIC, }; /* * Driver supports both text mode and graphics mode. Make sure the * buffer is always big enough to support both. */ static struct vga_softc vga_conssoftc; VT_DRIVER_DECLARE(vt_vga, vt_vga_driver); static inline void vga_setfg(struct vt_device *vd, term_color_t color) { struct vga_softc *sc = vd->vd_softc; if (sc->vga_curfg != color) { REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, color); sc->vga_curfg = color; } } static inline void vga_setbg(struct vt_device *vd, term_color_t color) { struct vga_softc *sc = vd->vd_softc; if (sc->vga_curbg != color) { REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, color); /* * Write 8 pixels using the background color to an * off-screen byte in the video memory. */ MEM_WRITE1(sc, VT_VGA_BGCOLOR_OFFSET, 0xff); /* * Read those 8 pixels back to load the background color * in the latches register. */ MEM_READ1(sc, VT_VGA_BGCOLOR_OFFSET); sc->vga_curbg = color; /* * The Set/Reset register doesn't contain the fg color * anymore, store an invalid color. */ sc->vga_curfg = 0xff; } } /* * Binary searchable table for Unicode to CP437 conversion. */ struct unicp437 { uint16_t unicode_base; uint8_t cp437_base; uint8_t length; }; static const struct unicp437 cp437table[] = { { 0x0020, 0x20, 0x5e }, { 0x00a0, 0x20, 0x00 }, { 0x00a1, 0xad, 0x00 }, { 0x00a2, 0x9b, 0x00 }, { 0x00a3, 0x9c, 0x00 }, { 0x00a5, 0x9d, 0x00 }, { 0x00a7, 0x15, 0x00 }, { 0x00aa, 0xa6, 0x00 }, { 0x00ab, 0xae, 0x00 }, { 0x00ac, 0xaa, 0x00 }, { 0x00b0, 0xf8, 0x00 }, { 0x00b1, 0xf1, 0x00 }, { 0x00b2, 0xfd, 0x00 }, { 0x00b5, 0xe6, 0x00 }, { 0x00b6, 0x14, 0x00 }, { 0x00b7, 0xfa, 0x00 }, { 0x00ba, 0xa7, 0x00 }, { 0x00bb, 0xaf, 0x00 }, { 0x00bc, 0xac, 0x00 }, { 0x00bd, 0xab, 0x00 }, { 0x00bf, 0xa8, 0x00 }, { 0x00c4, 0x8e, 0x01 }, { 0x00c6, 0x92, 0x00 }, { 0x00c7, 0x80, 0x00 }, { 0x00c9, 0x90, 0x00 }, { 0x00d1, 0xa5, 0x00 }, { 0x00d6, 0x99, 0x00 }, { 0x00dc, 0x9a, 0x00 }, { 0x00df, 0xe1, 0x00 }, { 0x00e0, 0x85, 0x00 }, { 0x00e1, 0xa0, 0x00 }, { 0x00e2, 0x83, 0x00 }, { 0x00e4, 0x84, 0x00 }, { 0x00e5, 0x86, 0x00 }, { 0x00e6, 0x91, 0x00 }, { 0x00e7, 0x87, 0x00 }, { 0x00e8, 0x8a, 0x00 }, { 0x00e9, 0x82, 0x00 }, { 0x00ea, 0x88, 0x01 }, { 0x00ec, 0x8d, 0x00 }, { 0x00ed, 0xa1, 0x00 }, { 0x00ee, 0x8c, 0x00 }, { 0x00ef, 0x8b, 0x00 }, { 0x00f0, 0xeb, 0x00 }, { 0x00f1, 0xa4, 0x00 }, { 0x00f2, 0x95, 0x00 }, { 0x00f3, 0xa2, 0x00 }, { 0x00f4, 0x93, 0x00 }, { 0x00f6, 0x94, 0x00 }, { 0x00f7, 0xf6, 0x00 }, { 0x00f8, 0xed, 0x00 }, { 0x00f9, 0x97, 0x00 }, { 0x00fa, 0xa3, 0x00 }, { 0x00fb, 0x96, 0x00 }, { 0x00fc, 0x81, 0x00 }, { 0x00ff, 0x98, 0x00 }, { 0x0192, 0x9f, 0x00 }, { 0x0393, 0xe2, 0x00 }, { 0x0398, 0xe9, 0x00 }, { 0x03a3, 0xe4, 0x00 }, { 0x03a6, 0xe8, 0x00 }, { 0x03a9, 0xea, 0x00 }, { 0x03b1, 0xe0, 0x01 }, { 0x03b4, 0xeb, 0x00 }, { 0x03b5, 0xee, 0x00 }, { 0x03bc, 0xe6, 0x00 }, { 0x03c0, 0xe3, 0x00 }, { 0x03c3, 0xe5, 0x00 }, { 0x03c4, 0xe7, 0x00 }, { 0x03c6, 0xed, 0x00 }, { 0x03d5, 0xed, 0x00 }, { 0x2010, 0x2d, 0x00 }, { 0x2014, 0x2d, 0x00 }, { 0x2018, 0x60, 0x00 }, { 0x2019, 0x27, 0x00 }, { 0x201c, 0x22, 0x00 }, { 0x201d, 0x22, 0x00 }, { 0x2022, 0x07, 0x00 }, { 0x203c, 0x13, 0x00 }, { 0x207f, 0xfc, 0x00 }, { 0x20a7, 0x9e, 0x00 }, { 0x20ac, 0xee, 0x00 }, { 0x2126, 0xea, 0x00 }, { 0x2190, 0x1b, 0x00 }, { 0x2191, 0x18, 0x00 }, { 0x2192, 0x1a, 0x00 }, { 0x2193, 0x19, 0x00 }, { 0x2194, 0x1d, 0x00 }, { 0x2195, 0x12, 0x00 }, { 0x21a8, 0x17, 0x00 }, { 0x2202, 0xeb, 0x00 }, { 0x2208, 0xee, 0x00 }, { 0x2211, 0xe4, 0x00 }, { 0x2212, 0x2d, 0x00 }, { 0x2219, 0xf9, 0x00 }, { 0x221a, 0xfb, 0x00 }, { 0x221e, 0xec, 0x00 }, { 0x221f, 0x1c, 0x00 }, { 0x2229, 0xef, 0x00 }, { 0x2248, 0xf7, 0x00 }, { 0x2261, 0xf0, 0x00 }, { 0x2264, 0xf3, 0x00 }, { 0x2265, 0xf2, 0x00 }, { 0x2302, 0x7f, 0x00 }, { 0x2310, 0xa9, 0x00 }, { 0x2320, 0xf4, 0x00 }, { 0x2321, 0xf5, 0x00 }, { 0x2500, 0xc4, 0x00 }, { 0x2502, 0xb3, 0x00 }, { 0x250c, 0xda, 0x00 }, { 0x2510, 0xbf, 0x00 }, { 0x2514, 0xc0, 0x00 }, { 0x2518, 0xd9, 0x00 }, { 0x251c, 0xc3, 0x00 }, { 0x2524, 0xb4, 0x00 }, { 0x252c, 0xc2, 0x00 }, { 0x2534, 0xc1, 0x00 }, { 0x253c, 0xc5, 0x00 }, { 0x2550, 0xcd, 0x00 }, { 0x2551, 0xba, 0x00 }, { 0x2552, 0xd5, 0x00 }, { 0x2553, 0xd6, 0x00 }, { 0x2554, 0xc9, 0x00 }, { 0x2555, 0xb8, 0x00 }, { 0x2556, 0xb7, 0x00 }, { 0x2557, 0xbb, 0x00 }, { 0x2558, 0xd4, 0x00 }, { 0x2559, 0xd3, 0x00 }, { 0x255a, 0xc8, 0x00 }, { 0x255b, 0xbe, 0x00 }, { 0x255c, 0xbd, 0x00 }, { 0x255d, 0xbc, 0x00 }, { 0x255e, 0xc6, 0x01 }, { 0x2560, 0xcc, 0x00 }, { 0x2561, 0xb5, 0x00 }, { 0x2562, 0xb6, 0x00 }, { 0x2563, 0xb9, 0x00 }, { 0x2564, 0xd1, 0x01 }, { 0x2566, 0xcb, 0x00 }, { 0x2567, 0xcf, 0x00 }, { 0x2568, 0xd0, 0x00 }, { 0x2569, 0xca, 0x00 }, { 0x256a, 0xd8, 0x00 }, { 0x256b, 0xd7, 0x00 }, { 0x256c, 0xce, 0x00 }, { 0x2580, 0xdf, 0x00 }, { 0x2584, 0xdc, 0x00 }, { 0x2588, 0xdb, 0x00 }, { 0x258c, 0xdd, 0x00 }, { 0x2590, 0xde, 0x00 }, { 0x2591, 0xb0, 0x02 }, { 0x25a0, 0xfe, 0x00 }, { 0x25ac, 0x16, 0x00 }, { 0x25b2, 0x1e, 0x00 }, { 0x25ba, 0x10, 0x00 }, { 0x25bc, 0x1f, 0x00 }, { 0x25c4, 0x11, 0x00 }, { 0x25cb, 0x09, 0x00 }, { 0x25d8, 0x08, 0x00 }, { 0x25d9, 0x0a, 0x00 }, { 0x263a, 0x01, 0x01 }, { 0x263c, 0x0f, 0x00 }, { 0x2640, 0x0c, 0x00 }, { 0x2642, 0x0b, 0x00 }, { 0x2660, 0x06, 0x00 }, { 0x2663, 0x05, 0x00 }, { 0x2665, 0x03, 0x01 }, { 0x266a, 0x0d, 0x00 }, { 0x266c, 0x0e, 0x00 }, }; static uint8_t vga_get_cp437(term_char_t c) { int min, mid, max; min = 0; max = (sizeof(cp437table) / sizeof(struct unicp437)) - 1; if (c < cp437table[0].unicode_base || c > cp437table[max].unicode_base + cp437table[max].length) return '?'; while (max >= min) { mid = (min + max) / 2; if (c < cp437table[mid].unicode_base) max = mid - 1; else if (c > cp437table[mid].unicode_base + cp437table[mid].length) min = mid + 1; else return (c - cp437table[mid].unicode_base + cp437table[mid].cp437_base); } return '?'; } static void vga_blank(struct vt_device *vd, term_color_t color) { struct vga_softc *sc = vd->vd_softc; u_int ofs; vga_setfg(vd, color); for (ofs = 0; ofs < VT_VGA_MEMSIZE; ofs++) MEM_WRITE1(sc, ofs, 0xff); } static inline void vga_bitblt_put(struct vt_device *vd, u_long dst, term_color_t color, uint8_t v) { struct vga_softc *sc = vd->vd_softc; /* Skip empty writes, in order to avoid palette changes. */ if (v != 0x00) { vga_setfg(vd, color); /* * When this MEM_READ1() gets disabled, all sorts of * artifacts occur. This is because this read loads the * set of 8 pixels that are about to be changed. There * is one scenario where we can avoid the read, namely * if all pixels are about to be overwritten anyway. */ if (v != 0xff) { MEM_READ1(sc, dst); /* The bg color was trashed by the reads. */ sc->vga_curbg = 0xff; } MEM_WRITE1(sc, dst, v); } } static void vga_setpixel(struct vt_device *vd, int x, int y, term_color_t color) { vga_bitblt_put(vd, (y * VT_VGA_WIDTH / 8) + (x / 8), color, 0x80 >> (x % 8)); } static void vga_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, term_color_t color) { int x, y; for (y = y1; y <= y2; y++) { if (fill || (y == y1) || (y == y2)) { for (x = x1; x <= x2; x++) vga_setpixel(vd, x, y, color); } else { vga_setpixel(vd, x1, y, color); vga_setpixel(vd, x2, y, color); } } } static void vga_compute_shifted_pattern(const uint8_t *src, unsigned int bytes, unsigned int src_x, unsigned int x_count, unsigned int dst_x, uint8_t *pattern, uint8_t *mask) { unsigned int n; n = src_x / 8; /* * This mask has bits set, where a pixel (ether 0 or 1) * comes from the source bitmap. */ if (mask != NULL) { *mask = (0xff >> (8 - x_count)) << (8 - x_count - dst_x); } if (n == (src_x + x_count - 1) / 8) { /* All the pixels we want are in the same byte. */ *pattern = src[n]; if (dst_x >= src_x) *pattern >>= (dst_x - src_x % 8); else *pattern <<= (src_x % 8 - dst_x); } else { /* The pixels we want are split into two bytes. */ if (dst_x >= src_x % 8) { *pattern = src[n] << (8 - dst_x - src_x % 8) | src[n + 1] >> (dst_x - src_x % 8); } else { *pattern = src[n] << (src_x % 8 - dst_x) | src[n + 1] >> (8 - src_x % 8 - dst_x); } } } static void vga_copy_bitmap_portion(uint8_t *pattern_2colors, uint8_t *pattern_ncolors, const uint8_t *src, const uint8_t *src_mask, unsigned int src_width, unsigned int src_x, unsigned int dst_x, unsigned int x_count, unsigned int src_y, unsigned int dst_y, unsigned int y_count, term_color_t fg, term_color_t bg, int overwrite) { unsigned int i, bytes; uint8_t pattern, relevant_bits, mask; bytes = (src_width + 7) / 8; for (i = 0; i < y_count; ++i) { vga_compute_shifted_pattern(src + (src_y + i) * bytes, bytes, src_x, x_count, dst_x, &pattern, &relevant_bits); if (src_mask == NULL) { /* * No src mask. Consider that all wanted bits * from the source are "authoritative". */ mask = relevant_bits; } else { /* * There's an src mask. We shift it the same way * we shifted the source pattern. */ vga_compute_shifted_pattern( src_mask + (src_y + i) * bytes, bytes, src_x, x_count, dst_x, &mask, NULL); /* Now, only keep the wanted bits among them. */ mask &= relevant_bits; } /* * Clear bits from the pattern which must be * transparent, according to the source mask. */ pattern &= mask; /* Set the bits in the 2-colors array. */ if (overwrite) pattern_2colors[dst_y + i] &= ~mask; pattern_2colors[dst_y + i] |= pattern; /* * Set the same bits in the n-colors array. This one * supports transparency, when a given bit is cleared in * all colors. */ if (overwrite) { /* * Ensure that the pixels used by this bitmap are * cleared in other colors. */ for (int j = 0; j < 16; ++j) pattern_ncolors[(dst_y + i) * 16 + j] &= ~mask; } pattern_ncolors[(dst_y + i) * 16 + fg] |= pattern; pattern_ncolors[(dst_y + i) * 16 + bg] |= (~pattern & mask); } } static void vga_bitblt_pixels_block_2colors(struct vt_device *vd, const uint8_t *masks, term_color_t fg, term_color_t bg, unsigned int x, unsigned int y, unsigned int height) { unsigned int i, offset; struct vga_softc *sc; /* * The great advantage of Write Mode 3 is that we just need * to load the foreground in the Set/Reset register, load the * background color in the latches register (this is done * through a write in offscreen memory followed by a read of * that data), then write the pattern to video memory. This * pattern indicates if the pixel should use the foreground * color (bit set) or the background color (bit cleared). */ vga_setbg(vd, bg); vga_setfg(vd, fg); sc = vd->vd_softc; offset = (VT_VGA_WIDTH * y + x) / 8; for (i = 0; i < height; ++i, offset += VT_VGA_WIDTH / 8) { MEM_WRITE1(sc, offset, masks[i]); } } static void vga_bitblt_pixels_block_ncolors(struct vt_device *vd, const uint8_t *masks, unsigned int x, unsigned int y, unsigned int height) { unsigned int i, j, offset; struct vga_softc *sc; uint8_t mask; sc = vd->vd_softc; /* * To draw a pixels block with N colors (N > 2), we write each * color one by one: * 1. Use the color as the foreground color * 2. Read the pixels block into the latches * 3. Draw the calculated mask * 4. Go back to #1 for subsequent colors. * * FIXME: Use Write Mode 0 to remove the need to read from video * memory. */ for (i = 0; i < height; ++i) { for (j = 0; j < 16; ++j) { mask = masks[i * 16 + j]; if (mask == 0) continue; vga_setfg(vd, j); offset = (VT_VGA_WIDTH * (y + i) + x) / 8; if (mask != 0xff) { MEM_READ1(sc, offset); /* The bg color was trashed by the reads. */ sc->vga_curbg = 0xff; } MEM_WRITE1(sc, offset, mask); } } } static void vga_bitblt_one_text_pixels_block(struct vt_device *vd, const struct vt_buf *vb, - const struct vt_font *vf, unsigned int x, unsigned int y -#ifndef SC_NO_CUTPASTE - , const struct vt_mouse_cursor *cursor, - term_color_t cursor_fg, term_color_t cursor_bg -#endif - ) + const struct vt_font *vf, unsigned int x, unsigned int y, + int cursor_displayed) { unsigned int i, col, row, src_x, x_count; unsigned int used_colors_list[16], used_colors; uint8_t pattern_2colors[vf->vf_height]; uint8_t pattern_ncolors[vf->vf_height * 16]; term_char_t c; term_color_t fg, bg; const uint8_t *src; #ifndef SC_NO_CUTPASTE + struct vt_mouse_cursor *cursor; unsigned int mx, my; #endif /* * The current pixels block. * * We fill it with portions of characters, because both "grids" * may not match. * * i is the index in this pixels block. */ i = x; used_colors = 0; memset(used_colors_list, 0, sizeof(used_colors_list)); memset(pattern_2colors, 0, sizeof(pattern_2colors)); memset(pattern_ncolors, 0, sizeof(pattern_ncolors)); if (i < vd->vd_offset.tp_col) { /* * i is in the margin used to center the text area on * the screen. */ i = vd->vd_offset.tp_col; } while (i < x + VT_VGA_PIXELS_BLOCK) { /* * Find which character is drawn on this pixel in the * pixels block. * * While here, record what colors it uses. */ col = (i - vd->vd_offset.tp_col) / vf->vf_width; row = (y - vd->vd_offset.tp_row) / vf->vf_height; c = VTBUF_GET_FIELD(vb, row, col); src = vtfont_lookup(vf, c); vt_determine_colors(c, VTBUF_ISCURSOR(vb, row, col), &fg, &bg); if ((used_colors_list[fg] & 0x1) != 0x1) used_colors++; if ((used_colors_list[bg] & 0x2) != 0x2) used_colors++; used_colors_list[fg] |= 0x1; used_colors_list[bg] |= 0x2; /* * Compute the portion of the character we want to draw, * because the pixels block may start in the middle of a * character. * * The first pixel to draw in the character is * the current position - * the start position of the character * * The last pixel to draw is either * - the last pixel of the character, or * - the pixel of the character matching the end of * the pixels block * whichever comes first. This position is then * changed to be relative to the start position of the * character. */ src_x = i - (col * vf->vf_width + vd->vd_offset.tp_col); x_count = min( (col + 1) * vf->vf_width + vd->vd_offset.tp_col, x + VT_VGA_PIXELS_BLOCK); x_count -= col * vf->vf_width + vd->vd_offset.tp_col; x_count -= src_x; /* Copy a portion of the character. */ vga_copy_bitmap_portion(pattern_2colors, pattern_ncolors, src, NULL, vf->vf_width, src_x, i % VT_VGA_PIXELS_BLOCK, x_count, 0, 0, vf->vf_height, fg, bg, 0); /* We move to the next portion. */ i += x_count; } #ifndef SC_NO_CUTPASTE /* * Copy the mouse pointer bitmap if it's over the current pixels * block. * * We use the saved cursor position (saved in vt_flush()), because * the current position could be different than the one used * to mark the area dirty. */ + cursor = vd->vd_mcursor; mx = vd->vd_moldx + vd->vd_offset.tp_col; my = vd->vd_moldy + vd->vd_offset.tp_row; - if (cursor != NULL && + if (cursor_displayed && ((mx >= x && x + VT_VGA_PIXELS_BLOCK - 1 >= mx) || (mx < x && mx + cursor->width >= x)) && ((my >= y && y + vf->vf_height - 1 >= my) || (my < y && my + cursor->height >= y))) { unsigned int dst_x, src_y, dst_y, y_count; /* Compute the portion of the cursor we want to copy. */ src_x = x > mx ? x - mx : 0; dst_x = mx > x ? mx - x : 0; x_count = min( min(cursor->width - src_x, x + VT_VGA_PIXELS_BLOCK - mx), VT_VGA_PIXELS_BLOCK); /* * The cursor isn't aligned on the Y-axis with * characters, so we need to compute the vertical * start/count. */ src_y = y > my ? y - my : 0; dst_y = my > y ? my - y : 0; y_count = min( min(cursor->height - src_y, y + vf->vf_height - my), vf->vf_height); /* Copy the cursor portion. */ vga_copy_bitmap_portion(pattern_2colors, pattern_ncolors, cursor->map, cursor->mask, cursor->width, src_x, dst_x, x_count, src_y, dst_y, y_count, - cursor_fg, cursor_bg, 1); + vd->vd_mcursor_fg, vd->vd_mcursor_bg, 1); - if ((used_colors_list[cursor_fg] & 0x1) != 0x1) + if ((used_colors_list[vd->vd_mcursor_fg] & 0x1) != 0x1) used_colors++; - if ((used_colors_list[cursor_bg] & 0x2) != 0x2) + if ((used_colors_list[vd->vd_mcursor_bg] & 0x2) != 0x2) used_colors++; } #endif /* * The pixels block is completed, we can now draw it on the * screen. */ if (used_colors == 2) vga_bitblt_pixels_block_2colors(vd, pattern_2colors, fg, bg, x, y, vf->vf_height); else vga_bitblt_pixels_block_ncolors(vd, pattern_ncolors, x, y, vf->vf_height); } static void vga_bitblt_text_gfxmode(struct vt_device *vd, const struct vt_buf *vb, - const struct vt_font *vf, const term_rect_t *area -#ifndef SC_NO_CUTPASTE - , const struct vt_mouse_cursor *cursor, - term_color_t cursor_fg, term_color_t cursor_bg -#endif - ) + const struct vt_font *vf, const term_rect_t *area, int cursor_displayed) { unsigned int col, row; unsigned int x1, y1, x2, y2, x, y; /* * Compute the top-left pixel position aligned with the video * adapter pixels block size. * * This is calculated from the top-left column of te dirty area: * * 1. Compute the top-left pixel of the character: * col * font width + x offset * * NOTE: x offset is used to center the text area on the * screen. It's expressed in pixels, not in characters * col/row! * * 2. Find the pixel further on the left marking the start of * an aligned pixels block (eg. chunk of 8 pixels): * character's x / blocksize * blocksize * * The division, being made on integers, achieves the * alignment. * * For the Y-axis, we need to compute the character's y * coordinate, but we don't need to align it. */ col = area->tr_begin.tp_col; row = area->tr_begin.tp_row; x1 = (int)((col * vf->vf_width + vd->vd_offset.tp_col) / VT_VGA_PIXELS_BLOCK) * VT_VGA_PIXELS_BLOCK; y1 = row * vf->vf_height + vd->vd_offset.tp_row; /* * Compute the bottom right pixel position, again, aligned with * the pixels block size. * * The same rules apply, we just add 1 to base the computation * on the "right border" of the dirty area. */ col = area->tr_end.tp_col; row = area->tr_end.tp_row; x2 = (int)((col * vf->vf_width + vd->vd_offset.tp_col + VT_VGA_PIXELS_BLOCK - 1) / VT_VGA_PIXELS_BLOCK) * VT_VGA_PIXELS_BLOCK; y2 = row * vf->vf_height + vd->vd_offset.tp_row; /* * Clip the area to the screen size. * * FIXME: The problem with handling the dirty area in character * cells is that when using different fonts, the dirty area was * possibly calculated with a different font than the one we use * here, leading to out-of-screen coordinates. The dirty area * should be stored in pixels. */ x2 = min(x2, vd->vd_width - 1); y2 = min(y2, vd->vd_height - 1); /* * Now, we take care of N pixels line at a time (the first for * loop, N = font height), and for these lines, draw one pixels * block at a time (the second for loop), not a character at a * time. * * Therefore, on the X-axis, characters my be drawn partially if * they are not aligned on 8-pixels boundary. * * However, the operation is repeated for the full height of the * font before moving to the next character, because it allows * to keep the color settings and write mode, before perhaps * changing them with the next one. */ for (y = y1; y < y2; y += vf->vf_height) { for (x = x1; x < x2; x += VT_VGA_PIXELS_BLOCK) { - vga_bitblt_one_text_pixels_block(vd, vb, vf, x, y -#ifndef SC_NO_CUTPASTE - , cursor, cursor_fg, cursor_bg -#endif - ); + vga_bitblt_one_text_pixels_block(vd, vb, vf, x, y, + cursor_displayed); } } } static void vga_bitblt_text_txtmode(struct vt_device *vd, const struct vt_buf *vb, - const term_rect_t *area -#ifndef SC_NO_CUTPASTE - , const struct vt_mouse_cursor *cursor, - term_color_t cursor_fg, term_color_t cursor_bg -#endif - ) + const term_rect_t *area, int cursor_displayed) { struct vga_softc *sc; unsigned int col, row; term_char_t c; term_color_t fg, bg; uint8_t ch, attr; sc = vd->vd_softc; for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; ++col) { /* * Get next character and its associated fg/bg * colors. */ c = VTBUF_GET_FIELD(vb, row, col); vt_determine_colors(c, VTBUF_ISCURSOR(vb, row, col), &fg, &bg); /* * Convert character to CP437, which is the * character set used by the VGA hardware by * default. */ ch = vga_get_cp437(c); /* Convert colors to VGA attributes. */ attr = bg << 4 | fg; MEM_WRITE1(sc, 0x18000 + (row * 80 + col) * 2 + 0, ch); MEM_WRITE1(sc, 0x18000 + (row * 80 + col) * 2 + 1, attr); } } } static void vga_bitblt_text(struct vt_device *vd, const struct vt_buf *vb, - const struct vt_font *vf, const term_rect_t *area -#ifndef SC_NO_CUTPASTE - , const struct vt_mouse_cursor *cursor, - term_color_t cursor_fg, term_color_t cursor_bg -#endif - ) + const struct vt_font *vf, const term_rect_t *area, int cursor_displayed) { if (!(vd->vd_flags & VDF_TEXTMODE)) { - vga_bitblt_text_gfxmode(vd, vb, vf, area -#ifndef SC_NO_CUTPASTE - , cursor, cursor_fg, cursor_bg -#endif - ); + vga_bitblt_text_gfxmode(vd, vb, vf, area, cursor_displayed); } else { - vga_bitblt_text_txtmode(vd, vb, area -#ifndef SC_NO_CUTPASTE - , cursor, cursor_fg, cursor_bg -#endif - ); + vga_bitblt_text_txtmode(vd, vb, area, cursor_displayed); } } static void vga_initialize_graphics(struct vt_device *vd) { struct vga_softc *sc = vd->vd_softc; /* Clock select. */ REG_WRITE1(sc, VGA_GEN_MISC_OUTPUT_W, VGA_GEN_MO_VSP | VGA_GEN_MO_HSP | VGA_GEN_MO_PB | VGA_GEN_MO_ER | VGA_GEN_MO_IOA); /* Set sequencer clocking and memory mode. */ REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_CLOCKING_MODE); REG_WRITE1(sc, VGA_SEQ_DATA, VGA_SEQ_CM_89); REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_MEMORY_MODE); REG_WRITE1(sc, VGA_SEQ_DATA, VGA_SEQ_MM_OE | VGA_SEQ_MM_EM); /* Set the graphics controller in graphics mode. */ REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_MISCELLANEOUS); REG_WRITE1(sc, VGA_GC_DATA, 0x04 + VGA_GC_MISC_GA); /* Program the CRT controller. */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_HORIZ_TOTAL); REG_WRITE1(sc, VGA_CRTC_DATA, 0x5f); /* 760 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_HORIZ_DISP_END); REG_WRITE1(sc, VGA_CRTC_DATA, 0x4f); /* 640 - 8 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_START_HORIZ_BLANK); REG_WRITE1(sc, VGA_CRTC_DATA, 0x50); /* 640 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_END_HORIZ_BLANK); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_EHB_CR + 2); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_START_HORIZ_RETRACE); REG_WRITE1(sc, VGA_CRTC_DATA, 0x54); /* 672 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_END_HORIZ_RETRACE); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_EHR_EHB + 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_VERT_TOTAL); REG_WRITE1(sc, VGA_CRTC_DATA, 0x0b); /* 523 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_OVERFLOW); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_OF_VT9 | VGA_CRTC_OF_LC8 | VGA_CRTC_OF_VBS8 | VGA_CRTC_OF_VRS8 | VGA_CRTC_OF_VDE8); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_MAX_SCAN_LINE); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_MSL_LC9); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_VERT_RETRACE_START); REG_WRITE1(sc, VGA_CRTC_DATA, 0xea); /* 480 + 10 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_VERT_RETRACE_END); REG_WRITE1(sc, VGA_CRTC_DATA, 0x0c); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_VERT_DISPLAY_END); REG_WRITE1(sc, VGA_CRTC_DATA, 0xdf); /* 480 - 1*/ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_OFFSET); REG_WRITE1(sc, VGA_CRTC_DATA, 0x28); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_START_VERT_BLANK); REG_WRITE1(sc, VGA_CRTC_DATA, 0xe7); /* 480 + 7 */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_END_VERT_BLANK); REG_WRITE1(sc, VGA_CRTC_DATA, 0x04); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_MODE_CONTROL); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_MC_WB | VGA_CRTC_MC_AW | VGA_CRTC_MC_SRS | VGA_CRTC_MC_CMS); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_LINE_COMPARE); REG_WRITE1(sc, VGA_CRTC_DATA, 0xff); /* 480 + 31 */ REG_WRITE1(sc, VGA_GEN_FEATURE_CTRL_W, 0); REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_MAP_MASK); REG_WRITE1(sc, VGA_SEQ_DATA, VGA_SEQ_MM_EM3 | VGA_SEQ_MM_EM2 | VGA_SEQ_MM_EM1 | VGA_SEQ_MM_EM0); REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_CHAR_MAP_SELECT); REG_WRITE1(sc, VGA_SEQ_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_ENABLE_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, 0x0f); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_COLOR_COMPARE); REG_WRITE1(sc, VGA_GC_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_DATA_ROTATE); REG_WRITE1(sc, VGA_GC_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_READ_MAP_SELECT); REG_WRITE1(sc, VGA_GC_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_MODE); REG_WRITE1(sc, VGA_GC_DATA, 0); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_COLOR_DONT_CARE); REG_WRITE1(sc, VGA_GC_DATA, 0x0f); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_BIT_MASK); REG_WRITE1(sc, VGA_GC_DATA, 0xff); } static void vga_initialize(struct vt_device *vd, int textmode) { struct vga_softc *sc = vd->vd_softc; uint8_t x; /* Make sure the VGA adapter is not in monochrome emulation mode. */ x = REG_READ1(sc, VGA_GEN_MISC_OUTPUT_R); REG_WRITE1(sc, VGA_GEN_MISC_OUTPUT_W, x | VGA_GEN_MO_IOA); /* Unprotect CRTC registers 0-7. */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_VERT_RETRACE_END); x = REG_READ1(sc, VGA_CRTC_DATA); REG_WRITE1(sc, VGA_CRTC_DATA, x & ~VGA_CRTC_VRE_PR); /* * Wait for the vertical retrace. * NOTE: this code reads the VGA_GEN_INPUT_STAT_1 register, which has * the side-effect of clearing the internal flip-flip of the attribute * controller's write register. This means that because this code is * here, we know for sure that the first write to the attribute * controller will be a write to the address register. Removing this * code therefore also removes that guarantee and appropriate measures * need to be taken. */ do { x = REG_READ1(sc, VGA_GEN_INPUT_STAT_1); x &= VGA_GEN_IS1_VR | VGA_GEN_IS1_DE; } while (x != (VGA_GEN_IS1_VR | VGA_GEN_IS1_DE)); /* Now, disable the sync. signals. */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_MODE_CONTROL); x = REG_READ1(sc, VGA_CRTC_DATA); REG_WRITE1(sc, VGA_CRTC_DATA, x & ~VGA_CRTC_MC_HR); /* Asynchronous sequencer reset. */ REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_RESET); REG_WRITE1(sc, VGA_SEQ_DATA, VGA_SEQ_RST_SR); if (!textmode) vga_initialize_graphics(vd); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_PRESET_ROW_SCAN); REG_WRITE1(sc, VGA_CRTC_DATA, 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_CURSOR_START); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_CS_COO); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_CURSOR_END); REG_WRITE1(sc, VGA_CRTC_DATA, 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_START_ADDR_HIGH); REG_WRITE1(sc, VGA_CRTC_DATA, 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_START_ADDR_LOW); REG_WRITE1(sc, VGA_CRTC_DATA, 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_CURSOR_LOC_HIGH); REG_WRITE1(sc, VGA_CRTC_DATA, 0); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_CURSOR_LOC_LOW); REG_WRITE1(sc, VGA_CRTC_DATA, 0x59); REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_UNDERLINE_LOC); REG_WRITE1(sc, VGA_CRTC_DATA, VGA_CRTC_UL_UL); if (textmode) { /* Set the attribute controller to blink disable. */ REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_MODE_CONTROL); REG_WRITE1(sc, VGA_AC_WRITE, 0); } else { /* Set the attribute controller in graphics mode. */ REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_MODE_CONTROL); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_MC_GA); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_HORIZ_PIXEL_PANNING); REG_WRITE1(sc, VGA_AC_WRITE, 0); } REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(0)); REG_WRITE1(sc, VGA_AC_WRITE, 0); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(1)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_R); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(2)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_G); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(3)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SG | VGA_AC_PAL_R); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(4)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(5)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_R | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(6)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_G | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(7)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_R | VGA_AC_PAL_G | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(8)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(9)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_R); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(10)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_G); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(11)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_R | VGA_AC_PAL_G); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(12)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(13)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_R | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(14)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_G | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PALETTE(15)); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_PAL_SR | VGA_AC_PAL_SG | VGA_AC_PAL_SB | VGA_AC_PAL_R | VGA_AC_PAL_G | VGA_AC_PAL_B); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_OVERSCAN_COLOR); REG_WRITE1(sc, VGA_AC_WRITE, 0); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_COLOR_PLANE_ENABLE); REG_WRITE1(sc, VGA_AC_WRITE, 0x0f); REG_WRITE1(sc, VGA_AC_WRITE, VGA_AC_COLOR_SELECT); REG_WRITE1(sc, VGA_AC_WRITE, 0); if (!textmode) { u_int ofs; /* * Done. Clear the frame buffer. All bit planes are * enabled, so a single-paged loop should clear all * planes. */ for (ofs = 0; ofs < VT_VGA_MEMSIZE; ofs++) { MEM_WRITE1(sc, ofs, 0); } } /* Re-enable the sequencer. */ REG_WRITE1(sc, VGA_SEQ_ADDRESS, VGA_SEQ_RESET); REG_WRITE1(sc, VGA_SEQ_DATA, VGA_SEQ_RST_SR | VGA_SEQ_RST_NAR); /* Re-enable the sync signals. */ REG_WRITE1(sc, VGA_CRTC_ADDRESS, VGA_CRTC_MODE_CONTROL); x = REG_READ1(sc, VGA_CRTC_DATA); REG_WRITE1(sc, VGA_CRTC_DATA, x | VGA_CRTC_MC_HR); if (!textmode) { /* Switch to write mode 3, because we'll mainly do bitblt. */ REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_MODE); REG_WRITE1(sc, VGA_GC_DATA, 3); REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_ENABLE_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, 0x0f); /* * Clear the colors we think are loaded into Set/Reset or * the latches. */ sc->vga_curfg = sc->vga_curbg = 0xff; } } static int vga_probe(struct vt_device *vd) { return (CN_INTERNAL); } static int vga_init(struct vt_device *vd) { struct vga_softc *sc; int textmode; if (vd->vd_softc == NULL) vd->vd_softc = (void *)&vga_conssoftc; sc = vd->vd_softc; textmode = 0; #if defined(__amd64__) || defined(__i386__) sc->vga_fb_tag = X86_BUS_SPACE_MEM; sc->vga_fb_handle = KERNBASE + VGA_MEM_BASE; sc->vga_reg_tag = X86_BUS_SPACE_IO; sc->vga_reg_handle = VGA_REG_BASE; #else # error "Architecture not yet supported!" #endif TUNABLE_INT_FETCH("hw.vga.textmode", &textmode); if (textmode) { vd->vd_flags |= VDF_TEXTMODE; vd->vd_width = 80; vd->vd_height = 25; } else { vd->vd_width = VT_VGA_WIDTH; vd->vd_height = VT_VGA_HEIGHT; } vga_initialize(vd, textmode); return (CN_INTERNAL); } static void vga_postswitch(struct vt_device *vd) { /* Reinit VGA mode, to restore view after app which change mode. */ vga_initialize(vd, (vd->vd_flags & VDF_TEXTMODE)); /* Ask vt(9) to update chars on visible area. */ vd->vd_flags |= VDF_INVALID; } Index: head/sys/dev/vt/vt.h =================================================================== --- head/sys/dev/vt/vt.h (revision 270330) +++ head/sys/dev/vt/vt.h (revision 270331) @@ -1,416 +1,420 @@ /*- * Copyright (c) 2009, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Ed Schouten under sponsorship from the * FreeBSD Foundation. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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_VT_VT_H_ #define _DEV_VT_VT_H_ #include #include #include #include #include #include #include #include #include #include #include #include "opt_syscons.h" #include "opt_splash.h" #ifndef VT_MAXWINDOWS #ifdef MAXCONS #define VT_MAXWINDOWS MAXCONS #else #define VT_MAXWINDOWS 12 #endif #endif #ifndef VT_ALT_TO_ESC_HACK #define VT_ALT_TO_ESC_HACK 1 #endif #define VT_CONSWINDOW 0 #if defined(SC_TWOBUTTON_MOUSE) || defined(VT_TWOBUTTON_MOUSE) #define VT_MOUSE_PASTEBUTTON MOUSE_BUTTON3DOWN /* right button */ #define VT_MOUSE_EXTENDBUTTON MOUSE_BUTTON2DOWN /* not really used */ #else #define VT_MOUSE_PASTEBUTTON MOUSE_BUTTON2DOWN /* middle button */ #define VT_MOUSE_EXTENDBUTTON MOUSE_BUTTON3DOWN /* right button */ #endif /* defined(SC_TWOBUTTON_MOUSE) || defined(VT_TWOBUTTON_MOUSE) */ #define SC_DRIVER_NAME "vt" #ifdef VT_DEBUG #define DPRINTF(_l, ...) if (vt_debug > (_l)) printf( __VA_ARGS__ ) #define VT_CONSOLECTL_DEBUG #define VT_SYSMOUSE_DEBUG #else #define DPRINTF(_l, ...) do {} while (0) #endif #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) #define VT_SYSCTL_INT(_name, _default, _descr) \ static int vt_##_name = _default; \ SYSCTL_INT(_kern_vt, OID_AUTO, _name, CTLFLAG_RWTUN, &vt_##_name, _default,\ _descr); /* Allow to disable some special keys by users. */ #define VT_DEBUG_KEY_ENABLED (1 << 0) #define VT_REBOOT_KEY_ENABLED (1 << 1) #define VT_HALT_KEY_ENABLED (1 << 2) #define VT_POWEROFF_KEY_ENABLED (1 << 3) struct vt_driver; void vt_allocate(struct vt_driver *, void *); void vt_resume(void); void vt_suspend(void); typedef unsigned int vt_axis_t; /* * List of locks * (d) locked by vd_lock * (b) locked by vb_lock * (G) locked by Giant * (u) unlocked, locked by higher levels * (c) const until freeing * (?) yet to be determined */ /* * Per-device datastructure. */ +#ifndef SC_NO_CUTPASTE +struct vt_mouse_cursor; +#endif + struct vt_device { struct vt_window *vd_windows[VT_MAXWINDOWS]; /* (c) Windows. */ struct vt_window *vd_curwindow; /* (d) Current window. */ struct vt_window *vd_savedwindow;/* (?) Saved for suspend. */ struct vt_window *vd_markedwin; /* (?) Copy/paste buf owner. */ const struct vt_driver *vd_driver; /* (c) Graphics driver. */ void *vd_softc; /* (u) Driver data. */ +#ifndef SC_NO_CUTPASTE + struct vt_mouse_cursor *vd_mcursor; /* (?) Cursor bitmap. */ + term_color_t vd_mcursor_fg; /* (?) Cursor fg color. */ + term_color_t vd_mcursor_bg; /* (?) Cursor bg color. */ +#endif uint16_t vd_mx; /* (?) Current mouse X. */ uint16_t vd_my; /* (?) current mouse Y. */ vt_axis_t vd_moldx; /* (?) Mouse X as of last redraw. */ vt_axis_t vd_moldy; /* (?) Mouse Y as of last redraw. */ uint32_t vd_mstate; /* (?) Mouse state. */ term_pos_t vd_offset; /* (?) Pixel offset. */ vt_axis_t vd_width; /* (?) Screen width. */ vt_axis_t vd_height; /* (?) Screen height. */ struct mtx vd_lock; /* Per-device lock. */ struct cv vd_winswitch; /* (d) Window switch notify. */ struct callout vd_timer; /* (d) Display timer. */ volatile unsigned int vd_timer_armed;/* (?) Display timer started.*/ int vd_flags; /* (d) Device flags. */ #define VDF_TEXTMODE 0x01 /* Do text mode rendering. */ #define VDF_SPLASH 0x02 /* Splash screen active. */ #define VDF_ASYNC 0x04 /* vt_timer() running. */ #define VDF_INVALID 0x08 /* Entire screen should be re-rendered. */ #define VDF_DEAD 0x10 /* Early probing found nothing. */ #define VDF_INITIALIZED 0x20 /* vtterm_cnprobe already done. */ #define VDF_MOUSECURSOR 0x40 /* Mouse cursor visible. */ #define VDF_QUIET_BELL 0x80 /* Disable bell. */ int vd_keyboard; /* (G) Keyboard index. */ unsigned int vd_kbstate; /* (?) Device unit. */ unsigned int vd_unit; /* (c) Device unit. */ }; /* * Per-window terminal screen buffer. * * Because redrawing is performed asynchronously, the buffer keeps track * of a rectangle that needs to be redrawn (vb_dirtyrect). Because this * approach seemed to cause suboptimal performance (when the top left * and the bottom right of the screen are modified), it also uses a set * of bitmasks to keep track of the rows and columns (mod 64) that have * been modified. */ struct vt_bufmask { uint64_t vbm_row, vbm_col; #define VBM_DIRTY UINT64_MAX }; struct vt_buf { struct mtx vb_lock; /* Buffer lock. */ term_pos_t vb_scr_size; /* (b) Screen dimensions. */ int vb_flags; /* (b) Flags. */ #define VBF_CURSOR 0x1 /* Cursor visible. */ #define VBF_STATIC 0x2 /* Buffer is statically allocated. */ #define VBF_MTX_INIT 0x4 /* Mutex initialized. */ #define VBF_SCROLL 0x8 /* scroll locked mode. */ #define VBF_HISTORY_FULL 0x10 /* All rows filled. */ int vb_history_size; #define VBF_DEFAULT_HISTORY_SIZE 500 int vb_roffset; /* (b) History rows offset. */ int vb_curroffset; /* (b) Saved rows offset. */ term_pos_t vb_cursor; /* (u) Cursor position. */ term_pos_t vb_mark_start; /* (b) Copy region start. */ term_pos_t vb_mark_end; /* (b) Copy region end. */ int vb_mark_last; /* Last mouse event. */ term_rect_t vb_dirtyrect; /* (b) Dirty rectangle. */ struct vt_bufmask vb_dirtymask; /* (b) Dirty bitmasks. */ term_char_t *vb_buffer; /* (u) Data buffer. */ term_char_t **vb_rows; /* (u) Array of rows */ }; void vtbuf_copy(struct vt_buf *, const term_rect_t *, const term_pos_t *); void vtbuf_fill_locked(struct vt_buf *, const term_rect_t *, term_char_t); void vtbuf_init_early(struct vt_buf *); void vtbuf_init(struct vt_buf *, const term_pos_t *); void vtbuf_grow(struct vt_buf *, const term_pos_t *, int); void vtbuf_putchar(struct vt_buf *, const term_pos_t *, term_char_t); void vtbuf_cursor_position(struct vt_buf *, const term_pos_t *); void vtbuf_scroll_mode(struct vt_buf *vb, int yes); void vtbuf_undirty(struct vt_buf *, term_rect_t *, struct vt_bufmask *); void vtbuf_sethistory_size(struct vt_buf *, int); int vtbuf_iscursor(const struct vt_buf *vb, int row, int col); void vtbuf_cursor_visibility(struct vt_buf *, int); #ifndef SC_NO_CUTPASTE void vtbuf_mouse_cursor_position(struct vt_buf *vb, int col, int row); int vtbuf_set_mark(struct vt_buf *vb, int type, int col, int row); int vtbuf_get_marked_len(struct vt_buf *vb); void vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz); #endif #define VTB_MARK_NONE 0 #define VTB_MARK_END 1 #define VTB_MARK_START 2 #define VTB_MARK_WORD 3 #define VTB_MARK_ROW 4 #define VTB_MARK_EXTEND 5 #define VTB_MARK_MOVE 6 #define VTBUF_SLCK_ENABLE(vb) vtbuf_scroll_mode((vb), 1) #define VTBUF_SLCK_DISABLE(vb) vtbuf_scroll_mode((vb), 0) #define VTBUF_MAX_HEIGHT(vb) \ ((vb)->vb_history_size) #define VTBUF_GET_ROW(vb, r) \ ((vb)->vb_rows[((vb)->vb_roffset + (r)) % VTBUF_MAX_HEIGHT(vb)]) #define VTBUF_GET_FIELD(vb, r, c) \ ((vb)->vb_rows[((vb)->vb_roffset + (r)) % VTBUF_MAX_HEIGHT(vb)][(c)]) #define VTBUF_FIELD(vb, r, c) \ ((vb)->vb_rows[((vb)->vb_curroffset + (r)) % VTBUF_MAX_HEIGHT(vb)][(c)]) #define VTBUF_ISCURSOR(vb, r, c) \ vtbuf_iscursor((vb), (r), (c)) #define VTBUF_DIRTYROW(mask, row) \ ((mask)->vbm_row & ((uint64_t)1 << ((row) % 64))) #define VTBUF_DIRTYCOL(mask, col) \ ((mask)->vbm_col & ((uint64_t)1 << ((col) % 64))) #define VTBUF_SPACE_CHAR(attr) (' ' | (attr)) #define VHS_SET 0 #define VHS_CUR 1 #define VHS_END 2 int vthistory_seek(struct vt_buf *, int offset, int whence); void vthistory_addlines(struct vt_buf *vb, int offset); void vthistory_getpos(const struct vt_buf *, unsigned int *offset); /* * Per-window datastructure. */ struct vt_window { struct vt_device *vw_device; /* (c) Device. */ struct terminal *vw_terminal; /* (c) Terminal. */ struct vt_buf vw_buf; /* (u) Screen buffer. */ struct vt_font *vw_font; /* (d) Graphical font. */ unsigned int vw_number; /* (c) Window number. */ int vw_kbdmode; /* (?) Keyboard mode. */ char *vw_kbdsq; /* Escape sequence queue*/ unsigned int vw_flags; /* (d) Per-window flags. */ int vw_mouse_level;/* Mouse op mode. */ #define VWF_BUSY 0x1 /* Busy reconfiguring device. */ #define VWF_OPENED 0x2 /* TTY in use. */ #define VWF_SCROLL 0x4 /* Keys influence scrollback. */ #define VWF_CONSOLE 0x8 /* Kernel message console window. */ #define VWF_VTYLOCK 0x10 /* Prevent window switch. */ #define VWF_MOUSE_HIDE 0x20 /* Disable mouse events processing. */ #define VWF_READY 0x40 /* Window fully initialized. */ #define VWF_SWWAIT_REL 0x10000 /* Program wait for VT acquire is done. */ #define VWF_SWWAIT_ACQ 0x20000 /* Program wait for VT release is done. */ pid_t vw_pid; /* Terminal holding process */ struct proc *vw_proc; struct vt_mode vw_smode; /* switch mode */ struct callout vw_proc_dead_timer; struct vt_window *vw_switch_to; }; #define VT_AUTO 0 /* switching is automatic */ #define VT_PROCESS 1 /* switching controlled by prog */ #define VT_KERNEL 255 /* switching controlled in kernel */ #define IS_VT_PROC_MODE(vw) ((vw)->vw_smode.mode == VT_PROCESS) /* * Per-device driver routines. */ -#ifndef SC_NO_CUTPASTE -struct vt_mouse_cursor { - uint8_t map[64 * 64 / 8]; - uint8_t mask[64 * 64 / 8]; - uint8_t width; - uint8_t height; -}; -#endif - typedef int vd_init_t(struct vt_device *vd); typedef int vd_probe_t(struct vt_device *vd); typedef void vd_postswitch_t(struct vt_device *vd); typedef void vd_blank_t(struct vt_device *vd, term_color_t color); /* * FIXME: Remove vd_bitblt_t and vd_putchar_t, once vd_bitblt_text_t is * provided by all drivers. */ typedef void vd_bitbltchr_t(struct vt_device *vd, const uint8_t *src, const uint8_t *mask, int bpl, vt_axis_t top, vt_axis_t left, unsigned int width, unsigned int height, term_color_t fg, term_color_t bg); typedef void vd_putchar_t(struct vt_device *vd, term_char_t, vt_axis_t top, vt_axis_t left, term_color_t fg, term_color_t bg); typedef void vd_bitblt_text_t(struct vt_device *vd, const struct vt_buf *vb, - const struct vt_font *vf, const term_rect_t *area -#ifndef SC_NO_CUTPASTE - , const struct vt_mouse_cursor *cursor, - term_color_t cursor_fg, term_color_t cursor_bg -#endif - ); + const struct vt_font *vf, const term_rect_t *area, int cursor_displayed); typedef int vd_fb_ioctl_t(struct vt_device *, u_long, caddr_t, struct thread *); typedef int vd_fb_mmap_t(struct vt_device *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *); typedef void vd_drawrect_t(struct vt_device *, int, int, int, int, int, term_color_t); typedef void vd_setpixel_t(struct vt_device *, int, int, term_color_t); struct vt_driver { char vd_name[16]; /* Console attachment. */ vd_probe_t *vd_probe; vd_init_t *vd_init; /* Drawing. */ vd_blank_t *vd_blank; vd_bitbltchr_t *vd_bitbltchr; /* FIXME: Deprecated. */ vd_drawrect_t *vd_drawrect; vd_setpixel_t *vd_setpixel; vd_bitblt_text_t *vd_bitblt_text; /* Framebuffer ioctls, if present. */ vd_fb_ioctl_t *vd_fb_ioctl; /* Framebuffer mmap, if present. */ vd_fb_mmap_t *vd_fb_mmap; /* Text mode operation. */ vd_putchar_t *vd_putchar; /* FIXME: Deprecated. */ /* Update display setting on vt switch. */ vd_postswitch_t *vd_postswitch; /* Priority to know which one can override */ int vd_priority; #define VD_PRIORITY_DUMB 10 #define VD_PRIORITY_GENERIC 100 #define VD_PRIORITY_SPECIFIC 1000 }; /* * Console device madness. * * Utility macro to make early vt(4) instances work. */ extern const struct terminal_class vt_termclass; void vt_upgrade(struct vt_device *vd); #define PIXEL_WIDTH(w) ((w) / 8) #define PIXEL_HEIGHT(h) ((h) / 16) #ifndef VT_FB_DEFAULT_WIDTH #define VT_FB_DEFAULT_WIDTH 2048 #endif #ifndef VT_FB_DEFAULT_HEIGHT #define VT_FB_DEFAULT_HEIGHT 1200 #endif /* name argument is not used yet. */ #define VT_DRIVER_DECLARE(name, drv) DATA_SET(vt_drv_set, drv) /* * Fonts. * * Remapping tables are used to map Unicode points to glyphs. They need * to be sorted, because vtfont_lookup() performs a binary search. Each * font has two remapping tables, for normal and bold. When a character * is not present in bold, it uses a normal glyph. When no glyph is * available, it uses glyph 0, which is normally equal to U+FFFD. */ struct vt_font_map { uint32_t vfm_src; uint16_t vfm_dst; uint16_t vfm_len; }; struct vt_font { struct vt_font_map *vf_map[VFNT_MAPS]; uint8_t *vf_bytes; unsigned int vf_height, vf_width; unsigned int vf_map_count[VFNT_MAPS]; unsigned int vf_refcount; }; + +#ifndef SC_NO_CUTPASTE +struct vt_mouse_cursor { + uint8_t map[64 * 64 / 8]; + uint8_t mask[64 * 64 / 8]; + uint8_t width; + uint8_t height; +}; +#endif const uint8_t *vtfont_lookup(const struct vt_font *vf, term_char_t c); struct vt_font *vtfont_ref(struct vt_font *vf); void vtfont_unref(struct vt_font *vf); int vtfont_load(vfnt_t *f, struct vt_font **ret); /* Sysmouse. */ void sysmouse_process_event(mouse_info_t *mi); #ifndef SC_NO_CUTPASTE void vt_mouse_event(int type, int x, int y, int event, int cnt, int mlevel); void vt_mouse_state(int show); #endif #define VT_MOUSE_SHOW 1 #define VT_MOUSE_HIDE 0 /* Utilities. */ void vt_determine_colors(term_char_t c, int cursor, term_color_t *fg, term_color_t *bg); #endif /* !_DEV_VT_VT_H_ */ Index: head/sys/dev/vt/vt_core.c =================================================================== --- head/sys/dev/vt/vt_core.c (revision 270330) +++ head/sys/dev/vt/vt_core.c (revision 270331) @@ -1,2266 +1,2272 @@ /*- * Copyright (c) 2009, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Ed Schouten under sponsorship from the * FreeBSD Foundation. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #include #endif static tc_bell_t vtterm_bell; static tc_cursor_t vtterm_cursor; static tc_putchar_t vtterm_putchar; static tc_fill_t vtterm_fill; static tc_copy_t vtterm_copy; static tc_param_t vtterm_param; static tc_done_t vtterm_done; static tc_cnprobe_t vtterm_cnprobe; static tc_cngetc_t vtterm_cngetc; static tc_opened_t vtterm_opened; static tc_ioctl_t vtterm_ioctl; static tc_mmap_t vtterm_mmap; const struct terminal_class vt_termclass = { .tc_bell = vtterm_bell, .tc_cursor = vtterm_cursor, .tc_putchar = vtterm_putchar, .tc_fill = vtterm_fill, .tc_copy = vtterm_copy, .tc_param = vtterm_param, .tc_done = vtterm_done, .tc_cnprobe = vtterm_cnprobe, .tc_cngetc = vtterm_cngetc, .tc_opened = vtterm_opened, .tc_ioctl = vtterm_ioctl, .tc_mmap = vtterm_mmap, }; /* * Use a constant timer of 25 Hz to redraw the screen. * * XXX: In theory we should only fire up the timer when there is really * activity. Unfortunately we cannot always start timers. We really * don't want to process kernel messages synchronously, because it * really slows down the system. */ #define VT_TIMERFREQ 25 /* Bell pitch/duration. */ #define VT_BELLDURATION ((5 * hz + 99) / 100) #define VT_BELLPITCH 800 #define VT_LOCK(vd) mtx_lock(&(vd)->vd_lock) #define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock) #define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \ (vw)->vw_number) static SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "vt(9) parameters"); VT_SYSCTL_INT(enable_altgr, 1, "Enable AltGr key (Do not assume R.Alt as Alt)"); VT_SYSCTL_INT(debug, 0, "vt(9) debug level"); VT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode"); VT_SYSCTL_INT(suspendswitch, 1, "Switch to VT0 before suspend"); VT_SYSCTL_INT(spclkeys, (VT_DEBUG_KEY_ENABLED|VT_REBOOT_KEY_ENABLED| VT_HALT_KEY_ENABLED|VT_POWEROFF_KEY_ENABLED), "Enabled special keys " "handled by vt(4)"); static struct vt_device vt_consdev; static unsigned int vt_unit = 0; static MALLOC_DEFINE(M_VT, "vt", "vt device"); struct vt_device *main_vd = &vt_consdev; /* Boot logo. */ extern unsigned int vt_logo_width; extern unsigned int vt_logo_height; extern unsigned int vt_logo_depth; extern unsigned char vt_logo_image[]; /* Font. */ extern struct vt_font vt_font_default; #ifndef SC_NO_CUTPASTE extern struct vt_mouse_cursor vt_default_mouse_pointer; #endif static int signal_vt_rel(struct vt_window *); static int signal_vt_acq(struct vt_window *); static int finish_vt_rel(struct vt_window *, int, int *); static int finish_vt_acq(struct vt_window *); static int vt_window_switch(struct vt_window *); static int vt_late_window_switch(struct vt_window *); static int vt_proc_alive(struct vt_window *); static void vt_resize(struct vt_device *); static void vt_update_static(void *); SET_DECLARE(vt_drv_set, struct vt_driver); #define _VTDEFH MAX(100, PIXEL_HEIGHT(VT_FB_DEFAULT_HEIGHT)) #define _VTDEFW MAX(200, PIXEL_WIDTH(VT_FB_DEFAULT_WIDTH)) static struct terminal vt_consterm; static struct vt_window vt_conswindow; static struct vt_device vt_consdev = { .vd_driver = NULL, .vd_softc = NULL, .vd_flags = VDF_INVALID, .vd_windows = { [VT_CONSWINDOW] = &vt_conswindow, }, .vd_curwindow = &vt_conswindow, .vd_markedwin = NULL, .vd_kbstate = 0, + +#ifndef SC_NO_CUTPASTE + .vd_mcursor = &vt_default_mouse_pointer, + .vd_mcursor_fg = TC_WHITE, + .vd_mcursor_bg = TC_BLACK, +#endif }; static term_char_t vt_constextbuf[(_VTDEFW) * (VBF_DEFAULT_HISTORY_SIZE)]; static term_char_t *vt_constextbufrows[VBF_DEFAULT_HISTORY_SIZE]; static struct vt_window vt_conswindow = { .vw_number = VT_CONSWINDOW, .vw_flags = VWF_CONSOLE, .vw_buf = { .vb_buffer = &vt_constextbuf[0], .vb_rows = &vt_constextbufrows[0], .vb_history_size = VBF_DEFAULT_HISTORY_SIZE, .vb_curroffset = 0, .vb_roffset = 0, .vb_flags = VBF_STATIC, .vb_mark_start = {.tp_row = 0, .tp_col = 0,}, .vb_mark_end = {.tp_row = 0, .tp_col = 0,}, .vb_scr_size = { .tp_row = _VTDEFH, .tp_col = _VTDEFW, }, }, .vw_device = &vt_consdev, .vw_terminal = &vt_consterm, .vw_kbdmode = K_XLATE, }; static struct terminal vt_consterm = { .tm_class = &vt_termclass, .tm_softc = &vt_conswindow, .tm_flags = TF_CONS, }; static struct consdev vt_consterm_consdev = { .cn_ops = &termcn_cnops, .cn_arg = &vt_consterm, .cn_name = "ttyv0", }; /* Add to set of consoles. */ DATA_SET(cons_set, vt_consterm_consdev); /* * Right after kmem is done to allow early drivers to use locking and allocate * memory. */ SYSINIT(vt_update_static, SI_SUB_KMEM, SI_ORDER_ANY, vt_update_static, &vt_consdev); /* Delay until all devices attached, to not waste time. */ SYSINIT(vt_early_cons, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_ANY, vt_upgrade, &vt_consdev); /* Initialize locks/mem depended members. */ static void vt_update_static(void *dummy) { if (!vty_enabled(VTY_VT)) return; if (main_vd->vd_driver != NULL) printf("VT: running with driver \"%s\".\n", main_vd->vd_driver->vd_name); else printf("VT: init without driver.\n"); mtx_init(&main_vd->vd_lock, "vtdev", NULL, MTX_DEF); cv_init(&main_vd->vd_winswitch, "vtwswt"); } static void vt_schedule_flush(struct vt_device *vd, int ms) { if (ms <= 0) /* Default to initial value. */ ms = 1000 / VT_TIMERFREQ; callout_schedule(&vd->vd_timer, hz / (1000 / ms)); } static void vt_resume_flush_timer(struct vt_device *vd, int ms) { if (!atomic_cmpset_int(&vd->vd_timer_armed, 0, 1)) return; vt_schedule_flush(vd, ms); } static void vt_suspend_flush_timer(struct vt_device *vd) { if (!atomic_cmpset_int(&vd->vd_timer_armed, 1, 0)) return; callout_drain(&vd->vd_timer); } static void vt_switch_timer(void *arg) { vt_late_window_switch((struct vt_window *)arg); } static int vt_window_preswitch(struct vt_window *vw, struct vt_window *curvw) { DPRINTF(40, "%s\n", __func__); curvw->vw_switch_to = vw; /* Set timer to allow switch in case when process hang. */ callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer, vt_switch_timer, (void *)vw); /* Notify process about vt switch attempt. */ DPRINTF(30, "%s: Notify process.\n", __func__); signal_vt_rel(curvw); return (0); } static int vt_window_postswitch(struct vt_window *vw) { signal_vt_acq(vw); return (0); } /* vt_late_window_switch will done VT switching for regular case. */ static int vt_late_window_switch(struct vt_window *vw) { int ret; callout_stop(&vw->vw_proc_dead_timer); ret = vt_window_switch(vw); if (ret) return (ret); /* Notify owner process about terminal availability. */ if (vw->vw_smode.mode == VT_PROCESS) { ret = vt_window_postswitch(vw); } return (ret); } /* Switch window. */ static int vt_proc_window_switch(struct vt_window *vw) { struct vt_window *curvw; struct vt_device *vd; int ret; vd = vw->vw_device; curvw = vd->vd_curwindow; if (curvw->vw_flags & VWF_VTYLOCK) return (EBUSY); /* Ask current process permitions to switch away. */ if (curvw->vw_smode.mode == VT_PROCESS) { DPRINTF(30, "%s: VT_PROCESS ", __func__); if (vt_proc_alive(curvw) == FALSE) { DPRINTF(30, "Dead. Cleaning."); /* Dead */ } else { DPRINTF(30, "%s: Signaling process.\n", __func__); /* Alive, try to ask him. */ ret = vt_window_preswitch(vw, curvw); /* Wait for process answer or timeout. */ return (ret); } DPRINTF(30, "\n"); } ret = vt_late_window_switch(vw); return (ret); } /* Switch window ignoring process locking. */ static int vt_window_switch(struct vt_window *vw) { struct vt_device *vd = vw->vw_device; struct vt_window *curvw = vd->vd_curwindow; keyboard_t *kbd; VT_LOCK(vd); if (curvw == vw) { /* Nothing to do. */ VT_UNLOCK(vd); return (0); } if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) { VT_UNLOCK(vd); return (EINVAL); } vt_suspend_flush_timer(vd); vd->vd_curwindow = vw; vd->vd_flags |= VDF_INVALID; cv_broadcast(&vd->vd_winswitch); VT_UNLOCK(vd); if (vd->vd_driver->vd_postswitch) vd->vd_driver->vd_postswitch(vd); vt_resume_flush_timer(vd, 0); /* Restore per-window keyboard mode. */ mtx_lock(&Giant); kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd != NULL) { kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode); } mtx_unlock(&Giant); DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number); return (0); } static inline void vt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size) { size->tp_row = vd->vd_height; size->tp_col = vd->vd_width; if (vf != NULL) { size->tp_row /= vf->vf_height; size->tp_col /= vf->vf_width; } } static inline void vt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size) { size->ws_row = size->ws_ypixel = vd->vd_height; size->ws_col = size->ws_xpixel = vd->vd_width; if (vf != NULL) { size->ws_row /= vf->vf_height; size->ws_col /= vf->vf_width; } } static void vt_scroll(struct vt_window *vw, int offset, int whence) { int diff; term_pos_t size; if ((vw->vw_flags & VWF_SCROLL) == 0) return; vt_termsize(vw->vw_device, vw->vw_font, &size); diff = vthistory_seek(&vw->vw_buf, offset, whence); /* * Offset changed, please update Nth lines on sceen. * +N - Nth lines at top; * -N - Nth lines at bottom. */ if (diff < -size.tp_row || diff > size.tp_row) { vw->vw_device->vd_flags |= VDF_INVALID; return; } vw->vw_device->vd_flags |= VDF_INVALID; /*XXX*/ } static int vt_machine_kbdevent(int c) { switch (c) { case SPCLKEY | DBG: if (vt_spclkeys & VT_DEBUG_KEY_ENABLED) kdb_enter(KDB_WHY_BREAK, "manual escape to debugger"); return (1); case SPCLKEY | RBT: if (vt_spclkeys & VT_REBOOT_KEY_ENABLED) /* XXX: Make this configurable! */ shutdown_nice(0); return (1); case SPCLKEY | HALT: if (vt_spclkeys & VT_HALT_KEY_ENABLED) shutdown_nice(RB_HALT); return (1); case SPCLKEY | PDWN: if (vt_spclkeys & VT_POWEROFF_KEY_ENABLED) shutdown_nice(RB_HALT|RB_POWEROFF); return (1); }; return (0); } static void vt_scrollmode_kbdevent(struct vt_window *vw, int c, int console) { struct vt_device *vd; term_pos_t size; vd = vw->vw_device; /* Only special keys handled in ScrollLock mode */ if ((c & SPCLKEY) == 0) return; c &= ~SPCLKEY; if (console == 0) { if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) { vw = vd->vd_windows[c - F_SCR]; if (vw != NULL) vt_proc_window_switch(vw); return; } VT_LOCK(vd); } switch (c) { case SLK: { /* Turn scrolling off. */ vt_scroll(vw, 0, VHS_END); VTBUF_SLCK_DISABLE(&vw->vw_buf); vw->vw_flags &= ~VWF_SCROLL; break; } case FKEY | F(49): /* Home key. */ vt_scroll(vw, 0, VHS_SET); break; case FKEY | F(50): /* Arrow up. */ vt_scroll(vw, -1, VHS_CUR); break; case FKEY | F(51): /* Page up. */ vt_termsize(vd, vw->vw_font, &size); vt_scroll(vw, -size.tp_row, VHS_CUR); break; case FKEY | F(57): /* End key. */ vt_scroll(vw, 0, VHS_END); break; case FKEY | F(58): /* Arrow down. */ vt_scroll(vw, 1, VHS_CUR); break; case FKEY | F(59): /* Page down. */ vt_termsize(vd, vw->vw_font, &size); vt_scroll(vw, size.tp_row, VHS_CUR); break; } if (console == 0) VT_UNLOCK(vd); } static int vt_processkey(keyboard_t *kbd, struct vt_device *vd, int c) { struct vt_window *vw = vd->vd_curwindow; int state = 0; #if VT_ALT_TO_ESC_HACK if (c & RELKEY) { switch (c & ~RELKEY) { case (SPCLKEY | RALT): if (vt_enable_altgr != 0) break; case (SPCLKEY | LALT): vd->vd_kbstate &= ~ALKED; } /* Other keys ignored for RELKEY event. */ return (0); } else { switch (c & ~RELKEY) { case (SPCLKEY | RALT): if (vt_enable_altgr != 0) break; case (SPCLKEY | LALT): vd->vd_kbstate |= ALKED; } } #else if (c & RELKEY) /* Other keys ignored for RELKEY event. */ return (0); #endif if (vt_machine_kbdevent(c)) return (0); if (vw->vw_flags & VWF_SCROLL) { vt_scrollmode_kbdevent(vw, c, 0/* Not a console */); /* Scroll mode keys handled, nothing to do more. */ return (0); } if (c & SPCLKEY) { c &= ~SPCLKEY; if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) { vw = vd->vd_windows[c - F_SCR]; if (vw != NULL) vt_proc_window_switch(vw); return (0); } switch (c) { case SLK: { kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); VT_LOCK(vd); if (state & SLKED) { /* Turn scrolling on. */ vw->vw_flags |= VWF_SCROLL; VTBUF_SLCK_ENABLE(&vw->vw_buf); } else { /* Turn scrolling off. */ vw->vw_flags &= ~VWF_SCROLL; VTBUF_SLCK_DISABLE(&vw->vw_buf); vt_scroll(vw, 0, VHS_END); } VT_UNLOCK(vd); break; } case FKEY | F(1): case FKEY | F(2): case FKEY | F(3): case FKEY | F(4): case FKEY | F(5): case FKEY | F(6): case FKEY | F(7): case FKEY | F(8): case FKEY | F(9): case FKEY | F(10): case FKEY | F(11): case FKEY | F(12): /* F1 through F12 keys. */ terminal_input_special(vw->vw_terminal, TKEY_F1 + c - (FKEY | F(1))); break; case FKEY | F(49): /* Home key. */ terminal_input_special(vw->vw_terminal, TKEY_HOME); break; case FKEY | F(50): /* Arrow up. */ terminal_input_special(vw->vw_terminal, TKEY_UP); break; case FKEY | F(51): /* Page up. */ terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP); break; case FKEY | F(53): /* Arrow left. */ terminal_input_special(vw->vw_terminal, TKEY_LEFT); break; case FKEY | F(55): /* Arrow right. */ terminal_input_special(vw->vw_terminal, TKEY_RIGHT); break; case FKEY | F(57): /* End key. */ terminal_input_special(vw->vw_terminal, TKEY_END); break; case FKEY | F(58): /* Arrow down. */ terminal_input_special(vw->vw_terminal, TKEY_DOWN); break; case FKEY | F(59): /* Page down. */ terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN); break; case FKEY | F(60): /* Insert key. */ terminal_input_special(vw->vw_terminal, TKEY_INSERT); break; case FKEY | F(61): /* Delete key. */ terminal_input_special(vw->vw_terminal, TKEY_DELETE); break; } } else if (KEYFLAGS(c) == 0) { /* Don't do UTF-8 conversion when doing raw mode. */ if (vw->vw_kbdmode == K_XLATE) { #if VT_ALT_TO_ESC_HACK if (vd->vd_kbstate & ALKED) { /* * Prepend ESC sequence if one of ALT keys down. */ terminal_input_char(vw->vw_terminal, 0x1b); } #endif terminal_input_char(vw->vw_terminal, KEYCHAR(c)); } else terminal_input_raw(vw->vw_terminal, c); } return (0); } static int vt_kbdevent(keyboard_t *kbd, int event, void *arg) { struct vt_device *vd = arg; int c; switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: mtx_lock(&Giant); vd->vd_keyboard = -1; kbd_release(kbd, (void *)vd); mtx_unlock(&Giant); return (0); default: return (EINVAL); } while ((c = kbdd_read_char(kbd, 0)) != NOKEY) vt_processkey(kbd, vd, c); return (0); } static int vt_allocate_keyboard(struct vt_device *vd) { int idx0, idx; keyboard_t *k0, *k; keyboard_info_t ki; idx0 = kbd_allocate("kbdmux", -1, vd, vt_kbdevent, vd); vd->vd_keyboard = idx0; if (idx0 >= 0) { DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0); 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; kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki); } } else { DPRINTF(20, "%s: no kbdmux allocated\n", __func__); idx0 = kbd_allocate("*", -1, vd, vt_kbdevent, vd); if (idx0 < 0) { DPRINTF(10, "%s: No keyboard found.\n", __func__); return (-1); } } DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); return (idx0); } static void vtterm_bell(struct terminal *tm) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; if (vd->vd_flags & VDF_QUIET_BELL) return; sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION); } static void vtterm_beep(struct terminal *tm, u_int param) { u_int freq, period; if ((param == 0) || ((param & 0xffff) == 0)) { vtterm_bell(tm); return; } period = ((param >> 16) & 0xffff) * hz / 1000; freq = 1193182 / (param & 0xffff); sysbeep(freq, period); } static void vtterm_cursor(struct terminal *tm, const term_pos_t *p) { struct vt_window *vw = tm->tm_softc; vtbuf_cursor_position(&vw->vw_buf, p); } static void vtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c) { struct vt_window *vw = tm->tm_softc; vtbuf_putchar(&vw->vw_buf, p, c); } static void vtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c) { struct vt_window *vw = tm->tm_softc; vtbuf_fill_locked(&vw->vw_buf, r, c); } static void vtterm_copy(struct terminal *tm, const term_rect_t *r, const term_pos_t *p) { struct vt_window *vw = tm->tm_softc; vtbuf_copy(&vw->vw_buf, r, p); } static void vtterm_param(struct terminal *tm, int cmd, unsigned int arg) { struct vt_window *vw = tm->tm_softc; switch (cmd) { case TP_SHOWCURSOR: vtbuf_cursor_visibility(&vw->vw_buf, arg); break; case TP_MOUSE: vw->vw_mouse_level = arg; break; } } void vt_determine_colors(term_char_t c, int cursor, term_color_t *fg, term_color_t *bg) { term_color_t tmp; int invert; invert = 0; *fg = TCHAR_FGCOLOR(c); if (TCHAR_FORMAT(c) & TF_BOLD) *fg = TCOLOR_LIGHT(*fg); *bg = TCHAR_BGCOLOR(c); if (TCHAR_FORMAT(c) & TF_REVERSE) invert ^= 1; if (cursor) invert ^= 1; if (invert) { tmp = *fg; *fg = *bg; *bg = tmp; } } static void vt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c, int iscursor, unsigned int row, unsigned int col) { term_color_t fg, bg; vt_determine_colors(c, iscursor, &fg, &bg); if (vf != NULL) { const uint8_t *src; vt_axis_t top, left; src = vtfont_lookup(vf, c); /* * Align the terminal to the centre of the screen. * Fonts may not always be able to fill the entire * screen. */ top = row * vf->vf_height + vd->vd_offset.tp_row; left = col * vf->vf_width + vd->vd_offset.tp_col; vd->vd_driver->vd_bitbltchr(vd, src, NULL, 0, top, left, vf->vf_width, vf->vf_height, fg, bg); } else { vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c), row, col, fg, bg); } } static void vt_flush(struct vt_device *vd) { struct vt_window *vw; struct vt_font *vf; struct vt_bufmask tmask; unsigned int row, col; term_rect_t tarea; term_pos_t size; term_char_t *r; + int cursor_displayed; #ifndef SC_NO_CUTPASTE - struct vt_mouse_cursor *cursor; int bpl, h, w; #endif vw = vd->vd_curwindow; if (vw == NULL) return; vf = vw->vw_font; if (((vd->vd_flags & VDF_TEXTMODE) == 0) && (vf == NULL)) return; if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY) return; + cursor_displayed = 0; + #ifndef SC_NO_CUTPASTE - cursor = NULL; if ((vd->vd_flags & VDF_MOUSECURSOR) && /* Mouse support enabled. */ !(vw->vw_flags & VWF_MOUSE_HIDE)) { /* Cursor displayed. */ if (vd->vd_moldx != vd->vd_mx || vd->vd_moldy != vd->vd_my) { /* * Mark last mouse position as dirty to erase. * * FIXME: The font size could be different among * all windows, so the column/row calculation * below isn't correct for all windows. * * FIXME: The cursor can span more than one * character cell. vtbuf_mouse_cursor_position * marks surrounding cells as dirty. But due * to font size possibly inconsistent across * windows, this may not be sufficient. This * causes part of the cursor to not be erased. * * FIXME: The vt_buf lock is acquired twice in a * row. */ vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_moldx / vf->vf_width, vd->vd_moldy / vf->vf_height); /* * Save point of last mouse cursor to erase it * later. */ vd->vd_moldx = vd->vd_mx; vd->vd_moldy = vd->vd_my; } if (!kdb_active && panicstr == NULL) { /* Mouse enabled, and DDB isn't active. */ - cursor = &vt_default_mouse_pointer; + cursor_displayed = 1; /* Mark new mouse position as dirty. */ vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mx / vf->vf_width, vd->vd_my / vf->vf_height); } } #endif vtbuf_undirty(&vw->vw_buf, &tarea, &tmask); vt_termsize(vd, vf, &size); /* Force a full redraw when the screen contents are invalid. */ if (vd->vd_flags & VDF_INVALID) { tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0; tarea.tr_end = size; tmask.vbm_row = tmask.vbm_col = VBM_DIRTY; vd->vd_flags &= ~VDF_INVALID; } if (vd->vd_driver->vd_bitblt_text != NULL) { if (tarea.tr_begin.tp_col < tarea.tr_end.tp_col) { - vd->vd_driver->vd_bitblt_text(vd, &vw->vw_buf, vf, &tarea -#ifndef SC_NO_CUTPASTE - , cursor, TC_WHITE, TC_BLACK -#endif - ); + vd->vd_driver->vd_bitblt_text(vd, &vw->vw_buf, vf, + &tarea, cursor_displayed); } } else { /* * FIXME: Once all backend drivers expose the * vd_bitblt_text_t callback, this code can be removed. */ for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) { if (!VTBUF_DIRTYROW(&tmask, row)) continue; r = VTBUF_GET_ROW(&vw->vw_buf, row); for (col = tarea.tr_begin.tp_col; col < tarea.tr_end.tp_col; col++) { if (!VTBUF_DIRTYCOL(&tmask, col)) continue; vt_bitblt_char(vd, vf, r[col], VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col); } } #ifndef SC_NO_CUTPASTE - if (cursor != NULL) { - bpl = (cursor->width + 7) >> 3; /* Bytes per source line. */ - w = cursor->width; - h = cursor->height; + if (cursor_displayed) { + /* Bytes per source line. */ + bpl = (vd->vd_mcursor->width + 7) >> 3; + w = vd->vd_mcursor->width; + h = vd->vd_mcursor->height; - if ((vd->vd_mx + cursor->width) > + if ((vd->vd_mx + vd->vd_mcursor->width) > (size.tp_col * vf->vf_width)) w = (size.tp_col * vf->vf_width) - vd->vd_mx - 1; - if ((vd->vd_my + cursor->height) > + if ((vd->vd_my + vd->vd_mcursor->height) > (size.tp_row * vf->vf_height)) h = (size.tp_row * vf->vf_height) - vd->vd_my - 1; - vd->vd_driver->vd_bitbltchr(vd, cursor->map, cursor->mask, bpl, + vd->vd_driver->vd_bitbltchr(vd, + vd->vd_mcursor->map, vd->vd_mcursor->mask, bpl, vd->vd_offset.tp_row + vd->vd_my, vd->vd_offset.tp_col + vd->vd_mx, - w, h, TC_WHITE, TC_BLACK); + w, h, vd->vd_mcursor_fg, vd->vd_mcursor_bg); } #endif } } static void vt_timer(void *arg) { struct vt_device *vd; vd = arg; /* Update screen if required. */ vt_flush(vd); /* Schedule for next update. */ vt_schedule_flush(vd, 0); } static void vtterm_done(struct terminal *tm) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; if (kdb_active || panicstr != NULL) { /* Switch to the debugger. */ if (vd->vd_curwindow != vw) { vd->vd_curwindow = vw; vd->vd_flags |= VDF_INVALID; if (vd->vd_driver->vd_postswitch) vd->vd_driver->vd_postswitch(vd); } vd->vd_flags &= ~VDF_SPLASH; vt_flush(vd); } else if (!(vd->vd_flags & VDF_ASYNC)) { vt_flush(vd); } } #ifdef DEV_SPLASH static void vtterm_splash(struct vt_device *vd) { vt_axis_t top, left; /* Display a nice boot splash. */ if (!(vd->vd_flags & VDF_TEXTMODE) && (boothowto & RB_MUTE)) { top = (vd->vd_height - vt_logo_height) / 2; left = (vd->vd_width - vt_logo_width) / 2; switch (vt_logo_depth) { case 1: /* XXX: Unhardcode colors! */ vd->vd_driver->vd_bitbltchr(vd, vt_logo_image, NULL, 0, top, left, vt_logo_width, vt_logo_height, 0xf, 0x0); } vd->vd_flags |= VDF_SPLASH; } } #endif static void vtterm_cnprobe(struct terminal *tm, struct consdev *cp) { struct vt_driver *vtd, **vtdlist, *vtdbest = NULL; struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; struct winsize wsz; term_attr_t attr; term_char_t c; if (!vty_enabled(VTY_VT)) return; if (vd->vd_flags & VDF_INITIALIZED) /* Initialization already done. */ return; SET_FOREACH(vtdlist, vt_drv_set) { vtd = *vtdlist; if (vtd->vd_probe == NULL) continue; if (vtd->vd_probe(vd) == CN_DEAD) continue; if ((vtdbest == NULL) || (vtd->vd_priority > vtdbest->vd_priority)) vtdbest = vtd; } if (vtdbest == NULL) { cp->cn_pri = CN_DEAD; vd->vd_flags |= VDF_DEAD; } else { vd->vd_driver = vtdbest; cp->cn_pri = vd->vd_driver->vd_init(vd); } /* Check if driver's vt_init return CN_DEAD. */ if (cp->cn_pri == CN_DEAD) { vd->vd_flags |= VDF_DEAD; } /* Initialize any early-boot keyboard drivers */ kbd_configure(KB_CONF_PROBE_ONLY); vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1); vd->vd_windows[VT_CONSWINDOW] = vw; sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw)); /* Attach default font if not in TEXTMODE. */ if ((vd->vd_flags & VDF_TEXTMODE) == 0) vw->vw_font = vtfont_ref(&vt_font_default); vtbuf_init_early(&vw->vw_buf); vt_winsize(vd, vw->vw_font, &wsz); c = (boothowto & RB_MUTE) == 0 ? TERMINAL_KERN_ATTR : TERMINAL_NORM_ATTR; attr.ta_format = TCHAR_FORMAT(c); attr.ta_fgcolor = TCHAR_FGCOLOR(c); attr.ta_bgcolor = TCHAR_BGCOLOR(c); terminal_set_winsize_blank(tm, &wsz, 1, &attr); if (vtdbest != NULL) { #ifdef DEV_SPLASH vtterm_splash(vd); #endif vd->vd_flags |= VDF_INITIALIZED; } } static int vtterm_cngetc(struct terminal *tm) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; keyboard_t *kbd; int state; u_int c; if (vw->vw_kbdsq && *vw->vw_kbdsq) return (*vw->vw_kbdsq++); state = 0; /* Make sure the splash screen is not there. */ if (vd->vd_flags & VDF_SPLASH) { /* Remove splash */ vd->vd_flags &= ~VDF_SPLASH; /* Mark screen as invalid to force update */ vd->vd_flags |= VDF_INVALID; vt_flush(vd); } /* Stripped down keyboard handler. */ kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd == NULL) return (-1); /* Force keyboard input mode to K_XLATE */ c = K_XLATE; kbdd_ioctl(kbd, KDSKBMODE, (void *)&c); /* Switch the keyboard to polling to make it work here. */ kbdd_poll(kbd, TRUE); c = kbdd_read_char(kbd, 0); kbdd_poll(kbd, FALSE); if (c & RELKEY) return (-1); if (vw->vw_flags & VWF_SCROLL) { vt_scrollmode_kbdevent(vw, c, 1/* Console mode */); vt_flush(vd); return (-1); } /* Stripped down handling of vt_kbdevent(), without locking, etc. */ if (c & SPCLKEY) { switch (c) { case SPCLKEY | SLK: kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); if (state & SLKED) { /* Turn scrolling on. */ vw->vw_flags |= VWF_SCROLL; VTBUF_SLCK_ENABLE(&vw->vw_buf); } else { /* Turn scrolling off. */ vt_scroll(vw, 0, VHS_END); vw->vw_flags &= ~VWF_SCROLL; VTBUF_SLCK_DISABLE(&vw->vw_buf); } break; /* XXX: KDB can handle history. */ case SPCLKEY | FKEY | F(50): /* Arrow up. */ vw->vw_kbdsq = "\x1b[A"; break; case SPCLKEY | FKEY | F(58): /* Arrow down. */ vw->vw_kbdsq = "\x1b[B"; break; case SPCLKEY | FKEY | F(55): /* Arrow right. */ vw->vw_kbdsq = "\x1b[C"; break; case SPCLKEY | FKEY | F(53): /* Arrow left. */ vw->vw_kbdsq = "\x1b[D"; break; } /* Force refresh to make scrollback work. */ vt_flush(vd); } else if (KEYFLAGS(c) == 0) { return (KEYCHAR(c)); } if (vw->vw_kbdsq && *vw->vw_kbdsq) return (*vw->vw_kbdsq++); return (-1); } static void vtterm_opened(struct terminal *tm, int opened) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; VT_LOCK(vd); vd->vd_flags &= ~VDF_SPLASH; if (opened) vw->vw_flags |= VWF_OPENED; else { vw->vw_flags &= ~VWF_OPENED; /* TODO: finish ACQ/REL */ } VT_UNLOCK(vd); } static int vt_change_font(struct vt_window *vw, struct vt_font *vf) { struct vt_device *vd = vw->vw_device; struct terminal *tm = vw->vw_terminal; term_pos_t size; struct winsize wsz; /* * Changing fonts. * * Changing fonts is a little tricky. We must prevent * simultaneous access to the device, so we must stop * the display timer and the terminal from accessing. * We need to switch fonts and grow our screen buffer. * * XXX: Right now the code uses terminal_mute() to * prevent data from reaching the console driver while * resizing the screen buffer. This isn't elegant... */ VT_LOCK(vd); if (vw->vw_flags & VWF_BUSY) { /* Another process is changing the font. */ VT_UNLOCK(vd); return (EBUSY); } if (vd->vd_flags & VDF_TEXTMODE) { /* Our device doesn't need fonts. */ VT_UNLOCK(vd); return (ENOTTY); } vw->vw_flags |= VWF_BUSY; VT_UNLOCK(vd); vt_termsize(vd, vf, &size); vt_winsize(vd, vf, &wsz); /* Save offset to font aligned area. */ vd->vd_offset.tp_col = (vd->vd_width % vf->vf_width) / 2; vd->vd_offset.tp_row = (vd->vd_height % vf->vf_height) / 2; /* Grow the screen buffer and terminal. */ terminal_mute(tm, 1); vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size); terminal_set_winsize_blank(tm, &wsz, 0, NULL); terminal_mute(tm, 0); /* Actually apply the font to the current window. */ VT_LOCK(vd); if (vw->vw_font != vf) { /* * In case vt_change_font called to update size we don't need * to update font link. */ vtfont_unref(vw->vw_font); vw->vw_font = vtfont_ref(vf); } /* Force a full redraw the next timer tick. */ if (vd->vd_curwindow == vw) vd->vd_flags |= VDF_INVALID; vw->vw_flags &= ~VWF_BUSY; VT_UNLOCK(vd); return (0); } static int vt_set_border(struct vt_window *vw, struct vt_font *vf, term_color_t c) { struct vt_device *vd = vw->vw_device; int x, y, off_x, off_y; if (vd->vd_driver->vd_drawrect == NULL) return (ENOTSUP); x = vd->vd_width - 1; y = vd->vd_height - 1; off_x = vd->vd_offset.tp_col; off_y = vd->vd_offset.tp_row; /* Top bar. */ if (off_y > 0) vd->vd_driver->vd_drawrect(vd, 0, 0, x, off_y - 1, 1, c); /* Left bar. */ if (off_x > 0) vd->vd_driver->vd_drawrect(vd, 0, off_y, off_x - 1, y - off_y, 1, c); /* Right bar. May be 1 pixel wider than necessary due to rounding. */ vd->vd_driver->vd_drawrect(vd, x - off_x, off_y, x, y - off_y, 1, c); /* Bottom bar. May be 1 mixel taller than necessary due to rounding. */ vd->vd_driver->vd_drawrect(vd, 0, y - off_y, x, y, 1, c); return (0); } static int vt_proc_alive(struct vt_window *vw) { struct proc *p; if (vw->vw_smode.mode != VT_PROCESS) return (FALSE); if (vw->vw_proc) { if ((p = pfind(vw->vw_pid)) != NULL) PROC_UNLOCK(p); if (vw->vw_proc == p) return (TRUE); vw->vw_proc = NULL; vw->vw_smode.mode = VT_AUTO; DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid); vw->vw_pid = 0; } return (FALSE); } static int signal_vt_rel(struct vt_window *vw) { if (vw->vw_smode.mode != VT_PROCESS) return (FALSE); if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { vw->vw_proc = NULL; vw->vw_pid = 0; return (TRUE); } vw->vw_flags |= VWF_SWWAIT_REL; PROC_LOCK(vw->vw_proc); kern_psignal(vw->vw_proc, vw->vw_smode.relsig); PROC_UNLOCK(vw->vw_proc); DPRINTF(1, "sending relsig to %d\n", vw->vw_pid); return (TRUE); } static int signal_vt_acq(struct vt_window *vw) { if (vw->vw_smode.mode != VT_PROCESS) return (FALSE); if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) cnavailable(vw->vw_terminal->consdev, FALSE); if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { vw->vw_proc = NULL; vw->vw_pid = 0; return (TRUE); } vw->vw_flags |= VWF_SWWAIT_ACQ; PROC_LOCK(vw->vw_proc); kern_psignal(vw->vw_proc, vw->vw_smode.acqsig); PROC_UNLOCK(vw->vw_proc); DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid); return (TRUE); } static int finish_vt_rel(struct vt_window *vw, int release, int *s) { if (vw->vw_flags & VWF_SWWAIT_REL) { vw->vw_flags &= ~VWF_SWWAIT_REL; if (release) { callout_drain(&vw->vw_proc_dead_timer); vt_late_window_switch(vw->vw_switch_to); } return (0); } return (EINVAL); } static int finish_vt_acq(struct vt_window *vw) { if (vw->vw_flags & VWF_SWWAIT_ACQ) { vw->vw_flags &= ~VWF_SWWAIT_ACQ; return (0); } return (EINVAL); } #ifndef SC_NO_CUTPASTE static void vt_mouse_terminput_button(struct vt_device *vd, int button) { struct vt_window *vw; struct vt_font *vf; char mouseb[6] = "\x1B[M"; int i, x, y; vw = vd->vd_curwindow; vf = vw->vw_font; /* Translate to char position. */ x = vd->vd_mx / vf->vf_width; y = vd->vd_my / vf->vf_height; /* Avoid overflow. */ x = MIN(x, 255 - '!'); y = MIN(y, 255 - '!'); mouseb[3] = ' ' + button; mouseb[4] = '!' + x; mouseb[5] = '!' + y; for (i = 0; i < sizeof(mouseb); i++ ) terminal_input_char(vw->vw_terminal, mouseb[i]); } static void vt_mouse_terminput(struct vt_device *vd, int type, int x, int y, int event, int cnt) { switch (type) { case MOUSE_BUTTON_EVENT: if (cnt > 0) { /* Mouse button pressed. */ if (event & MOUSE_BUTTON1DOWN) vt_mouse_terminput_button(vd, 0); if (event & MOUSE_BUTTON2DOWN) vt_mouse_terminput_button(vd, 1); if (event & MOUSE_BUTTON3DOWN) vt_mouse_terminput_button(vd, 2); } else { /* Mouse button released. */ vt_mouse_terminput_button(vd, 3); } break; #ifdef notyet case MOUSE_MOTION_EVENT: if (mouse->u.data.z < 0) { /* Scroll up. */ sc_mouse_input_button(vd, 64); } else if (mouse->u.data.z > 0) { /* Scroll down. */ sc_mouse_input_button(vd, 65); } break; #endif } } void vt_mouse_event(int type, int x, int y, int event, int cnt, int mlevel) { struct vt_device *vd; struct vt_window *vw; struct vt_font *vf; term_pos_t size; term_char_t *buf; int i, len, mark; vd = main_vd; vw = vd->vd_curwindow; vf = vw->vw_font; mark = 0; if (vw->vw_flags & VWF_MOUSE_HIDE) return; /* Mouse disabled. */ if (vf == NULL) /* Text mode. */ return; /* * TODO: add flag about pointer position changed, to not redraw chars * under mouse pointer when nothing changed. */ if (vw->vw_mouse_level > 0) vt_mouse_terminput(vd, type, x, y, event, cnt); switch (type) { case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* Movement */ x += vd->vd_mx; y += vd->vd_my; vt_termsize(vd, vf, &size); /* Apply limits. */ x = MAX(x, 0); y = MAX(y, 0); x = MIN(x, (size.tp_col * vf->vf_width) - 1); y = MIN(y, (size.tp_row * vf->vf_height) - 1); vd->vd_mx = x; vd->vd_my = y; if ((vd->vd_mstate & MOUSE_BUTTON1DOWN) && (vtbuf_set_mark(&vw->vw_buf, VTB_MARK_MOVE, vd->vd_mx / vf->vf_width, vd->vd_my / vf->vf_height) == 1)) { /* * We have something marked to copy, so update pointer * to window with selection. */ vd->vd_markedwin = vw; } return; /* Done */ case MOUSE_BUTTON_EVENT: /* Buttons */ break; default: return; /* Done */ } switch (event) { case MOUSE_BUTTON1DOWN: switch (cnt % 4) { case 0: /* up */ mark = VTB_MARK_END; break; case 1: /* single click: start cut operation */ mark = VTB_MARK_START; break; case 2: /* double click: cut a word */ mark = VTB_MARK_WORD; break; case 3: /* triple click: cut a line */ mark = VTB_MARK_ROW; break; } break; case VT_MOUSE_PASTEBUTTON: switch (cnt) { case 0: /* up */ break; default: if (vd->vd_markedwin == NULL) return; /* Get current selecton size in bytes. */ len = vtbuf_get_marked_len(&vd->vd_markedwin->vw_buf); if (len <= 0) return; buf = malloc(len, M_VT, M_WAITOK | M_ZERO); /* Request cupy/paste buffer data, no more than `len' */ vtbuf_extract_marked(&vd->vd_markedwin->vw_buf, buf, len); len /= sizeof(term_char_t); for (i = 0; i < len; i++ ) { if (buf[i] == '\0') continue; terminal_input_char(vw->vw_terminal, buf[i]); } /* Done, so cleanup. */ free(buf, M_VT); break; } return; /* Done */ case VT_MOUSE_EXTENDBUTTON: switch (cnt) { case 0: /* up */ if (!(vd->vd_mstate & MOUSE_BUTTON1DOWN)) mark = VTB_MARK_EXTEND; else mark = 0; break; default: mark = VTB_MARK_EXTEND; break; } break; default: return; /* Done */ } /* Save buttons state. */ if (cnt > 0) vd->vd_mstate |= event; else vd->vd_mstate &= ~event; if (vtbuf_set_mark(&vw->vw_buf, mark, vd->vd_mx / vf->vf_width, vd->vd_my / vf->vf_height) == 1) { /* * We have something marked to copy, so update pointer to * window with selection. */ vd->vd_markedwin = vw; } } void vt_mouse_state(int show) { struct vt_device *vd; struct vt_window *vw; vd = main_vd; vw = vd->vd_curwindow; switch (show) { case VT_MOUSE_HIDE: vw->vw_flags |= VWF_MOUSE_HIDE; break; case VT_MOUSE_SHOW: vw->vw_flags &= ~VWF_MOUSE_HIDE; break; } /* * Mark mouse position as dirty. * * FIXME: See comments in vt_flush(). */ vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mx / vw->vw_font->vf_width, vd->vd_my / vw->vw_font->vf_height); } #endif static int vtterm_mmap(struct terminal *tm, vm_ooffset_t offset, vm_paddr_t * paddr, int nprot, vm_memattr_t *memattr) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; if (vd->vd_driver->vd_fb_mmap) return (vd->vd_driver->vd_fb_mmap(vd, offset, paddr, nprot, memattr)); return (ENXIO); } static int vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data, struct thread *td) { struct vt_window *vw = tm->tm_softc; struct vt_device *vd = vw->vw_device; keyboard_t *kbd; int error, i, s; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; switch (cmd) { case _IO('v', 4): cmd = VT_RELDISP; break; case _IO('v', 5): cmd = VT_ACTIVATE; break; case _IO('v', 6): cmd = VT_WAITACTIVE; break; case _IO('K', 20): cmd = KDSKBSTATE; break; case _IO('K', 67): cmd = KDSETRAD; break; case _IO('K', 7): cmd = KDSKBMODE; break; case _IO('K', 8): cmd = KDMKTONE; break; case _IO('K', 63): cmd = KIOCSOUND; break; case _IO('K', 66): cmd = KDSETLED; break; case _IO('c', 110): cmd = CONS_SETKBD; break; default: goto skip_thunk; } ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; skip_thunk: #endif switch (cmd) { case KDSETRAD: /* set keyboard repeat & delay rates (old) */ if (*(int *)data & ~0x7f) return (EINVAL); case GIO_KEYMAP: case PIO_KEYMAP: case GIO_DEADKEYMAP: case PIO_DEADKEYMAP: case GETFKEY: case SETFKEY: case KDGKBINFO: case KDGKBTYPE: case KDSKBSTATE: /* set keyboard state (locks) */ case KDGKBSTATE: /* get keyboard state (locks) */ case KDGETREPEAT: /* get keyboard repeat & delay rates */ case KDSETREPEAT: /* set keyboard repeat & delay rates (new) */ case KDSETLED: /* set keyboard LED status */ case KDGETLED: /* get keyboard LED status */ case KBADDKBD: /* add/remove keyboard to/from mux */ case KBRELKBD: { error = 0; mtx_lock(&Giant); kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd != NULL) error = kbdd_ioctl(kbd, cmd, data); mtx_unlock(&Giant); if (error == ENOIOCTL) { if (cmd == KDGKBTYPE) { /* always return something? XXX */ *(int *)data = 0; } else { return (ENODEV); } } return (error); } case KDGKBMODE: { int mode = -1; mtx_lock(&Giant); kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd != NULL) { kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode); } mtx_unlock(&Giant); DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode); *(int *)data = mode; return (0); } case KDSKBMODE: { int mode; mode = *(int *)data; switch (mode) { case K_XLATE: case K_RAW: case K_CODE: vw->vw_kbdmode = mode; if (vw == vd->vd_curwindow) { keyboard_t *kbd; error = 0; mtx_lock(&Giant); kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd != NULL) { error = kbdd_ioctl(kbd, KDSKBMODE, (void *)&mode); } mtx_unlock(&Giant); } return (0); default: return (EINVAL); } } case FBIOGTYPE: case FBIO_GETWINORG: /* get frame buffer window origin */ case FBIO_GETDISPSTART: /* get display start address */ case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ case FBIO_BLANK: /* blank display */ if (vd->vd_driver->vd_fb_ioctl) return (vd->vd_driver->vd_fb_ioctl(vd, cmd, data, td)); break; case CONS_BLANKTIME: /* XXX */ return (0); case CONS_GET: /* XXX */ *(int *)data = M_CG640x480; return (0); case CONS_BELLTYPE: /* set bell type sound */ if ((*(int *)data) & CONS_QUIET_BELL) vd->vd_flags |= VDF_QUIET_BELL; else vd->vd_flags &= ~VDF_QUIET_BELL; return (0); case CONS_GETINFO: { vid_info_t *vi = (vid_info_t *)data; vi->m_num = vd->vd_curwindow->vw_number + 1; /* XXX: other fields! */ return (0); } case CONS_GETVERS: *(int *)data = 0x200; return (0); case CONS_MODEINFO: /* XXX */ return (0); case CONS_MOUSECTL: { mouse_info_t *mouse = (mouse_info_t*)data; /* * All the commands except MOUSE_SHOW nd MOUSE_HIDE * should not be applied to individual TTYs, but only to * consolectl. */ switch (mouse->operation) { case MOUSE_HIDE: if (vd->vd_flags & VDF_MOUSECURSOR) { vd->vd_flags &= ~VDF_MOUSECURSOR; #ifndef SC_NO_CUTPASTE vt_mouse_state(VT_MOUSE_HIDE); #endif } return (0); case MOUSE_SHOW: if (!(vd->vd_flags & VDF_MOUSECURSOR)) { vd->vd_flags |= VDF_MOUSECURSOR; vd->vd_mx = vd->vd_width / 2; vd->vd_my = vd->vd_height / 2; #ifndef SC_NO_CUTPASTE vt_mouse_state(VT_MOUSE_SHOW); #endif } return (0); default: return (EINVAL); } } case PIO_VFONT: { struct vt_font *vf; error = vtfont_load((void *)data, &vf); if (error != 0) return (error); error = vt_change_font(vw, vf); if (error == 0) { /* XXX: replace 0 with current bg color. */ vt_set_border(vw, vf, 0); } vtfont_unref(vf); return (error); } case GIO_SCRNMAP: { scrmap_t *sm = (scrmap_t *)data; /* We don't have screen maps, so return a handcrafted one. */ for (i = 0; i < 256; i++) sm->scrmap[i] = i; return (0); } case KDSETMODE: /* XXX */ 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); #if defined(__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) */ #if defined(__i386__) td->td_frame->tf_eflags &= ~PSL_IOPL; #elif defined(__amd64__) td->td_frame->tf_rflags &= ~PSL_IOPL; #endif return (0); case KDMKTONE: /* sound the bell */ vtterm_beep(tm, *(u_int *)data); return (0); case KIOCSOUND: /* make tone (*data) hz */ /* TODO */ return (0); case CONS_SETKBD: /* set the new keyboard */ mtx_lock(&Giant); error = 0; if (vd->vd_keyboard != *(int *)data) { kbd = kbd_get_keyboard(*(int *)data); if (kbd == NULL) { mtx_unlock(&Giant); return (EINVAL); } i = kbd_allocate(kbd->kb_name, kbd->kb_unit, (void *)vd, vt_kbdevent, vd); if (i >= 0) { if (vd->vd_keyboard != -1) { kbd_release(kbd, (void *)vd); } kbd = kbd_get_keyboard(i); vd->vd_keyboard = i; (void)kbdd_ioctl(kbd, KDSKBMODE, (caddr_t)&vd->vd_curwindow->vw_kbdmode); } else { error = EPERM; /* XXX */ } } mtx_unlock(&Giant); return (error); case CONS_RELKBD: /* release the current keyboard */ mtx_lock(&Giant); error = 0; if (vd->vd_keyboard != -1) { kbd = kbd_get_keyboard(vd->vd_keyboard); if (kbd == NULL) { mtx_unlock(&Giant); return (EINVAL); } error = kbd_release(kbd, (void *)vd); if (error == 0) { vd->vd_keyboard = -1; } } mtx_unlock(&Giant); return (error); case VT_ACTIVATE: { int win; win = *(int *)data - 1; DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win); if ((win > VT_MAXWINDOWS) || (win < 0)) return (EINVAL); return (vt_proc_window_switch(vd->vd_windows[win])); } case VT_GETACTIVE: *(int *)data = vd->vd_curwindow->vw_number + 1; return (0); case VT_GETINDEX: *(int *)data = vw->vw_number + 1; return (0); case VT_LOCKSWITCH: /* TODO: Check current state, switching can be in progress. */ if ((*(int *)data) == 0x01) vw->vw_flags |= VWF_VTYLOCK; else if ((*(int *)data) == 0x02) vw->vw_flags &= ~VWF_VTYLOCK; else return (EINVAL); return (0); case VT_OPENQRY: VT_LOCK(vd); for (i = 0; i < VT_MAXWINDOWS; i++) { vw = vd->vd_windows[i]; if (vw == NULL) continue; if (!(vw->vw_flags & VWF_OPENED)) { *(int *)data = vw->vw_number + 1; VT_UNLOCK(vd); return (0); } } VT_UNLOCK(vd); return (EINVAL); case VT_WAITACTIVE: error = 0; i = *(unsigned int *)data; if (i > VT_MAXWINDOWS) return (EINVAL); if (i != 0) vw = vd->vd_windows[i - 1]; VT_LOCK(vd); while (vd->vd_curwindow != vw && error == 0) error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock); VT_UNLOCK(vd); return (error); 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, VT_UNIT(vw)); if (vw->vw_smode.mode == VT_PROCESS) { p1 = pfind(vw->vw_pid); if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) { if (p1) PROC_UNLOCK(p1); DPRINTF(5, "error EPERM\n"); return (EPERM); } if (p1) PROC_UNLOCK(p1); } if (mode->mode == VT_AUTO) { vw->vw_smode.mode = VT_AUTO; vw->vw_proc = NULL; vw->vw_pid = 0; DPRINTF(5, "VT_AUTO, "); if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) cnavailable(vw->vw_terminal->consdev, TRUE); /* were we in the middle of the vty switching process? */ if (finish_vt_rel(vw, TRUE, &s) == 0) DPRINTF(5, "reset WAIT_REL, "); if (finish_vt_acq(vw) == 0) DPRINTF(5, "reset WAIT_ACQ, "); return (0); } else if (mode->mode == VT_PROCESS) { if (!ISSIGVALID(mode->relsig) || !ISSIGVALID(mode->acqsig) || !ISSIGVALID(mode->frsig)) { DPRINTF(5, "error EINVAL\n"); return (EINVAL); } DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid); bcopy(data, &vw->vw_smode, sizeof(struct vt_mode)); vw->vw_proc = td->td_proc; vw->vw_pid = vw->vw_proc->p_pid; if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) cnavailable(vw->vw_terminal->consdev, FALSE); } else { DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n", mode->mode); return (EINVAL); } DPRINTF(5, "\n"); return (0); } case VT_GETMODE: /* get screen switcher mode */ bcopy(&vw->vw_smode, data, sizeof(struct vt_mode)); return (0); case VT_RELDISP: /* screen switcher ioctl */ /* * This must be the current vty which is in the VT_PROCESS * switching mode... */ if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode != VT_PROCESS)) { return (EINVAL); } /* ...and this process is controlling it. */ if (vw->vw_proc != td->td_proc) { return (EPERM); } error = EINVAL; switch(*(int *)data) { case VT_FALSE: /* user refuses to release screen, abort */ if ((error = finish_vt_rel(vw, FALSE, &s)) == 0) DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME, VT_UNIT(vw)); break; case VT_TRUE: /* user has released screen, go on */ /* finish_vt_rel(..., TRUE, ...) should not be locked */ if (vw->vw_flags & VWF_SWWAIT_REL) { if ((error = finish_vt_rel(vw, TRUE, &s)) == 0) DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n", SC_DRIVER_NAME, VT_UNIT(vw)); } else { error = EINVAL; } return (error); case VT_ACKACQ: /* acquire acknowledged, switch completed */ if ((error = finish_vt_acq(vw)) == 0) DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME, VT_UNIT(vw)); break; default: break; } return (error); } return (ENOIOCTL); } static struct vt_window * vt_allocate_window(struct vt_device *vd, unsigned int window) { struct vt_window *vw; struct terminal *tm; term_pos_t size; struct winsize wsz; vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO); vw->vw_device = vd; vw->vw_number = window; vw->vw_kbdmode = K_XLATE; if ((vd->vd_flags & VDF_TEXTMODE) == 0) vw->vw_font = vtfont_ref(&vt_font_default); vt_termsize(vd, vw->vw_font, &size); vt_winsize(vd, vw->vw_font, &wsz); vtbuf_init(&vw->vw_buf, &size); tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw); terminal_set_winsize(tm, &wsz); vd->vd_windows[window] = vw; callout_init(&vw->vw_proc_dead_timer, 0); return (vw); } void vt_upgrade(struct vt_device *vd) { struct vt_window *vw; unsigned int i; if (!vty_enabled(VTY_VT)) return; for (i = 0; i < VT_MAXWINDOWS; i++) { vw = vd->vd_windows[i]; if (vw == NULL) { /* New window. */ vw = vt_allocate_window(vd, i); } if (!(vw->vw_flags & VWF_READY)) { callout_init(&vw->vw_proc_dead_timer, 0); terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw)); vw->vw_flags |= VWF_READY; if (vw->vw_flags & VWF_CONSOLE) { /* For existing console window. */ EVENTHANDLER_REGISTER(shutdown_pre_sync, vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT); } } } VT_LOCK(vd); if (vd->vd_curwindow == NULL) vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW]; if (!(vd->vd_flags & VDF_ASYNC)) { /* Attach keyboard. */ vt_allocate_keyboard(vd); /* Init 25 Hz timer. */ callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0); /* Start timer when everything ready. */ vd->vd_flags |= VDF_ASYNC; callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd); vd->vd_timer_armed = 1; } VT_UNLOCK(vd); /* Refill settings with new sizes. */ vt_resize(vd); } static void vt_resize(struct vt_device *vd) { struct vt_window *vw; int i; for (i = 0; i < VT_MAXWINDOWS; i++) { vw = vd->vd_windows[i]; VT_LOCK(vd); /* Assign default font to window, if not textmode. */ if (!(vd->vd_flags & VDF_TEXTMODE) && vw->vw_font == NULL) vw->vw_font = vtfont_ref(&vt_font_default); VT_UNLOCK(vd); /* Resize terminal windows */ while (vt_change_font(vw, vw->vw_font) == EBUSY) { DPRINTF(100, "%s: vt_change_font() is busy, " "window %d\n", __func__, i); } } } void vt_allocate(struct vt_driver *drv, void *softc) { struct vt_device *vd; if (!vty_enabled(VTY_VT)) return; if (main_vd->vd_driver == NULL) { main_vd->vd_driver = drv; printf("VT: initialize with new VT driver \"%s\".\n", drv->vd_name); } else { /* * Check if have rights to replace current driver. For example: * it is bad idea to replace KMS driver with generic VGA one. */ if (drv->vd_priority <= main_vd->vd_driver->vd_priority) { printf("VT: Driver priority %d too low. Current %d\n ", drv->vd_priority, main_vd->vd_driver->vd_priority); return; } printf("VT: Replacing driver \"%s\" with new \"%s\".\n", main_vd->vd_driver->vd_name, drv->vd_name); } vd = main_vd; VT_LOCK(vd); if (vd->vd_flags & VDF_ASYNC) { /* Stop vt_flush periodic task. */ vt_suspend_flush_timer(vd); /* * Mute current terminal until we done. vt_change_font (called * from vt_resize) will unmute it. */ terminal_mute(vd->vd_curwindow->vw_terminal, 1); } /* * Reset VDF_TEXTMODE flag, driver who require that flag (vt_vga) will * set it. */ vd->vd_flags &= ~VDF_TEXTMODE; vd->vd_driver = drv; vd->vd_softc = softc; vd->vd_driver->vd_init(vd); VT_UNLOCK(vd); /* Update windows sizes and initialize last items. */ vt_upgrade(vd); #ifdef DEV_SPLASH if (vd->vd_flags & VDF_SPLASH) vtterm_splash(vd); #endif if (vd->vd_flags & VDF_ASYNC) { /* Allow to put chars now. */ terminal_mute(vd->vd_curwindow->vw_terminal, 0); /* Rerun timer for screen updates. */ vt_resume_flush_timer(vd, 0); } /* * Register as console. If it already registered, cnadd() will ignore * it. */ termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal); } void vt_suspend() { if (vt_suspendswitch == 0) return; /* Save current window. */ main_vd->vd_savedwindow = main_vd->vd_curwindow; /* Ask holding process to free window and switch to console window */ vt_proc_window_switch(main_vd->vd_windows[VT_CONSWINDOW]); } void vt_resume() { if (vt_suspendswitch == 0) return; /* Switch back to saved window */ if (main_vd->vd_savedwindow != NULL) vt_proc_window_switch(main_vd->vd_savedwindow); main_vd->vd_savedwindow = NULL; }