Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3447,6 +3447,7 @@ dev/vt/vt_core.c optional vt dev/vt/vt_cpulogos.c optional vt splash dev/vt/vt_font.c optional vt +dev/vt/vt_screenshot.c optional vt dev/vt/vt_sysmouse.c optional vt dev/vte/if_vte.c optional vte pci dev/watchdog/watchdog.c standard Index: sys/dev/vt/vt.h =================================================================== --- sys/dev/vt/vt.h +++ sys/dev/vt/vt.h @@ -425,6 +425,10 @@ #define VT_MOUSE_SHOW 1 #define VT_MOUSE_HIDE 0 +/* Screenshot. */ +int vt_screenshot_grph(struct vt_device *vd, vt_scrshot_grph_t *); +int vt_screenshot_text(struct vt_window *vw, vt_scrshot_text_t *); + /* Utilities. */ void vt_compute_drawable_area(struct vt_window *); void vt_determine_colors(term_char_t c, int cursor, Index: sys/dev/vt/vt_core.c =================================================================== --- sys/dev/vt/vt_core.c +++ sys/dev/vt/vt_core.c @@ -2558,6 +2558,7 @@ case FBIO_GETRGBOFFS: /* get RGB offsets */ if (vd->vd_driver->vd_fb_ioctl) return (vd->vd_driver->vd_fb_ioctl(vd, cmd, data, td)); + printf("VT(%s) not supported.\r\n", vd->vd_driver->vd_name); break; case CONS_BLANKTIME: /* XXX */ @@ -2598,6 +2599,12 @@ if (vi->size != sizeof(struct vid_info)) return (EINVAL); + vi->mv_rsz = tm->tm_winsize.ws_row; + vi->mv_csz = tm->tm_winsize.ws_col; + vtbuf_lock(&vw->vw_buf); + vi->mv_hsz = vw->vw_buf.vb_history_size; + vtbuf_unlock(&vw->vw_buf); + if (vw == vd->vd_curwindow) { mtx_lock(&Giant); if ((kbd = vd->vd_keyboard) != NULL) @@ -2647,6 +2654,16 @@ return (EINVAL); } } + case CONS_VTSCRSHOT_GRPH: { + vt_scrshot_grph_t *ptr = (vt_scrshot_grph_t *)data; + error = vt_screenshot_grph(vd, ptr); + return (error); + } + case CONS_VTSCRSHOT_TEXT: { + vt_scrshot_text_t *ptr = (vt_scrshot_text_t *)data; + error = vt_screenshot_text(vw, ptr); + return (error); + } case PIO_VFONT: { struct vt_font *vf; Index: sys/dev/vt/vt_screenshot.c =================================================================== --- /dev/null +++ sys/dev/vt/vt_screenshot.c @@ -0,0 +1,88 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 + * + * 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 +#include +#include +#include +#include +#include + +#include + +int +vt_screenshot_grph(struct vt_device* vd, vt_scrshot_grph_t *ptr) +{ + struct fb_info *info; + void *from_p; + + info = vd->vd_softc; + if (ptr->size != info->fb_size) + return (EINVAL); + + from_p = (void *)info->fb_vbase; + return (copyout(from_p, ptr->buf, info->fb_size)); +} + +int +vt_screenshot_text(struct vt_window* vw, vt_scrshot_text_t *ptr) +{ + int retval = 0; + struct vt_buf *vb; + int row, hrow; + const size_t csize = sizeof(u_int); + u_int *outp = ptr->buf;; + u_int **rows; + void *colp, *bufp; + + if (ptr->x < 0 || ptr->y < 0 || ptr->xsize < 0 || ptr->ysize < 0) + return (EINVAL); + + /* Check if virtual terminal is locked */ + if (vw->vw_flags & VWF_VTYLOCK) + return (EBUSY); + + vb = &vw->vw_buf; + rows = (u_int **)vb->vb_rows; + if ((ptr->xsize + ptr->x) > vb->vb_scr_size.tp_col || + (ptr->ysize + ptr->y) > vb->vb_scr_size.tp_row) { + return (EINVAL); + } + /* Copy current window to output buffer */ + for (row = ptr->y; row < (ptr->y + ptr->ysize); row++) { + /* Translate current view row number to history row */ + hrow = (vb->vb_roffset + row) % vb->vb_history_size; + // colp = (void *)&vb->vb_rows[hrow][ptr->x]; + colp = (void *)&rows[hrow][ptr->x]; + bufp = (void *)outp; + retval = copyout(colp, bufp, ptr->xsize*csize); + if (retval != 0) + break; + outp += ptr->xsize; + } + return (retval); +} Index: sys/sys/consio.h =================================================================== --- sys/sys/consio.h +++ sys/sys/consio.h @@ -293,10 +293,25 @@ int ysize; u_int16_t* buf; }; +struct vt_scrshot_text { + int x; + int y; + int xsize; + int ysize; + u_int *buf; +}; +struct vt_scrshot_grph { + size_t size; + void *buf; +}; typedef struct scrshot scrshot_t; +typedef struct vt_scrshot_text vt_scrshot_text_t; +typedef struct vt_scrshot_grph vt_scrshot_grph_t; /* Snapshot the current video buffer */ #define CONS_SCRSHOT _IOWR('c', 105, scrshot_t) +#define CONS_VTSCRSHOT_TEXT _IOWR('c', 114, vt_scrshot_text_t) +#define CONS_VTSCRSHOT_GRPH _IOWR('c', 115, vt_scrshot_grph_t) /* get/set the current terminal emulator info. */ #define TI_NAME_LEN 32 Index: usr.sbin/vidcontrol/vidcontrol.c =================================================================== --- usr.sbin/vidcontrol/vidcontrol.c +++ usr.sbin/vidcontrol/vidcontrol.c @@ -40,10 +40,13 @@ #include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -53,20 +56,29 @@ #include #include #include +#include #include "path.h" #include "decode.h" #define DATASIZE(x) ((x).w * (x).h * 256 / 8) +#define TCHAR_NONCHAR(c) ((c) & ~0x1fffff) +/* from sys/terminal.h */ +#define TCHAR_CHARACTER(c) ((c) & 0x1fffff) +#define TCHAR_FORMAT(c) (((c) >> 21) & 0x1f) +#define TCHAR_FGCOLOR(c) (((c) >> 26) & 0x7) +#define TCHAR_BGCOLOR(c) (((c) >> 29) & 0x7) /* Screen dump modes */ #define DUMP_FMT_RAW 1 #define DUMP_FMT_TXT 2 +#define DUMP_FMT_CTXT 3 /* Screen dump options */ #define DUMP_FBF 0 #define DUMP_ALL 1 /* Screen dump file format revision */ #define DUMP_FMT_REV 1 +#define DUMP_FMT_REV_VT 2 static const char *legal_colors[16] = { "black", "blue", "green", "cyan", @@ -188,7 +200,7 @@ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", "usage: vidcontrol [-Cx] [-b color] [-c appearance] [-f [[size] file]]", " [-g geometry] [-h size] [-i active | adapter | mode]", -" [-M char] [-m on | off]", +" [-M char] [-m on | off] [-p text | term | raw]", " [-r foreground background] [-S on | off] [-s number]", " [-T xterm | cons25] [-t N | off] [mode]", " [foreground [background]] [show]"); @@ -1338,6 +1350,191 @@ } +/* + * Snapshot the text buffer or video memory of the termnal. + */ +static void +vt_dump_screen(int mode, int opt) +{ + struct fbtype fb; + vt_scrshot_grph_t gshot; + u_int32_t xs, ys, depth; + vt_scrshot_text_t tshot; + vid_info_t info; + int x, y, ret; + iconv_t cd; + teken_char_t oc, uc, fmt; + size_t isize, osize; + char obuf[8], escbuf[BUFSIZ], escsq[BUFSIZ]; + char *ibuf_p, *obuf_p; + teken_char_t nc_curstate, nc_prevstate; + teken_char_t fg, bg; + bool need_fini = false; + int colormap[TC_NCOLORS] = { + [TC_BLACK] = 30, + [TC_RED] = 31, + [TC_GREEN] = 32, + [TC_YELLOW] = 33, + [TC_BLUE] = 34, + [TC_MAGENTA] = 35, + [TC_CYAN] = 36, + [TC_WHITE] = 37 + }; + + if (mode == DUMP_FMT_RAW) { + if (ioctl(0, FBIOGTYPE, &fb) == -1) { + revert(); + err(1, "Dumping in BMP not supported"); + } + if (fb.fb_depth != 32) { + revert(); + errx(0, "Pixel depth %d not supported", fb.fb_depth); + } + gshot.buf = (void *)malloc(fb.fb_size); + gshot.size = fb.fb_size; + if (gshot.buf == NULL) { + revert(); + errx(1, "failed to allocate memory for dump"); + } + if (ioctl(0, CONS_VTSCRSHOT_GRPH, &gshot) == -1) { + revert(); + err(1, "getting frame buffer data"); + } + /* output RAW */ + xs = (u_int32_t)fb.fb_width; + ys = (u_int32_t)fb.fb_height; + depth = (u_int32_t)fb.fb_depth; + printf("SCRSHOT_%c%c", DUMP_FMT_REV_VT, 3); + fwrite(&xs, sizeof(u_int32_t), 1, stdout); + fwrite(&ys, sizeof(u_int32_t), 1, stdout); + fwrite(&depth, sizeof(u_int32_t), 1, stdout); + fwrite(gshot.buf, sizeof(u_int32_t), xs*ys, stdout); + fflush(stdout); + } + else { + info.size = sizeof(info); + if (ioctl(0, CONS_GETINFO, &info) == -1) { + revert(); + err(1, "getting console information"); + } + tshot.x = tshot.y = 0; + tshot.xsize = info.mv_csz; + tshot.ysize = info.mv_rsz; + if (opt == DUMP_ALL) + tshot.ysize = info.mv_hsz; + tshot.buf = (u_int *)alloca(tshot.xsize * tshot.ysize + * sizeof(u_int)); + if (tshot.buf == NULL) { + revert(); + errx(1, "failed to allocate memory for dump"); + } + if (ioctl(0, CONS_VTSCRSHOT_TEXT, &tshot) == -1) { + revert(); + err(1, "dumping screen"); + } + + cd = iconv_open("UTF-8", "UCS-4LE"); + if (cd == (iconv_t)-1) { + revert(); + err(1, "iconv_open()"); + } + nc_prevstate = nc_curstate = 0; + for (y = 0; y < tshot.ysize; y++) { + for (x = 0; x < tshot.xsize; x++) { + oc = (teken_char_t)(0xffffffff & tshot.buf[tshot.xsize*y + x]); + nc_prevstate = nc_curstate; + nc_curstate = TCHAR_NONCHAR(oc); + fmt = TCHAR_FORMAT(oc); + + if ((fmt & TF_CJK_RIGHT) == TF_CJK_RIGHT) + continue; + + if (mode == DUMP_FMT_CTXT && nc_prevstate != nc_curstate) { + need_fini = true; + bzero(escsq, BUFSIZ); + fg = TCHAR_FGCOLOR(oc); + bg = TCHAR_BGCOLOR(oc); + + strncat(escsq, "\e[", BUFSIZ - strlen(escsq) -1); + /* reset */ + strncat(escsq, "0;", BUFSIZ - strlen(escsq) -1); + if ((fmt & TF_BOLD) == TF_BOLD) + strncat(escsq, "1;", BUFSIZ - strlen(escsq) -1); + if ((fmt & TF_UNDERLINE) == TF_UNDERLINE) + strncat(escsq, "4;", BUFSIZ - strlen(escsq) -1); + if ((fmt & TF_BLINK) == TF_BLINK) + strncat(escsq, "5;", BUFSIZ - strlen(escsq) -1); + if ((fmt & TF_REVERSE) == TF_REVERSE) + strncat(escsq, "7;", BUFSIZ - strlen(escsq) -1); + + bzero(escbuf, BUFSIZ); + if (fg != TC_NCOLORS && (fg & TC_LIGHT) == TC_LIGHT) + strncat(escsq, "2;", BUFSIZ - strlen(escsq) -1); + switch(fg & ~TC_LIGHT) { + case TC_BLACK: + case TC_RED: + case TC_GREEN: + case TC_YELLOW: + case TC_BLUE: + case TC_MAGENTA: + case TC_CYAN: + case TC_WHITE: + snprintf(escbuf, sizeof(escbuf), "%2d;", + colormap[fg & ~TC_LIGHT]); + strncat(escsq, escbuf, BUFSIZ - strlen(escsq) -1); + } + + bzero(escbuf, BUFSIZ); + if (bg != TC_NCOLORS && (bg & TC_LIGHT) == TC_LIGHT) + strncat(escsq, "2;", BUFSIZ - strlen(escsq) -1); + switch(bg & ~TC_LIGHT) { + case TC_BLACK: + case TC_RED: + case TC_GREEN: + case TC_YELLOW: + case TC_BLUE: + case TC_MAGENTA: + case TC_CYAN: + case TC_WHITE: + snprintf(escbuf, sizeof(escbuf), "%2d;", + colormap[bg & ~TC_LIGHT]); + strncat(escsq, escbuf, BUFSIZ - strlen(escsq) -1); + } + + /* terminate */ + escsq[strlen(escsq) - 1] = 'm'; + + printf("%s", escsq); + } + + uc = TCHAR_CHARACTER(oc); + ibuf_p = (char *)&uc; + isize = sizeof(uc); + bzero(obuf, 8); + obuf_p = obuf; + osize = sizeof(obuf); + iconv(cd, &ibuf_p, &isize, &obuf_p, &osize); + if (isize != 0) { + iconv_close(cd); + revert(); + err(1, "iconv()"); + } + printf("%s", obuf); + } + printf("%s", "\r\n"); + } + if (need_fini) { + printf("%s", "\e[0m"); + } + ret = iconv_close(cd); + if (ret == -1) { + revert(); + err(1, "iconv_close()"); + } + } +} + + /* * Set the console history buffer size. */ @@ -1427,7 +1624,7 @@ int main(int argc, char **argv) { - char *font, *type, *termmode; + char *dumparg, *font, *type, *termmode; const char *opts; int dumpmod, dumpopt, opt; @@ -1439,7 +1636,7 @@ dumpopt = DUMP_FBF; termmode = NULL; if (vt4_mode) - opts = "b:Cc:fg:h:i:M:m:r:S:s:T:t:x"; + opts = "b:Cc:fg:h:i:M:m:p:r:S:s:T:t:x"; else opts = "b:Cc:deE:fg:h:Hi:l:LM:m:pPr:S:s:T:t:x"; @@ -1495,7 +1692,7 @@ warnx("incorrect geometry: %s", optarg); usage(); } - break; + break; case 'h': set_history(optarg); break; @@ -1522,9 +1719,26 @@ set_mouse(optarg); break; case 'p': - dumpmod = DUMP_FMT_RAW; + if (vt4_mode) { + dumparg = optarg; + if (strcmp(dumparg, "text") == 0) + dumpmod = DUMP_FMT_TXT; + else if (strcmp(dumparg, "term") == 0) + dumpmod = DUMP_FMT_CTXT; + else if (strcmp(dumparg, "raw") == 0) + dumpmod = DUMP_FMT_RAW; + else { + revert(); + err(1, "invalid argument for -p option."); + } + } + else { + dumpmod = DUMP_FMT_RAW; + } break; case 'P': + if (vt4_mode) + break; dumpmod = DUMP_FMT_TXT; break; case 'r': @@ -1552,8 +1766,10 @@ usage(); } - if (dumpmod != 0) + if (!vt4_mode && dumpmod != 0) dump_screen(dumpmod, dumpopt); + if (vt4_mode && dumpmod != 0) + vt_dump_screen(dumpmod, dumpopt); video_mode(argc, argv, &optind); set_normal_colors(argc, argv, &optind);