Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144103026
D35754.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D35754.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D35754: vt(4): Add support for console screen reader using sysctls.
Attached
Detach File
Event Timeline
Log In to Comment