Initial commit.
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* See LICENSE for licensing information
|
||||
*
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <gio/gio.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "accessibility_feedback.h"
|
||||
|
||||
static int feedback_fd = -1;
|
||||
|
||||
struct screen_reader {
|
||||
const char *service;
|
||||
const char *path;
|
||||
const char *interface;
|
||||
};
|
||||
|
||||
static const struct screen_reader screen_readers[] = {
|
||||
{
|
||||
"org.stormux.Cthulhu1.Service",
|
||||
"/org/stormux/Cthulhu1/Service",
|
||||
"org.stormux.Cthulhu1.Service",
|
||||
},
|
||||
{
|
||||
"org.gnome.Orca1.Service",
|
||||
"/org/gnome/Orca1/Service",
|
||||
"org.gnome.Orca1.Service",
|
||||
},
|
||||
};
|
||||
|
||||
static const char *feedback_message(accessibility_feedback_t feedback) {
|
||||
switch (feedback) {
|
||||
case ACCESSIBILITY_SCREEN_LOCKED:
|
||||
return "Screen locked. Enter password.";
|
||||
case ACCESSIBILITY_ENTER_PASSWORD:
|
||||
return "Enter password.";
|
||||
case ACCESSIBILITY_CHARACTER_TYPED:
|
||||
return "star";
|
||||
case ACCESSIBILITY_CHARACTER_ERASED:
|
||||
return "backspace star";
|
||||
case ACCESSIBILITY_NOTHING_TO_ERASE:
|
||||
return "backspace blank";
|
||||
case ACCESSIBILITY_PASSWORD_CLEARED:
|
||||
return "Password cleared.";
|
||||
case ACCESSIBILITY_VERIFYING:
|
||||
return "Verifying.";
|
||||
case ACCESSIBILITY_AUTHENTICATED:
|
||||
return "Authenticated.";
|
||||
case ACCESSIBILITY_INCORRECT_PASSWORD:
|
||||
return "Incorrect password. Enter password.";
|
||||
case ACCESSIBILITY_CAPS_LOCK_ON:
|
||||
return "Caps lock on.";
|
||||
case ACCESSIBILITY_CAPS_LOCK_OFF:
|
||||
return "Caps lock off.";
|
||||
case ACCESSIBILITY_NUM_LOCK_ON:
|
||||
return "Num lock on.";
|
||||
case ACCESSIBILITY_NUM_LOCK_OFF:
|
||||
return "Num lock off.";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool service_has_owner(GDBusConnection *connection, const char *service) {
|
||||
GError *error = NULL;
|
||||
GVariant *result = g_dbus_connection_call_sync(
|
||||
connection,
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"NameHasOwner",
|
||||
g_variant_new("(s)", service),
|
||||
G_VARIANT_TYPE("(b)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
250,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
if (result == NULL) {
|
||||
g_clear_error(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
gboolean has_owner = false;
|
||||
g_variant_get(result, "(b)", &has_owner);
|
||||
g_variant_unref(result);
|
||||
return has_owner;
|
||||
}
|
||||
|
||||
static void present_message(GDBusConnection *connection, const char *message) {
|
||||
for (size_t i = 0; i < sizeof(screen_readers) / sizeof(screen_readers[0]); i++) {
|
||||
const struct screen_reader *reader = &screen_readers[i];
|
||||
if (!service_has_owner(connection, reader->service)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GError *error = NULL;
|
||||
GVariant *result = g_dbus_connection_call_sync(
|
||||
connection,
|
||||
reader->service,
|
||||
reader->path,
|
||||
reader->interface,
|
||||
"PresentMessage",
|
||||
g_variant_new("(s)", message),
|
||||
G_VARIANT_TYPE("(b)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
500,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
if (result != NULL) {
|
||||
g_variant_unref(result);
|
||||
}
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_stars(GDBusConnection *connection, size_t count) {
|
||||
char message[640];
|
||||
size_t position = 0;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const char *separator = (i == 0 ? "" : " ");
|
||||
position += snprintf(message + position, sizeof(message) - position, "%sstar", separator);
|
||||
}
|
||||
|
||||
present_message(connection, message);
|
||||
}
|
||||
|
||||
static void close_sleep_lock_fd(void) {
|
||||
const char *sleep_lock_fd = getenv("XSS_SLEEP_LOCK_FD");
|
||||
if (sleep_lock_fd == NULL || *sleep_lock_fd == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
long int fd = strtol(sleep_lock_fd, &endptr, 10);
|
||||
if (*endptr == '\0') {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void feedback_worker(int read_fd, int inherited_xcb_fd) {
|
||||
if (inherited_xcb_fd >= 0) {
|
||||
close(inherited_xcb_fd);
|
||||
}
|
||||
close_sleep_lock_fd();
|
||||
|
||||
GError *error = NULL;
|
||||
GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
|
||||
if (connection == NULL) {
|
||||
g_clear_error(&error);
|
||||
}
|
||||
|
||||
accessibility_feedback_t feedback[128];
|
||||
ssize_t bytes_read;
|
||||
while ((bytes_read = read(read_fd, feedback, sizeof(feedback))) != 0) {
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
size_t count = (size_t)bytes_read / sizeof(feedback[0]);
|
||||
size_t stars = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (feedback[i] == ACCESSIBILITY_CHARACTER_TYPED) {
|
||||
stars++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connection != NULL && stars > 0) {
|
||||
flush_stars(connection, stars);
|
||||
}
|
||||
stars = 0;
|
||||
|
||||
const char *message = feedback_message(feedback[i]);
|
||||
if (connection != NULL && message != NULL) {
|
||||
present_message(connection, message);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection != NULL && stars > 0) {
|
||||
flush_stars(connection, stars);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection != NULL) {
|
||||
g_object_unref(connection);
|
||||
}
|
||||
close(read_fd);
|
||||
}
|
||||
|
||||
void accessibility_feedback_start(int inherited_xcb_fd) {
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = fcntl(pipe_fds[1], F_GETFL);
|
||||
if (flags == -1 || fcntl(pipe_fds[1], F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
close(pipe_fds[0]);
|
||||
close(pipe_fds[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
close(pipe_fds[0]);
|
||||
close(pipe_fds[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
close(pipe_fds[1]);
|
||||
feedback_worker(pipe_fds[0], inherited_xcb_fd);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
close(pipe_fds[0]);
|
||||
feedback_fd = pipe_fds[1];
|
||||
}
|
||||
|
||||
void accessibility_feedback_notify(accessibility_feedback_t feedback) {
|
||||
if (feedback_fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t result;
|
||||
do {
|
||||
result = write(feedback_fd, &feedback, sizeof(feedback));
|
||||
} while (result == -1 && errno == EINTR);
|
||||
|
||||
if (result == -1 && errno == EPIPE) {
|
||||
close(feedback_fd);
|
||||
feedback_fd = -1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user