Зоопарк средств кастомизации или "Hello, world!" с точки зрения различных программ экранного доступа

Дата публикации:2011
Twitter Facebook Vkontakte

Программа экранного доступа (screenreader) - это ключевой инструмент для пользователя с нарушениями зрения. Чем функциональнее и гибче screenreader, тем более высокий уровень доступа он обеспечивает. В виду этого, большое значение имеет наличие в нём не только гибких настроек, но и функционала программируемой кастомизации, то есть возможности быстрой модификации продукта под нужды пользователя и, нередко, силами самих же пользователей.

Традиционно в среде рядовых пользователей данный функционал принято называть поддержкой скриптов. Как правило, именно этим термином обозначают возможность написания для screenreader'а программных дополнений. Тем не менее, с технической точки зрения это не всегда верно. Поэтому корректнее применять более общий термин - программная кастомизация. (Предвидя негодование поборников чистоты русского языка, предлагаем им придумать "чисто" русский вариант этого понятия.)

По традиции изучение любого компьютерного языка начинается с создания элементарной программы, выводящей текст "Hello, world!". В данной статье мы рассмотрим реализацию подобного функционала для нескольких программ экранного доступа, чтобы наглядно показать существующие в них различия средств кастомизации в рамках решения одной и той же задачи.

COBRA и VIRGO

Разработчик BAUM Retec AG
Платформа Windows OS
Используемый язык Visual Basic Scripting Edition

COBRA и VIRGO являются отдельными программами экранного доступа. Однако вследствие того, что COBRA разрабатывалась в том числе и на базе VIRGO, они имеют много общего. Это особенно заметно в реализации скриптового функционала, так как здесь программист будет иметь дело с аналогичными объектами и функциями, а также инструментами разработчика. Безусловно, существуют и различия, но в контексте поставленной задачи они не принципиальны.

В качестве среды разработки здесь используется язык Visual Basic Scripting Edition, входящий в состав встроенного системного компонента Windows Script Host, который расширен рядом функций и объектов программы экранного доступа для предоставления определённых возможностей screenreading'а. VBScript - это достаточно простой в синтаксическом отношении язык с динамической типизацией данных, который довольно лёгок в освоении.

В наиболее простом виде скрипт для COBRA и VIRGO, произносящий активным синтезатором речи заданный текст, выглядит следующим образом (комментарии начинаются с символа одинарной кавычки (')):

' Создаём функцию, доступную из других процедур, 
' фиксирующую нажатия клавиш.
Public Function OnCmd (CMD)
' Условный оператор: если набрана команда N061, то
	If CMD = "L6N1" Then
' Произносим заданный текст активным синтезатором
		Proc.SpeechOut "Hello, world!"
' Закрытие условного оператора
	End If
' Завершение функции
End Function

Усложним данный скрипт:

' Создаём функцию, доступную из других процедур,
' фиксирующую нажатия клавиш.
Public Function OnCmd (CMD)
' Присваиваем переменной "Hello" строковое значение
	Hello = "Hello, world!"

' Условный оператор: если набрана команда N061, то
	If CMD = "L6N1" Then
' Очищаем переменную "CMD"
		CMD = ""
' Произносим переменную "Hello" активным синтезатором
		Proc.SpeechOut Hello
' Отображаем переменную "Hello" на брайлевском дисплее
		Proc.BrailleOut Hello
' Закрытие условного оператора
	End If
' Завершение функции
End Function

Очистка "CMD", при грамотном программировании скрипта в принципе не обязательна, но она может лишний раз застраховать от незапланированной передачи команды в прочие процедуры.

JAWS for Windows

Разработчик Freedom Scientific
Платформа Windows OS
Используемый язык JAWS script

В программе экранного доступа JAWS используется специально разработанный скриптовый язык, который на первый взгляд напоминает нечто среднее между BASIC и Pascal, хотя любое сравнение подобного рода, разумеется, условно.

JAWS script имеет строгую типизацию данных и компилируется в байт-код специальным компонентом, входящим в пакет поставки screenreader'а. В целом он хорошо адаптирован для задач, стоящих перед программой экранного доступа, предоставляя ряд специфических возможностей, например, встроенные функции для получения данных о цвете пикселей под курсором или активной раскладке клавиатуры. Однако с другой стороны, в нём отсутствуют многие привычные для программирования возможности, в частности язык обладает достаточно малым диапазоном поддерживаемых типов данных (например, не доступна работа напрямую с вещественными числами или до недавнего времени с массивами). Также в этом языке долгое время отсутствовал цикл с автоизменением счетчика цикла.

Тем не менее, возвращаясь к основной теме статьи, рассмотрим решение заявленной задачи на языке JAWS script (комментарии начинаются с символа точки с запятой (;)):

; Создаём скрипт "Hello"
Script Hello ()
; Произносим активным синтезатором заданную строку текста
SayString ("Hello, world!")
; Завершаем скрипт
EndScript

Решим абсолютно ту же задачу более сложным путём:

; Пишем ключевое слово для обозначения блока глобальных переменных
Globals
; Объявляем глобальную переменную "Phrase" типа string
string Phrase
; Создаём пользовательскую функцию "HelloWorld"
Function HelloWorld ()
; Присваиваем переменной "Phrase" строковое значение
let Phrase = "Hello, world!"
; Произносим активным синтезатором значение переменной "Phrase"
SayString (Phrase)
; Завершаем функцию
EndFunction

; Создаём скрипт "Hello"
Script Hello ()
; Вызываем пользовательскую функцию "HelloWorld"
HelloWorld ()
; Завершаем скрипт
EndScript

Как первый, так и второй код активизируется вызовом скрипта "Hello". Назначение клавиатурных команд на те или иные скрипты находится в зоне компетенции не JAWS script как такового, а специального Диспетчера клавиатуры (Keyboard Manager). Это можно сделать как через GUI, так и посредством ручного редактирования отдельного JKM-файла, чей синтаксис аналогичен стандартным INI-файлам. Например:

; Секция команд для раскладки laptop (ноутбук)
[Laptop Keys]
; Назначение вызова скрипта "Hello" на команду Insert+Shift+H
Insert+Shift+H=Hello

Mobile Speak

Разработчик Code Factory
Платформа Windows Mobile OS
(2003 SE, 5.x, 6.x)
Используемый язык Lua

В программе экранного доступа Mobile Speak используется довольно популярный скриптовый язык Lua с динамической типизацией данных. Являясь мощным инструментом, он, в то же время, достаточно прост по своему синтаксису и, по заявлениям разработчиков, призван не плодить сущности сверх необходимого.

Lua - это одно из самых популярных решений для внедрения в программные продукты простого и гибкого функционала скриптинга. В частности, он очень распространён в различных играх для программирования карт и персонажей.

Возвращаясь же к его использованию в программе Mobile Speak, рассмотрим Lua-скрипт, произносящий заданный текст (комментарии начинаются последовательностью из двух символов минус (--)):

-- Произносим заданный текст активным синтезатором
TTS.Speak ("Hello, world!")

Усложним скрипт, привязав вызов этой функции к какому-либо событию:

-- Подключаем файл "msp_win.lua",
--  включающий все специальные функции Mobile Speak
require "msp_win.lua"

-- Создаём функцию, отрабатывающую каждый раз,
--  когда запускается приложение.
function OnAppStart ()
-- Присваиваем локальной переменной "Hello" строковое значение
local Hello = "Hello, world!"
-- Произносим переменную "Hello" активным синтезатором
TTS.Speak ("Hello)
-- Завершаем функцию
end

NVDA

Разработчик NV Access
Платформа Windows OS
Используемый язык Python

В программе экранного доступа NVDA используется популярный объектно-ориентированный язык Python с динамической типизацией данных, однако с возможностью явного указания типа переменных.

Следует отметить, что Python является не столько средством написания пользовательских программных расширений NVDA, которые здесь называются "плагинами" (plugins), сколько одним из основных инструментов разработки самой программы экранного доступа. Большая часть screenreader'а написана как раз на Python, а меньшая часть, отвечающая за взаимодействие с внешними процессами, на C++ для большей производительности.

В строгом смысле, Python-плагины, которые пишет пользователь, являются прямым продолжением программы, а не внешне подключаемым интерпретируемым дополнением. Поэтому не совсем корректно считать их скриптами в классическом понимании этого термина, так как уровень изоляции плагинов от самой NVDA крайне мал. В сущности, используя плагин, пользователь встраивает определённый функционал прямо в основной код программы.

Всё это предоставляет широчайшие возможности кастомизации NVDA, но требует более низкоуровневого программирования, если сравнивать со скриптуемыми программами экранного доступа, хотя бы уже рассмотренными ранее COBRA или JAWS.

Убедиться в этом можно на следующем примере (комментарии начинаются символом решётка (#)):

# Импортируем основной модуль глобальных плагинов
import globalPluginHandler
# Импортируем модуль пользовательского интерфейса
import ui
# Определяем класс "GlobalPlugin", 
# наследуемый от "globalPluginHandler.GlobalPlugin"
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
# Инициализируем скрипт NVDA 
# (метод от класса ScriptableObject с 
# несколькими устойчивыми требованиями)
def script_helloWorld(self, gesture):
# Выводим текст средствами интерфейса NVDA
ui.message("Hello, world!")
# Прикрепляем скрипт "helloWorld" к клавиатурной команде NVDA+Shift+H
__gestures={
"kb:NVDA+shift+h": "helloWorld",
}

Следует отметить, что по умолчанию каждый модуль Python представляет собой текстовый файл в кодировке, совместимой с семибитной кодировкой ASCII. Для кодировок, использующих старший бит, необходимо явно указывать их название. Это особо актуально для русскоязычных пользователей, так как кодировки с поддержкой кириллицы как раз выходят за допустимый диапазон. Причём это следует делать не только тогда, когда кириллица использована, например, в строковых литералах, но и даже просто в комментариях.

Таким образом, в первой или второй строках файла, содержащего в том числе и выше приведённый код, следует явно указывать кодировку. Например, так:

# Yavno propisivaem kodirovku fayla dlya ustraneniya oshibki v svyazi s ispolzovaniem nelatinskih bukv
# -*- coding: windows-1251 -*-

ui.message("Привет, мир!")

Orca

Разработчик Сообщество
Платформа Linux OS
Используемый язык Python

Для кастомизации программы экранного доступа Orca также используется язык Python. В целом в отношении особенностей программных расширений данного screenreader'а справедливо всё то, что ранее было сказано о плагинах NVDA. Также в силе остается замечание по поводу наличия символов кириллицы в исходных кодах и указания кодировки символов в первой или второй строки файла.

Произнесение заданного текста в рамках кастомизации этой программы выглядит следующим образом:

# Импортируем используемые имена из
# основных модулей Orca...
# Брайлевский вывод
import orca.braille
# События ввода
import orca.input_event
# Сочетания клавиш
import orca.keybindings
# Основной модуль
import orca.orca
# Речевой вывод
import orca.speech
# Средства поддержки интернационализации
from orca.orca_i18n import _

# Начало пользовательской функции,
# которая будет реагировать на нажатие комбинации клавиш.
def sayHello(script, inputEvent=None):
# Инициализируем локальную переменную.
message = _("Hello, world!")
# Озвучиваем сообщение средствами Orca
orca.speech.speak(message)
# Отображаем на брайлевском дисплее средствами Orca
orca.braille.displayMessage(message)
# Возвращаем true для возможной проверки из других функций
return True
# Завершение пользовательской функции

# В нижеследующих строках создается
# обработчик ввода, связанный с определенной ранее пользовательской функцией,
# и ассоциированный с сочетанием клавиш Orca Key + T (Insert + T).
# Затем это сочетание клавиш помещается в общий набор клавиатурных команд Orca.
sayHelloHandler = orca.input_event.InputEventHandler(
sayHello,
_("Says hello to this fine world."))
myKeyBindings = orca.keybindings.KeyBindings()
myKeyBindings.add(orca.keybindings.KeyBinding(
"t",
1 << orca.settings.MODIFIER_ORCA,
1 << orca.settings.MODIFIER_ORCA,
sayHelloHandler))
orca.settings.keyBindingsMap["default"] = myKeyBindings

Spiel

Разработчик Nolan Darilek и сообщество
Платформа Android OS
Используемый язык JavaScript

В программе экранного доступа Spiel для разработки скриптов используется объектно-ориентированный язык JavaScript с динамической типизацией данных.

К сожалению, на текущий момент, функционал скриптов Spiel ограничивается лишь возможностью программирования озвучивания тех или иных событий. Что обуславливается спецификой операционной системы Android.

Произношение заданного текста реализуется следующим образом (комментарии начинаются удвоенным символом наклонной черты (//)):

// Произносим заданный текст активным синтезатором
speak("Hello, world!")

Чтобы вызвать данный код, требуется привязать его к какому-либо событию:

// Определяем функцию, отрабатывающую при нажатии на кнопку
onViewClicked: function()
// Открываем тело функции
{
// Произносим заданный текст активным синтезатором
speak("Hello, world!");
// Возвращаем true, для возможной проверки из других функций
return true;
// закрываем тело функции
}

SuperNova

Разработчик Dolphin Computer Access
Платформа Windows OS
Используемый язык Lua

В программе экранного доступа SuperNova используется уже упоминавшийся ранее язык программирования Lua. Здесь задача произнесения заданного текста решается следующим образом:

-- Подключаем файл "dolphin.lua", включающий все специальные функции SuperNova
require "dolphin"
-- Создаём функцию, отрабатывающую каждый раз, когда запускается приложение
function EventScriptStartup ()
-- Регистрируем горячую клавишу DolphinKey+H 
-- для вызова пользовательской функции "Hello"
System.RegisterScriptKey (KEY_H, MODIFIER_CUSTOM, "Hello",
"Test function")
-- Завершаем функцию
end

-- Создаём пользовательскую функцию "Hello"
function Hello ()
-- Произносим активным синтезатором речи заданный текст
	Speak.Text ("Hello, world!")
-- Завершаем функцию
end

Thunder

Разработчик Screenreader.net
Платформа Windows OS
Используемый язык Visual Basic Scripting Edition

В программе экранного доступа Thunder используется уже упоминавшийся язык VBScript, входящий в состав WSH. Разумеется, встроенный интерпретатор воспринимает и несколько дополнительных конструкций, обеспечивающих работу со специфическими функциями, необходимыми для screenreading'а.

В самом элементарном виде скрипт, произносящий заданный текст, выглядит следующим образом:

' Создаём подпрограмму "Hello"
Sub Hello()
' Произносим заданный текст специальной VBS-функцией Thunder
Say("Hello, world!")
' Завершаем подпрограмму
End Sub

Однако для того, чтобы закрепить выполнение данной операции за клавиатурной командой Thunder, следует воспользоваться ещё одной дополнительной VBS-функцией "EventHotKey":

' Создаём подпрограмму со специальной функцией контроля горячих клавиш Thunder, доступную всем остальным процедурам
Public Sub EventHotKey(i)
' Открываем условную конструкцию "Select Case" для последовательной проверки выражения
Select case i
' Событие нажатие первой горячей клавиши Alt+1
case 1
' Произносим заданный текст специальной VBS-функцией Thunder
Say("Hello, world!")
' Закрываем условную конструкцию "Select Case"
	End Select
' Завершаем подпрограмму
End Sub

VoiceOver

Разработчик Apple
Платформа Mac OS X
Используемый язык AppleScript

В операционных системах Mac, начиная с версии 8, встроен общесистемный скриптовый язык AppleScript. Он является объектно-ориентированным языком программирования с динамической типизацией данных, хотя и с возможностью явного определения типа переменной. AppleScript автоматически определяет, какие объекты и команды программа распознаёт, основываясь на информации хранимой в каждом скриптуемом приложении. Встроенная утилита экранного доступа VoiceOver также имеет собственную объектную модель, с которой и осуществляется взаимодействие скрипта.

Таким образом, кастомизация VoiceOver осуществляется через общесистемную службу AppleScript.

Поставленную задачу с выводом заданного текста возможно решить двумя основными способами.

Во-первых, можно написать общий скрипт, который будет взаимодействовать непосредственно с системной TTS-службой и работать при любых условиях (комментарии начинаются с удвоенного символа минус (--)):

-- Произносим заданный текст основным голосом системы
say "Hello, world!"

Во-вторых, можно создать скрипт, который будет для вывода использовать непосредственно VoiceOver и работать только тогда, когда данная утилита активна:

-- Обращаемся к приложению VoiceOver
tell application "VoiceOver"
-- Выводим заданный текст посредством интерфейса VoiceOver
output "Hello, world!"
-- Завершаем процедуру обращения к приложению
end tell

Можно объединить эти два скрипта. Пускай сначала будет осуществляться проверка того, является ли утилита экранного доступа активной. Если да, то заданный текст будет выводиться посредством интерфейса VoiceOver, а если нет, то при помощи общей системной TTS-службы.

(* Часть кода, являющаяся функцией для проверки состояния VoiceOver взята из примеров, поставляемых Apple.
Согласно условиям использования "Apple sample code" указываем, что авторские права на выделенную часть скрипта принадлежат Apple Inc. *)
-- Начало функции для проверки состояния VoiceOver. © Apple Inc. 2008
on isVoiceOverRunningWithAppleScript()
	set isRunning to true
	tell application "VoiceOver"
		try
			set x to bounds of vo cursor
		on error
			set isRunning to false
		end try
	end tell
	return isRunning
end isVoiceOverRunningWithAppleScript
-- Конец функции для проверки состояния VoiceOver. © Apple Inc. 2008
(* Выше приведённая функция проверяет, запущена ли утилита VoiceOver.
Результатом её работы является возврат true, если да, и false, если нет. *)
-- Присваиваем переменной "Hello" строковое значение
set Hello to "Hello, world!"
-- Условное ветвление: 
-- если функция "isVoiceOverRunningWithAppleScript" возвращает true, то
if isVoiceOverRunningWithAppleScript() then
-- Обращаемся к приложению VoiceOver
	tell application "VoiceOver"
-- Выводим переменную "Hello" посредством интерфейса VoiceOver
		output Hello
-- Завершаем процедуру обращения к приложению
	end tell
-- Альтернативное действие условного ветвления: в противном случае
else
-- Произносим переменную "Hello" основным голосом системы
	say Hello
-- Завершаем условное ветвление
end if

Window-Eyes

Разработчик GW Micro
Платформа Windows OS
Используемый язык Любой с возможностью взаимодействия с объектами OLE/OM-Automation

Программа экранного доступа Window-Eyes, в отличии от других screenreader'ов, не ограничивает скрипт-программистов каким-либо одним языком.

Она включает в себя специальный COM/Automation-сервер - Window-Eyes app engine, который предоставляет возможность создавать скрипты на абсолютно любом языке программирования, который поддерживает взаимодействие с объектами OLE-Automation и может быть зарегистрирован в системе. Кроме того, Window-Eyes встраивается в ActiveScript engines, что даёт возможность взаимодействовать с её объектной моделью языкам, поддерживающим COM automation.

Таким образом, пользователю предоставляется широчайший выбор языков, которые могут быть использованы для скриптинга под Window-Eyes: от VBScript или JScript, до C++ или даже PHP.

Тем не менее, все стандартные программные расширения Window-Eyes, которые здесь принято называть "app" (модуль), написаны на VBScript. Это обуславливается как уже упоминавшейся общей простотой данного языка, так и его наличием во всех дистрибутивах Windows OS изначально.

Нижеследующий пример также написан на VBS:

' Создаём подпрограмму "Hello"
Sub Hello()
' Произносим заданный текст активным синтезатором
	Speak "Hello, world!"
' Завершаем подпрограмму
End Sub

Тем не менее, для вызова подпрограммы следует присвоить ей какую-либо горячую клавишу:

' Регистрируем клавиатурную команду CTRL+Shift+H 
' для подпрограммы "Hello"
Dim myHotkey : Set myHotkey = Keyboard.RegisterHotkey("Control-Shift-H", "Hello")
' Создаём подпрограмму "Hello"
Sub Hello()
' Произносим заданный текст активным синтезатором
	Speak "Hello, world!"
' Завершаем подпрограмму
End Sub


Распространение материалов сайта означает, что распространитель принимает условия лицензионного соглашения.
Идея и реализация: © Владимир Довыденков и Анатолий Камынин,  2004-2017
Rambler's Top100