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

Баги в Xlib.



   Привет!

  По ходу своей работы я обнаружил уже несколько багов в Xlib, которые
так или иначе могут мешать "вводу русских букв" или вообще - работе
приложения в локали koi8-r.

  Решил вам рассказать об этих "граблях", чтобы если вы встретитесь
с такими проявлениями не обвиняли во всем Lesstif, Навигатор или
"свои кривые руки" :-)

1)  Не "открывается" Input Method.

  Собственно это не баг, а просто некоторое несовершенство идеологии.
  Проявилось это в siag. Эта программа нормально запускается с некоторыми
iso8859 локалями, но "падает в корку" на локали koi8-r.

  Некоторые пояснения.
  Для того, чтобы работали функции X[mb|wc]LookupString, нужно сначала
"открыть Input Method" (XOpenIM) и "создать Input Context" (XCreateIC),
производный от IM. Далее этот IC используется как аргумент для функций
X[mb|wc]LookupString.
  В Xlib предусмотрены две "модели IM". По одной модели должен быть еще
Input Server, с которым приложение будет общаться по отдельному соединению.
По второй модели - все необходимые действия делаются внутри Xlib.
  Так вот. Недоработка заключается в том, что
- "по умолчанию" выбирается модель "клиент-сервер"
- а вот сервер как-раз в стандарной поставке XFree отсутствует.
  А для того, чтобы выбралась модель "все внутри библиотеки" надо чтобы -
- в приложении явно вызывалась XSetLocaleModifiers("@im=local");
- или в локали есть файл Compose.

  Если приложение не выставило соответствующий LocaleModifier, и файл
Compose отсутсвует (а он как-раз отсутствует в koi8-r), то будет выбран
метод "клиент-сервер". А поскольку сервер у нас отсутствует, то ...
вообще IM не открывается и IC не создается.
  Вообще-то нормальные приложения проверяют эту ситуацию и если с IM/IC
ничего не получилось, то просто "откатываются" к старой XLookupString.
  Но вот автор(ы) siag были уверены, что этого не понадобится.
В результате siag "падает в корку" при koi8-r, хотя нормально работает
с теми локалями, где есть файл Compose.

  Решение: Вообще-то надо немного модифицировать логику Xlib. И если
уж нет сервера, то пытаться обойтись "библиотечным" методом.
  А пока самое простое решение - сделать в koi8-r файл Compose, пусть даже
пустой.
  Кстати, это уже сделано (пустые Compose во всех локалях, где нет реальных
Compose) в каком-то из последних релизов XFree. Хотя это не решение проблемы,
а "обход".

2)  После ввода в "диалог" перестают вводится русские буквы.

  Вот это настоящий баг.
  Проявляется в Ted, nedit (возможно где-то еще).

  Пояснения.
  Функции X[mb|wc]Lookup* сами в свою очередь вызывают "традиционную"
XLookup*, для того, чтобы получить KeySym (а потом уже ее конвертировать
в символ).
  Чтобы "подавить" излишний в этом случае интеллект XLookup, вызывающие
процедуры перед ее вызовом устанавливают флаг ForceLatin1Lookup (то есть -
XLookup должна выдавать символ только если он из набора Latin1, а для
остальных вернуть только код клавиши).
  Естественно, после отработки XLookup, вызывающие процедурки должны этот флаг
"снять". Баг в том, что они делают это неправильно и флаг не убирается.

  Обычно приложения пользуются только XmbLookup или только XLookup.
  Но некоторые, в своем основном виджете (например - "окно редактора")
используют XLookup. А для всяких диалогов (нпример - "открыть файл")
вызывают виджеты из каких-нибудь "тулкитов" (например - Motif/Lesstif).
  Если при этом "тулкит" использует для ввода XmbLookup (не важно - какие
буковки вы туда вводили, главное, что она хоть раз да отработала),
то после вызова такого виджета XLookup "портится" и начинает пропускать
только Latin1.

  Решение: Ну патчики я уже послал в XFree. Будем надеяться, что в следующих
версиях этого не будет.
  А пока для устранения этой "интерференции" надо аккуратно собирать
приложения. Или они должны пользоваться везде XmbLookup, или "тулкиты"
надо к ним такие, чтобы использовали только XLookup. 

3) Несчастные GLGR чарсеты.

  Начну с пояснений.
  Я уже объяснял - что такое GL и GR чарсеты. Имеется ввиду, что все
символы данного чарсета лежат только в левой части кодовой таблицы
(коды 0-127) - или только в правой (128-255).
  В своей работе Xlib постоянно использует эту характеристику чарсета
(side) и при всевозможной конвертации строчек смотрит - какой чарсет
сейчас "в процессе" и в зависимости от side'а или "вычищает" старший бит
(если символ из чарсета типа  GL) или наоборот - "взводит" этот бит
во всех символах из чарсета типа GR.

  Беда в том, что этот side нигде явно не указывается (ни во внутренних
таблицах Xlib), ни в файлах XLC_LOCALE.
(Вообще-то, он присутствует в самих названиях чарсетов - KOI8-R:GR,
но на них Xlib внимание не обращает).
  А сам side Xlib "вычисляет" из esc-sequence (вот она есть в таблицах).
Если в sequence присутствует знак "(" - это GL чарсет, а если ")" или "-"
это GR.
  Но проблема в том, что в "нестандартных" последовательностях, какие
используются для koi8-r/koi8-u соответствующих знаков нет.
  Поэтому эти чарсеты Xlib помечает как GLGR (что в переводе означает -
"черт его знает - какая половина" :-)

  Вторая беда в том, что судя по всему "конвертеры" в Xlib писались
без учета этих "экзотических" чарсетов и, зачастую, не рассматривают
случай когда side == GLGR.
  Ну а что происходит с такими чарсетами (точнее символами из чарсета)
зависит от фантазии автора конкретного конвертера. Он может их вообще
выкинуть, или посчитать "по умолчанию" GR и "вклеить" им старший бит
(это то для koi8 не страшно), или наоборот - интерпретировать их как GL
и "вычистить" стрший бит (тогда koi8 превратится в "транслит").

  Я обнаружил, что как-раз так неправильно ведет себя перекодировщик
в COMPOUND_TEXT, но я никак не мог найти - где это проявляется.

  И вот недавно Alex Tutubalin пожаловался, что у него при переносе
"selection" из XEmacs в Netscape Navigator 4.7 в русских буковках
"обрезается восьмой бит".
   Судя по всему - он как раз "напоролся" на этот баг.

  Решение: По хорошему - надо исправлять все конвертеры. А еще лучше -
вообще избегать этого неопределенного GLGR.
  И вот последнее как-раз легко делается "внешними настройками".
  Можно в файле XLC_LOCALE добавить строчки
----------------
XLC_CHARSET_DEFINE
csd0	{
	charset_name KOI8-R
	side	GR
}
END XLC_CHARSET_DEFINE
------------
  Это объяснит библиотеке, что KOI8-R:GR - нормальный GR-чарсет,
а не "черт знает че".

4)  Клавиша CapsLock не отменяется клавишей Shift.

  Речь идет не о переключателе "рус/лат", а о "классической" CapsLock.
  Обычно при нажатии этой клавиши буквы "переходят в старший регистр",
но нажатием Shift можно обратно вернуться к "нижнему регистру".
  В XKB это не работает.
  Кстати, для русских букв вообще не работает CapsLock, поэтому проблема
с его отменой не стоит :-).
  Но русскую часть раскладки можно заставить реагировать на CapsLock просто
"доописав" ее в соответствующем файле раскладки
(например  - key.type[group2] = "ALPHABETIC";).
  А вот с обычной "латиницей" дело сложнее.

  Вообще-то здесь присутствут некоторый "баг в ДНК".
  В Xkb для выбора symbol'а клавиши из таблицы раскладки можно написать
как угодно сложный тип, который будет правильно выбирать "большие/маленькие"
буквы (shift level) в зависимости от CapsLock, Shift или их комбинации.
  Но после выбора symbol'а (KeySym) XLookupString должна его еще перевести
в обычный char. И вот на этом этапе опять проверяется - какие модификаторы
были установлены и, если есть CapsLock, выбирается "большая буква"
(независимо от всех других модификаторов).
  Вроде бы в XKB предусмотрели управление этим процессом. В описании типа
можно указать - передавать ли модификатор CapsLock второй процедуре
(перевод KeySym в char) или "снять" его, если он уже использован для
выбора keysym из таблицы раскладки.
  Но при этом из каких-то соображений "совместимости" сделали так, что
по умолчанию, если даже первый этап "проглотил" какие-то модификаторы,
второму этапу все равно подсовываются все модификаторы, как они были до
первого этапа (вот это, я считаю - "баг в ДНК").

  С другой строны, это поведение "по умолчанию" можно изменить - второму
этапу будут передаваться только те модификаторы, которые мы явно укажим
в описании типа.
  Для этого надо всего лишь пару "заклинаний" в шелле
set _XKB_OPTIONS_ENABLE=on
set _XKB_CONSUME_LOOKUP_MODS=on

  Но не торопитесь пробовать. Дальше идет тривиальный баг -
по переменной _XKB_CONSUME... взводится соответствующий флаг, но ...
взводится он в одной переменной, а проверяет его XLookupString совсем
в другом месте.

  Решение: Патчики (для последнего бага) посланы. Будем ждать.
Но честно говоря - надо бы изменить и поведение "по умолчанию", хотя оно
уже и "как бы стандарт".
  К счастью - это глупое поведение CapsLock не так уж мешает в работе.

5) Не всегда срабатывает "хакерский метод" с подменой locale С на koi8-r.

  (Я прекрасно помню, что Алексей Новодворский сильно возражает против
этого "хака". Но, поскольку меня он не убедил, я так же сильно настаиваю
на том, что "его можно использовать" :-)
  Но в данном случае оставим идеологические споры - можно так делать
или нельзя.

  Дело в том, что он не всегда срабатывает (хотя я думал, что он должен
помогать во всех случаях).

  А не работает он тогда, когда приложение использует XmbLookupString
(а не XLookup), но при этом "забывает" вызвать setlocale.
  Причина в том, что хотя encoding_name выберется из локали koi8-r,
но конверторы "подключатся" те, которые работают только в локали C.
А они напрочь не понимают никаких чарсетов кроме ISO8859-1.
  Для XLookup эти конветоры не используются и она "поддается на хак",
а для XmbLookup они нужны, ну и ... русские буковки "подавляются".

  Решать эту проблему все равно надо будет (почему - долго объяснять),
но я в данном случае хотел бы обратить ваше внимание на "баг в ДНК"
разработчиков некоторых приложений.

  Функции X[mb|wc]Lookup являются сильно "локале-зависимыми" и использовать
их без установленной локали (setlocale) практически бессмысленно.

  Однако авторы таких приложений как ETerm и xiterm ("i" означает -
"интернационализованный" !) считают, что setlocale нужна только для
японского/китайского языка. И поэтому обставили ее вызов соответствующими
ifdef'ами (а кому она мешает если даже нужна только Latin1?).

  Поэтому вышеупомянутые приложения можно заставить понимать русский,
только собрав их с "поддержкой KANJI". (Или наоборот - отключить им вообще
i18n, тогда они используют только XLookup и их можно хотя бы "допинать хаком"
:-).

  Ну а правильное решение - просто выкинуть эти ifdef'ы.

---------------------------------

  Ну вот пока все. Если еще вспомню/найду - напишу.

-- 
 Ivan U. Pascal         |   e-mail: pascal@tsu.ru
   Administrator of     |   Tomsk State University
     University Network |       Tomsk, Russia