Активация действий клавиатуры и мыши с помощью голосовых команд и xdotool

Эмуляция работы с клавиатурой путем голосового ввода команд с помощью библиотеки xdotool

xdotool – это полезная библиотека команд, позволяющая программистам эмулировать нажатия клавиш и работу с мышью. Возможности этого инструмента оказываются исключительно полезны, когда клавиатура или мышь отсутствуют, или в ситуациях, когда пользователь в силу физических ограничений не может использовать стандартные методы ввода. В данной статье рассматриваются две темы: введение в использование xdotool для работы с пользовательским интерфейсом в среде Linux® и использование голосового ввода для активации действий, обычно выполняемых через аппаратный ввод. В заключительном примере демонстрируется применение технологии XML для хранения фрагментов кода, которые xdotool может впоследствии вставлять в автоматически генерируемый код менеджера диалога между пользователем и компьютером.

Колин Бекингем, исследователь и автор, Freelance

Колин Бекингем (Colin Beckingham) ― независимый исследователь, писатель и программист из Канады. Имеет ученые степени Королевского университета в Кингстоне и Университета Виндзор. Работал в самых разных областях, включая банковские услуги, садоводство, конные скачки, преподавание, гражданскую службу, розничную торговлю, а также путешествия и туризм. Автор приложений на основе баз данных и многочисленных статей в газетах, журналах и в Интернете. В круг его научных интересов входят программирование с открытым исходным кодом, VoIP и управляемые голосом приложения для Linux. Адрес для контактов: colbec@start.ca.



07.03.2012

Большинство пользователей принимают клавиатуру и мышь как должное: можно нажать символ на клавиатуре - и он появится на экране; можно ввести строку символов, нажать Enter – и будут выполнены соответствующие действия, на локальном компьютере или в удаленном режиме. Вроде бы все просто. Но предположим, что по какой-либо причине мы не можем использовать клавиатуру или мышь, но по-прежнему необходимо вводить текст в окно или выполнять какие-либо действия в другом окне или на другой системе. Или требуется создать окно, изменить его размер, поместить в это окно Web-браузер и перейти на определенный URL, а затем пройти по ссылкам на Web-странице и открыть одну из них – и все это без использования мыши и клавиатуры, только с помощью собственного голоса и распознавателя речи. Подобный подход требует эмуляции работы с клавиатурой и мышью.

Часто используемые сокращения

  • CDATA: character data – символьные данные;
  • URL: Universal Resource Locator – URL-адрес;
  • XML: Extensible Markup Language – расширяемый язык разметки.

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

xdotool

xdotool (см. ссылки в разделе Ресурсы) – это библиотека функций, которая помогает отправлять команды в оконный интерфейс приложений. Она может отправить строку, которая будет напечатана в удаленном окне, изменить размер окна, отправить нажатия отдельных клавиш или их сочетаний и даже выполнить "двойной щелчок" правой кнопкой мыши. При этом удаленное приложение не будет знать, что им управляют опосредованно, а не напрямую. На Web-сайте xdotool приведены инструкции по установке этой библиотеки с помощью make.

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

xdotool search --name 'mytest'

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

На Web-сайте можно получить информацию об именах, которые используются для идентификации клавиш и их комбинаций. Например, хотя интуитивно кажется, что должно использоваться значение tab, на моей системе сработало только Tab.

Хотя команды xdotool можно последовательно запускать из окна терминала, некоторые внутренние переменные "очищаются" каждый раз при окончании работы xdotool. Поэтому удобнее объединять несколько опций и аргументов в одном вызове xdotool.

Но самый лучший способ знакомства с xdotool – это изучение работающего примера.


Автоматизация консоли

В листинге 1 приводится тестовый сценарий, в котором одно окно терминала (с именем work) используется для открытия другого окна терминала (с именем mytest) и последующей отправки нажатий клавиш в него. Команды написаны на PHP только для замедления процесса, чтобы можно было его подробно изучить. Для работы можно также использовать Perl или Python или любой другой удобный язык сценариев. Задача сценария – эмулировать нажатия на несколько клавиш, чтобы первый терминал вывел "Trying A" (A – это клавиша) и отправил эту же клавишу на второй терминал. Данные действия должны наблюдаться в обоих терминалах в одно и тоже время. Если код выполняется слишком быстро, можно увеличить значение константы WAIT.

Листинг 1. Общение между терминалами
<?php
// сценарий для проверки функциональности xdotool
define('WAIT',200000);
$charrarray = setca();
$execcmd = "xdotool search --name 'mytest'";
exec($execcmd,$out);
$windowid = $out[0];
if ($windowid) {
  echo "найдено окно с идентификатором ".$windowid."\n";
} else {
  //не удалось найти окно
  exec('konsole -p LocalTabTitleFormat=mytest');
  $execcmd = "xdotool search --name 'mytest'";
  exec($execcmd,$out);
  $windowid = $out[0];
}
// активация окна
$execcmd = "xdotool windowactivate --sync $windowid";
exec($execcmd,$out);
// проверка работы с клавишами
foreach ($charrarray as $k=>$char) {
  echo "Trying $char \n";
  send_key($k);
}
// функции, используемые в сценарии
function send_string($string) {
  $execcmd = "xdotool type $string";
  exec($execcmd,$out);
  usleep(WAIT);
}
function send_key($key) {
  $execcmd = "xdotool key $key";
  exec($execcmd,$out);
  usleep(WAIT);
}
function send_return() {
  $execcmd = "xdotool key Return";
  exec($execcmd,$out);
  usleep(WAIT);
}
function setca() {
$ca = array(
   "Up" => 'up',
   "Down" => 'down',
   "ampersand" => '&',
   "apostrophe" => '\'',
   "Left" => 'L',
   "Right" => 'R',
   "asciicircum" => '^',
   "asciitilde" => '~', 
   "asterisk" => '*',   
   "at" => '@',
   "backslash" => '\\',
   "bar" => '|',
   "braceleft" => '{',
   "braceright" => '}',
   "bracketleft" => '[',
   "bracketright" => ']',
   "colon" => ':',
   "comma" => ',',
   "dollar" => '$',
   "equal" => '=', 
   "exclam" => '!',
   "grave" => '`', 
   "greater" => '>',
   "less" => '<',   
   "minus" => '-',  
   "numbersign" => '#',
   "parenleft" => '(', 
   "parenright" => ')',
   "percent" => '%',   
   "period" => '.',    
   "plus" => '+',      
   "question" => '?',  
   "quotedbl" => '"',  
   "semicolon" => ';', 
   "space" => ' ',
   "BackSpace" => 'bs',
   "Tab" => '\t', 
   "underscore" => '_',
   "slash" => '/', 
   "eacute" => 'eac', 
   "ccedilla" => 'cced', 
   "udiaeresis" => 'uum', 
   "idiaeresis" => 'ïdi', 
   "Home" => '<',
   "End" => '>',
   "Return" => '\n'
);
return $ca;
}
?>

Сценарий начинается с объявления глобальной константы WAIT, которая замедляет работу функций, чтобы их взаимодействие было удобнее отслеживать. Затем происходит заполнение массива нажатиями клавиш, которые необходимо проверить. Полный список занимает гораздо больше места, а в представленном примере используются только стандартные символы и несколько необычных сочетаний. Библиотека xdotool ищет окно с названием mytest, и, если оно не обнаруживается, PHP создает новое окно с помощью команды konsole, которой в качестве параметра передается название окна:

exec('konsole -p LocalTabTitleFormat=mytest');

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

Когда мы убедимся, что подобрали правильную комбинацию аргументов и опции xdotool, можно начать пользоваться возможностями xdotool в части конвейерной обработки данных и поддержки сценариев. Приведенные вспомогательные конструкции PHP полезны в тех случаях, когда не ясно, какую функциональность следует использовать, и требуется полный доступ к диагностической информации.


Автоматизация текстового Web-браузера

Код из листинга 1 позволяет протестировать названия клавиш, но он был бы еще полезнее, если бы мог получать информацию из Интернета. В примере, представленном в листинге 2, открывается новое окно и запускается текстовый Web-браузер Lynx, в котором выполняется переход на Web-сайт www.ibm.com.

Листинг 2. Автоматизация Lynx
<?php
//сценарий для проверки функциональности xdotool
// получить доступ к окну для запуска lynx
define('WAIT',200000);
$winname = 'mylynx';
$execcmd = "xdotool search --name $winname";
exec($execcmd,$out);
$windowid = $out[0];
if ($windowid) {
  echo "найдено окно с идентификатором ".$windowid."\n";
} else {
  exec("konsole -p LocalTabTitleFormat=$winname");
  exec($execcmd,$out);
  $windowid = $out[0];
}
// активировать окно
$execcmd = "xdotool windowactivate --sync $windowid";
exec($execcmd,$out);
$execcmd = "xdotool getactivewindow windowsize --sync 750 750";
exec($execcmd,$out);
// начало отправки команд
send_string("lynx");
send_return(); // открыть Web-браузер Lynx
send_key("g"); // отправить команду, чтобы начать ввод URL-адреса
send_string("www.ibm.com"); // отправить URL-адрес
send_return();
function send_string($string) {
  $execcmd = "xdotool type $string";
  exec($execcmd,$out);
  usleep(WAIT);
}
function send_key($key) {
  $execcmd = "xdotool key $key";
  exec($execcmd,$out);
  usleep(WAIT);
}
function send_return() {
  $execcmd = "xdotool key Return";
  exec($execcmd,$out);
  usleep(WAIT);
}
?>

Как и в листинге 1, в коде из листингa 2 задается период ожидания, чтобы снизить скорость работы. Далее в нем выполняется поиск окна mylynx (не путать с окном mytest, упоминавшимся ранее), идентификатор окна сохраняется для дальнейшего использования, а само окно активируется и его размер изменяется. После этого в окне запускается экземпляр текстового Web-браузера Lynx. Сценарий отправляет в это окно нажатие символа "g", сообщая Lynx, что следует ожидать ввода URL, а затем отправляет строку www.ibm.com и символ конца строки. В результате открывается Web-сайт, который может принимать последующие команды, например, Tab и Return для выполнения навигации.

Почему в качестве примера для автоматизации был выбран именно Lynx? Если попробовать автоматизировать работу с другими Web-браузерами и приложениями, можно увидеть, что некоторые автоматизированные нажатия на клавиатуру просто игнорируются. Корни этой проблемы кроются в страхе перед проблемой межсайтовых сценариев (cross-site scripting). Похоже, что Lynx допускает больше имитированных нажатий клавиш, чем, например, Mozilla Firefox. В вашем случае ситуация может быть другой.


Автоматизация с помощью голоса

После того как удалась добиться требуемого результата с помощью автоматизации на основе эмуляции ввода с клавиатуры, возникает вопрос, как использовать для ввода и другие устройства. Например, можно воспользоваться голосовыми командами. Распознаватель речи с соответствующим образом настроенной аудиомоделью может принимать голосовые инструкции и передавать их в xdotool для выполнения команд. Для платформы Ubuntu существует готовое приложение kiku, которое использует xdotool и аудиомодель VoxForge (см. дополнительную информацию в разделе Ресурсы).


xdotool и менеджер диалога между пользователем и компьютером

В листинге 3 приведен исходный код примера, в котором xdotool используется для организации диалога между пользователем и компьютером.

Листинг 3. xdotool и менеджер диалога
<?php
...
function process($major,$minor) {
global $globalcontext;
  switch ($major) {
    case 'CONTEXT':
      switch ($minor) {
	case 'SOFTWARE':
	  $globalcontext = $minor;
	  echo "установлен глобальный контекст software\n";
	break;
	default:
	  echo "установлен контекст по умолчанию $minor\n";
	break;
      }
    break;
    case 'BROWSER':
      $ooc = ($globalcontext == 'SOFTWARE') ? true : false ;
      if ($ooc) {
	switch ($minor) {
	  case 'OPEN':
	    echo "открыть Web-браузер\n";
	    browser_open();
	  break;
	  case 'CLOSE':
	    echo "закрыть Web-браузер\n";
	    browser_close();
	  break;
	  case 'LOCATION':
	    echo "перейти на URL\n";
	    browser_location();
	  break;
	  default:
	    echo "действие по умолчанию $minor\n";
	  break;
	}
      } else {
	// OOC
	echo "команда распознана, но не поддерживается в данном контексте\n";
      }
    break;
    default:
	  echo "действие по умолчанию $major\n";
    break;
  }
}
function browser_open() {
global $windowid;
  $winname = 'mylynx';
  $execcmd = "xdotool search --name $winname";
  exec($execcmd,$out);
  $windowid = $out[0];
  if ($windowid) {
    echo "найдено окно с идентификатором ".$windowid."\n";
  } else {
    exec("konsole -p LocalTabTitleFormat=$winname");
    $execcmd = "xdotool search --name $winname";
    exec($execcmd,$out);
    $windowid = $out[0];
    echo "используется окно с идентификатором $windowid";
  }
  // активация окна
  $execcmd = "xdotool windowactivate --sync $windowid";
  exec($execcmd,$out);
  $execcmd = "xdotool getactivewindow windowsize --sync 800 800";
  exec($execcmd,$out);
  // отправка команд
  send_string("lynx");
  send_return(); // открытие Web-браузера Lynx
}
...
  ?>

В листинге 3 демонстрируются две новые функции. Первая функция process() принимает на вход два аргумента. Эти аргументы содержат строки, возращенные распознавателем речи. Дополнительную информацию о том, как были получены эти строки, можно найти в материалах, посвященных технологиям VoxForge или Sphinx (см. ссылки в разделе Ресурсы). В данном случае эти строки были получены из грамматической системы, содержащей, в частности, инструкции CONTEXT SOFTWARE и BROWSER OPEN.

Рассмотрим, что произойдет после того, как распознаватель речи услышит команду CONTEXT SOFTWARE. Значение строки major будет равно CONTEXT, а строки minorSOFTWARE. Функция process() объявляет глобальную переменную globalcontext, в которой будет храниться контекст, а затем в блоке switch устанавливает значение этой переменной равной SOFTWARE. Так как эта переменная является глобальной, то впоследствии, когда распознаватель речи вернет в переменных major и minor значение BROWSER OPEN, блок switch сможет проверить правильность установки контекста перед началом обработки полученных значений. Использование контекста необходимо, чтобы избежать получения некорректных результатов от распознавателя речи. Если текущим значением переменной globalcontext является SOCCER, то, очевидно, в этом контексте открытие Web-браузера будет неправильным действием, и благодаря дополнительной проверке Web-браузер открыт не будет.

Если же контекст установлен правильно, команда BROWSER OPEN активирует функцию browser_open(), которая аналогична фрагменту кода, использованному в листинге 2.

Чем сложнее становится система управления контекстом, тем настоятельнее требуется способ организации switch-блоков, следующих правилам, которые объявлены в отдельном артефакте и полностью воспроизводят структуру управления контекстом. Эти правила можно определять различными способами. Например, можно воспользоваться структурой SPRG (Speech Recognition Grammar Specification – спецификация грамматики распознавания речи) и SISR (Semantic Interpretation for Speech Recognition – интерпретация смысла при распознавании речи); дополнительную информацию об этих технологиях можно найти в отделе Ресурсы. Возможны и более простые подходы, например, хранить необходимые фрагменты кода, включая инструкции xdotool, в XML-структуре.


Использование XML для хранения контекста и фрагментов кода

В листинге 4 приведен XML-файл, содержащий информацию о блоках, составляющих предполагаемый менеджер диалога. Преимущество простого редактируемого XML-файла состоит в том, что все контексты и функции находятся в одном файле, и, поскольку они отделены от более сложных switch-блоков в менеджере диалога, изучать и редактировать структуру контекста становится гораздо проще. Эти данные содержат одну инструкцию xdotool для выполнения щелка левой кнопкой мыши в том месте, где в данный момент находится курсор. Применение этой команды допускается во всех контекстах, поэтому ей не требуется атрибут ctx.

Листинг 4. XML-структура
<?xml version="1.0" encoding="UTF-8"?>
<snips>
  <context>
    <func>click_left</func> 
    <func ctxt="software">browser_open</func>    
    <func ctxt="software">browser_location</func>    
    <func ctxt="software">browser_close</func>    
    <func ctxt="hardware">cpu_temperature</func>    
    <func ctxt="hardware">fan_speed</func>    
  </context>
  <snip fn="click_left">
<![CDATA[
function click_left() {
  exec('xdotool click 1');
}
]]>
  </snip>
  <snip fn="cpu_temperature">
<![CDATA[
function cpu_temperature() {
  $g = 0;
}
]]>
  </snip>
  <snip fn="fan_speed">
<![CDATA[
function fan_speed() {
  $g = 1;
}
]]>
  </snip>
  <snip fn="browser_open">
<![CDATA[
function browser_open() {
  $f = 0;
}
]]>
  </snip>
  <snip fn="browser_location">
<![CDATA[
function browser_location() {
  $f = 1;
}
]]>
</snip>
  <snip fn="browser_close">
<![CDATA[
function browser_close() {
  $f = 2;
}
]]>
</snip>
</snips>

В представленном коде корневым элементом является snips. Этот элемент содержит потомков двух типов:

  • элемент context с информацией о контексте;
  • несколько элементов snip, содержащих фрагменты кода.

Элемент context содержит элементы func, каждый из которых содержит в качестве значения имя функции и атрибут ctxt, задающий контекст, в котором можно использовать данную функцию. Так функцию fan_speed нельзя использовать в контексте, обозначенном software. У функции click_left нет четко определенного контекста, поэтому её можно использовать в любом контексте. Фрагменты кода хранятся в сегментах CDATA, которые гарантируют, что XML-парсер пропустит эти разделы документа и "одобрит" их вне зависимости от их содержания.

Теперь остается подготовить исходный код для преобразования данных о диалоге в отдельный исполняемый сценарий. В листинге 5 приведен PHP-сценарий для генерации исходного кода менеджера диалога.

Листинг 5. Генератор менеджера диалога, написанный на PHP
<?php
// извлечь структуру менеджера диалогов из xml-файла
$xml = simplexml_load_file('snipstor.xml');
// получить сведения о контекстах// сгенерировать основной блок switch
echo "function process(\$major,\$minor) {\n";
echo "global \$globalcontext;\n";
echo "  switch (\$major) {\n";
$majtmp = "";
$mintmp = "";
foreach ($xml->context->func as $mjmn) {
  list($major,$minor) = explode("_",$mjmn);
  //echo "$major,$minor\n";
  if ($major != $majtmp) {
    if ($majtmp != "") echo "      default:
      echo \"ошибка \$minor\";
      break;
      }\n    } else {
      echo 'OOC';\n    }\n";
    echo "  case '$major':\n";
    if ($minor != $mintmp) {
      $test = ($mjmn['ctxt']) ? "\$globalcontext == '".$mjmn['ctxt']."'" : 'true' ;
      echo "    if ($test) {\n";
      echo "      switch (\$minor) {\n";
      $mintmp = $minor;
    }
    echo "      case '$minor':
        $mjmn();
      break;\n";
    $majtmp = $major;
  } else {
    echo "      case '$minor':
      break;\n";
  }
}
echo "      default:
      echo \"ошибка \$minor\";
      break;
      }\n    } else {
      echo 'OOC';\n    }\n";
echo "  default:
      echo \"ошибка \$major\";
  break;
  }\n";
echo "}\n";
// сгенерировать фрагменты кода
echo "// функции\n";
foreach ($xml->snip as $snipfn) {
  echo trim($snipfn);
  echo "\n";
}
?>

Код, представленный в листинге 5, создает больше PHP-кода, чем можно использовать в менеджере диалога. Для начала он считывает структуру менеджера диалога, приведенную в листинге 4, из файла snipstor.xml в переменную SimpleXML. Затем выполняется считывание названий функций из элемента context и извлечение компонентов major и minor из этих названий. Эти компоненты потом используются для создания switch-блоков, управляющих менеджером диалога. По мере построения кода сценарий отслеживает, в каком контексте может использоваться данная команда (значение контекста берется из атрибута ctxt). Чтобы учесть различные контексты, которые могут применяться в текущем switch-блоке, в получившийся сценарий добавляются условные if-конструкции. Если контекст объявлен, то он вставляется как выражение, которое будет проверено в ходе исполнения программы, если же объявление контекста отсутствует, то вставляется просто выражение true, и программа всегда будет выполнять вложенные инструкции. Это означает, что данная команда может применяться в любом контексте. В конце сценарий выводит код, содержащий функции, которые будут использоваться switch-блоками.

В листинге 6 показан результат запуска PHP-сценария из листинга 5 для данных, приведенных в листинге 4.

Листинг 6. Исходный код менеджера диалога
function process($major,$minor) {
global $globalcontext;
  switch ($major) {
  case 'click':
    if (true) {
      switch ($minor) {
      case 'left':
        click_left();
      break;
      default:
      echo "ошибка $minor";
      break;
      }
    } else {
      echo 'OOC';
    }
  case 'browser':
    if ($globalcontext == 'software') {
      switch ($minor) {
      case 'open':
        browser_open();
      break;
      case 'location':
      break;
      case 'close':
      break;
      default:
      echo "ошибка $minor";
      break;
      }
    } else {
      echo 'OOC';
    }
  case 'cpu':
    if ($globalcontext == 'hardware') {
      switch ($minor) {
      case 'temperature':
        cpu_temperature();
      break;
      default:
      echo "ошибка $minor";
      break;
      }
    } else {
      echo 'OOC';
    }
  case 'fan':
    if ($globalcontext == 'hardware') {
      switch ($minor) {
      case 'speed':
        fan_speed();
      break;
      default:
      echo "ошибка $minor";
      break;
      }
    } else {
      echo 'OOC';
    }
  default:
      echo "ошибка $major";
  break;
  }
}
// функции
function click_left() {
  exec('xdotool click 1');
}
function cpu_temperature() {
  $g = 0;
}
function fan_speed() {
  $g = 1;
}
function browser_open() {
  $f = 0;
}
function browser_location() {
  $f = 1;
}
function browser_close() {
  $f = 2;
}

Полученный результат похож на код, приведенный в листинге 3. Стоит отметить, что содержимое CDATA-секций – это нормальный код, но сокращенный с целью упрощения. Аббревиатура OOC означает out-of-context (вне контекста). Это сообщение будет выведено, когда распознаватель речи услышит выражение, которое допустимо, но не имеет смысла согласно структуре файла snipstor.xml. Чтобы увеличить практическую пользу от примера, можно заменить содержимое CDATA-секции в функции browser_open() на код этой же функции, приведенный в листинге 3.


Заключение

Как показано в этой статье, xdotool – это полезная библиотека для вызова функций системы оконного интерфейса. Соединив ее с распознавателем речи, можно пользоваться голосом для запуска xdotool-команд под управлением менеджера диалога между пользователем и компьютером. Кроме того, поскольку при усложнении контекстов структура менеджера диалога может стать запутанной и громоздкой, можно поместить фрагменты кода, содержащие инструкции xdotool, в CDATA-секции XML-файла для хранения и последующей автоматической генерации менеджера диалога.

Ресурсы

Научиться

Получить продукты и технологии

  • xdotool: : Web-сайт с информацией о загрузке, установке и использовании xdotool для имитации ввода с клавиатуры и работы с мышью, перемещения и изменения размера окон и т.д.
  • kiku: Web-сайт с дополнительной информацией о kiku – менеджере диалога и распознавателе речи и о том, как использовать распознавание речи для управления операционной системой.
  • Lynx source distribution directory: домашняя страница Web-браузера Lynx с информацией о загрузке, руководством пользователя и справочной информацией.
  • Знакомьтесь с продуктами IBM различными способами: загружайте ознакомительные версии, испытывайте продукты в онлайновом режиме или в облачной среде или проведите несколько часов в SOA Sandbox, чтобы узнать, как эффективно создавать SOA-приложения.

Обсудить

  • Дискуссионные форумы раздела XML: форумы, посвященные обсуждению языка XML и связанных с ним.
  • Вступайте в сообщество My developerWorks. Устанавливайте связи с другими пользователями developerWorks, исследуя блоги, форумы, группы и wiki-ресурсы.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML, Linux, Open source
ArticleID=801021
ArticleTitle=Активация действий клавиатуры и мыши с помощью голосовых команд и xdotool
publish-date=03072012