blob: da70b16712b73646c5dc1a0fab9eeafbbf9d2217 [file] [log] [blame]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
/* apitest provides a small test utility for BRLTTY's API */
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <signal.h>
#ifdef __MINGW32__
#include "win_pthread.h"
#else
#include <pthread.h>
#endif
#include "cmdline.h"
#include "pid.h"
#include "brl_cmds.h"
#include "brl_dots.h"
#include "cmd.h"
#include "cmd_brlapi.h"
#include "async_wait.h"
#define BRLAPI_NO_DEPRECATED
#include "brlapi.h"
static brlapi_connectionSettings_t settings;
static char *opt_host;
static char *opt_auth;
static int opt_showName;
static int opt_showModelIdentifier;
static int opt_showSize;
static int opt_showDots;
static int opt_showKeyCodes;
static int opt_learnMode;
static int opt_parameters;
static int opt_suspendMode;
static int opt_threadMode;
BEGIN_OPTION_TABLE(programOptions)
{ .word = "brlapi",
.letter = 'b',
.argument = "[host][:port]",
.setting.string = &opt_host,
.description = "BrlAPIa host and/or port to connect to."
},
{ .word = "auth",
.letter = 'a',
.argument = "scheme+...",
.setting.string = &opt_auth,
.description = "BrlAPI authorization/authentication schemes."
},
{ .word = "name",
.letter = 'n',
.setting.flag = &opt_showName,
.description = "Show the name of the braille driver."
},
{ .word = "model",
.letter = 'm',
.setting.flag = &opt_showModelIdentifier,
.description = "Show the model identifier of the braille device."
},
{ .word = "window",
.letter = 'w',
.setting.flag = &opt_showSize,
.description = "Show the dimensions of the braille window."
},
{ .word = "dots",
.letter = 'd',
.setting.flag = &opt_showDots,
.description = "Show dot pattern."
},
{ .word = "keycodes",
.letter = 'k',
.setting.flag = &opt_showKeyCodes,
.description = "Enter interactive keycode learn mode."
},
{ .word = "learn",
.letter = 'l',
.setting.flag = &opt_learnMode,
.description = "Enter interactive command learn mode."
},
{ .word = "parameters",
.letter = 'p',
.setting.flag = &opt_parameters,
.description = "Test parameters"
},
{ .word = "suspend",
.letter = 's',
.setting.flag = &opt_suspendMode,
.description = "Suspend the braille driver (press ^C or send SIGUSR1 to resume)."
},
{ .word = "thread",
.letter = 't',
.setting.flag = &opt_threadMode,
.description = "Exercise threaded use"
},
END_OPTION_TABLE(programOptions)
static void showDisplaySize(void)
{
unsigned int x, y;
fprintf(stderr,"Getting display size: ");
if (brlapi_getDisplaySize(&x, &y)<0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
fprintf(stderr, "%dX%d\n", x, y);
}
static void showDriverName(void)
{
char name[30];
fprintf(stderr, "Getting driver name: ");
if (brlapi_getDriverName(name, sizeof(name))<0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
fprintf(stderr, "%s\n", name);
}
static void showModelIdentifier(void)
{
char identifier[30];
fprintf(stderr, "Getting model identifier: ");
if (brlapi_getModelIdentifier(identifier, sizeof(identifier))<0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
fprintf(stderr, "%s\n", identifier);
}
#define DOTS_TEXT "dots: "
#define DOTS_TEXTLEN (strlen(DOTS_TEXT))
#define DOTS_LEN 8
#define DOTS_TOTALLEN (DOTS_TEXTLEN + DOTS_LEN)
static void showDots(void)
{
unsigned int size;
{
unsigned int columns, rows;
if (brlapi_getDisplaySize(&columns, &rows) < 0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
size = columns * rows;
unsigned int minimum = DOTS_TOTALLEN;
if (size < minimum) {
fprintf(stderr, "can't show dots on a braille display with less than %u cells\n", minimum);
exit(PROG_EXIT_SEMANTIC);
}
}
if (brlapi_enterTtyMode(-1, NULL) < 0) {
brlapi_perror("enterTtyMode");
exit(PROG_EXIT_FATAL);
}
{
fprintf(stderr, "Showing dot patterns\n");
char text[size + 1];
memset(text, ' ', size);
text[size] = 0;
memcpy(text, DOTS_TEXT, DOTS_TEXTLEN);
unsigned char or[size];
memset(or, 0, size);
or[DOTS_TEXTLEN+0] = BRL_DOT_1;
or[DOTS_TEXTLEN+1] = BRL_DOT_2;
or[DOTS_TEXTLEN+2] = BRL_DOT_3;
or[DOTS_TEXTLEN+3] = BRL_DOT_4;
or[DOTS_TEXTLEN+4] = BRL_DOT_5;
or[DOTS_TEXTLEN+5] = BRL_DOT_6;
or[DOTS_TEXTLEN+6] = BRL_DOT_7;
or[DOTS_TEXTLEN+7] = BRL_DOT_8;
brlapi_writeArguments_t wa = BRLAPI_WRITEARGUMENTS_INITIALIZER;
wa.regionBegin = 1;
wa.regionSize = size;
wa.text = text;
wa.orMask = or;
if (brlapi_write(&wa) < 0) {
brlapi_perror("brlapi_write");
exit(PROG_EXIT_FATAL);
}
}
{
brlapi_keyCode_t key;
brlapi_readKey(1, &key);
}
}
static char *getKeyName (brlapi_keyCode_t key)
{
return brlapi_getParameterAlloc(BRLAPI_PARAM_KEY_SHORT_NAME, (key & ~BRLAPI_DRV_KEY_PRESS), BRLAPI_PARAMF_GLOBAL, NULL);
}
static void listKeys(void)
{
size_t length;
brlapi_param_driverKeycode_t *keys = brlapi_getParameterAlloc(BRLAPI_PARAM_DEVICE_KEY_CODES, 0, BRLAPI_PARAMF_GLOBAL, &length);
if (keys) {
length /= sizeof(*keys);
printf("%zu keys\n", length);
for (int i=0; i<length; i+=1) {
printf("key %04"BRLAPI_PRIxKEYCODE":", keys[i]);
{
char *name = getKeyName(keys[i]);
if (name) {
printf(" name %s", name);
free(name);
}
}
printf("\n");
}
free(keys);
}
}
static void showKeyCodes(void)
{
char buf[0X100];
fprintf(stderr, "Entering keycode learn mode\n");
if (brlapi_getDriverName(buf, sizeof(buf))==-1) {
brlapi_perror("getDriverName");
return;
}
if (brlapi_enterTtyMode(-1, buf)<0) {
brlapi_perror("enterTtyMode");
return;
}
if (brlapi_acceptAllKeys()==-1) {
brlapi_perror("acceptAllKeys");
return;
}
if (brlapi_writeText(BRLAPI_CURSOR_OFF, "showing key codes")<0) {
brlapi_perror("brlapi_writeText");
exit(PROG_EXIT_FATAL);
}
int res;
brlapi_keyCode_t key;
while ((res = brlapi_readKeyWithTimeout(10000, &key)) > 0) {
const char *action = (key & BRLAPI_DRV_KEY_PRESS)? "press": "release";
size_t length = snprintf(buf, sizeof(buf), "%04" BRLAPI_PRIxKEYCODE " (%" BRLAPI_PRIuKEYCODE ") %s", key, key, action);
{
char *name = getKeyName(key);
if (name) {
snprintf(&buf[length], (sizeof(buf) - length), ": %s", name);
free(name);
}
}
brlapi_writeText(BRLAPI_CURSOR_OFF, buf);
fprintf(stderr, "%s\n", buf);
}
if (res < 0) brlapi_perror("brlapi_readKey");
}
static void enterLearnMode(void)
{
int res;
brlapi_keyCode_t code;
int cmd;
char buf[0X100], *val;
fprintf(stderr,"Entering command learn mode\n");
if (brlapi_enterTtyMode(-1, NULL)<0) {
brlapi_perror("enterTtyMode");
return;
}
if (brlapi_writeText(BRLAPI_CURSOR_OFF, "command learn mode")<0) {
brlapi_perror("brlapi_writeText");
exit(PROG_EXIT_FATAL);
}
while ((res = brlapi_readKey(1, &code)) != -1) {
fprintf(stderr, "got key %016"BRLAPI_PRIxKEYCODE"\n",code);
cmd = cmdBrlapiToBrltty(code);
describeCommand(buf, sizeof(buf), cmd,
(CDO_IncludeName | CDO_IncludeOperand));
brlapi_writeText(BRLAPI_CURSOR_OFF, buf);
fprintf(stderr, "%s\n", buf);
val = brlapi_getParameterAlloc(BRLAPI_PARAM_COMMAND_LONG_NAME, cmd, BRLAPI_PARAMF_GLOBAL, NULL);
fprintf(stderr, "%s\n", val);
free(val);
if (cmd==BRL_CMD_LEARN) return;
}
brlapi_perror("brlapi_readKey");
}
static void brailleRetainDotsChanged(brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, void *priv, const void *data, size_t len)
{
const brlapi_param_retainDots_t *d = data;
if (parameter != BRLAPI_PARAM_RETAIN_DOTS)
{
printf("handler called for %x, another parameter than retaindot parameter?!\n", parameter);
return;
}
printf("new retain dots %zd: %d\n", len, *d);
}
static void testParameters(void)
{
brlapi_param_retainDots_t val;
if (brlapi_getParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, &val, sizeof(val)) < 0) {
brlapi_perror("getParameter");
}
printf("retain dots was %d\n", val);
printf("now watching retain dots parameter\n");
if (brlapi_watchParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, brailleRetainDotsChanged, NULL, NULL, 0) == 0) {
brlapi_perror("watchParameter");
}
val = 0;
printf("setting retain dots parameter to %d\n", val);
if (brlapi_setParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, &val, sizeof(val)) < 0) {
brlapi_perror("setParameter");
}
if (brlapi_getParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, &val, sizeof(val)) < 0) {
brlapi_perror("getParameter");
}
printf("retain dots now %d\n", val);
val = 1;
printf("setting retain dots parameter to %d\n", val);
if (brlapi_setParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, &val, sizeof(val)) < 0) {
brlapi_perror("setParameter");
}
if (brlapi_getParameter(BRLAPI_PARAM_RETAIN_DOTS, 0, BRLAPI_PARAMF_LOCAL, &val, sizeof(val)) < 0) {
brlapi_perror("getParameter");
}
printf("retain dots now %d\n", val);
listKeys();
}
#ifdef SIGUSR1
static void emptySignalHandler(int sig) { }
#endif /* SIGUSR1 */
static void suspendDriver(void)
{
char driver[30];
fprintf(stderr, "Getting driver name: ");
if (brlapi_getDriverName(driver, sizeof(driver))<0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
fprintf(stderr, "%s\n", driver);
fprintf(stderr, "Suspending driver\n");
if (brlapi_suspendDriver(driver)) {
brlapi_perror("suspend");
} else {
#ifdef SIGUSR1
signal(SIGUSR1,emptySignalHandler);
#endif /* SIGUSR1 */
{
ProcessIdentifier pid = getProcessIdentifier();
fprintf(stderr, "Waiting (to resume, send SIGUSR1 to process %"PRIpid")\n", pid);
}
brlapi_pause(-1);
#ifdef SIGUSR1
signal(SIGUSR1,SIG_DFL);
#endif /* SIGUSR1 */
fprintf(stderr, "Resuming driver\n");
if (brlapi_resumeDriver()) {
brlapi_perror("resumeDriver");
}
}
}
volatile int thread_done;
static void *thread_fun(void *foo)
{
brlapi_keyCode_t code;
do {
brlapi_readKey(1, &code);
printf("got key %"PRIx64"\n", code);
if (brlapi_readKeyWithTimeout(1000, &code) != 1) {
printf("didn't get a key within the 1s delay\n");
} else {
printf("got key %"PRIx64" within the 1s delay\n", code);
}
} while (code != (BRLAPI_KEY_TYPE_CMD | BRL_CMD_HOME));
thread_done = 1;
return NULL;
}
static void exerciseThreads(void)
{
pthread_t thread;
unsigned x, y;
unsigned i = 0;
if (brlapi_getDisplaySize(&x, &y)<0) {
brlapi_perror("failed");
exit(PROG_EXIT_FATAL);
}
if (brlapi_enterTtyMode(-1, NULL)<0) {
brlapi_perror("enterTtyMode");
exit(PROG_EXIT_FATAL);
}
pthread_create(&thread, NULL, thread_fun, NULL);
while (!thread_done) {
char buf[x+1];
snprintf(buf, sizeof(buf), "counting %d", i);
buf[x] = 0;
brlapi_writeText(BRLAPI_CURSOR_OFF, buf);
asyncWait(1000);
i++;
}
pthread_join(thread, NULL);
}
int
main (int argc, char *argv[]) {
ProgramExitStatus exitStatus = PROG_EXIT_SUCCESS;
brlapi_fileDescriptor fd;
{
const CommandLineDescriptor descriptor = {
.options = &programOptions,
.applicationName = "apitest",
.usage = {
.purpose = strtext("Test BrlAPI functions."),
}
};
PROCESS_OPTIONS(descriptor, argc, argv);
}
settings.host = opt_host;
settings.auth = opt_auth;
fprintf(stderr, "Connecting to BrlAPI... ");
if ((fd=brlapi_openConnection(&settings, &settings)) != (brlapi_fileDescriptor)(-1)) {
fprintf(stderr, "done (fd=%"PRIfd")\n", fd);
fprintf(stderr,"Connected to %s using auth %s\n", settings.host, settings.auth);
if (opt_showName) {
showDriverName();
}
if (opt_showModelIdentifier) {
showModelIdentifier();
}
if (opt_showSize) {
showDisplaySize();
}
if (opt_showDots) {
showDots();
}
if (opt_showKeyCodes) {
showKeyCodes();
}
if (opt_learnMode) {
enterLearnMode();
}
if (opt_parameters) {
testParameters();
}
if (opt_suspendMode) {
suspendDriver();
}
if (opt_threadMode) {
exerciseThreads();
}
brlapi_closeConnection();
fprintf(stderr, "Disconnected\n");
} else {
fprintf(stderr, "failed to connect to %s using auth %s",settings.host, settings.auth);
brlapi_perror("");
exitStatus = PROG_EXIT_FATAL;
}
return exitStatus;
}