Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144169075
D35754.id107923.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D35754.id107923.diff
View Options
diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile
--- a/sbin/devd/Makefile
+++ b/sbin/devd/Makefile
@@ -31,6 +31,8 @@
DEVD+= zfs.conf
.endif
+DEVD+= accessibility.conf
+
PROG_CXX=devd
SRCS= devd.cc token.l parse.y y.tab.h
MAN= devd.8 devd.conf.5
diff --git a/sbin/devd/accessibility.conf b/sbin/devd/accessibility.conf
new file mode 100644
--- /dev/null
+++ b/sbin/devd/accessibility.conf
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+# Use espeak by default using UTF-8
+notify 0 {
+ match "system" "VT";
+ match "subsystem" "ACCESSIBILITY";
+ match "type" "SPEAK";
+ action "rtprio 8 /usr/local/bin/espeak -- $text";
+};
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3466,6 +3466,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_accessibility.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
@@ -84,6 +84,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)
@@ -168,6 +170,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)
@@ -240,6 +244,7 @@
int vtbuf_get_marked_len(struct vt_buf *vb);
void vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz);
#endif
+void vtbuf_extract_accessibility(char *, size_t lines, size_t chars);
#define VTB_MARK_NONE 0
#define VTB_MARK_END 1
diff --git a/sys/dev/vt/vt_accessibility.c b/sys/dev/vt/vt_accessibility.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/vt/vt_accessibility.c
@@ -0,0 +1,159 @@
+/*-
+ * 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/sx.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/reboot.h>
+
+#include <sys/devctl.h>
+
+#include <dev/vt/vt.h>
+
+static SYSCTL_NODE(_kern_vt, OID_AUTO, accessibility,
+ CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Accessibility parameters");
+
+static int vt_access_enable(SYSCTL_HANDLER_ARGS);
+
+static unsigned int vt_access_enabled; /* default is disabled */
+static struct sx vt_access_sx;
+static struct callout vt_access_callout;
+
+SX_SYSINIT(vt_access_sx, &vt_access_sx, "VT accessibility");
+
+#define VT_ACCESS_LOCK() sx_xlock(&vt_access_sx)
+#define VT_ACCESS_UNLOCK() sx_xunlock(&vt_access_sx)
+
+static unsigned vt_lines_per_interval = 1; /* default value */
+
+SYSCTL_UINT(_kern_vt_accessibility, OID_AUTO, lines_per_interval, CTLFLAG_RWTUN,
+ &vt_lines_per_interval, 0, "Number of lines the VT accessibility module will feed per interval, between 1 and 1000");
+
+static unsigned vt_interval_ms = 1000; /* default value */
+
+SYSCTL_UINT(_kern_vt_accessibility, OID_AUTO, interval_ms, CTLFLAG_RWTUN,
+ &vt_interval_ms, 0, "Interval between accessibility requests in milliseconds, between 10ms and 60000ms");
+
+SYSCTL_PROC(_kern_vt_accessibility, OID_AUTO, enable,
+ CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &vt_access_enabled,
+ 0, vt_access_enable, "IU", "Set if VT accessibility is enabled");
+
+static void
+vt_access_worker_callout(void *arg)
+{
+ char buffer[512]; /* XXX the devctl buffer is not so big */
+ unsigned max_ln;
+ unsigned max_iv;
+ size_t len;
+
+ max_ln = vt_lines_per_interval;
+ if (max_ln < 1)
+ max_ln = 1;
+ else if (max_ln > 1000)
+ max_ln = 1000;
+
+ max_iv = vt_interval_ms;
+ if (max_iv < 10)
+ max_iv = 10;
+ else if (max_iv > 60000)
+ max_iv = 60000;
+
+ if (devctl_queue_empty()) {
+ snprintf(buffer, sizeof(buffer) - 2, "text=\"");
+ len = strlen(buffer);
+ vtbuf_extract_accessibility(buffer + len, max_ln, sizeof(buffer) - 2 - len);
+ len = strlen(buffer);
+
+ /* Check if the buffer was filled */
+ if (buffer[len - 1] != '"') {
+ buffer[len++] = '"';
+ buffer[len++] = 0;
+
+ MPASS(len <= sizeof(buffer));
+
+ devctl_notify("VT", "ACCESSIBILITY", "SPEAK", buffer);
+ }
+ }
+
+ /* Restart timer. */
+ if (vt_access_enabled != 0)
+ callout_reset(&vt_access_callout, (max_iv * hz) / 1000, &vt_access_worker_callout, arg);
+}
+
+static void
+vt_access_start(void)
+{
+ callout_init(&vt_access_callout, 1);
+
+ /* kick the callout */
+ vt_access_worker_callout(NULL);
+}
+
+static void
+vt_access_stop(void)
+{
+ callout_drain(&vt_access_callout);
+}
+
+static int
+vt_access_enable(SYSCTL_HANDLER_ARGS)
+{
+ unsigned val;
+ int error;
+
+ VT_ACCESS_LOCK();
+
+ /* Copy current value as required */
+ val = *(unsigned *)arg1;
+
+ error = SYSCTL_OUT(req, &val, sizeof(unsigned));
+ if (error || !req->newptr)
+ goto done;
+
+ error = SYSCTL_IN(req, &val, sizeof(unsigned));
+ if (error)
+ goto done;
+
+ /* Range check input value */
+ val = (val != 0) ? 1 : 0;
+
+ if (*(unsigned *)arg1 != val) {
+ *(unsigned *)arg1 = val;
+
+ if (val)
+ vt_access_start();
+ else
+ vt_access_stop();
+ }
+done:
+ VT_ACCESS_UNLOCK();
+ return (error);
+}
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,279 @@
}
}
+static bool
+vtbuf_extract_utf8(term_char_t ch, char *buffer, size_t *ppos, size_t size)
+{
+ char temp[32];
+ size_t len;
+
+ ch = TCHAR_CHARACTER(ch);
+
+ switch (ch) {
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '0' ... '9':
+ temp[0] = (char)ch;
+ temp[1] = 0;
+ break;
+ case '~':
+ strlcpy(temp, " tilde ", sizeof(temp));
+ break;
+ case '#':
+ strlcpy(temp, " hash ", sizeof(temp));
+ break;
+ case '+':
+ strlcpy(temp, " plus ", sizeof(temp));
+ break;
+ case '-':
+ strlcpy(temp, " minus ", sizeof(temp));
+ break;
+ case '_':
+ strlcpy(temp, " underscore ", sizeof(temp));
+ break;
+ case '*':
+ strlcpy(temp, " multiply ", sizeof(temp));
+ break;
+ case '/':
+ strlcpy(temp, " divide ", sizeof(temp));
+ break;
+ case '\\':
+ strlcpy(temp, " backslash ", sizeof(temp));
+ break;
+ case '=':
+ strlcpy(temp, " equal ", sizeof(temp));
+ break;
+ case '@':
+ strlcpy(temp, " at ", sizeof(temp));
+ break;
+ case '^':
+ strlcpy(temp, " xor ", sizeof(temp));
+ break;
+ case '|':
+ strlcpy(temp, " pipe ", sizeof(temp));
+ break;
+ case '&':
+ strlcpy(temp, " and ", sizeof(temp));
+ break;
+ case '(':
+ strlcpy(temp, " opening parenthesis ", sizeof(temp));
+ break;
+ case ')':
+ strlcpy(temp, " closing parenthesis ", sizeof(temp));
+ break;
+ case '[':
+ strlcpy(temp, " opening bracket ", sizeof(temp));
+ break;
+ case ']':
+ strlcpy(temp, " closing bracket ", sizeof(temp));
+ break;
+ case '{':
+ strlcpy(temp, " opening curlybracket ", sizeof(temp));
+ break;
+ case '}':
+ strlcpy(temp, " closing curlybracket ", sizeof(temp));
+ break;
+ case '.':
+ strlcpy(temp, " period ", sizeof(temp));
+ break;
+ case ',':
+ strlcpy(temp, " comma ", sizeof(temp));
+ break;
+ case ':':
+ strlcpy(temp, " colon ", sizeof(temp));
+ break;
+ case ';':
+ strlcpy(temp, " semicolon ", sizeof(temp));
+ break;
+ case '!':
+ strlcpy(temp, " not ", sizeof(temp));
+ break;
+ case '$':
+ strlcpy(temp, " dollar ", sizeof(temp));
+ break;
+ case '?':
+ strlcpy(temp, " questionmark ", sizeof(temp));
+ break;
+ case '>':
+ strlcpy(temp, " greaterthan ", sizeof(temp));
+ break;
+ case '<':
+ strlcpy(temp, " lessthan ", sizeof(temp));
+ break;
+ case '"':
+ strlcpy(temp, " quote ", sizeof(temp));
+ break;
+ case '\'':
+ strlcpy(temp, " apostrophe ", sizeof(temp));
+ break;
+ case '%':
+ strlcpy(temp, " percent ", sizeof(temp));
+ break;
+ default:
+ if (tchar_is_word_separator(ch)) {
+ temp[0] = ' ';
+ temp[1] = 0;
+ } else {
+ snprintf(temp, sizeof(temp), " uc %u ", (uint32_t)ch);
+ }
+ break;
+ }
+
+ len = strlen(temp) + 1;
+
+ if (*ppos + len > size)
+ return (false);
+
+ memcpy(buffer + *ppos, temp, len - 1);
+ *ppos += len - 1;
+ return (true);
+}
+
+static bool
+tchar_is_extended_word_separator(term_char_t ch)
+{
+ if (tchar_is_word_separator(ch)) {
+ return (true);
+ } else {
+ char temp[2];
+ size_t pos = 0;
+ return (!vtbuf_extract_utf8(ch, temp, &pos, sizeof(temp)));
+ }
+}
+
+static const char vt_accessibility_overflow[] = { "Line cannot be spoken" };
+
+void
+vtbuf_extract_accessibility(char *buffer, size_t lines, size_t sz)
+{
+ struct vt_device *vd;
+ struct vt_window *vw;
+ struct vt_buf *vb;
+ term_pos_t max;
+ term_char_t ch;
+ size_t pos;
+ size_t last_sep_off;
+ size_t last_line_off;
+ int last_sep_col;
+ int c;
+ int i;
+ int r;
+
+ MPASS(sz != 0);
+ MPASS(lines != 0);
+
+ vd = main_vd;
+ vw = vd->vd_curwindow;
+ vb = &vw->vw_buf;
+ max = vb->vb_scr_size;
+ pos = 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_ACCESSIBILITY(ch) != 0 ||
+ TCHAR_CHARACTER(ch) == 0)
+ continue;
+ if (tchar_is_extended_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_extended_word_separator(ch))
+ break;
+ }
+ break;
+ }
+
+ if (c == max.tp_col)
+ continue;
+
+ lines--;
+ last_sep_col = c;
+ last_sep_off = pos;
+ last_line_off = pos;
+
+ /* 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_extended_word_separator(ch)) {
+ if (vtbuf_extract_utf8(ch, buffer, &pos, sz) == false)
+ break;
+ /* Keep track of sensible stop locations. */
+ last_sep_col = c + 1;
+ last_sep_off = pos;
+ } else {
+ if (vtbuf_extract_utf8(ch, buffer, &pos, sz) == 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. */
+ pos = last_line_off;
+ c = max.tp_col;
+
+ for (size_t i = 0; vt_accessibility_overflow[i]; i++) {
+ if (vtbuf_extract_utf8(vt_accessibility_overflow[i],
+ buffer, &pos, sz) == false)
+ break;
+ }
+ } else {
+ /* Stop at last separator, if any. */
+ pos = last_sep_off;
+ c = last_sep_col;
+ }
+
+ /* Don't output this part again. */
+ while (c--)
+ vb->vb_rows[r][c] |= TACCESSIBILITY;
+ break;
+ } else {
+ /* Don't output this line again. */
+ while (c--)
+ vb->vb_rows[r][c] |= TACCESSIBILITY;
+
+ /* Remove trailing white space. */
+ while (pos != last_line_off && buffer[pos - 1] == ' ')
+ pos--;
+
+ if (pos == last_line_off) {
+ /* Grab another line, this one is empty. */
+ lines++;
+ } else {
+ /* Insert a word separator, just in case. */
+ if (vtbuf_extract_utf8(' ', buffer, &pos, sz))
+ break;
+ }
+ }
+ }
+ VTBUF_UNLOCK(vb);
+
+ /* Remove trailing white space from last line. */
+ while (pos != 0 && buffer[pos - 1] == ' ')
+ pos--;
+
+ /* Put the terminating character there. */
+ buffer[pos] = 0;
+}
+
+#ifndef SC_NO_CUTPASTE
void
vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz)
{
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");
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -674,6 +674,18 @@
pgsigio(&devsoftc.sigio, SIGIO, 0);
}
+bool
+devctl_queue_empty(void)
+{
+ bool retval;
+
+ mtx_lock(&devsoftc.mtx);
+ retval = STAILQ_EMPTY(&devsoftc.devq);
+ mtx_unlock(&devsoftc.mtx);
+
+ return (retval);
+}
+
/**
* @brief Send a 'notification' to userland, using standard ways
*/
diff --git a/sys/sys/devctl.h b/sys/sys/devctl.h
--- a/sys/sys/devctl.h
+++ b/sys/sys/devctl.h
@@ -13,6 +13,7 @@
* hook to send the message.
*/
bool devctl_process_running(void);
+bool devctl_queue_empty(void);
void devctl_notify(const char *__system, const char *__subsystem,
const char *__type, const char *__data);
struct sbuf;
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 accessibility 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_ACCESSIBILITY(c) (((c) >> 32) & 0x1)
+
+/* bit used by VT accessibility layer */
+#define TACCESSIBILITY ((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
Fri, Feb 6, 9:06 PM (17 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28450451
Default Alt Text
D35754.id107923.diff (15 KB)
Attached To
Mode
D35754: vt(4): Add support for console screen reader using sysctls.
Attached
Detach File
Event Timeline
Log In to Comment