diff --git a/usr.bin/Makefile b/usr.bin/Makefile --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -172,6 +172,7 @@ unvis \ vis \ vmstat \ + vtspeakd \ w \ wall \ wc \ diff --git a/usr.bin/vtspeakd/Makefile b/usr.bin/vtspeakd/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/vtspeakd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= vtspeakd +MAN= # + +.include diff --git a/usr.bin/vtspeakd/vtspeakd.c b/usr.bin/vtspeakd/vtspeakd.c new file mode 100644 --- /dev/null +++ b/usr.bin/vtspeakd/vtspeakd.c @@ -0,0 +1,220 @@ +/*- + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WRAP(str) (char []){str} + +static const char *speak_prog = "/usr/local/bin/espeak"; +static const char *beep_prog = "/usr/bin/beep"; +static void (*fn)(const char *); +static bool background; +static bool chatty; + +static void +sync_execv(const char *path, char *const argv[]) +{ + int pid; + + pid = fork(); + if (pid == 0) { + execv(path, argv); + exit(0); + } else if (pid != -1) { + waitpid(pid, NULL, 0); + } +} + +static void +feed(void) +{ + unsigned tmp = 1; + sysctlbyname("kern.vt.accessibility.feed", NULL, NULL, &tmp, sizeof(tmp)); +} + +static void +speak(const char *what) +{ + char buffer[256]; + char *data; + char *argv[4]; + + size_t len; + uint16_t row = 0; + uint16_t col = 0; + + len = sizeof(row); + sysctlbyname("kern.vt.accessibility.row", &row, &len, NULL, 0); + + len = sizeof(col); + sysctlbyname("kern.vt.accessibility.col", &col, &len, NULL, 0); + + len = 0; + sysctlbyname(what, NULL, &len, NULL, 0); + if (len == 0) + return; + + data = malloc(len + 1); + memset(data, 0, len + 1); + sysctlbyname(what, data, &len, NULL, 0); + + snprintf(buffer, sizeof(buffer), "row %u column %u ", row, col); + + argv[0] = WRAP("espeak"); + argv[1] = WRAP("--"); + argv[2] = buffer; + argv[3] = NULL; + + sync_execv(speak_prog, argv); + + argv[2] = data; + + sync_execv(speak_prog, argv); + + free(data); +} + +static void +beep(const char *what) +{ + char rbuf[16]; + char cbuf[16]; + + char *data; + char *argv[5]; + + size_t len; + uint16_t row = 0; + uint16_t col = 0; + + len = sizeof(row); + sysctlbyname("kern.vt.accessibility.row", &row, &len, NULL, 0); + + len = sizeof(col); + sysctlbyname("kern.vt.accessibility.col", &col, &len, NULL, 0); + + len = 0; + sysctlbyname(what, NULL, &len, NULL, 0); + if (len == 0) + return; + + data = malloc(3 + len + 1); + memset(data, 0, 3 + len + 1); + sysctlbyname(what, data + 3, &len, NULL, 0); + + data[0] = '-'; + data[1] = 't'; + data[2] = ' '; + + snprintf(rbuf, sizeof(rbuf), "-s %u", row); + snprintf(cbuf, sizeof(cbuf), "-s %u", col); + + argv[0] = WRAP("beep"); + argv[1] = rbuf; + argv[2] = WRAP("-F 110"); + argv[3] = WRAP("-D 50"); + argv[4] = NULL; + + sync_execv(beep_prog, argv); + + argv[0] = WRAP("beep"); + argv[1] = cbuf; + argv[2] = WRAP("-F 220"); + argv[3] = WRAP("-D 50"); + argv[4] = NULL; + + sync_execv(beep_prog, argv); + + argv[0] = WRAP("beep"); + argv[1] = data; + argv[2] = WRAP("-F 330"); + argv[3] = WRAP("-D 50"); + argv[4] = NULL; + + sync_execv(beep_prog, argv); + + free(data); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [parameters]\n" + "\t" "-b Beep the console contents (default)\n" + "\t" "-s Speak the console contents\n" + "\t" "-c Be chatty\n" + "\t" "-B Run in background\n" + "\t" "-h Show usage\n", + getprogname()); + exit(1); +} + +int +main(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, "Bbcsh")) != -1) { + switch (c) { + case 'B': + background = true; + break; + case 'b': + fn = &beep; + break; + case 'c': + chatty = true; + break; + case 's': + fn = &speak; + break; + default: + usage(); + break; + } + } + + if (fn == NULL) + fn = &beep; + + if (background && daemon(0, 0) != 0) + errx(1, "daemon(0,0) failed"); + + for (;;) { + fn(chatty ? "kern.vt.accessibility.text_chatty" : "kern.vt.accessibility.text_utf8"); + feed(); + sleep(1); + } + return (0); +}