diff --git a/sys/dev/vt/font/vt_mouse_cursor.c b/sys/dev/vt/font/vt_mouse_cursor.c index a35de00..0219c7c 100644 --- a/sys/dev/vt/font/vt_mouse_cursor.c +++ b/sys/dev/vt/font/vt_mouse_cursor.c @@ -43,7 +43,7 @@ struct mouse_cursor vt_default_mouse_pointer = { 0x7c, /* "_*****_ " */ 0x7e, /* "_******_" */ 0x68, /* "_**_****" */ - 0x4c, /* "_*__**__" */ + 0x4c, /* "_*__**_ " */ 0x0c, /* " _ _**_ " */ 0x06, /* " _**_" */ 0x06, /* " _**_" */ @@ -58,8 +58,8 @@ struct mouse_cursor vt_default_mouse_pointer = { 0xfe, /* "_______ " */ 0xff, /* "________" */ 0xff, /* "________" */ - 0xff, /* "________" */ - 0x1e, /* " ____ " */ + 0xfe, /* "_______ " */ + 0x5e, /* " _ ____ " */ 0x0f, /* " ____" */ 0x0f, /* " ____" */ 0x0f, /* " ____" */ diff --git a/sys/dev/vt/hw/vga/vt_vga.c b/sys/dev/vt/hw/vga/vt_vga.c index 5dbf5bd..68ad196 100644 --- a/sys/dev/vt/hw/vga/vt_vga.c +++ b/sys/dev/vt/hw/vga/vt_vga.c @@ -54,7 +54,7 @@ struct vga_softc { bus_space_handle_t vga_fb_handle; bus_space_tag_t vga_reg_tag; bus_space_handle_t vga_reg_handle; - int vga_curcolor; + term_color_t vga_curfg, vga_curbg; }; /* Convenience macros. */ @@ -71,10 +71,25 @@ struct vga_softc { #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 +// FIXME #define VT_VGA_PATTERN_OFFSET VT_VGA_MEMSIZE + 1 + static vd_probe_t vga_probe; static vd_init_t vga_init; static vd_blank_t vga_blank; -static vd_bitbltchr_t vga_bitbltchr; +static vd_bitblt_text_t vga_bitblt_text; static vd_drawrect_t vga_drawrect; static vd_setpixel_t vga_setpixel; static vd_putchar_t vga_putchar; @@ -85,7 +100,8 @@ static const struct vt_driver vt_vga_driver = { .vd_probe = vga_probe, .vd_init = vga_init, .vd_blank = vga_blank, - .vd_bitbltchr = vga_bitbltchr, + .vd_bitbltchr = NULL, + .vd_bitblt_text = vga_bitblt_text, .vd_drawrect = vga_drawrect, .vd_setpixel = vga_setpixel, .vd_putchar = vga_putchar, @@ -101,152 +117,45 @@ static struct vga_softc vga_conssoftc; VT_DRIVER_DECLARE(vt_vga, vt_vga_driver); static inline void -vga_setcolor(struct vt_device *vd, term_color_t color) +vga_setfg(struct vt_device *vd, term_color_t color) { struct vga_softc *sc = vd->vd_softc; - if (sc->vga_curcolor != color) { + if (sc->vga_curfg != color) { REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_SET_RESET); REG_WRITE1(sc, VGA_GC_DATA, color); - sc->vga_curcolor = color; + sc->vga_curfg = color; } } -static void -vga_blank(struct vt_device *vd, term_color_t color) -{ - struct vga_softc *sc = vd->vd_softc; - u_int ofs; - - vga_setcolor(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) +vga_setbg(struct vt_device *vd, term_color_t color) { struct vga_softc *sc = vd->vd_softc; - /* Skip empty writes, in order to avoid palette changes. */ - if (v != 0x00) { - vga_setcolor(vd, color); + if (sc->vga_curbg != color) { + REG_WRITE1(sc, VGA_GC_ADDRESS, VGA_GC_SET_RESET); + REG_WRITE1(sc, VGA_GC_DATA, 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. + * Write 8 pixels using the background color to an + * off-screen byte in the video memory. */ - if (v != 0xff) - MEM_READ1(sc, dst); - 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); - } - } -} - -/* - * Shift bitmap of one row of the glyph. - * a - array of bytes with src bitmap and result storage. - * m - resulting background color bitmask. - * size - number of bytes per glyph row (+ one byte to store shift overflow). - * shift - offset for target bitmap. - */ - -static void -vga_shift_u8array(uint8_t *a, uint8_t *m, int size, int shift) -{ - int i; - - for (i = (size - 1); i > 0; i--) { - a[i] = (a[i] >> shift) | (a[i-1] << (7 - shift)); - m[i] = ~a[i]; - } - a[0] = (a[0] >> shift); - m[0] = ~a[0] & (0xff >> shift); - m[size - 1] = ~a[size - 1] & (0xff << (7 - shift)); -} - -/* XXX: fix gaps on mouse track when character size is not rounded to 8. */ -static void -vga_bitbltchr(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) -{ - uint8_t aa[64], ma[64], *r; - int dst, shift, sz, x, y; - struct vga_softc *sc; - - if ((left + width) > VT_VGA_WIDTH) - return; - if ((top + height) > VT_VGA_HEIGHT) - return; - - sc = vd->vd_softc; - - sz = (width + 7) / 8; - shift = left % 8; - - dst = (VT_VGA_WIDTH * top + left) / 8; + MEM_WRITE1(sc, VT_VGA_BGCOLOR_OFFSET, 0xff); - for (y = 0; y < height; y++) { - r = (uint8_t *)src + (y * sz); - memcpy(aa, r, sz); - aa[sz] = 0; - vga_shift_u8array(aa, ma, sz + 1, shift); - - vga_setcolor(vd, bg); - for (x = 0; x < (sz + 1); x ++) { - if (ma[x] == 0) - continue; - /* - * XXX Only mouse cursor can go out of screen. - * So for mouse it have to just return, but for regular - * characters it have to panic, to indicate error in - * size/coordinates calculations. - */ - if ((dst + x) >= (VT_VGA_WIDTH * VT_VGA_HEIGHT)) - return; - if (ma[x] != 0xff) - MEM_READ1(sc, dst + x); - MEM_WRITE1(sc, dst + x, ma[x]); - } + /* + * Read those 8 pixels back to load the background color + * in the latches register. + */ + MEM_READ1(sc, VT_VGA_BGCOLOR_OFFSET); - vga_setcolor(vd, fg); - for (x = 0; x < (sz + 1); x ++) { - if (aa[x] == 0) - continue; - if (aa[x] != 0xff) - MEM_READ1(sc, dst + x); - MEM_WRITE1(sc, dst + x, aa[x]); - } + sc->vga_curbg = color; - dst += VT_VGA_WIDTH / 8; + /* + * The Set/Reset register doesn't contain the fg color + * anymore, store an invalid color. + */ + sc->vga_curfg = 0xff; } } @@ -376,6 +285,571 @@ vga_get_cp437(term_char_t c) } 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); + } + } +} + +/* FIXME */ int +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); +/* static */ int +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); + } + + return (1); /* FIXME */ +} + +/* FIXME */ int +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); +/* static */ int +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; + + 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]); + } + + return (0); /* FIXME */ +} + +/* FIXME */ int +vga_bitblt_pixels_block_ncolors(struct vt_device *vd, const uint8_t *masks, + unsigned int x, unsigned int y, unsigned int height); +/* static */ int +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. + */ + + 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); + } + } + + return (0); // FIXME +} + +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 mouse_cursor *cursor, + unsigned int cursor_x, unsigned int cursor_y, + term_color_t cursor_fg, term_color_t cursor_bg +#endif + ) +{ + 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 + 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. + */ + mx = cursor_x + vd->vd_offset.tp_col; + my = cursor_y + vd->vd_offset.tp_row; + if (cursor != NULL && + ((mx >= x && x + VT_VGA_PIXELS_BLOCK - 1 >= mx) || + (mx < x && mx + cursor->w >= x)) && + ((my >= y && y + vf->vf_height - 1 >= my) || + (my < y && my + cursor->h >= 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->w - 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->h - 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->w, + src_x, dst_x, x_count, src_y, dst_y, y_count, + cursor_fg, cursor_bg, 1); + + if ((used_colors_list[cursor_fg] & 0x1) != 0x1) + used_colors++; + if ((used_colors_list[cursor_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 mouse_cursor *cursor, + term_color_t cursor_fg, term_color_t cursor_bg +#endif + ) +{ + unsigned int col, row; + unsigned int x1, y1, x2, y2, x, y, cursor_x, cursor_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; + + /* Save cursor position, in case it's modified while we draw. */ + cursor_x = vd->vd_mdirtyx; + cursor_y = vd->vd_mdirtyy; + + /* + * 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_x, cursor_y, cursor_fg, cursor_bg +#endif + ); + } + } +} + +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 mouse_cursor *cursor, + term_color_t cursor_fg, term_color_t cursor_bg +#endif + ) +{ + 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 mouse_cursor *cursor, + term_color_t cursor_fg, term_color_t cursor_bg +#endif + ) +{ + + if (!(vd->vd_flags & VDF_TEXTMODE)) { + vga_bitblt_text_gfxmode(vd, vb, vf, area +#ifndef SC_NO_CUTPASTE + , cursor, cursor_fg, cursor_bg +#endif + ); + } else { + vga_bitblt_text_txtmode(vd, vb, area +#ifndef SC_NO_CUTPASTE + , cursor, cursor_fg, cursor_bg +#endif + ); + } +} + +static void vga_putchar(struct vt_device *vd, term_char_t c, vt_axis_t top, vt_axis_t left, term_color_t fg, term_color_t bg) { @@ -604,7 +1078,6 @@ vga_initialize(struct vt_device *vd, int textmode) * planes. */ for (ofs = 0; ofs < VT_VGA_MEMSIZE; ofs++) { - MEM_READ1(sc, ofs); MEM_WRITE1(sc, ofs, 0); } } @@ -623,6 +1096,12 @@ vga_initialize(struct vt_device *vd, int textmode) 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; } } diff --git a/sys/dev/vt/vt.h b/sys/dev/vt/vt.h index 7dc90e6..0dc6f29 100644 --- a/sys/dev/vt/vt.h +++ b/sys/dev/vt/vt.h @@ -133,6 +133,7 @@ struct vt_device { 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. */ @@ -196,7 +197,7 @@ 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(struct vt_buf *vb, int row, int col); +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); @@ -275,6 +276,15 @@ struct vt_window { #define IS_VT_PROC_MODE(vw) ((vw)->vw_smode.mode == VT_PROCESS) +#ifndef SC_NO_CUTPASTE +struct mouse_cursor { + uint8_t map[64 * 64 / 8]; + uint8_t mask[64 * 64 / 8]; + uint8_t w; + uint8_t h; +}; +#endif + /* * Per-device driver routines. * @@ -290,6 +300,13 @@ typedef void vd_blank_t(struct vt_device *vd, term_color_t color); 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_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 mouse_cursor *cursor, + term_color_t cursor_fg, term_color_t cursor_bg +#endif + ); 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 int vd_fb_ioctl_t(struct vt_device *, u_long, caddr_t, struct thread *); @@ -308,6 +325,7 @@ struct vt_driver { /* Drawing. */ vd_blank_t *vd_blank; vd_bitbltchr_t *vd_bitbltchr; + vd_bitblt_text_t *vd_bitblt_text; vd_drawrect_t *vd_drawrect; vd_setpixel_t *vd_setpixel; @@ -376,15 +394,6 @@ struct vt_font { unsigned int vf_refcount; }; -#ifndef SC_NO_CUTPASTE -struct mouse_cursor { - uint8_t map[64 * 64 / 8]; - uint8_t mask[64 * 64 / 8]; - uint8_t w; - uint8_t h; -}; -#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); @@ -399,5 +408,9 @@ void vt_mouse_state(int show); #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_ */ diff --git a/sys/dev/vt/vt_buf.c b/sys/dev/vt/vt_buf.c index 4003ce8..e6efcd2 100644 --- a/sys/dev/vt/vt_buf.c +++ b/sys/dev/vt/vt_buf.c @@ -148,7 +148,7 @@ vtbuf_wth(struct vt_buf *vb, int row) /* Translate history row to current view row number. */ static int -vtbuf_htw(struct vt_buf *vb, int row) +vtbuf_htw(const struct vt_buf *vb, int row) { /* @@ -162,7 +162,7 @@ vtbuf_htw(struct vt_buf *vb, int row) } int -vtbuf_iscursor(struct vt_buf *vb, int row, int col) +vtbuf_iscursor(const struct vt_buf *vb, int row, int col) { int sc, sr, ec, er, tmp; diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c index fbffa9c..f477079 100644 --- a/sys/dev/vt/vt_core.c +++ b/sys/dev/vt/vt_core.c @@ -228,6 +228,47 @@ vt_update_static(void *dummy) } 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; +#if 0 + if (vd->vd_timer_armed == 1) + return; + vd->vd_timer_armed = 1; +#endif + + 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; +#if 0 + if (vd->vd_timer_armed == 0) + return; + vd->vd_timer_armed = 0; +#endif + + callout_drain(&vd->vd_timer); +} + +static void vt_switch_timer(void *arg) { @@ -330,6 +371,8 @@ vt_window_switch(struct vt_window *vw) return (EINVAL); } + vt_suspend_flush_timer(vd); + vd->vd_curwindow = vw; vd->vd_flags |= VDF_INVALID; cv_broadcast(&vd->vd_winswitch); @@ -338,6 +381,8 @@ vt_window_switch(struct vt_window *vw) 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); @@ -751,7 +796,7 @@ vtterm_param(struct terminal *tm, int cmd, unsigned int arg) } } -static inline void +void vt_determine_colors(term_char_t c, int cursor, term_color_t *fg, term_color_t *bg) { @@ -832,6 +877,38 @@ vt_flush(struct vt_device *vd) if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY) return; +#ifndef SC_NO_CUTPASTE + m = NULL; + if ((vd->vd_flags & VDF_MOUSECURSOR) && /* Mouse support enabled. */ + !(vw->vw_flags & VWF_MOUSE_HIDE)) { /* Cursor displayed. */ + if (vd->vd_mdirtyx != vd->vd_mx || + vd->vd_mdirtyy != vd->vd_my) { + /* + * Mark last and current mouse position as dirty + * to erase/redraw it. + */ + vtbuf_mouse_cursor_position(&vw->vw_buf, + vd->vd_mdirtyx / vf->vf_width, + vd->vd_mdirtyy / vf->vf_height); + vtbuf_mouse_cursor_position(&vw->vw_buf, + vd->vd_mx / vf->vf_width, + vd->vd_my / vf->vf_height); + + /* + * Save point of last mouse cursor to erase it + * later. + */ + vd->vd_mdirtyx = vd->vd_mx; + vd->vd_mdirtyy = vd->vd_my; + } + + if (!kdb_active && panicstr == NULL) { + /* Mouse enabled, and DDB isn't active. */ + m = &vt_default_mouse_pointer; + } + } +#endif + vtbuf_undirty(&vw->vw_buf, &tarea, &tmask); vt_termsize(vd, vf, &size); @@ -844,71 +921,75 @@ vt_flush(struct vt_device *vd) 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 - if ((vw->vw_flags & VWF_MOUSE_HIDE) == 0) { - /* Mark last mouse position as dirty to erase. */ - vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mdirtyx, - vd->vd_mdirtyy); - } + , m, TC_WHITE, TC_BLACK #endif - - 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)) + ); + } + } else { + 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); + vt_bitblt_char(vd, vf, r[col], + VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col); + } } - } #ifndef SC_NO_CUTPASTE - /* Mouse disabled. */ - if (vw->vw_flags & VWF_MOUSE_HIDE) - return; - - /* No mouse for DDB. */ - if (kdb_active || panicstr != NULL) - return; - - if ((vd->vd_flags & (VDF_MOUSECURSOR|VDF_TEXTMODE)) == - VDF_MOUSECURSOR) { - m = &vt_default_mouse_pointer; - bpl = (m->w + 7) >> 3; /* Bytes per source line. */ - w = m->w; - h = m->h; - - if ((vd->vd_mx + m->w) > (size.tp_col * vf->vf_width)) - w = (size.tp_col * vf->vf_width) - vd->vd_mx - 1; - if ((vd->vd_my + m->h) > (size.tp_row * vf->vf_height)) - h = (size.tp_row * vf->vf_height) - vd->vd_my - 1; - - vd->vd_driver->vd_bitbltchr(vd, m->map, m->mask, bpl, - vd->vd_offset.tp_row + vd->vd_my, - vd->vd_offset.tp_col + vd->vd_mx, - w, h, TC_WHITE, TC_BLACK); - /* Save point of last mouse cursor to erase it later. */ - vd->vd_mdirtyx = vd->vd_mx / vf->vf_width; - vd->vd_mdirtyy = vd->vd_my / vf->vf_height; - } + if (m != NULL) { + bpl = (m->w + 7) >> 3; /* Bytes per source line. */ + w = m->w; + h = m->h; + + if ((vd->vd_mx + m->w) > (size.tp_col * vf->vf_width)) + w = (size.tp_col * vf->vf_width) - vd->vd_mx - 1; + if ((vd->vd_my + m->h) > (size.tp_row * vf->vf_height)) + h = (size.tp_row * vf->vf_height) - vd->vd_my - 1; + + vd->vd_driver->vd_bitbltchr(vd, m->map, m->mask, bpl, + vd->vd_offset.tp_row + vd->vd_my, + vd->vd_offset.tp_col + vd->vd_mx, + w, h, TC_WHITE, TC_BLACK); + } #endif + } } static void vt_timer(void *arg) { struct vt_device *vd; + struct bintime start, end; + struct timeval diff; + int next; vd = arg; /* Update screen if required. */ + getbinuptime(&start); vt_flush(vd); + getbinuptime(&end); /* Schedule for next update. */ - callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); + bintime_sub(&end, &start); + bintime2timeval(&end, &diff); + if (diff.tv_sec == 0 && diff.tv_usec != 0) { + next = max( + (1000 / VT_TIMERFREQ) - (diff.tv_usec / 1000), + 5 /* At least 5 ms */ + ); + } else { + next = 1000 / VT_TIMERFREQ; + } + vt_schedule_flush(vd, next); } static void @@ -1531,6 +1612,9 @@ vt_mouse_state(int show) vw->vw_flags &= ~VWF_MOUSE_HIDE; break; } + + /* Mark mouse position as dirty. */ + vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mx, vd->vd_my); } #endif @@ -1703,7 +1787,7 @@ skip_thunk: /* XXX: other fields! */ return (0); } - case CONS_GETVERS: + case CONS_GETVERS: *(int *)data = 0x200; return (0); case CONS_MODEINFO: @@ -1713,20 +1797,29 @@ skip_thunk: mouse_info_t *mouse = (mouse_info_t*)data; /* - * This has no effect on vt(4). We don't draw any mouse - * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to - * prevent excessive errors. All the other commands + * 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: - vd->vd_flags &= ~VDF_MOUSECURSOR; - return (0); case MOUSE_SHOW: - vd->vd_mx = vd->vd_width / 2; - vd->vd_my = vd->vd_height / 2; - vd->vd_flags |= VDF_MOUSECURSOR; + 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); + 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); default: return (EINVAL); @@ -1749,7 +1842,6 @@ skip_thunk: } case GIO_SCRNMAP: { scrmap_t *sm = (scrmap_t *)data; - int i; /* We don't have screen maps, so return a handcrafted one. */ for (i = 0; i < 256; i++) @@ -2047,6 +2139,7 @@ vt_upgrade(struct vt_device *vd) /* 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); @@ -2106,7 +2199,7 @@ vt_allocate(struct vt_driver *drv, void *softc) if (vd->vd_flags & VDF_ASYNC) { /* Stop vt_flush periodic task. */ - callout_drain(&vd->vd_timer); + vt_suspend_flush_timer(vd); /* * Mute current terminal until we done. vt_change_font (called * from vt_resize) will unmute it. @@ -2137,7 +2230,7 @@ vt_allocate(struct vt_driver *drv, void *softc) /* Allow to put chars now. */ terminal_mute(vd->vd_curwindow->vw_terminal, 0); /* Rerun timer for screen updates. */ - callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); + vt_resume_flush_timer(vd, 0); } /* diff --git a/sys/dev/vt/vt_sysmouse.c b/sys/dev/vt/vt_sysmouse.c index 21b2400..189bcad 100644 --- a/sys/dev/vt/vt_sysmouse.c +++ b/sys/dev/vt/vt_sysmouse.c @@ -347,9 +347,6 @@ sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, return (EINVAL); sysmouse_level = level; -#ifndef SC_NO_CUTPASTE - vt_mouse_state((level == 0)?VT_MOUSE_SHOW:VT_MOUSE_HIDE); -#endif return (0); } case MOUSE_SETMODE: { @@ -362,10 +359,6 @@ sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, case 0: case 1: sysmouse_level = mode->level; -#ifndef SC_NO_CUTPASTE - vt_mouse_state((mode->level == 0)?VT_MOUSE_SHOW: - VT_MOUSE_HIDE); -#endif break; default: return (EINVAL);