blob: 92edd21b0ad2b3c3d62b711eeedce448d274c202 [file] [log] [blame] [edit]
/*
* 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>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <fcntl.h>
#include "parameters.h"
#include "embed.h"
#include "log.h"
#include "report.h"
#include "strfmt.h"
#include "pgmprivs.h"
#include "lock.h"
#include "activity.h"
#include "update.h"
#include "cmd.h"
#include "cmd_navigation.h"
#include "brl.h"
#include "brl_utils.h"
#include "spk.h"
#include "spk_input.h"
#include "scr.h"
#include "scr_special.h"
#include "status.h"
#include "blink.h"
#include "variables.h"
#include "datafile.h"
#include "ttb.h"
#include "atb.h"
#include "ctb.h"
#include "ktb.h"
#include "ktb_keyboard.h"
#include "kbd.h"
#include "alert.h"
#include "bell.h"
#include "leds.h"
#include "tune.h"
#include "notes.h"
#include "message.h"
#include "file.h"
#include "parse.h"
#include "dynld.h"
#include "async_handle.h"
#include "async_alarm.h"
#include "program.h"
#include "messages.h"
#include "revision.h"
#include "service.h"
#include "cmdline.h"
#include "profile_types.h"
#include "brl_input.h"
#include "cmd_queue.h"
#include "core.h"
#include "api_control.h"
#include "prefs.h"
#include "utf8.h"
#include "io_generic.h"
#include "io_usb.h"
#include "io_bluetooth.h"
#ifdef __MINGW32__
int isWindowsService = 0;
#endif /* __MINGW32__ */
#ifdef __MSDOS__
#include "system_msdos.h"
#endif /* __MSDOS__ */
static void
makeProgramBanner (char *buffer, size_t size, int includeRevision) {
const char *revision = includeRevision? getRevisionIdentifier(): "";
snprintf(buffer, size, "%s %s%s%s",
PACKAGE_NAME, PACKAGE_VERSION,
(*revision? " rev ": ""), revision);
}
static void
logProgramBanner (void) {
char banner[0X100];
makeProgramBanner(banner, sizeof(banner), 1);
{
int pushed = pushLogPrefix("");
logMessage(LOG_NOTICE, "%s [%s]", banner, PACKAGE_URL);
if (pushed) popLogPrefix();
}
}
static void
logProperty (const char *value, const char *variable, const char *label) {
if (value && *value) {
if (variable) setGlobalVariable(variable, value);
} else {
value = "none";
}
logMessage(LOG_INFO, "%s: %s", label, value);
}
static const char optionOperand_none[] = "no";
static const char optionOperand_autodetect[] = "auto";
static const char optionOperand_off[] = "off";
static const char *const *const fallbackBrailleDrivers =
NULL_TERMINATED_STRING_ARRAY(
optionOperand_none
);
static const char *const *const autodetectableBrailleDrivers_serial =
NULL_TERMINATED_STRING_ARRAY(
"md", "pm", "ts", "ht", "bn", "al", "bm", "pg", "sk"
);
static const char *const *const autodetectableBrailleDrivers_USB =
NULL_TERMINATED_STRING_ARRAY(
"al", "bm", "bn", "cn", "dp", "eu", "fs", "hd", "hm", "ht", "hw", "ic", "mt", "pg", "pm", "sk", "vo"
);
static const char *const *const autodetectableBrailleDrivers_Bluetooth =
NULL_TERMINATED_STRING_ARRAY(
"np", "ht", "al", "bm"
);
#define SERVICE_NAME "BrlAPI"
#define SERVICE_DESCRIPTION "Braille Devices API"
static
STR_BEGIN_FORMATTER(formatLogLevelString, unsigned int index)
switch (index) {
case 0:
STR_PRINTF("0-%u", logLevelCount-1);
break;
case 1: {
for (unsigned int level=0; level<logLevelCount; level+=1) {
if (level) STR_PRINTF(" ");
STR_PRINTF("%s", logLevelNames[level]);
}
break;
}
case 2: {
LogCategoryIndex category;
STR_PRINTF("%s", logCategoryName_all);
for (category=0; category<LOG_CATEGORY_COUNT; category+=1) {
const char *name = getLogCategoryName(category);
if (name && *name) {
STR_PRINTF(" %s", name);
}
}
break;
}
case 3:
STR_PRINTF("%c", logCategoryPrefix_disable);
break;
default:
break;
}
STR_END_FORMATTER
static const char *const *const screenContentQualityChoices =
NULL_TERMINATED_STRING_ARRAY(
"none", "low", "poor", "fair", "good", "high"
);
STR_BEGIN_FORMATTER(formatScreenContentQualityChoices, unsigned int index)
switch (index) {
case 0: {
const char *const *choices = screenContentQualityChoices;
const char *const *choice = choices;
while (*choice) {
if (choice != choices) STR_PRINTF(" ");
STR_PRINTF("%s", *choice);
choice += 1;
}
break;
}
default:
break;
}
STR_END_FORMATTER
static int opt_installService;
static const char *const optionStrings_InstallService[] = {
SERVICE_NAME,
NULL
};
static int opt_removeService;
static const char *const optionStrings_RemoveService[] = {
SERVICE_NAME,
NULL
};
static char *opt_startMessage;
static char *opt_stopMessage;
static char *opt_localeDirectory;
static int opt_version;
static int opt_verify;
static int opt_quiet;
static int opt_noDaemon;
static int opt_standardError;
static char *opt_logLevel;
static char *opt_logFile;
static int opt_bootParameters = 1;
static int opt_environmentVariables;
static char *opt_messageTime;
static int opt_cancelExecution;
static const char *const optionStrings_CancelExecution[] = {
PACKAGE_TARNAME,
NULL
};
static char *opt_promptPatterns;
static int opt_stayPrivileged;
static char *opt_privilegeParameters;
static char *opt_pidFile;
static char *opt_configurationFile;
static char *opt_updatableDirectory;
static char *opt_writableDirectory;
char *opt_driversDirectory;
char *opt_brailleDevice;
static char **brailleDevices = NULL;
static const char *brailleDevice = NULL;
int opt_releaseDevice;
static char *opt_brailleDriver;
static char **brailleDrivers = NULL;
static const BrailleDriver *brailleDriver = NULL;
static void *brailleObject = NULL;
static int brailleDriverConstructed;
static char *opt_brailleParameters;
static char *brailleParameters = NULL;
static char **brailleDriverParameters = NULL;
static char *opt_preferencesFile;
static char *opt_overridePreferences;
static char *oldPreferencesFile = NULL;
static int oldPreferencesEnabled = 1;
char *opt_tablesDirectory;
char *opt_textTable;
char *opt_contractionTable;
char *opt_attributesTable;
char *opt_keyboardTable;
KeyTable *keyboardTable = NULL;
static KeyboardMonitorObject *keyboardMonitor = NULL;
static char *opt_keyboardProperties;
static KeyboardProperties keyboardProperties;
#ifdef ENABLE_API
static int opt_noApi;
static char *opt_apiParameters = NULL;
static char **apiParameters = NULL;
#endif /* ENABLE_API */
#ifdef ENABLE_SPEECH_SUPPORT
static char *opt_speechDriver;
static char **speechDrivers = NULL;
static const SpeechDriver *speechDriver = NULL;
static void *speechObject = NULL;
static char *opt_speechParameters;
static char *speechParameters = NULL;
static char **speechDriverParameters = NULL;
static char *opt_speechInput;
static SpeechInputObject *speechInputObject;
int opt_quietIfNoBraille;
static char *opt_autospeakThreshold;
unsigned int autospeakMinimumScreenContentQuality;
static void
setAutospeakThreshold (void) {
const char *choice = opt_autospeakThreshold;
int ok = validateChoice(
&autospeakMinimumScreenContentQuality,
choice, screenContentQualityChoices
);
if (!ok) {
logMessage(LOG_ERR, "%s: %s",
gettext("unknown screen content quality"),
choice
);
}
logProperty(
screenContentQualityChoices[autospeakMinimumScreenContentQuality],
"autospeakThreshold", "Autospeak Threshold"
);
}
#endif /* ENABLE_SPEECH_SUPPORT */
static char *opt_screenDriver;
static char **screenDrivers = NULL;
static const ScreenDriver *screenDriver = NULL;
static void *screenObject = NULL;
static char *opt_screenParameters;
static char *screenParameters = NULL;
static char **screenDriverParameters = NULL;
static const char *const optionStrings_TextTable[] = {
optionOperand_autodetect,
NULL
};
static const char *const optionStrings_BrailleDriver[] = {
optionOperand_autodetect,
optionOperand_none,
BRAILLE_DRIVER_CODES,
NULL
};
static const char *const optionStrings_ScreenDriver[] = {
optionOperand_autodetect,
optionOperand_none,
SCREEN_DRIVER_CODES,
NULL
};
#ifdef ENABLE_SPEECH_SUPPORT
static const char *const optionStrings_SpeechDriver[] = {
optionOperand_autodetect,
optionOperand_none,
SPEECH_DRIVER_CODES,
NULL
};
#endif /* ENABLE_SPEECH_SUPPORT */
BEGIN_OPTION_TABLE(programOptions)
{ .word = "version",
.letter = 'V',
.setting.flag = &opt_version,
.description = strtext("Log the versions of the core, API, and built-in drivers, and then exit.")
},
{ .word = "environment-variables",
.letter = 'E',
.setting.flag = &opt_environmentVariables,
.description = strtext("Recognize environment variables.")
},
{ .word = "configuration-file",
.letter = 'f',
.flags = OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_configurationFile,
.internal.setting = CONFIGURATION_DIRECTORY "/" CONFIGURATION_FILE,
.internal.adjust = fixInstallPath,
.description = strtext("Path to default settings file.")
},
{ .word = "braille-driver",
.letter = 'b',
.bootParameter = 1,
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("driver,..."),
.setting.string = &opt_brailleDriver,
.internal.setting = optionOperand_autodetect,
.description = strtext("Braille driver code (%s, %s, or one of {%s})."),
.strings.array = optionStrings_BrailleDriver
},
{ .word = "braille-parameters",
.letter = 'B',
.bootParameter = 4,
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_brailleParameters,
.internal.setting = BRAILLE_PARAMETERS,
.description = strtext("Parameters for the braille driver.")
},
{ .word = "braille-device",
.letter = 'd',
.bootParameter = 2,
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("identifier,..."),
.setting.string = &opt_brailleDevice,
.internal.setting = BRAILLE_DEVICE,
.description = strtext("Device for accessing braille display.")
},
{ .word = "release-device",
.letter = 'r',
.flags = OPT_Config | OPT_EnvVar,
.setting.flag = &opt_releaseDevice,
#ifdef WINDOWS
.internal.setting = OPT_WORD_TRUE,
#else /* WINDOWS */
.internal.setting = OPT_WORD_FALSE,
#endif /* WINDOWS */
.description = strtext("Release braille device when screen or window is unreadable.")
},
{ .word = "text-table",
.letter = 't',
.bootParameter = 3,
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_textTable,
.internal.setting = optionOperand_autodetect,
.description = strtext("Name of or path to text table (or %s)."),
.strings.array = optionStrings_TextTable
},
{ .word = "contraction-table",
.letter = 'c',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_contractionTable,
.internal.setting = optionOperand_autodetect,
.description = strtext("Name of or path to contraction table.")
},
{ .word = "attributes-table",
.letter = 'a',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_attributesTable,
.description = strtext("Name of or path to attributes table.")
},
#ifdef ENABLE_SPEECH_SUPPORT
{ .word = "speech-driver",
.letter = 's',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("driver,..."),
.setting.string = &opt_speechDriver,
.internal.setting = optionOperand_autodetect,
.description = strtext("Speech driver code (%s, %s, or one of {%s})."),
.strings.array = optionStrings_SpeechDriver
},
{ .word = "speech-parameters",
.letter = 'S',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_speechParameters,
.internal.setting = SPEECH_PARAMETERS,
.description = strtext("Parameters for the speech driver.")
},
{ .word = "speech-input",
.letter = 'i',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_speechInput,
.description = strtext("Name of or path to speech input object.")
},
{ .word = "quiet-if-no-braille",
.letter = 'Q',
.flags = OPT_Config | OPT_EnvVar,
.setting.flag = &opt_quietIfNoBraille,
.description = strtext("Do not autospeak when braille is not being used.")
},
{ .word = "autospeak-threshold",
.flags = OPT_Config | OPT_EnvVar | OPT_Format,
.argument = strtext("quality"),
.setting.string = &opt_autospeakThreshold,
.description = strtext("Minimum screen content quality to autospeak (one of {%s})."),
.strings.format = formatScreenContentQualityChoices
},
#endif /* ENABLE_SPEECH_SUPPORT */
{ .word = "screen-driver",
.letter = 'x',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("driver,..."),
.setting.string = &opt_screenDriver,
.internal.setting = DEFAULT_SCREEN_DRIVER,
.description = strtext("Screen driver code (%s, %s, or one of {%s})."),
.strings.array = optionStrings_ScreenDriver
},
{ .word = "screen-parameters",
.letter = 'X',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_screenParameters,
.internal.setting = SCREEN_PARAMETERS,
.description = strtext("Parameters for the screen driver.")
},
{ .word = "keyboard-table",
.letter = 'k',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_keyboardTable,
.internal.setting = optionOperand_off,
.description = strtext("Name of or path to keyboard table.")
},
{ .word = "keyboard-properties",
.letter = 'K',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_keyboardProperties,
.description = strtext("Properties of eligible keyboards.")
},
{ .word = "preferences-file",
.letter = 'F',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_preferencesFile,
.internal.setting = PREFERENCES_FILE,
.description = strtext("Name of or path to default preferences file.")
},
{ .word = "override-preferences",
.letter = 'o',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_overridePreferences,
.description = strtext("Explicit preference settings.")
},
#ifdef ENABLE_API
{ .word = "no-api",
.letter = 'N',
.flags = OPT_Config | OPT_EnvVar,
.setting.flag = &opt_noApi,
.description = strtext("Disable the application programming interface.")
},
{ .word = "api-parameters",
.letter = 'A',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_apiParameters,
.internal.setting = API_PARAMETERS,
.description = strtext("Parameters for the application programming interface.")
},
#endif /* ENABLE_API */
{ .word = "quiet",
.letter = 'q',
.flags = OPT_Config | OPT_EnvVar,
.setting.flag = &opt_quiet,
.description = strtext("Suppress start-up messages.")
},
{ .word = "log-level",
.letter = 'l',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar | OPT_Format,
.argument = strtext("lvl|cat,..."),
.setting.string = &opt_logLevel,
.description = strtext("Logging level (%s or one of {%s}) and/or log categories to enable (any combination of {%s}, each optionally prefixed by %s to disable)."),
.strings.format = formatLogLevelString
},
{ .word = "log-file",
.letter = 'L',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_logFile,
.description = strtext("Path to log file.")
},
{ .word = "standard-error",
.letter = 'e',
.setting.flag = &opt_standardError,
.description = strtext("Log to standard error rather than to the system log.")
},
{ .word = "no-daemon",
.letter = 'n',
.setting.flag = &opt_noDaemon,
.description = strtext("Remain a foreground process.")
},
{ .word = "stay-privileged",
.letter = 'z',
.flags = OPT_Config | OPT_EnvVar,
.setting.flag = &opt_stayPrivileged,
.description = strtext("Don't switch to an unprivileged user or relinquish any privileges (group memberships, capabilities, etc).")
},
{ .word = "privilege-parameters",
.letter = 'Z',
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("name=value,..."),
.setting.string = &opt_privilegeParameters,
.internal.setting = PRIVILEGE_PARAMETERS,
.description = strtext("Parameters for the privilege establishment stage.")
},
{ .word = "message-time",
.letter = 'M',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("csecs"),
.setting.string = &opt_messageTime,
.description = strtext("Message hold timeout (in 10ms units).")
},
{ .word = "start-message",
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("text"),
.setting.string = &opt_startMessage,
.description = strtext("The text to be shown when the braille driver starts and to be spoken when the speech driver starts.")
},
{ .word = "stop-message",
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("text"),
.setting.string = &opt_stopMessage,
.description = strtext("The text to be shown when the braille driver stops.")
},
{ .word = "prompt-patterns",
.flags = OPT_Extend | OPT_Config | OPT_EnvVar,
.argument = strtext("regexp,..."),
.setting.string = &opt_promptPatterns,
.description = strtext("Patterns that match command prompts.")
},
#ifdef HAVE_PCM_SUPPORT
{ .word = "pcm-device",
.letter = 'p',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("device"),
.setting.string = &opt_pcmDevice,
.description = strtext("PCM (soundcard digital audio) device specifier.")
},
#endif /* HAVE_PCM_SUPPORT */
#ifdef HAVE_MIDI_SUPPORT
{ .word = "midi-device",
.letter = 'm',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("device"),
.setting.string = &opt_midiDevice,
.description = strtext("MIDI (Musical Instrument Digital Interface) device specifier.")
},
#endif /* HAVE_MIDI_SUPPORT */
{ .word = "tables-directory",
.letter = 'T',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("directory"),
.setting.string = &opt_tablesDirectory,
.internal.setting = TABLES_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory containing tables.")
},
{ .word = "drivers-directory",
.letter = 'D',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("directory"),
.setting.string = &opt_driversDirectory,
.internal.setting = DRIVERS_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory containing drivers.")
},
{ .word = "updatable-directory",
.letter = 'U',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("directory"),
.setting.string = &opt_updatableDirectory,
.internal.setting = UPDATABLE_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory which contains files that can be updated.")
},
{ .word = "writable-directory",
.letter = 'W',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("directory"),
.setting.string = &opt_writableDirectory,
.internal.setting = WRITABLE_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory which can be written to.")
},
{ .word = "locale-directory",
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("directory"),
.setting.string = &opt_localeDirectory,
.internal.setting = LOCALE_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory which contains message localizations.")
},
{ .word = "pid-file",
.letter = 'P',
.flags = OPT_Config | OPT_EnvVar,
.argument = strtext("file"),
.setting.string = &opt_pidFile,
.internal.adjust = fixInstallPath,
.description = strtext("Path to process identifier file.")
},
{ .word = "cancel-execution",
.letter = 'C',
.setting.flag = &opt_cancelExecution,
.description = strtext("Stop an existing instance of %s, and then exit."),
.strings.array = optionStrings_CancelExecution
},
{ .word = "install-service",
.letter = 'I',
.setting.flag = &opt_installService,
.description = strtext("Install the %s service, and then exit."),
.strings.array = optionStrings_InstallService
},
{ .word = "remove-service",
.letter = 'R',
.setting.flag = &opt_removeService,
.description = strtext("Remove the %s service, and then exit."),
.strings.array = optionStrings_RemoveService
},
{ .word = "verify",
.letter = 'v',
.setting.flag = &opt_verify,
.description = strtext("Write the start-up logs, and then exit.")
},
END_OPTION_TABLE(programOptions)
int
changeLogLevel (const char *operand) {
int ok = 1;
char **strings = splitString(operand, ',', NULL);
if (strings) {
char **string = strings;
while (*string) {
unsigned int level;
if (isLogLevel(&level, *string)) {
systemLogLevel = level;
} else if (!setLogCategory(*string)) {
logMessage(LOG_ERR, "%s: %s", gettext("unknown log level or category"), *string);
ok = 0;
}
string += 1;
}
deallocateStrings(strings);
}
return ok;
}
int
changeLogCategories (const char *operand) {
disableAllLogCategories();
return changeLogLevel(operand);
}
static void
exitLog (void *data) {
closeSystemLog();
closeLogFile();
}
static void
setLogLevels (void) {
systemLogLevel = LOG_NOTICE;
disableAllLogCategories();
changeLogLevel(opt_logLevel);
{
unsigned char level;
if (opt_standardError) {
level = systemLogLevel;
} else {
level = LOG_NOTICE;
if (opt_version || opt_verify) level += 1;
if (opt_quiet) level -= 1;
}
stderrLogLevel = level;
}
}
static void
establishPrivileges (void) {
const char *platform = getPrivilegeParametersPlatform();
const char *const *names = getPrivilegeParameterNames();
char **parameters = getParameters(names, platform, opt_privilegeParameters);
if (parameters) {
logParameters(names, parameters, "Privilege Parameter");
establishProgramPrivileges(parameters, opt_stayPrivileged);
deallocateStrings(parameters);
}
}
ProgramExitStatus
brlttyPrepare (int argc, char *argv[]) {
{
const CommandLineDescriptor descriptor = {
.options = &programOptions,
.applicationName = "brltty",
.configurationFile = &opt_configurationFile,
.doEnvironmentVariables = &opt_environmentVariables,
.doBootParameters = &opt_bootParameters,
.usage = {
.purpose = strtext("Screen reader for those who use a braille device."),
}
};
ProgramExitStatus exitStatus = processOptions(&descriptor, &argc, &argv);
switch (exitStatus) {
case PROG_EXIT_SYNTAX:
case PROG_EXIT_SUCCESS:
break;
default:
return exitStatus;
}
}
if (argc) {
logMessage(LOG_ERR, "%s: %s", gettext("excess argument"), argv[0]);
}
setMessagesDirectory(opt_localeDirectory);
setUpdatableDirectory(opt_updatableDirectory);
setWritableDirectory(opt_writableDirectory);
setLogLevels();
onProgramExit("log", exitLog, NULL);
{
const char *logFile;
if (*opt_logFile) {
logFile = opt_logFile;
openLogFile(logFile);
} else {
logFile = "<system>";
openSystemLog();
}
logProgramBanner();
logProperty(logFile, "logFile", "Log File");
logProperty(opt_logLevel, "logLevel", "Log Level");
}
logProperty(getMessagesLocale(), "messagesLocale", "Messages Locale");
logProperty(getMessagesDomain(), "messagesDomain", "Messages Domain");
logProperty(getMessagesDirectory(), "messagesDirectory", "Messages Directory");
#ifdef ENABLE_SPEECH_SUPPORT
setAutospeakThreshold();
#endif /* ENABLE_SPEECH_SUPPORT */
establishPrivileges();
return PROG_EXIT_SUCCESS;
}
static int
setTextTable (const char *name) {
if (!name) name = "";
if (!replaceTextTable(opt_tablesDirectory, name)) return 0;
if (!*name) name = TEXT_TABLE;
changeStringSetting(&opt_textTable, name);
api.updateParameter(BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE, 0);
return 1;
}
static int
setTextTableForLocale (void) {
changeStringSetting(&opt_textTable, "");
char *name = getTextTableForLocale(opt_tablesDirectory);
if (name) {
logMessage(LOG_DEBUG, "using autoselected text table: %s", name);
int ok = setTextTable(name);
free(name);
if (ok) return 1;
}
return 0;
}
int
changeTextTable (const char *name) {
if (strcmp(name, optionOperand_autodetect) == 0) {
return setTextTableForLocale();
}
return setTextTable(name);
}
static void
exitTextTable (void *data) {
setTextTable(NULL);
}
static int
setContractionTable (const char *name) {
if (!name) name = "";
if (!replaceContractionTable(opt_tablesDirectory, name)) return 0;
if (!*name) name = CONTRACTION_TABLE;
changeStringSetting(&opt_contractionTable, name);
api.updateParameter(BRLAPI_PARAM_LITERARY_BRAILLE_TABLE, 0);
return 1;
}
static int
setContractionTableForLocale (void) {
changeStringSetting(&opt_contractionTable, "");
char *name = getContractionTableForLocale(opt_tablesDirectory);
if (name) {
logMessage(LOG_DEBUG, "using autoselected contraction table: %s", name);
int ok = setContractionTable(name);
free(name);
if (ok) return 1;
}
return 0;
}
int
changeContractionTable (const char *name) {
if (strcmp(name, optionOperand_autodetect) == 0) {
return setContractionTableForLocale();
}
return setContractionTable(name);
}
static void
exitContractionTable (void *data) {
setContractionTable(NULL);
}
static void
setTextAndContractionTables (void) {
int usingInternalTextTable = 0;
if (*opt_textTable) {
if (strcmp(opt_textTable, optionOperand_autodetect) == 0) {
setTextTableForLocale();
} else if (!setTextTable(opt_textTable)) {
changeStringSetting(&opt_textTable, "");
}
}
if (!*opt_textTable) {
logMessage(LOG_DEBUG, "using internal text table: %s", TEXT_TABLE);
changeStringSetting(&opt_textTable, TEXT_TABLE);
usingInternalTextTable = 1;
}
logProperty(opt_textTable, "textTable", "Text Table");
onProgramExit("text-table", exitTextTable, NULL);
if (*opt_contractionTable) {
if (strcmp(opt_contractionTable, optionOperand_autodetect) == 0) {
if (setContractionTableForLocale()) {
if (usingInternalTextTable) {
if (!isContractedBraille()) {
setContractedBraille(1);
logMessage(LOG_DEBUG, "contracted braille has been enabled");
}
}
}
} else if (!setContractionTable(opt_contractionTable)) {
changeStringSetting(&opt_contractionTable, "");
}
}
if (!*opt_contractionTable) {
if (setContractionTable(NULL)) {
logMessage(LOG_DEBUG, "using internal contraction table: %s", CONTRACTION_TABLE);
}
}
logProperty(opt_contractionTable, "contractionTable", "Contraction Table");
onProgramExit("contraction-table", exitContractionTable, NULL);
}
int
changeAttributesTable (const char *name) {
if (!name) name = "";
if (!replaceAttributesTable(opt_tablesDirectory, name)) return 0;
if (!*name) name = ATTRIBUTES_TABLE;
changeStringSetting(&opt_attributesTable, name);
return 1;
}
static void
exitAttributesTable (void *data) {
changeAttributesTable(NULL);
}
static void
setAttributesTable (void) {
if (*opt_attributesTable) {
if (!changeAttributesTable(opt_attributesTable)) {
changeStringSetting(&opt_attributesTable, "");
}
}
if (!*opt_attributesTable) {
changeStringSetting(&opt_attributesTable, ATTRIBUTES_TABLE);
}
logProperty(opt_attributesTable, "attributesTable", "Attributes Table");
onProgramExit("attributes-table", exitAttributesTable, NULL);
}
static KeyTableState
handleKeyboardEvent (KeyGroup group, KeyNumber number, int press) {
if (keyboardTable) {
if (!scr.unreadable) {
return processKeyEvent(keyboardTable, getCurrentCommandContext(), group, number, press);
}
resetKeyTable(keyboardTable);
}
return KTS_UNBOUND;
}
static int
startKeyboardMonitor (void) {
return !!(keyboardMonitor = newKeyboardMonitorObject(&keyboardProperties, handleKeyboardEvent));
}
static void
stopKeyboardMonitor (void) {
if (keyboardMonitor) {
destroyKeyboardMonitorObject(keyboardMonitor);
keyboardMonitor = NULL;
}
}
static int
prepareKeyboardMonitorActivity (void *data) {
return 1;
}
static int
startKeyboardMonitorActivity (void *data) {
return startKeyboardMonitor();
}
static void
stopKeyboardMonitorActivity (void *data) {
stopKeyboardMonitor();
}
static const ActivityMethods keyboardMonitorActivityMethods = {
.activityName = "keyboard-monitor",
.retryInterval = KEYBOARD_MONITOR_START_RETRY_INTERVAL,
.prepare = prepareKeyboardMonitorActivity,
.start = startKeyboardMonitorActivity,
.stop = stopKeyboardMonitorActivity
};
static ActivityObject *keyboardMonitorActivity = NULL;
static void
exitKeyboardMonitor (void *data) {
if (keyboardMonitorActivity) {
destroyActivity(keyboardMonitorActivity);
keyboardMonitorActivity = NULL;
}
}
static ActivityObject *
getKeyboardMonitorActivity (int allocate) {
if (!keyboardMonitorActivity) {
if (allocate) {
if (!(keyboardMonitorActivity = newActivity(&keyboardMonitorActivityMethods, NULL))) {
return NULL;
}
onProgramExit("keyboard-monitor", exitKeyboardMonitor, NULL);
}
}
return keyboardMonitorActivity;
}
static void
enableKeyboardMonitor (void) {
ActivityObject *activity = getKeyboardMonitorActivity(1);
if (activity) startActivity(activity);
}
static void
disableKeyboardMonitor (void) {
ActivityObject *activity = getKeyboardMonitorActivity(0);
if (activity) stopActivity(activity);
}
static unsigned int brailleHelpPageNumber = 0;
static unsigned int keyboardHelpPageNumber = 0;
static int
enableHelpPage (unsigned int *pageNumber) {
if (!*pageNumber) {
if (!constructHelpScreen()) return 0;
if (!(*pageNumber = addHelpPage())) return 0;
}
return setHelpPageNumber(*pageNumber);
}
static int
enableBrailleHelpPage (void) {
return enableHelpPage(&brailleHelpPageNumber);
}
static int
enableKeyboardHelpPage (void) {
return enableHelpPage(&keyboardHelpPageNumber);
}
static void
disableHelpPage (unsigned int pageNumber) {
if (pageNumber) {
if (setHelpPageNumber(pageNumber)) {
clearHelpPage();
}
}
}
static void
disableBrailleHelpPage (void) {
disableHelpPage(brailleHelpPageNumber);
}
static void
disableKeyboardHelpPage (void) {
disableHelpPage(keyboardHelpPageNumber);
}
static int
handleWcharHelpLine (const wchar_t *line, void *data UNUSED) {
return addHelpLine(line);
}
static int
handleUtf8HelpLine (const LineHandlerParameters *parameters) {
const char *utf8 = parameters->line.text;
size_t size = parameters->line.length + 1;
wchar_t characters[size];
wchar_t *character = characters;
convertUtf8ToWchars(&utf8, &character, size);
return handleWcharHelpLine(characters, parameters->data);
}
static int
loadHelpFile (const char *file) {
int loaded = 0;
FILE *stream;
if ((stream = openDataFile(file, "r", 0))) {
if (processLines(stream, handleUtf8HelpLine, NULL)) loaded = 1;
fclose(stream);
}
return loaded;
}
static char *
makeBrailleKeyTablePath (void) {
return makeInputTablePath(opt_tablesDirectory, braille->definition.code, brl.keyBindings);
}
static void
makeBrailleHelpPage (const char *keyTablePath) {
if (enableBrailleHelpPage()) {
if (brl.keyTable) {
listKeyTable(brl.keyTable, NULL, handleWcharHelpLine, NULL);
} else {
char *keyHelpPath = replaceFileExtension(keyTablePath, KEY_HELP_EXTENSION);
if (keyHelpPath) {
if (loadHelpFile(keyHelpPath)) {
logMessage(LOG_INFO, "%s: %s", gettext("Key Help"), keyHelpPath);
} else {
logMessage(LOG_WARNING, "%s: %s", gettext("cannot open key help"), keyHelpPath);
}
free(keyHelpPath);
}
}
if (!getHelpLineCount()) {
addHelpLine(WS_C("help not available"));
message(NULL, gettext("no key bindings"), 0);
}
}
}
static void
makeKeyboardHelpPage (void) {
if (enableKeyboardHelpPage()) {
listKeyTable(keyboardTable, NULL, handleWcharHelpLine, NULL);
}
}
static void
exitKeyboardTable (void *data) {
if (keyboardTable) {
destroyKeyTable(keyboardTable);
keyboardTable = NULL;
}
disableKeyboardHelpPage();
}
int
changeKeyboardTable (const char *name) {
KeyTable *table = NULL;
if (!*name) name = "";
if (strcmp(name, optionOperand_off) == 0) name = "";
if (*name) {
char *path = makeKeyboardTablePath(opt_tablesDirectory, name);
if (path) {
logMessage(LOG_DEBUG, "compiling keyboard table: %s", path);
if (!(table = compileKeyTable(path, KEY_NAME_TABLES(keyboard)))) {
logMessage(LOG_ERR, "%s: %s", gettext("cannot compile keyboard table"), path);
}
free(path);
}
if (!table) return 0;
}
if (keyboardTable) {
disableKeyboardHelpPage();
disableKeyboardMonitor();
destroyKeyTable(keyboardTable);
keyboardTable = NULL;
}
if (table) {
setKeyTableLogLabel(table, "kbd");
setLogKeyEventsFlag(table, &LOG_CATEGORY_FLAG(KEYBOARD_KEYS));
keyboardTable = table;
enableKeyboardMonitor();
makeKeyboardHelpPage();
}
if (!*name) name = optionOperand_off;
logMessage(LOG_DEBUG, "keyboard table changed: %s -> %s", opt_keyboardTable, name);
changeStringSetting(&opt_keyboardTable, name);
return 1;
}
static void
setKeyboardTable (void) {
parseKeyboardProperties(&keyboardProperties, opt_keyboardProperties);
onProgramExit("keyboard-table", exitKeyboardTable, NULL);
changeKeyboardTable(opt_keyboardTable);
logProperty(opt_keyboardTable, "keyboardTable", "Keyboard Table");
}
int
haveStatusCells (void) {
return brl.statusColumns > 0;
}
static void
brailleWindowReconfigured (unsigned int rows, unsigned int columns) {
textStart = 0;
textCount = columns;
statusStart = 0;
statusCount = 0;
if (!(textMaximized || haveStatusCells())) {
unsigned int separatorWidth = (prefs.statusSeparator == ssNone)? 0: 1;
unsigned int reserved = 1 + separatorWidth;
if (brl.textColumns > reserved) {
unsigned int statusWidth = prefs.statusCount;
if (!statusWidth) statusWidth = getStatusFieldsLength(prefs.statusFields);
statusWidth = MIN(statusWidth, brl.textColumns-reserved);
if (statusWidth > 0) {
switch (prefs.statusPosition) {
case spLeft:
statusStart = 0;
statusCount = statusWidth;
textStart = statusCount + separatorWidth;
textCount = columns - textStart;
break;
case spRight:
statusCount = statusWidth;
statusStart = columns - statusCount;
textCount = statusStart - separatorWidth;
textStart = 0;
break;
}
}
}
}
logMessage(LOG_DEBUG,
"regions: text=%u.%u status=%u.%u",
textStart, textCount,
statusStart, statusCount
);
fullWindowShift = MAX(textCount-prefs.brailleWindowOverlap, 1);
halfWindowShift = textCount / 2;
verticalWindowShift = (rows > 1)? rows: 5;
logMessage(LOG_DEBUG,
"shifts: full=%u half=%u vertical=%u",
fullWindowShift, halfWindowShift, verticalWindowShift
);
}
void
reconfigureBrailleWindow (void) {
brailleWindowReconfigured(brl.textRows, brl.textColumns);
}
static void
applyBraillePreferences (void) {
reconfigureBrailleWindow();
setBrailleFirmness(&brl, prefs.brailleFirmness);
setTouchSensitivity(&brl, prefs.touchSensitivity);
setAutorepeatProperties(&brl, prefs.autorepeatEnabled,
PREFS2MSECS(prefs.longPressTime),
PREFS2MSECS(prefs.autorepeatInterval));
if (brl.keyTable) {
setKeyAutoreleaseTime(brl.keyTable, prefs.autoreleaseTime);
}
}
#ifdef ENABLE_SPEECH_SUPPORT
static void
applySpeechPreferences (void) {
setSpeechVolume(&spk, prefs.speechVolume, 0);
setSpeechRate(&spk, prefs.speechRate, 0);
setSpeechPitch(&spk, prefs.speechPitch, 0);
setSpeechPunctuation(&spk, prefs.speechPunctuation, 0);
}
#endif /* ENABLE_SPEECH_SUPPORT */
static void
applyAllPreferences (void) {
setConsoleBellMonitoring(prefs.consoleBellAlert);
setLedMonitoring(prefs.keyboardLedAlerts);
tuneSetDevice(prefs.tuneDevice);
applyBraillePreferences();
#ifdef ENABLE_SPEECH_SUPPORT
applySpeechPreferences();
#endif /* ENABLE_SPEECH_SUPPORT */
}
void
setPreferences (const PreferenceSettings *newPreferences) {
prefs = *newPreferences;
applyAllPreferences();
}
static void
ensureStatusFields (void) {
const unsigned char *fields = braille->statusFields;
unsigned int count = brl.statusColumns * brl.statusRows;
if (!fields && count) {
static const unsigned char fields1[] = {
sfWindowRow, sfEnd
};
static const unsigned char fields2[] = {
sfWindowRow, sfCursorRow, sfEnd
};
static const unsigned char fields3[] = {
sfWindowRow, sfCursorRow, sfCursorColumn, sfEnd
};
static const unsigned char fields4[] = {
sfWindowCoordinates2, sfCursorCoordinates2, sfEnd
};
static const unsigned char fields5[] = {
sfWindowCoordinates2, sfCursorCoordinates2, sfStateDots, sfEnd
};
static const unsigned char fields6[] = {
sfWindowCoordinates2, sfCursorCoordinates2, sfStateDots, sfScreenNumber,
sfEnd
};
static const unsigned char fields7[] = {
sfWindowCoordinates2, sfCursorCoordinates2, sfStateDots, sfTime,
sfEnd
};
static const unsigned char *const fieldsTable[] = {
fields1, fields2, fields3, fields4, fields5, fields6, fields7
};
static const unsigned char fieldsCount = ARRAY_COUNT(fieldsTable);
if (count > fieldsCount) count = fieldsCount;
fields = fieldsTable[count - 1];
}
setStatusFields(fields);
}
static void
setPreferenceOverrides (void) {
int count;
char **settings = splitString(opt_overridePreferences, PARAMETER_SEPARATOR_CHARACTER, &count);
if (settings) {
char **setting = settings;
char **end = setting + count;
while (setting < end) {
setPreference(*setting);
setting += 1;
}
deallocateStrings(settings);
}
}
static void
finishPreferencesLoad (void) {
setPreferenceOverrides();
applyAllPreferences();
}
int
loadPreferences (int reset) {
int ok = 0;
int found = 0;
if (reset) {
resetPreferences();
} else {
{
char *path = makePreferencesFilePath(opt_preferencesFile);
if (path) {
if (testFilePath(path)) {
found = 1;
if (loadPreferencesFile(path)) ok = 1;
oldPreferencesEnabled = 0;
} else {
logMessage(LOG_DEBUG, "preferences file not found: %s", path);
}
free(path);
}
}
if (oldPreferencesEnabled) {
const char *path = oldPreferencesFile;
if (path) {
if (testFilePath(path)) {
found = 1;
if (loadPreferencesFile(path)) ok = 1;
} else {
logMessage(LOG_DEBUG, "old preferences file not found: %s", path);
}
}
}
}
if (!found) {
char *path = makePath(opt_tablesDirectory, "default.prefs");
if (path) {
if (loadPreferencesFile(path)) ok = 1;
free(path);
}
}
finishPreferencesLoad();
return ok;
}
int
savePreferences (void) {
int ok = 0;
char *path = makePreferencesFilePath(opt_preferencesFile);
if (path) {
if (savePreferencesFile(path)) {
ok = 1;
oldPreferencesEnabled = 0;
}
free(path);
}
return ok;
}
#ifdef ENABLE_API
static void
exitApiServer (void *data) {
if (api.isServerLinked()) api.unlinkServer();
if (api.isServerRunning()) api.stopServer();
if (apiParameters) {
deallocateStrings(apiParameters);
apiParameters = NULL;
}
}
#endif /* ENABLE_API */
static void
startApiServer (void) {
#ifdef ENABLE_API
if (!(opt_noApi || api.isServerRunning())) {
const char *const *parameters = api.getServerParameters();
apiParameters = getParameters(parameters,
NULL,
opt_apiParameters);
if (apiParameters) {
api.logServerIdentity(0);
logParameters(parameters, apiParameters, "API Parameter");
if (!opt_verify) {
if (api.startServer(apiParameters)) {
onProgramExit("api-server", exitApiServer, NULL);
}
}
}
}
#endif /* ENABLE_API */
}
typedef struct {
const char *driverType;
const char *const *requestedDrivers;
const char *const *autodetectableDrivers;
const char * (*getDefaultDriver) (void);
int (*haveDriver) (const char *code);
int (*initializeDriver) (const char *code, int verify);
} DriverActivationData;
static int
activateDriver (const DriverActivationData *data, int verify) {
int oneDriver = data->requestedDrivers[0] && !data->requestedDrivers[1];
int autodetect = oneDriver && (strcmp(data->requestedDrivers[0], optionOperand_autodetect) == 0);
const char *const defaultDrivers[] = {data->getDefaultDriver(), NULL};
const char *const *driver;
if (!oneDriver || autodetect) verify = 0;
if (!autodetect) {
driver = data->requestedDrivers;
} else if (defaultDrivers[0]) {
driver = defaultDrivers;
} else if (*(driver = data->autodetectableDrivers)) {
logMessage(LOG_DEBUG, "performing %s driver autodetection", data->driverType);
} else {
logMessage(LOG_DEBUG, "no autodetectable %s drivers", data->driverType);
}
if (!*driver) {
driver = fallbackBrailleDrivers;
autodetect = 0;
}
while (*driver) {
if (!autodetect || data->haveDriver(*driver)) {
logMessage(LOG_DEBUG, "checking for %s driver: %s", data->driverType, *driver);
if (data->initializeDriver(*driver, verify)) return 1;
}
++driver;
}
logMessage(LOG_DEBUG, "%s driver not found", data->driverType);
return 0;
}
static void
unloadDriverObject (void **object) {
#ifdef ENABLE_SHARED_OBJECTS
if (*object) {
unloadSharedObject(*object);
*object = NULL;
}
#endif /* ENABLE_SHARED_OBJECTS */
}
void
forgetDevices (void) {
usbForgetDevices();
bthForgetDevices();
}
static void
initializeBrailleDisplay (void) {
constructBrailleDisplay(&brl);
brl.bufferResized = &brailleWindowReconfigured;
}
static LockDescriptor *
getBrailleDriverLock (void) {
static LockDescriptor *lock = NULL;
return getLockDescriptor(&lock, "braille-driver");
}
void
lockBrailleDriver (void) {
obtainExclusiveLock(getBrailleDriverLock());
}
void
unlockBrailleDriver (void) {
releaseLock(getBrailleDriverLock());
}
int
isBrailleDriverConstructed (void) {
return brailleDriverConstructed;
}
static void
setBrailleDriverConstructed (int yes) {
lockBrailleDriver();
brailleDriverConstructed = yes;
unlockBrailleDriver();
if (brailleDriverConstructed) {
announceBrailleOnline();
} else {
announceBrailleOffline();
}
static const brlapi_param_t parameters[] = {
BRLAPI_PARAM_DRIVER_CODE,
BRLAPI_PARAM_DRIVER_NAME,
BRLAPI_PARAM_DRIVER_VERSION,
BRLAPI_PARAM_DEVICE_MODEL,
BRLAPI_PARAM_DEVICE_CELL_SIZE,
BRLAPI_PARAM_DISPLAY_SIZE,
BRLAPI_PARAM_DEVICE_IDENTIFIER,
BRLAPI_PARAM_DEVICE_SPEED,
BRLAPI_PARAM_DEVICE_KEY_CODES,
BRLAPI_PARAM_BOUND_COMMAND_CODES,
};
const brlapi_param_t *parameter = parameters;
const brlapi_param_t *end = parameter + ARRAY_COUNT(parameters);
while (parameter < end) {
api.updateParameter(*parameter++, 0);
}
}
int
constructBrailleDriver (void) {
initializeBrailleDisplay();
if (braille->construct(&brl, brailleDriverParameters, brailleDevice)) {
if (ensureBrailleBuffer(&brl, LOG_INFO)) {
if (brl.keyBindings) {
char *keyTablePath = makeBrailleKeyTablePath();
logMessage(LOG_INFO, "%s: %s", gettext("Key Bindings"), brl.keyBindings);
if (keyTablePath) {
if (brl.keyNames) {
if ((brl.keyTable = compileKeyTable(keyTablePath, brl.keyNames))) {
logMessage(LOG_INFO, "%s: %s", gettext("Key Table"), keyTablePath);
setKeyTableLogLabel(brl.keyTable, "brl");
setLogKeyEventsFlag(brl.keyTable, &LOG_CATEGORY_FLAG(BRAILLE_KEYS));
setKeyboardEnabledFlag(brl.keyTable, &prefs.brailleKeyboardEnabled);
} else {
logMessage(LOG_WARNING, "%s: %s", gettext("cannot compile key table"), keyTablePath);
}
}
if (haveBrailleDisplay()) makeBrailleHelpPage(keyTablePath);
free(keyTablePath);
}
}
setBrailleDriverConstructed(1);
startBrailleInput();
return 1;
}
braille->destruct(&brl);
} else {
logMessage(LOG_DEBUG, "%s: %s -> %s",
"braille driver initialization failed",
braille->definition.code, brailleDevice);
}
return 0;
}
void
destructBrailleDriver (void) {
stopBrailleInput();
drainBrailleOutput(&brl, 0);
setBrailleDriverConstructed(0);
braille->destruct(&brl);
disableBrailleHelpPage();
destructBrailleDisplay(&brl);
}
int
isBrailleOnline (void) {
return isBrailleDriverConstructed() && !brl.isOffline;
}
static int
initializeBrailleDriver (const char *code, int verify) {
if ((braille = loadBrailleDriver(code, &brailleObject, opt_driversDirectory))) {
brailleDriverParameters = getParameters(braille->parameters,
braille->definition.code,
brailleParameters);
if (brailleDriverParameters) {
int constructed = verify;
if (!constructed) {
logMessage(LOG_DEBUG, "initializing braille driver: %s -> %s",
braille->definition.code, brailleDevice);
if (constructBrailleDriver()) {
brailleDriver = braille;
constructed = 1;
}
}
if (constructed) {
identifyBrailleDriver(braille, 0);
logMessage(LOG_INFO, "%s: %s", gettext("Braille Device"), brailleDevice);
logParameters(
braille->parameters,
brailleDriverParameters,
"Braille Parameter"
);
{
const char *strings[] = {
CONFIGURATION_DIRECTORY, "/",
PACKAGE_TARNAME, "-",
braille->definition.code, ".prefs"
};
oldPreferencesFile = joinStrings(strings, ARRAY_COUNT(strings));
}
if (oldPreferencesFile) {
logMessage(LOG_INFO, "%s: %s", gettext("Old Preferences File"), oldPreferencesFile);
api.linkServer();
return 1;
} else {
logMallocError();
}
}
deallocateStrings(brailleDriverParameters);
brailleDriverParameters = NULL;
}
unloadDriverObject(&brailleObject);
} else {
logMessage(LOG_ERR, "%s: %s", gettext("braille driver not loadable"), code);
}
braille = &noBraille;
return 0;
}
static int
activateBrailleDriver (int verify) {
int oneDevice = brailleDevices[0] && !brailleDevices[1];
const char *const *device = (const char *const *)brailleDevices;
if (!oneDevice) verify = 0;
while (*device) {
const char *const *autodetectableDrivers = NULL;
brailleDevice = *device;
logMessage(LOG_DEBUG, "checking braille device: %s", brailleDevice);
{
const char *dev = brailleDevice;
const GioPublicProperties *properties = gioGetPublicProperties(&dev);
if (properties) {
logMessage(LOG_DEBUG, "braille device type: %s", properties->type.name);
switch (properties->type.identifier) {
case GIO_TYPE_SERIAL: {
autodetectableDrivers = autodetectableBrailleDrivers_serial;
break;
}
case GIO_TYPE_USB: {
autodetectableDrivers = autodetectableBrailleDrivers_USB;
break;
}
case GIO_TYPE_BLUETOOTH: {
if (!(autodetectableDrivers = bthGetDriverCodes(dev, BLUETOOTH_DEVICE_NAME_OBTAIN_TIMEOUT))) {
autodetectableDrivers = autodetectableBrailleDrivers_Bluetooth;
}
break;
}
default:
break;
}
} else {
logMessage(LOG_DEBUG, "unrecognized braille device type");
}
}
if (!autodetectableDrivers) {
static const char *noDrivers[] = {NULL};
autodetectableDrivers = noDrivers;
}
{
const DriverActivationData data = {
.driverType = "braille",
.requestedDrivers = (const char *const *)brailleDrivers,
.autodetectableDrivers = autodetectableDrivers,
.getDefaultDriver = getDefaultBrailleDriver,
.haveDriver = haveBrailleDriver,
.initializeDriver = initializeBrailleDriver
};
if (activateDriver(&data, verify)) return 1;
}
device += 1;
}
brailleDevice = NULL;
return 0;
}
static void
deactivateBrailleDriver (void) {
if (brailleDriver) {
api.unlinkServer();
if (brailleDriverConstructed) destructBrailleDriver();
braille = &noBraille;
brailleDevice = NULL;
brailleDriver = NULL;
}
unloadDriverObject(&brailleObject);
stopAllBlinkDescriptors();
if (brailleDriverParameters) {
deallocateStrings(brailleDriverParameters);
brailleDriverParameters = NULL;
}
if (oldPreferencesFile) {
free(oldPreferencesFile);
oldPreferencesFile = NULL;
}
}
static int
startBrailleDriver (void) {
forgetDevices();
if (activateBrailleDriver(0)) {
if (oldPreferencesEnabled) {
loadPreferencesFile(oldPreferencesFile);
finishPreferencesLoad();
} else {
applyBraillePreferences();
}
ensureStatusFields();
alert(ALERT_BRAILLE_ON);
ses->winx = 0;
trackScreenCursor(1);
if (clearStatusCells(&brl)) {
if (opt_quiet) {
scheduleUpdate("braille driver start");
return 1;
}
{
char banner[0X100];
const char *text = opt_startMessage;
if (*text) {
text = gettext(text);
} else {
makeProgramBanner(banner, sizeof(banner), 0);
text = banner;
}
if (message(NULL, text, MSG_SILENT)) return 1;
}
}
deactivateBrailleDriver();
}
return 0;
}
static void
stopBrailleDriver (void) {
deactivateBrailleDriver();
alert(ALERT_BRAILLE_OFF);
}
static int
prepareBrailleDriverActivity (void *data) {
initializeBrailleDisplay();
ensureBrailleBuffer(&brl, LOG_DEBUG);
return 1;
}
static int
startBrailleDriverActivity (void *data) {
return startBrailleDriver();
}
static void
stopBrailleDriverActivity (void *data) {
stopBrailleDriver();
}
static const ActivityMethods brailleDriverActivityMethods = {
.activityName = "braille-driver",
.retryInterval = BRAILLE_DRIVER_START_RETRY_INTERVAL,
.prepare = prepareBrailleDriverActivity,
.start = startBrailleDriverActivity,
.stop = stopBrailleDriverActivity
};
static ActivityObject *brailleDriverActivity = NULL;
static void
writeBrailleMessage (const char *text) {
clearStatusCells(&brl);
message(NULL, text, (MSG_NODELAY | MSG_SILENT | MSG_SYNC));
brl.noDisplay = 1;
}
static void
exitBrailleDriver (void *data) {
if (brailleDriverConstructed) {
const char *text = opt_stopMessage;
if (*text) {
text = gettext(text);
} else {
text = gettext("BRLTTY stopped");
}
writeBrailleMessage(text);
}
if (brailleDriverActivity) {
destroyActivity(brailleDriverActivity);
brailleDriverActivity = NULL;
}
forgetDevices();
}
static ActivityObject *
getBrailleDriverActivity (int allocate) {
if (!brailleDriverActivity) {
if (allocate) {
if (!(brailleDriverActivity = newActivity(&brailleDriverActivityMethods, NULL))) {
return NULL;
}
onProgramExit("braille-driver", exitBrailleDriver, NULL);
}
}
return brailleDriverActivity;
}
static int canEnableBrailleDriver = 1;
void
enableBrailleDriver (void) {
if (canEnableBrailleDriver) {
ActivityObject *activity = getBrailleDriverActivity(1);
if (activity) startActivity(activity);
}
}
void
disableBrailleDriver (const char *reason) {
ActivityObject *activity = getBrailleDriverActivity(0);
if (activity) {
if (reason) writeBrailleMessage(reason);
stopActivity(activity);
}
}
void
setBrailleOn (void) {
if (!canEnableBrailleDriver) {
canEnableBrailleDriver = 1;
enableBrailleDriver();
}
}
void
setBrailleOff (const char *message) {
canEnableBrailleDriver = 0;
disableBrailleDriver(message);
}
void
restartBrailleDriver (void) {
disableBrailleDriver(gettext("braille driver restarting"));
awaitActivityStopped(brailleDriverActivity);
brl.hasFailed = 0;
logMessage(LOG_INFO, gettext("reinitializing braille driver"));
enableBrailleDriver();
}
static void
exitBrailleData (void *data) {
if (brailleDrivers) {
deallocateStrings(brailleDrivers);
brailleDrivers = NULL;
}
if (brailleParameters) {
free(brailleParameters);
brailleParameters = NULL;
}
if (brailleDevices) {
deallocateStrings(brailleDevices);
brailleDevices = NULL;
}
}
int
changeBrailleDriver (const char *driver) {
return changeListSetting(&brailleDrivers, &opt_brailleDriver, driver);
}
int
changeBrailleParameters (const char *parameters) {
if (!parameters) parameters = "";
return changeStringSetting(&brailleParameters, parameters);
}
int
changeBrailleDevice (const char *device) {
return changeListSetting(&brailleDevices, &opt_brailleDevice, device);
}
#ifdef ENABLE_SPEECH_SUPPORT
static AsyncHandle autospeakDelayAlarm = NULL;
static void
cancelAutospeakDelayAlarm (void) {
if (autospeakDelayAlarm) {
asyncCancelRequest(autospeakDelayAlarm);
autospeakDelayAlarm = NULL;
}
}
static void
endAutospeakDelay (SpeechSynthesizer *spk) {
cancelAutospeakDelayAlarm();
if (!spk->canAutospeak) {
spk->canAutospeak = 1;
scheduleUpdate("banner spoken");
}
}
ASYNC_ALARM_CALLBACK(handleAutospeakDelayAlarm) {
asyncDiscardHandle(autospeakDelayAlarm);
autospeakDelayAlarm = NULL;
endAutospeakDelay(&spk);
}
static void
beginAutospeakDelay (int duration) {
if (asyncNewRelativeAlarm(&autospeakDelayAlarm, duration,
handleAutospeakDelayAlarm, NULL)) {
spk.canAutospeak = 0;
}
}
static void
setSpeechFinished (SpeechSynthesizer *spk) {
spk->track.isActive = 0;
spk->track.speechLocation = SPK_LOC_NONE;
endAutospeakDelay(spk);
}
static void
setSpeechLocation (SpeechSynthesizer *spk, int location) {
if (spk->track.isActive) {
if (scr.number == spk->track.screenNumber) {
if (location != spk->track.speechLocation) {
spk->track.speechLocation = location;
if (ses->trackScreenCursor) trackSpeech();
}
return;
}
setSpeechFinished(spk);
}
}
static void
initializeSpeechSynthesizer (void) {
constructSpeechSynthesizer(&spk);
spk.setFinished = setSpeechFinished;
spk.setLocation = setSpeechLocation;
}
int
constructSpeechDriver (void) {
initializeSpeechSynthesizer();
if (startSpeechDriverThread(&spk, speechDriverParameters)) {
return 1;
} else {
logMessage(LOG_DEBUG, "speech driver initialization failed: %s",
speech->definition.code);
}
return 0;
}
void
destructSpeechDriver (void) {
stopSpeechDriverThread(&spk);
destructSpeechSynthesizer(&spk);
}
static int
initializeSpeechDriver (const char *code, int verify) {
if ((speech = loadSpeechDriver(code, &speechObject, opt_driversDirectory))) {
speechDriverParameters = getParameters(speech->parameters,
speech->definition.code,
speechParameters);
if (speechDriverParameters) {
int constructed = verify;
if (!constructed) {
logMessage(LOG_DEBUG, "initializing speech driver: %s",
speech->definition.code);
if (constructSpeechDriver()) {
constructed = 1;
speechDriver = speech;
}
}
if (constructed) {
identifySpeechDriver(speech, 0);
logParameters(
speech->parameters,
speechDriverParameters,
"Speech Parameter"
);
return 1;
}
deallocateStrings(speechDriverParameters);
speechDriverParameters = NULL;
}
unloadDriverObject(&speechObject);
} else {
logMessage(LOG_ERR, "%s: %s", gettext("speech driver not loadable"), code);
}
speech = &noSpeech;
return 0;
}
static int
activateSpeechDriver (int verify) {
static const char *const autodetectableDrivers[] = {
NULL
};
const DriverActivationData data = {
.driverType = "speech",
.requestedDrivers = (const char *const *)speechDrivers,
.autodetectableDrivers = autodetectableDrivers,
.getDefaultDriver = getDefaultSpeechDriver,
.haveDriver = haveSpeechDriver,
.initializeDriver = initializeSpeechDriver
};
return activateDriver(&data, verify);
}
static void
deactivateSpeechDriver (void) {
if (speechDriver) {
destructSpeechDriver();
speech = &noSpeech;
speechDriver = NULL;
}
unloadDriverObject(&speechObject);
if (speechDriverParameters) {
deallocateStrings(speechDriverParameters);
speechDriverParameters = NULL;
}
}
static int
startSpeechDriver (void) {
if (!activateSpeechDriver(0)) return 0;
applySpeechPreferences();
if (!opt_quiet && spk.sayBanner) {
char banner[0X100];
const char *text = opt_startMessage;
if (*text) {
text = gettext(text);
} else {
makeProgramBanner(banner, sizeof(banner), 0);
text = banner;
}
sayString(&spk, text, SAY_OPT_MUTE_FIRST);
beginAutospeakDelay(SPEECH_DRIVER_START_AUTOSPEAK_DELAY);
} else if (isAutospeakActive()) {
autospeak(AUTOSPEAK_FORCE);
}
return 1;
}
static void
stopSpeechDriver (void) {
cancelAutospeakDelayAlarm();
muteSpeech(&spk, "driver stop");
deactivateSpeechDriver();
}
static int
prepareSpeechDriverActivity (void *data) {
initializeSpeechSynthesizer();
return 1;
}
static int
startSpeechDriverActivity (void *data) {
return startSpeechDriver();
}
static void
stopSpeechDriverActivity (void *data) {
stopSpeechDriver();
}
static const ActivityMethods speechDriverActivityMethods = {
.activityName = "speech-driver",
.retryInterval = SPEECH_DRIVER_START_RETRY_INTERVAL,
.prepare = prepareSpeechDriverActivity,
.start = startSpeechDriverActivity,
.stop = stopSpeechDriverActivity
};
static ActivityObject *speechDriverActivity = NULL;
static void
exitSpeechDriver (void *data) {
if (speechDriverActivity) {
destroyActivity(speechDriverActivity);
speechDriverActivity = NULL;
}
}
static ActivityObject *
getSpeechDriverActivity (int allocate) {
if (!speechDriverActivity) {
if (allocate) {
if (!(speechDriverActivity = newActivity(&speechDriverActivityMethods, NULL))) {
return NULL;
}
onProgramExit("speech-driver", exitSpeechDriver, NULL);
}
}
return speechDriverActivity;
}
void
enableSpeechDriver (int sayBanner) {
ActivityObject *activity = getSpeechDriverActivity(1);
spk.sayBanner = sayBanner;
if (activity) startActivity(activity);
}
void
disableSpeechDriver (const char *reason) {
ActivityObject *activity = getSpeechDriverActivity(0);
if (activity) {
if (reason) {
sayString(&spk, reason, SAY_OPT_MUTE_FIRST);
drainSpeech(&spk);
}
stopActivity(activity);
}
}
void
restartSpeechDriver (void) {
disableSpeechDriver(gettext("speech driver restarting"));
awaitActivityStopped(speechDriverActivity);
logMessage(LOG_INFO, gettext("reinitializing speech driver"));
enableSpeechDriver(1);
}
static void
exitSpeechData (void *data) {
if (speechDrivers) {
deallocateStrings(speechDrivers);
speechDrivers = NULL;
}
if (speechParameters) {
free(speechParameters);
speechParameters = NULL;
}
}
static void
exitSpeechInput (void *data) {
if (speechInputObject) {
destroySpeechInputObject(speechInputObject);
speechInputObject = NULL;
}
}
int
changeSpeechDriver (const char *driver) {
return changeListSetting(&speechDrivers, &opt_speechDriver, driver);
}
int
changeSpeechParameters (const char *parameters) {
if (!parameters) parameters = "";
return changeStringSetting(&speechParameters, parameters);
}
#endif /* ENABLE_SPEECH_SUPPORT */
static int
initializeScreenDriver (const char *code, int verify) {
if ((screen = loadScreenDriver(code, &screenObject, opt_driversDirectory))) {
screenDriverParameters = getParameters(
getScreenParameters(screen),
screen->definition.code,
screenParameters
);
if (screenDriverParameters) {
int constructed = verify;
if (!constructed) {
logMessage(LOG_DEBUG,
"initializing screen driver: %s",
screen->definition.code
);
if (constructScreenDriver(screenDriverParameters)) {
constructed = 1;
screenDriver = screen;
}
}
if (constructed) {
identifyScreenDriver(screen, 0);
logParameters(
getScreenParameters(screen),
screenDriverParameters,
"Screen Parameter"
);
return 1;
}
deallocateStrings(screenDriverParameters);
screenDriverParameters = NULL;
}
unloadDriverObject(&screenObject);
} else {
logMessage(LOG_ERR, "%s: %s", gettext("screen driver not loadable"), code);
}
setNoScreen();
return 0;
}
static int
activateScreenDriver (int verify) {
static const char *const autodetectableDrivers[] = {
NULL
};
const DriverActivationData data = {
.driverType = "screen",
.requestedDrivers = (const char *const *)screenDrivers,
.autodetectableDrivers = autodetectableDrivers,
.getDefaultDriver = getDefaultScreenDriver,
.haveDriver = haveScreenDriver,
.initializeDriver = initializeScreenDriver
};
return activateDriver(&data, verify);
}
static void
deactivateScreenDriver (void) {
if (screenDriver) {
destructScreenDriver();
setNoScreen();
screenDriver = NULL;
}
unloadDriverObject(&screenObject);
if (screenDriverParameters) {
deallocateStrings(screenDriverParameters);
screenDriverParameters = NULL;
}
}
static int
startScreenDriver (void) {
if (!activateScreenDriver(0)) return 0;
if (isMainScreen()) scheduleUpdate("main screen started");
return 1;
}
static void
stopScreenDriver (void) {
deactivateScreenDriver();
}
static int
prepareScreenDriverActivity (void *data) {
return 1;
}
static int
startScreenDriverActivity (void *data) {
return startScreenDriver();
}
static void
stopScreenDriverActivity (void *data) {
stopScreenDriver();
}
static const ActivityMethods screenDriverActivityMethods = {
.activityName = "screen-driver",
.retryInterval = SCREEN_DRIVER_START_RETRY_INTERVAL,
.prepare = prepareScreenDriverActivity,
.start = startScreenDriverActivity,
.stop = stopScreenDriverActivity
};
static ActivityObject *screenDriverActivity = NULL;
static void
exitScreenDriver (void *data) {
if (screenDriverActivity) {
destroyActivity(screenDriverActivity);
screenDriverActivity = NULL;
}
}
static ActivityObject *
getScreenDriverActivity (int allocate) {
if (!screenDriverActivity) {
if (allocate) {
if (!(screenDriverActivity = newActivity(&screenDriverActivityMethods, NULL))) {
return NULL;
}
onProgramExit("screen-driver", exitScreenDriver, NULL);
}
}
return screenDriverActivity;
}
void
enableScreenDriver (void) {
ActivityObject *activity = getScreenDriverActivity(1);
setNoScreenDriverReason(NULL);
if (activity) startActivity(activity);
}
void
disableScreenDriver (const char *reason) {
ActivityObject *activity = getScreenDriverActivity(0);
setNoScreenDriverReason(reason);
if (activity) stopActivity(activity);
}
void
restartScreenDriver (void) {
disableScreenDriver(gettext("screen driver restarting"));
awaitActivityStopped(screenDriverActivity);
logMessage(LOG_INFO, gettext("reinitializing screen driver"));
enableScreenDriver();
}
static void
exitScreenData (void *data) {
endSpecialScreens();
if (screenDrivers) {
deallocateStrings(screenDrivers);
screenDrivers = NULL;
}
if (screenParameters) {
free(screenParameters);
screenParameters = NULL;
}
}
int
changeScreenDriver (const char *driver) {
return changeListSetting(&screenDrivers, &opt_screenDriver, driver);
}
int
changeScreenParameters (const char *parameters) {
if (!parameters) parameters = "";
return changeStringSetting(&screenParameters, parameters);
}
int
changeMessageLocale (const char *locale) {
int changed = !!setlocale(LC_ALL, locale);
if (changed) {
api.updateParameter(BRLAPI_PARAM_MESSAGE_LOCALE, 0);
} else {
logMessage(LOG_WARNING, "message locale change failed: %s", locale);
}
return changed;
}
static void
exitPidFile (void *data) {
#if defined(GRUB_RUNTIME)
#else /* remove pid file */
unlink(opt_pidFile);
#endif /* remove pid file */
}
static int
makePidFile (ProcessIdentifier pid) {
return createPidFile(opt_pidFile, pid);
}
static int tryPidFile (void);
ASYNC_ALARM_CALLBACK(retryPidFile) {
tryPidFile();
}
static int
tryPidFile (void) {
if (makePidFile(0)) {
onProgramExit("pid-file", exitPidFile, NULL);
} else if (errno == EEXIST) {
return 0;
} else {
asyncNewRelativeAlarm(NULL, PID_FILE_CREATE_RETRY_INTERVAL, retryPidFile, NULL);
}
return 1;
}
#if defined(__MINGW32__)
static void
background (void) {
char *variableName;
{
const char *strings[] = {programName, "_DAEMON"};
variableName = joinStrings(strings, ARRAY_COUNT(strings));
}
{
int i;
for (i=0; variableName[i]; i+=1) {
char c = variableName[i];
if (c == '_') continue;
if (isdigit((unsigned char)c) && (i > 0)) continue;
if (isalpha((unsigned char)c)) {
if (islower((unsigned char)c)) variableName[i] = toupper((unsigned char)c);
continue;
}
variableName[i] = '_';
}
}
if (!getenv(variableName)) {
LPTSTR commandLine = GetCommandLine();
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
if (!SetEnvironmentVariable(variableName, "BACKGROUND")) {
logWindowsSystemError("SetEnvironmentVariable");
exit(PROG_EXIT_FATAL);
}
if (!CreateProcess(NULL, commandLine, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED,
NULL, NULL, &startupInfo, &processInfo)) {
logWindowsSystemError("CreateProcess");
exit(PROG_EXIT_FATAL);
}
{
int created = makePidFile(processInfo.dwProcessId);
int resumed = ResumeThread(processInfo.hThread) != -1;
if (!created) {
if (errno == EEXIST) {
ExitProcess(PROG_EXIT_FATAL);
}
}
if (!resumed) {
logWindowsSystemError("ResumeThread");
ExitProcess(PROG_EXIT_FATAL);
}
}
ExitProcess(PROG_EXIT_SUCCESS);
}
free(variableName);
}
#elif defined(__MSDOS__)
static void
background (void) {
msdosBackground();
}
#elif defined(GRUB_RUNTIME)
static void
background (void) {
}
#else /* Unix */
static void
background (void) {
int fds[2];
if (pipe(fds) == -1) {
logSystemError("pipe");
exit(PROG_EXIT_FATAL);
}
fflush(stdout);
fflush(stderr);
{
pid_t child = fork();
if (child == -1) {
logSystemError("fork");
exit(PROG_EXIT_FATAL);
}
if (child) {
ProgramExitStatus exitStatus = PROG_EXIT_SUCCESS;
if (close(fds[0]) == -1) logSystemError("close");
if (!makePidFile(child)) {
if (errno == EEXIST) {
exitStatus = PROG_EXIT_SEMANTIC;
}
}
if (close(fds[1]) == -1) logSystemError("close");
_exit(exitStatus);
}
}
if (close(fds[1]) == -1) logSystemError("close");
{
unsigned char buffer[1];
if (read(fds[0], buffer, sizeof(buffer)) == -1) logSystemError("read");
if (close(fds[0]) == -1) logSystemError("close");
}
if (setsid() == -1) {
logSystemError("setsid");
exit(PROG_EXIT_FATAL);
}
}
#endif /* background() */
static int
validateInterval (int *value, const char *string) {
if (!*string) return 1;
{
static const int minimum = 1;
int ok = validateInteger(value, string, &minimum, NULL);
if (ok) *value *= 10;
return ok;
}
}
ProgramExitStatus
brlttyStart (void) {
if (opt_cancelExecution) {
ProgramExitStatus exitStatus;
if (!*opt_pidFile) {
exitStatus = PROG_EXIT_SEMANTIC;
logMessage(LOG_ERR, "%s", gettext("pid file not specified"));
} else if (cancelProgram(opt_pidFile)) {
exitStatus = PROG_EXIT_FORCE;
} else {
exitStatus = PROG_EXIT_FATAL;
}
return exitStatus;
}
{
int stop = 0;
if (opt_removeService) {
removeService(SERVICE_NAME);
stop = 1;
}
if (opt_installService) {
installService(SERVICE_NAME, SERVICE_DESCRIPTION, opt_configurationFile);
stop = 1;
}
if (stop) return PROG_EXIT_FORCE;
}
if (!validateInterval(&messageHoldTimeout, opt_messageTime)) {
logMessage(LOG_ERR, "%s: %s", gettext("invalid message hold timeout"), opt_messageTime);
}
if (opt_version) {
logMessage(LOG_INFO, "Copyright %s", PACKAGE_COPYRIGHT);
identifyScreenDrivers(1);
#ifdef ENABLE_API
api.logServerIdentity(1);
#endif /* ENABLE_API */
identifyBrailleDrivers(1);
#ifdef ENABLE_SPEECH_SUPPORT
identifySpeechDrivers(1);
#endif /* ENABLE_SPEECH_SUPPORT */
return PROG_EXIT_FORCE;
}
if (opt_verify) opt_noDaemon = 1;
if (!opt_noDaemon
#ifdef __MINGW32__
&& !isWindowsService
#endif
) {
background();
}
if (*opt_pidFile) {
if (!tryPidFile()) {
return PROG_EXIT_SEMANTIC;
}
}
if (!opt_noDaemon) {
stderrLogLevel = 0;
detachStandardInput();
detachStandardOutput();
if (!opt_standardError) detachStandardError();
#ifdef __MINGW32__
{
HANDLE h = CreateFile("NUL", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (!h) {
logWindowsSystemError("CreateFile[NUL]");
} else {
SetStdHandle(STD_INPUT_HANDLE, h);
SetStdHandle(STD_OUTPUT_HANDLE, h);
if (!opt_standardError) {
SetStdHandle(STD_ERROR_HANDLE, h);
}
}
}
#endif /* __MINGW32__ */
}
/*
* From this point, all IO functions as printf, puts, perror, etc. can't be
* used anymore since we are a daemon. The logMessage() facility should
* be used instead.
*/
changeScreenDriver(opt_screenDriver);
changeScreenParameters(opt_screenParameters);
beginSpecialScreens();
onProgramExit("screen-data", exitScreenData, NULL);
suppressTuneDeviceOpenErrors();
{
char *directory;
if ((directory = getWorkingDirectory())) {
logProperty(directory, "workingDirectory", "Working Directory");
free(directory);
} else {
logMessage(LOG_WARNING, "%s: %s", gettext("cannot determine working directory"), strerror(errno));
}
}
logProperty(opt_configurationFile, "configurationFile", "Configuration File");
logProperty(opt_tablesDirectory, "tablesDirectory", "Tables Directory");
logProperty(opt_driversDirectory, "driversDirectory", "Drivers Directory");
logProperty(opt_writableDirectory, "writableDirectory", "Writable Directory");
logProperty(opt_updatableDirectory, "updatableDirectory", "Updatable Directory");
logProperty(opt_preferencesFile, "preferencesFile", "Preferences File");
resetPreferences();
loadPreferences(0);
if (opt_promptPatterns && *opt_promptPatterns) {
int count;
char **patterns = splitString(opt_promptPatterns, PARAMETER_SEPARATOR_CHARACTER, &count);
if (patterns) {
for (int index=0; index<count; index+=1) {
if (!addPromptPattern(patterns[index])) break;
}
deallocateStrings(patterns);
}
}
setTextAndContractionTables();
setAttributesTable();
setKeyboardTable();
/* initialize screen driver */
if (opt_verify) {
if (activateScreenDriver(1)) deactivateScreenDriver();
} else {
setNoScreen();
enableScreenDriver();
}
/* The device(s) the braille display might be connected to. */
if (!*opt_brailleDevice) {
logMessage(LOG_ERR, gettext("braille device not specified"));
return PROG_EXIT_SYNTAX;
}
constructBrailleDisplay(&brl);
changeBrailleDriver(opt_brailleDriver);
changeBrailleParameters(opt_brailleParameters);
changeBrailleDevice(opt_brailleDevice);
brailleDriverConstructed = 0;
onProgramExit("braille-data", exitBrailleData, NULL);
if (opt_verify) {
if (activateBrailleDriver(1)) deactivateBrailleDriver();
} else {
enableBrailleDriver();
}
#ifdef ENABLE_SPEECH_SUPPORT
constructSpeechSynthesizer(&spk);
changeSpeechDriver(opt_speechDriver);
changeSpeechParameters(opt_speechParameters);
onProgramExit("speech-data", exitSpeechData, NULL);
if (opt_verify) {
if (activateSpeechDriver(1)) deactivateSpeechDriver();
} else {
enableSpeechDriver(1);
}
/* Create the file system object for speech input. */
logProperty(opt_speechInput, "speechInput", "Speech Input");
if (!opt_verify) {
if (*opt_speechInput) {
speechInputObject = newSpeechInputObject(opt_speechInput);
onProgramExit("speech-input", exitSpeechInput, NULL);
}
}
#endif /* ENABLE_SPEECH_SUPPORT */
startApiServer();
if (!opt_verify) notifyServiceReady();
return opt_verify? PROG_EXIT_FORCE: PROG_EXIT_SUCCESS;
}
static char *configuredLocale = "";
static int
changeLocale (const char *locale) {
if (changeMessageLocale(locale)) return 1;
setlocale(LC_ALL, configuredLocale);
return 0;
}
static const ProfileProperty languageProfileProperties[] = {
{ .name = WS_C("locale"),
.defaultValue = &configuredLocale,
.change = changeLocale
},
#ifdef ENABLE_SPEECH_SUPPORT
{ .name = WS_C("speech-driver"),
.defaultValue = &opt_speechDriver,
.change = changeSpeechDriver
},
{ .name = WS_C("speech-parameters"),
.defaultValue = &opt_speechParameters,
.change = changeSpeechParameters
},
#endif /* ENABLE_SPEECH_SUPPORT */
{ .name = WS_C("text-table"),
.defaultValue = &opt_textTable,
.change = changeTextTable
},
{ .name = WS_C("contraction-table"),
.defaultValue = &opt_contractionTable,
.change = changeContractionTable
},
};
static int
beginLanguageProfile (void) {
#ifdef ENABLE_SPEECH_SUPPORT
disableSpeechDriver(NULL);
awaitActivityStopped(speechDriverActivity);
#endif /* ENABLE_SPEECH_SUPPORT */
return 1;
}
static int
endLanguageProfile (void) {
#ifdef ENABLE_SPEECH_SUPPORT
enableSpeechDriver(0);
#endif /* ENABLE_SPEECH_SUPPORT */
if (brl.keyTable) {
char *path = makeBrailleKeyTablePath();
if (path) {
disableBrailleHelpPage();
makeBrailleHelpPage(path);
free(path);
}
}
if (keyboardTable) {
disableKeyboardHelpPage();
makeKeyboardHelpPage();
}
return 1;
}
const ProfileDescriptor languageProfile = {
.category = strtext("Language"),
.extension = LANGUAGE_PROFILE_EXTENSION,
.begin = beginLanguageProfile,
.end = endLanguageProfile,
.properties = {
.array = languageProfileProperties,
.count = ARRAY_COUNT(languageProfileProperties)
}
};