Обратите внимание, что новости можно получать по RSS.
X
-

Информационные технологии, Infused Bytes - архив

23 января 1999, 00:00 (9607 дней назад, №6057)Разработка приложений для PalmOS. Русификатор CyrHack
(Тимур Ташпулатов, 23 января 1999)

 

Вступление.

TymHack ;-)Петр Соболев попросил меня написать статью о программировании для PalmPilot на примере CyrHack. Мне и самому было бы интересно рассказать об этом - глядишь, полку приложений для пилота прибудет :)

Почему именно CyrHack? Хотелось, с одной стороны, попробовать написать что-нибудь для PalmPilot; с другой - плохо представлялось, что именно. На тот момент (август-сентябрь 98) в конференции пользователей КПК шло активное обсуждение единственного программного продукта, выполнявшего локализацию пилота - PiLoc фирмы Парагон. При всех достоинствах PiLoc были у него и недостатки - например, в распространявшейся бесплатно версии PiLoc 2.0 присутствовала ошибка, благодаря которой активная работа аппаратными кнопками приводила к зависанию устройства с потерей всего содержимого ОЗУ (превращала Pilot в "калькулятор за $400"). Кроме того, раздражала позиция Парагона по отношению к поддержке своего продукта - несмотря на многочисленные жалобы пользователей PiLoc, фирма хранила молчание относительно ошибки и своих планов по ее устранению. Мол, мы монополисты - куда ж вы от нас денетесь.

Мне известны еще несколько проектов по локализации PalmOS - это CyRus (shareware; автор Сергей Меньшиков) и Russian III (sourceware; автор Константин Кляцкин). Именно Константину CyrHack обязан некоторыми важными решениями. Недавно появился еще один - PaPiRus от фирмы ФизТехСофт. Больше русификаторов, хороших и разных!

 

С точки зpения пpогpаммиста..

..PalmPilot мало чем отличается от обычного компьютера. Процессор, совместимый по системе команд с M68000, память (от 128К до 2М, с возможностью расширения до 16М), устройства ввода/вывода (ЖК-дисплей разрешением 160х160 с сенсорной панелью, несколько кнопок и последовательный порт) - что еще нужно, чтобы достойно встретить старость?

Средств для создания программ под PalmOS более чем достаточно - от бесплатных GNU SDK и ASDK до крутых коммерческих вроде CodeWarrior от Metrowerks. Существуют даже средства разработки прямо на пилоте (например, Pocket C, пара бейсиков, Forth и даже LISP). Писать можно на чем угодно - от ассемблера до C и Java. На http://www.palm.com можно бесплатно разжиться SDK 3.0, содержащим документацию по PalmOS 3.0, неплохой эмулятор PalmPilot для Windows 95 POSE (сделан на основе известного эмулятора CoPilot) и образ ROM для него. Существуют также эмуляторы для MacOS и X-Windows (xcopilot).

Мне оказалось удобней писать на ассемблере. Во-первых, код получается достаточно компактный; во-вторых, компилятор PILA входит в поставку бесплатного ASDK (альтернативный SDK), доступен в исходных текстах и легко собирается практически под любую платформу.

Более подробно о средствах разработки можно узнать на www.massena.com

 

Хаки

CyrHackХак (hack, patch) - небольшая программа (или кусочек кода), изменяющий или расширяющий функциональность ОС. Как правило, хак подменяет один или несколько системных вызовов, а также может выполнять некоторые не вполне стандартные с точки зрения системы действия, вроде прямого доступа к глобальным переменным системы или регистрам микропроцессора (какое кощунство!).

Чтобы несколько хаков, перехватывающих одни и те же вызовы (systraps), не подрались между собой (и не привели систему в состояние перманентного изумления), в 1996 году Edward Keyes предложил элегантный способ их организации - путем введения специального API по установке/удалению хаков, и разработал замечательную программу HackMaster. Благодаря HackMaster разработчику хака больше не нужно отвлекаться на обвязку своего кода, зачастую состоящего из двух-трех десятков команд, процедурами установки и удаления хака, отработки системных событий и тому подобной шелухой - все это берет на себя HackMaster.

На страничке, посвященной HackMaster, можно также найти статью о том, как самостоятельно написать хак. Статья настолько живая и интересная, что решение о том, что альтернативный русификатор для пилота должен быть выполнен в виде хака, окрепло окончательно.

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

Второй недостаток, весьма чувствительный - хаку недоступны глобальные переменные. Есть и третий - если хак подменяет некоторые системные значения своими, то в случае деактивации хака отыграть их обратно нет возможности (В HackMaster версии 0.9 не была реализована так называемая Custom Install Procedure; Ed Keyes обещал реализовать ее в версии 2.0, которая сейчас находится в стадии бета-тестирования, но, насколько мне известно, так и не выполнил обещания). Наконец, хаку недоступна возможность выполнить какие-либо действия сразу после сброса системы. Наверняка есть и еще недостатки.

 

О гpаффити

В CyrHack не была реализована собственная граффити, поскольку в PalmOS, предоставляющей программисту [относительно] удобный и [достаточно] хорошо сгруппированный набор вызовов API по работе с памятью, вводом-выводом, пользовательским интерфейсом и т.п., не были предусмотрены средства по модификации, расширению или полной замене Graffiti Manager. В принципе, ничего сложного в перехвате всех вызовов Graffiti Manager и подмене их своим кодом нет, но вот реализация собственного движка (engine) по распознаванию рукописного ввода - задача не из простых (и недешевых ;)). Поэтому представляется разумным подход "малой кровью", использованный в PiLoc, Russian III и CyrHack - использование сходных по начертанию штрихов для ввода кириллицы.

 

Как это работает

Условно функции, выполняемые CyrHackом, можно разделить на три группы
    - вывод кириллицы (шрифты)
    - ввод кириллицы (Граффити, экранная клавиатура)
    - сервисные (сортировка, установка значений по включению питания и проч.)

ВЫВОД

Замена встроенных шрифтов PalmOS на русифицированные осуществляется модификацией таблицы шрифтов. Делается это не от хорошей жизни - хотя в API имеется вызов FntGetFontPtr, который, казалось бы, позволяет обойтись без хирургического вмешательства в содержимое означенной таблицы, но практически ни один из вызовов Font Manager им не пользуется - все лезут напрямую в память.

(Механизм работы со шрифтами несколько изменился в PalmOS версии 3.0, в частности, появилась возможность регистрировать в системе свои шрифты (Custom fonts) с возможностью их последующего использования; в CyrHack не используется)

Собственно модификация таблицы шрифтов производится в перехватываемом вызове FntSetFont в рамках инициализации структур хака (начальные значения переключателей, указатели на таблицы перекодировок и тому подобное):

pSys    equ     'psys'
        systrap FtrGet(#pSys.l, #1.w, &osVersion(a6).l)
        move.l  osVersion(a6), Features.osversion(a3)
        cmp.b   #03, osVersion(a6)
        beq.s   PalmOS3
PalmOS12
        lea     Fonts(pc), a0
        move.l  a0, $01d2.l
        adda.l  #2694.l, a0
        move.l  a0, $01d6.l
        adda.l  #3002.l, a0
        move.l  a0, $01da.l
        adda.l  #3598+3782.l, a0
        move.l  a0, $01de.l
        bra.s   PalmOS99
PalmOS3
        systrap MemPtrNew(#32.l)
        move.l  a0,Features.fontptr(a3)
        move.l  a0,a2
        systrap MemPtrSetOwner(a2.l, #0.l)
        movea.l $01da.l, a0
        systrap MemMove(a2.l, a0.l, #32.l)
        lea     Fonts(pc), a0
        move.l  a0, (a2)
        adda.l  #2694.l, a0
        move.l  a0, 4(a2)
        adda.l  #3002.l, a0
        move.l  a0, 8(a2)
        adda.l  #3598.l, a0
        move.l  a0, 28(a2)
        adda.l  #3782.l, a0
        move.l  a0,12(a2)
        move.l  a2, $01da.l
PalmOS99

Для поддержки в CyrHack различных кодировок кириллицы был выбран довольно панковский подход. Самое простое решение - добавление в него шрифтов в соответствующих кодировках - сильно увеличило бы размер хака (в среднем на 10К для каждой кодировки), что не есть хорошо в условиях вечного дефицита памяти, особенно на устройствах с 512К и менее. Было решено перехватить вызов WinDrawChars и перекодировать выводимые на экран символы в соответствии с таблицами (128 байт на кодировку). Разумеется, при этом вывод на экран существенно замедляется, но, во-первых, он и так притормаживается вызовами Feature Manager, а во-вторых, мы же панки!

; 0 - 1251, 1 - koi8r, 2 - 866, 3 - Mac, etc
        tst.l           Features.encoding(a4)
        beq.s   Go
        movem.l d0-d2/a0-a3, -(a7)

; T r a n s l a t i o n
        move.l  Features.chrtable(a4), a1
        move.l  Features.encoding(a4), d0
        subi.l  #1, d0
        asl.l           #7, d0
        adda.l  d0, a1
        moveq   #0, d0
; счетчик цикла
        move.w  wLen(a6), d0
        move.l  Features.buffer(a4),a2
        move.l  pChar(a6),a3
; адрес процедуры трансляции
        move.l  Features.translation(a4),a0
        jsr     (a0)
; e n d

        movem.l (a7)+,d0-d2/a0-a3
; подмена исходной строки оттранслированной
        move.l  Features.buffer(a4),pbuf(a6)
Go
        move.w  bY(a6), -(a7)
        move.w  bX(a6), -(a7)
        move.w  wLen(a6), -(a7)
        move.l  pbuf(a6), -(a7)

        move.l  pfnOldProc(a6), a0
        jsr (a0)

Но шрифты в PalmOS пропорциональные - отсюда вытекает необходимость перехватывать еще и несколько вызовов Font Manager, в частности, FntCharsWidth, FntLineWidth и FntCharWidth. Судя по тому, что результат довольно коряв, стопроцентного успеха пока достичь не удается :-)

ВВОД

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

        cmpi.l  #tkbd, type(a6)
        bne.s   No
        cmpi.w  #$2710, id(a6)
        bne.s   No

; start fiddling
        move.l  Features.encoding(a0), d0
        add.w   d0, id(a6)
No
        movem.l (a7)+, d0-d2/a0-a2
        move.w  id(a6), -(a7)
        move.l  type(a6), -(a7)

        move.l  pfnOldProc(a6), a0
        jsr (a0)

С Граффити дело обстоит немного сложнее. Перехват EvtEnqueueKey - очевидный способ для модификации кодов, помещаемых в Key Queue - несовместим с "тяжелыми" вызовами, вроде вызовов Feature Manager. Поэтому было решено перехватывать SysHandleEvent (весьма удобное место для полета фантазии, например, для переключения языка щелчком по индикатору RU/EN):

        movea.l event(a6), a0
        move.w  EventType.eType(a0), d0

; check if it is key event
        cmp.w   #keyDownEvent, d0
        bne     PenDown
        move.w  EventType.data + keyDown.chr(a3), d3

Toggle
        cmp.w   #161, d3         ; Ext.shift + |
        bne.s   Switch
        move.l  Features.ruslat(a2), d0
        eori.l  #1, d0
        move.l  d0, Features.ruslat(a2)
        lea.l   DrawIndicator(pc), a0
        jsr     (a0)
        moveq   #0,d0
        bra     Change

; cycle encodings
Switch
        cmp.w   #165, d3         ; Ext.shift + Y
        bne     Go
        addi.l  #1, Features.encoding(a2)
        cmpi.l  #4, Features.encoding(a2)
        blt.s   Switch1
        move.l  #0,Features.encoding(a2)
Switch1
        systrap FrmGetActiveFormID()
        systrap FrmUpdateForm(d0.w, #frmRedrawUpdateCode.w)

Русское Граффити сделано аналогично PiLoc - так привычней и проще. Введенные с помощью Граффити коды перекодируются с помощью простой таблицы (64 байт на кодировку).

; remap stroke if RU is active
        cmp.l   #1, Features.ruslat(a2)          ; 0 - EN, 1 - RU
        bne.s   Done0

Remap
        move.l  Features.grftable(a2), a0
        move.w  d3, d0
        subi.b  #'A', d0
        add.l   d0, a0
        move.b  (a0), d0
Change
        move.w  d0, EventType.data + keyDown.chr(a3)

GSI (Graffiti Shift Indicator) сделан с помощью двух шрифтов, различающихся только значками RU/Ru/ru и EN/En/en. Не совсем здорово в смысле объема (лишних ~600 байт), но зато прикольно. Шрифты просто переключаются в зависимости от активной раскладки (снова патчится многострадальная таблица шрифтов).

proc DrawIndicator()
beginproc
        move.l  d0,-(a7)
; patch GSI font entry
        cmp.b   #03, Features.osversion(a2)
        beq.s   PalmOS03
PalmOS12
        move.l  Features.gsien(a2), $01de.l
        tst.l   Features.ruslat(a2)
        beq.s   DrawGSI
        add.l   #334,$01de.l
        bra.s   DrawGSI
PalmOS03
        move.l  Features.fontptr(a2), a0
        move.l  Features.gsien(a2), 12(a0)
        tst.l   Features.ruslat(a2)
        beq.s   DrawGSI
        add.l   #334,12(a0)
DrawGSI
        systrap GsiEnabled()
        systrap GsiEnable(d0.b)
        move.l  (a7)+, d0
endproc

Переключение раскладки щелчком по индикатору EN/RU (красивая идея, правда?) сделано довольно просто. Так как ранее мы уже начали заигрывать с вызовом SysHandleEvent, то не составит труда получить (и использовать раньше других) координаты пера - достаточно обработать penDownEvent. Дальше остается только определить, не произошло ли опускание пера на область, занятую индикатором (ради этого пришлось перехватить вызов GsiSetLocation, потому что в PalmOS (сюрприз!) нет законного способа узнать координаты индикатора на экране. Попутно выяснилось, что в документации display- и window-relative координаты перепутаны. Поубивал бы, ей-богу).

PenDown
        move.l  #0, handled(a6)
; check if it is penDown event
        cmp.w   #penDownEvent, d0
        bne     Done
; get window coordinates of pen
        moveq   #0, d0
        move.w  EventType.screenX(a0), d0        ; x
        cmp.w   Features.gsix(a2), d0
        bmi     PenDown0
        sub.w   Features.gsix(a2), d0
        cmpi.w  #10, d0
        bgt     PenDown0

        move.w  EventType.screenY(a0), d0        ; y
        cmp.w   Features.gsiy(a2), d0
        bmi     PenDown0
        sub.w   Features.gsiy(a2), d0
        cmpi.w  #8, d0
        bgt     PenDown0
; if GSI is not enabled then we just skip along
        systrap GsiEnabled()
        tst.b   d0
        beq.s   PenDown0
        move.l  #1, handled(a6)
; reverse ruslat
        move.l  Features.ruslat(a2), d0
        eori.l  #1, d0
        move.l  d0, Features.ruslat(a2)

        lea.l   DrawIndicator(pc), a0
        jsr     (a0)

; когда-нибудь дойдут руки приделать звуковую индикацию :)
;       systrap SndPlaySystemSound(#1.b)                

PenDown0

Вот с чем пришлось повозиться, так это с Commands и Shortcuts. Похоже, в PalmOS опять-таки не существует нормального способа определить, находится ли система в настоящий момент в состоянии приема Command (во всяком случае, эксперименты с атрибутами меню, перехватом MenuEraseStatus и шаманским бубном результата не дали), и способ борьбы был позаимствован из исходных текстов RussianIII (использование значений TimGetSeconds). С шорткатами тоже пришлось прогнуться - помимо перехвата GrfGetAndExpandMacro - чтобы английское "Dinner", введенное с помощью шортката при активной русской раскладке, не превращалось в загадочное "Диннея".

СЕРВИС

Нормальная сортировка кириллицы достигается перехватом GetCharSortValue и GetCharCaselessValue и подменой "родных" таблиц сортировки "правильными":

proc    MyGetCharCaselessValue()
beginproc
        lea.l   SortTable(pc), a0
endproc

Панель управления хака сделана больше из любопытства, чем из необходимости :-) все, что требуется от хакописателя - это обеспечить наличие в хаке формы, представляющей панель на экране, и обработчик событий к ней; об остальном позаботится HackMaster.

Установка Power On потребовала перехвата вызова HwrLCDWake - в качестве образца использовались исходные тексты LogoHack:

; check Power mode selector
        cmp.l   #2, Features.powermode(a2)
        beq.s   Go
Lat
        move.l  Features.powermode(a2), Features.ruslat(a2)
Go

 

Заключение.

Как видно, программировать под PalmOS нетрудно. Обширный инструментарий, относительно подробная документация и большой выбор ресурсов Интернет, посвященных разработке приложений для Pilot, делают это занятие почти что развлечением.

Разумеется, PalmOS не лишена недостатков (а порой и просто маразмов). Например, в API отсутствует возможность определить, засвечена ли точка на экране; многие моменты неясно или просто неверно отражены в документации (вспомним путаницу с экранными и оконными координатами). Зато имеются зачатки здравого смысла, вроде FntGetFontPtr:

link      a6, #0
movea.l   $01d6.w, a0
unlk      a6
rts

Всего-то ничего, но по каким-то соображениям разработчики постеснялись вставить вызовы FntGetFontPtr в остальные функции Font Manager и вместо этого старательно лезут напрямую в память, - остается только развести руками.

К чести 3Com надо отметить, что обращение в их службу поддержки не остается без ответа. Правда, ответ этот может воспоследовать через несколько недель (сразу выдается только специальный квиток-подтверждение о том, что-де "ваш номер в очереди 78675645"). Большим подспорьем является лист рассылки Palm Developers (palm-dev-forum@3com.com), где тусуются зубры из Palm Computing, 3Com и массы мелких компаний-разработчиков софта под PalmOS.

Что касается CyrHack, то после релиза я выдам исходные тексты на всеобщее растерзание и начну делать очередную версию в виде нормального приложения, а не хака.


Опубликовано: Тимур Ташпулатов

Случайная заметка

2448 дней назад, 02:2330 августа 2018 Тут, в процессе очередного обновления, в ноуте умерли Win 7 (к слову - прямо посреди моего доклада на CC'2018). Поскольку попытки их оживить ничего не дали, я решил восстановиться с бэкапа (rdr образ диска от января месяца), и, чтобы два раза не вставать, заодно поменять SSD (был Intel 167gb, купил Intel 512gb). Что, казалось бы, может быть проще? Но ...далее

Избранное

2930 дней назад, 01:575 мая 2017 Часть 1: От четырёх до восьми Я люблю читать воспоминания людей, заставших первые шаги вычислительной техники в их стране. В них всегда есть какая-то романтика, причём какого она рода — сильно зависит от того, с каких компьютеров люди начали. Обычно это определяется обстоятельствами — местом работы, учёбы, а иногда и вовсе — ...далее

2442 дня назад, 20:305 сентября 2018 "Finally, we come to the instruction we've all been waiting for – SEX!" / из статьи про микропроцессор CDP1802 / В начале 1970-х в США были весьма популярны простые электронные игры типа Pong (в СССР их аналоги появились в продаже через 5-10 лет). Как правило, такие игры не имели микропроцессора и памяти в современном понимании этих слов, а строились на жёсткой ...далее