[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

FYI: Xkb и locale



  Привет!

  Извините, если это не совсем по теме, но, может быть вам будет интересно...
  Собственно, меня интересовал вопрос - как "локализация" влияет на
руссификацию клавиатуры через Xkb.

  Результаты моих изысканий таковы...

  Как известно, нормальная "раскладка клавиатуры" XKB для русских символов
генерирут коды, которые называются Cyrillic_* и предствляют собой двухбайтные
числа, старший байт которых - 6 (который и определяет, что это "кириллица"),
а младший в большинстве случаев соответствует коду символа в koi8 (хотя,
это и не так уж важно).

   Преобразование этих кодов в обычные однобайтные коды koi8 или iso8859-5
делается соответствующими подпрограммками из Xlib - XLookupString и
XmbLookupString. Надо отметить, что первая - более древняя и более "тупая".
Вторая - использует "input context" (это, в общем-то, объект, который
содержит некоторые данные, в том числе и относяшиеся к категориям
"локализации", и набор методов для всевозможных преобразований входных
символов и последовательностей).
   Большинство современных программ пытаются создать input context и,
если это им удается, то пользуют XmbLookup*, в противном случае
используется XLookup*.

  Так вот. Для того, чтобы обе эти функции соглашались преобразовывать
коды Cyrillic, нужно правильно выставить текущую locale. То есть, она
должна указывать на соответствующий "иксовый" файл (XLC_LOCALE).
  В противном случае обе функции возврашают строки нулевой длины, что
и проявляется как "русские буковки не вводятся".

  Если смотреть немного глубже, то надо заметить, что
- XLookup* интересует из XLC_LOCALE только параметр (точнее, там он назывется
"класс") encoding_name
- значение это класса должно быть KOI8-R или ISO8859-5 (case не важен),
поскольку эти названия "зашиты" в самой Xlib, как и таблицы перекодировки.
- XLookup* может взять это же значение из переменной окружения _XKB_CHARSET,
тогда установка locale в программе не нужна. Однако, это не распространяется
на XmbLookup*.
- наконец, XmbLookup* осуществляет преобразование в два этапа. Сначала,
в соответствии с encoding_name она преобразует код типа Cyrillic в
цепочку из специальной "esc-последовательности" и однобайтного кода
(такого же, какой делает и XLookup*), а затем, эту цепочку опять "сворачивает"
в одинобайтный символ, уже в соответствии со значением класса ct_encoding
("ct" - compaund text) из той же XLC_LOCALE. Так что эти два класса должны
иметь одинаковое значение (по крайней мере для кириллических "локалей").

  Итак. Для того, чтобы "русские буквы вводились", приложение должно
вызвать в самом начале setlocale(LC_ALL, ""), чтобы взять текущую locale
из "окружения". (Естественно, если очень хочется, то locale можно задать
прямо в вызве setlocale()).
  Исключение - приложение пользуется только XLookup* и задана переменная
_XKB_CHARSET. Но, поскольку сейчас таких приложений практически не осталось,
то и ценность этого способа (через задание _XKB_CHARSET) стремится к нулю.

  В документации Чернова говорится, что "поправить" приложение можно,
вставив вызов XtSetLanguageProc(NULL,NULL,NULL).
  Во-первых, это функция не Xlib, а Xt. Во-вторых, она сама ничего,
относящегося к locale, не вызывает, а только устанавливает указатель
на соответствующую функцию, которая будет вызываться потом, при инициализации
Xt'ишных виджетов. Естественно, в той самой функции первым делом стоит
вызов все той же setlocale(LC_ALL,"") (точнее, второй аргумент не "",
а бeрется из LANG).
  Таким образом, Чернов "закладывется" на то, что все приложения используют
Xt или Xaw (или другую, которая работает "поверх" Xt). Конечно, на "сырой"
Xlib практически ничего уже не пишут. Но есть немало "тулкитов", которые
работают "поверх" Xlib и не пользуют Xt. (Например - Tcl/TK, если не ошибаюсь).
  Поэтому, "метод Чернова" не универсален. Гораздо более универсальный -
вызвать setlocale. Хотя, конечно, если уж используется так или иначе Xt, то
правильнее вызывать XtSetLanguageProc.

   Кстати, о Tcl/Tk...
Недавно в ньюсах были сетования по поводу все той же "буковки не вводятся".
Это замечательно лечится, ести вставить вызов setlocale в процедурку
application Init (не помню, как точно она называется).
   После этого русские буквы вводятся, только "фонт" по умолчанию стоит
"неподходящий". Но, если явно указывать соответствующие фонты в программе,
то все выглядит нормально.

  Несколько слов о setlocale (вызов которой оказывается так полезен)...
Напомню, что setlocale(..., "") устанавливает locale в соответствии с
переменными окружения и запоминает соответствующие значения.
А setlocale(..., NULL) - возвращает текущее значение (то есть, по смыслу
это не "set", а "get"). Xlib часто использует вторую форму вызова для
того, чтобы "вспомнить" какая locale в данный момент у задачи.
  Для тех ОС, у которых нет соответствующей функции в libc, Xlib имеет
свою "заглушку" для setlocale - _Xsetlocale. Естественно, она никакого
отношения к "libc'ишной" не имеет.
  Для того, чтобы Xlib использовала эту "заглушку", она должна быть собрана
с опцией X_LOCALE.
  Так вот. Тонкость состоит в том, что
- если Xlib собрана со своей setlocale, то и приложение должно вызвать
эту setlocale (иначе, значение категорий locale будет запоминаться в
"libc'ишной", а Xlib будет пытаться получить их от "иксовой" setlocale).
Для того, чтобы приложение вызвало именно "заглушку", перед вызовом должно
быть
#define X_LOCALE
#include <X11/locale.h>
....
setlocale(LC_ALL,"");
Естественно, при этом "libc'ишные" функции, зависящие от locale не будут
работать правильно.
 - если же Xlib собрана без X_LOCALE (что и должно быть в нормальных ОС,
где аналогичная функция есть), то следует вызывать обычную "libc'ишную"
setlocale.

  И, наконец, пару слов о Qt 2.0....
Выкачал я "снапшот" от 19990309, посмотрел.
Еще раз повторю - она для перкодировки кодов Cyrillic использует те же
Xlib'овские XLookup* и XmbLookup* (как я уже написал - в зависимости от
того, удается ли создать "input context").
  Так что, для корректной перекодировки вроде бы достаточно вызова
setlocale (который у нее уже есть), но ...
  Строчки символов она хочет хранить в unicode, поэтому, уже перекодированные
символы она пытается "завернуть" в unicode, а вот для этой перекодировки уже
использует свои таблицы. А выбор их делает через свой внутренний "алиасинг",
о котором и писал AEN.

----
P.S. Еще раз извините, если все изложенное не вписывается в тематику
этого list'а.

P.P.S. Уже совсем не в тему. Ну и фигня этот Xlib. Такого "лохматого" кода
я еще не видел. Сплошной "quick&dirty" (по крайней мере в той части, что
я рассматривал).
  В 3.3.2 действительно было два комплекта таблиц перекодировки - один в XKB,
другой в функциях для "input context". В 3.3.3 оставили один набор, но две
разные таблицы соответствия esc-sequence и encoding. При этом сумели не только
перепутать koi8 и cyrillic (так у них называется iso8859-5) в одном месте, но
в другом еще перепутали cyrillic и arabic.
  В общем, еще немало иттераций будет, пока все это заработает без глюков.
-- 
 Ivan U. Pascal         |   e-mail: pascal@tsu.ru
   Administrator of     |   Tomsk State University
     University Network |       Tomsk, Russia