Page MenuHomeFreeBSD

D35754.id107923.diff
No OneTemporary

D35754.id107923.diff

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

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)

Event Timeline