Page MenuHomeFreeBSD

D35754.diff
No OneTemporary

D35754.diff

diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3473,6 +3473,7 @@
dev/vt/hw/vga/vt_vga.c optional vt vt_vga
dev/vt/logo/logo_freebsd.c optional vt splash
dev/vt/logo/logo_beastie.c optional vt splash
+dev/vt/vt_screen_reader.c optional vt
dev/vt/vt_buf.c optional vt
dev/vt/vt_consolectl.c optional vt
dev/vt/vt_core.c optional vt
diff --git a/sys/dev/vt/vt.h b/sys/dev/vt/vt.h
--- a/sys/dev/vt/vt.h
+++ b/sys/dev/vt/vt.h
@@ -85,6 +85,8 @@
#endif
#define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG)
+SYSCTL_DECL(_kern_vt);
+
#define VT_SYSCTL_INT(_name, _default, _descr) \
int vt_##_name = (_default); \
SYSCTL_INT(_kern_vt, OID_AUTO, _name, CTLFLAG_RWTUN, &vt_##_name, 0, _descr)
@@ -169,6 +171,8 @@
term_color_t *vd_drawnbg; /* (?) Most recent bg color drawn. */
};
+extern struct vt_device *main_vd;
+
#define VD_PASTEBUF(vd) ((vd)->vd_pastebuf.vpb_buf)
#define VD_PASTEBUFSZ(vd) ((vd)->vd_pastebuf.vpb_bufsz)
#define VD_PASTEBUFLEN(vd) ((vd)->vd_pastebuf.vpb_len)
@@ -241,6 +245,7 @@
int vtbuf_get_marked_len(struct vt_buf *vb);
void vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz, int mark);
#endif
+int vtbuf_extract_screen_reader(char *buf, size_t *, term_pos_t *, size_t lines, size_t bufsz);
#define VTB_MARK_NONE 0
#define VTB_MARK_END 1
@@ -450,4 +455,7 @@
void vtterm_draw_cpu_logos(struct vt_device *);
+/* Prototypes for screen reader layer. */
+void vt_screen_reader_hup(void);
+
#endif /* !_DEV_VT_VT_H_ */
diff --git a/sys/dev/vt/vt_buf.c b/sys/dev/vt/vt_buf.c
--- a/sys/dev/vt/vt_buf.c
+++ b/sys/dev/vt/vt_buf.c
@@ -746,6 +746,7 @@
return (sz * sizeof(term_char_t));
}
+#endif
static bool
tchar_is_word_separator(term_char_t ch)
@@ -770,6 +771,204 @@
}
}
+static bool
+vtbuf_extract_utf8(term_char_t ch, char *buffer, size_t *ppos, size_t size)
+{
+ char temp[4];
+ size_t len;
+
+ ch = TCHAR_CHARACTER(ch);
+
+ /* Convert to UTF-8. */
+
+ if (ch < 0x80) {
+ temp[0] = ch;
+ len = 1;
+ } else if (ch < 0x800) {
+ temp[0] = 0xc0 | (ch >> 6);
+ temp[1] = 0x80 | (ch & 0x3f);
+ len = 2;
+ } else if (ch < 0x10000) {
+ temp[0] = 0xe0 | (ch >> 12);
+ temp[1] = 0x80 | ((ch >> 6) & 0x3f);
+ temp[2] = 0x80 | (ch & 0x3f);
+ len = 3;
+ } else {
+ temp[0] = 0xf0 | (ch >> 18);
+ temp[1] = 0x80 | ((ch >> 12) & 0x3f);
+ temp[2] = 0x80 | ((ch >> 6) & 0x3f);
+ temp[3] = 0x80 | (ch & 0x3f);
+ len = 4;
+ }
+
+ /*
+ * Make sure everything fits into the buffer.
+ */
+ if (*ppos + len > size)
+ return (false);
+
+ memcpy(buffer + *ppos, temp, len);
+ *ppos += len;
+ return (true);
+}
+
+static const char vt_access_overflow[] = { "Line is not accessible" };
+
+int
+vtbuf_extract_screen_reader(char *buffer, size_t *ppos, term_pos_t *cursor,
+ size_t lines, size_t bufsz)
+{
+ struct vt_device *vd;
+ struct vt_window *vw;
+ struct vt_buf *vb;
+ term_pos_t max;
+ term_char_t ch;
+ size_t lines_old;
+ size_t last_sep_off;
+ size_t last_line_off;
+ int last_sep_col;
+ int c;
+ int i;
+ int r;
+
+ MPASS(bufsz != 0);
+ MPASS(lines != 0);
+
+ vd = main_vd;
+ if (vd == NULL)
+ return (EINVAL);
+ vw = vd->vd_curwindow;
+ if (vw == NULL)
+ return (EINVAL);
+ vb = &vw->vw_buf;
+ max = vb->vb_scr_size;
+ lines_old = lines;
+
+ /* set default cursor */
+ cursor->tp_row = 0;
+ cursor->tp_col = 0;
+
+ VTBUF_LOCK(vb);
+ for (i = 0; lines != 0 && i != max.tp_row; i++) {
+ /* Get real row number in VT buffer. */
+ r = (vb->vb_roffset + i) % vb->vb_history_size;
+
+ /* Find first character, get whole word, if any. */
+ for (c = 0; c != max.tp_col; c++) {
+ ch = vb->vb_rows[r][c];
+
+ if (TCHAR_SCREEN_READER(ch) != 0 ||
+ TCHAR_CHARACTER(ch) == 0)
+ continue;
+ if (tchar_is_word_separator(ch))
+ break;
+ /* Go back a bit to get context, if any. */
+ while (c != 0) {
+ c--;
+ ch = vb->vb_rows[r][c];
+ if (TCHAR_CHARACTER(ch) == 0)
+ continue;
+ if (tchar_is_word_separator(ch))
+ break;
+ }
+ break;
+ }
+
+ /* Skip initial word separators, if any. */
+ for (; c != max.tp_col; c++) {
+ ch = vb->vb_rows[r][c];
+
+ if (TCHAR_CHARACTER(ch) == 0 ||
+ tchar_is_word_separator(ch))
+ continue;
+ break;
+ }
+
+ if (c == max.tp_col)
+ continue;
+
+ /* Check for first line and store screen position. */
+ if (lines == lines_old) {
+ cursor->tp_row = i;
+ cursor->tp_col = c;
+ }
+
+ lines--;
+ last_sep_col = c;
+ last_sep_off = *ppos;
+ last_line_off = *ppos;
+
+ /* Try to output line. */
+ for( ; c != max.tp_col; c++) {
+ ch = vb->vb_rows[r][c];
+
+ if (TCHAR_CHARACTER(ch) == 0)
+ continue;
+
+ if (tchar_is_word_separator(ch)) {
+ if (vtbuf_extract_utf8(ch, buffer, ppos, bufsz) == false)
+ break;
+ /* Keep track of sensible stop locations. */
+ last_sep_col = c + 1;
+ last_sep_off = *ppos;
+ } else {
+ if (vtbuf_extract_utf8(ch, buffer, ppos, bufsz) == false)
+ break;
+ }
+ }
+
+ /* Check if buffer is full (whole line was not consumed). */
+ if (c != max.tp_col) {
+ /* Check if this is the first line. */
+ if (last_line_off == 0) {
+ /* XXX Pretend line was consumed. */
+ *ppos = last_line_off;
+ c = max.tp_col;
+
+ for (size_t i = 0; vt_access_overflow[i]; i++) {
+ if (vtbuf_extract_utf8(vt_access_overflow[i],
+ buffer, ppos, bufsz) == false)
+ break;
+ }
+ } else {
+ /* Stop at last separator, if any. */
+ *ppos = last_sep_off;
+ c = last_sep_col;
+ }
+
+ /* Don't output this part again. */
+ while (c--)
+ vb->vb_rows[r][c] |= TSCREEN_READER;
+ break;
+ } else {
+ /* Don't output this line again. */
+ while (c--)
+ vb->vb_rows[r][c] |= TSCREEN_READER;
+
+ /* Remove trailing white space. */
+ while (*ppos != last_line_off && buffer[*ppos - 1] == ' ')
+ (*ppos)--;
+
+ if (*ppos == last_line_off) {
+ /* Grab another line, this one is empty. */
+ lines++;
+ } else {
+ /* Insert a word separator, just in case. */
+ if (vtbuf_extract_utf8(' ', buffer, ppos, bufsz) == false)
+ break;
+ }
+ }
+ }
+ VTBUF_UNLOCK(vb);
+
+ /* Remove trailing white space from last line. */
+ while (*ppos != 0 && buffer[*ppos - 1] == ' ')
+ (*ppos)--;
+
+ return (lines != lines_old ? 0 : ENOENT);
+}
+
+#ifndef SC_NO_CUTPASTE
void
vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz, int mark)
{
diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c
--- a/sys/dev/vt/vt_core.c
+++ b/sys/dev/vt/vt_core.c
@@ -126,7 +126,7 @@
#define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
(vw)->vw_number)
-static SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"vt(9) parameters");
static VT_SYSCTL_INT(enable_altgr, 1, "Enable AltGr key (Do not assume R.Alt as Alt)");
static VT_SYSCTL_INT(enable_bell, 0, "Enable bell");
@@ -1109,6 +1109,9 @@
struct vt_window *vw = tm->tm_softc;
struct vt_device *vd = vw->vw_device;
+ /* piggyback screen reader hangup on the bell event */
+ vt_screen_reader_hup();
+
vtterm_devctl(vt_enable_bell, vd->vd_flags & VDF_QUIET_BELL,
vw->vw_bell_pitch, vw->vw_bell_duration);
diff --git a/sys/dev/vt/vt_screen_reader.c b/sys/dev/vt/vt_screen_reader.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/vt/vt_screen_reader.c
@@ -0,0 +1,170 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Hans Petter Selasky
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+#include <sys/sx.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/reboot.h>
+
+#include <dev/vt/vt.h>
+
+#define VT_ACCESS_UTF8_DATA_MAX 1000 /* bytes */
+
+static MALLOC_DEFINE(M_VTACCESS, "vt_screen_reader", "VT screen reader");
+
+static SYSCTL_NODE(_kern_vt, OID_AUTO, screen_reader,
+ CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Screen reader parameters");
+
+static int vt_screen_reader_text_utf8(SYSCTL_HANDLER_ARGS);
+static int vt_screen_reader_feed(SYSCTL_HANDLER_ARGS);
+
+static term_pos_t vt_screen_reader_cursor;
+
+static char *vt_screen_reader_utf8_data;
+static size_t vt_screen_reader_utf8_length;
+
+static struct sx vt_screen_reader_sx;
+
+static void vt_screen_reader_hup_callback(void *, int);
+
+static struct task vt_screen_reader_hup_task =
+ TASK_INITIALIZER(0, &vt_screen_reader_hup_callback, NULL);
+
+SX_SYSINIT(vt_screen_reader_sx, &vt_screen_reader_sx, "VT screen reader");
+
+#define VT_ACCESS_LOCK() sx_xlock(&vt_screen_reader_sx)
+#define VT_ACCESS_UNLOCK() sx_xunlock(&vt_screen_reader_sx)
+
+static unsigned vt_screen_reader_lines = 1; /* default value */
+static unsigned vt_screen_reader_hup_pid; /* send HUP signal to this PID, if non-zero */
+
+SYSCTL_UINT(_kern_vt_screen_reader, OID_AUTO, lines, CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
+ &vt_screen_reader_lines, 0, "Number of lines the VT screen reader module will feed, between 1 and 1000");
+
+SYSCTL_UINT(_kern_vt_screen_reader, OID_AUTO, hangup_pid, CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &vt_screen_reader_hup_pid, 0, "Send hangup signal to this PID, if non-zero");
+
+SYSCTL_U16(_kern_vt_screen_reader, OID_AUTO, row, CTLFLAG_RD | CTLFLAG_MPSAFE,
+ &vt_screen_reader_cursor.tp_row, 0, "Current VT screen reader row number, between 0 and 65535 inclusivly");
+
+SYSCTL_U16(_kern_vt_screen_reader, OID_AUTO, col, CTLFLAG_RD | CTLFLAG_MPSAFE,
+ &vt_screen_reader_cursor.tp_col, 0, "Current VT screen reader column number, between 0 and 65535 inclusivly");
+
+SYSCTL_PROC(_kern_vt_screen_reader, OID_AUTO, text_utf8, CTLTYPE_STRING | CTLFLAG_RD |
+ CTLFLAG_MPSAFE, NULL, 0, &vt_screen_reader_text_utf8, "A", "Current VT screen reader buffer in UTF-8 format");
+
+SYSCTL_PROC(_kern_vt_screen_reader, OID_AUTO, feed,
+ CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL,
+ 0, &vt_screen_reader_feed, "IU", "Set to non-zero to feed a new VT screen reader buffer");
+
+static int
+vt_screen_reader_text_utf8(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+
+ VT_ACCESS_LOCK();
+ if (vt_screen_reader_utf8_data != NULL) {
+ error = SYSCTL_OUT(req, vt_screen_reader_utf8_data,
+ vt_screen_reader_utf8_length);
+ } else {
+ error = 0;
+ }
+ VT_ACCESS_UNLOCK();
+
+ return (error);
+}
+
+static int
+vt_screen_reader_feed(SYSCTL_HANDLER_ARGS)
+{
+ unsigned max_ln;
+ unsigned val;
+ int error;
+
+ VT_ACCESS_LOCK();
+
+ val = 0;
+ error = SYSCTL_OUT(req, &val, sizeof(val));
+ if (error || !req->newptr)
+ goto done;
+
+ error = SYSCTL_IN(req, &val, sizeof(val));
+ if (error || val == 0)
+ goto done;
+
+ if (vt_screen_reader_utf8_data == NULL) {
+ vt_screen_reader_utf8_data =
+ malloc(VT_ACCESS_UTF8_DATA_MAX, M_VTACCESS,
+ M_WAITOK | M_ZERO);
+ } else {
+ /* Re-use the UTF-8 buffer */
+ }
+
+ max_ln = vt_screen_reader_lines;
+ if (max_ln < 1)
+ max_ln = 1;
+ else if (max_ln > 1000)
+ max_ln = 1000;
+
+ vt_screen_reader_utf8_length = 0;
+
+ /* Update the screen reader buffer: */
+ if (vtbuf_extract_screen_reader(vt_screen_reader_utf8_data,
+ &vt_screen_reader_utf8_length, &vt_screen_reader_cursor, max_ln,
+ VT_ACCESS_UTF8_DATA_MAX) != 0)
+ vt_screen_reader_utf8_length = 0;
+done:
+ VT_ACCESS_UNLOCK();
+ return (error);
+}
+
+static void
+vt_screen_reader_hup_callback(void *arg, int pending)
+{
+ struct proc *p;
+ pid_t pid;
+
+ pid = atomic_swap_int(&vt_screen_reader_hup_pid, 0);
+ if (pid <= 0)
+ return;
+ p = pfind(pid);
+ if (p == NULL)
+ return;
+ kern_psignal(p, SIGHUP);
+ PROC_UNLOCK(p);
+}
+
+void
+vt_screen_reader_hup(void)
+{
+ taskqueue_enqueue(taskqueue_thread, &vt_screen_reader_hup_task);
+}
diff --git a/sys/sys/terminal.h b/sys/sys/terminal.h
--- a/sys/sys/terminal.h
+++ b/sys/sys/terminal.h
@@ -69,19 +69,25 @@
* 21-25: Bold, underline, blink, reverse, right part of CJK fullwidth character
* 26-28: Foreground color
* 29-31: Background color
+ * 32: Used by VT screen reader layer
+ * 33-63: Unused
*/
-typedef uint32_t term_char_t;
+typedef uint64_t term_char_t;
#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)
+#define TCHAR_SCREEN_READER(c) (((c) >> 32) & 0x1)
+
+/* bit used by VT screen reader layer */
+#define TSCREEN_READER ((term_char_t)1 << 32)
typedef teken_attr_t term_attr_t;
typedef teken_color_t term_color_t;
#define TCOLOR_FG(c) (((c) & 0x7) << 26)
-#define TCOLOR_BG(c) (((c) & 0x7) << 29)
+#define TCOLOR_BG(c) ((term_char_t)((c) & 0x7) << 29)
#define TCOLOR_LIGHT(c) ((c) | 0x8)
#define TCOLOR_DARK(c) ((c) & ~0x8)

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 5, 7:31 PM (9 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28443682
Default Alt Text
D35754.diff (13 KB)

Event Timeline