понедельник, 19 ноября 2007 г.

eurokbd

Собрал на скорую руку cab для модифицированной eurokbd, о которой я писал ранее.

LooxLight 2007-11-19

Подробнее о Looxlight (утилита для Fujitsu-Siemens Loox N560/C550) смотрите здесь.

Обновление!

Вот cab.
Вот исходники.

Добавил возможность блокрировать экран и клавиатуру. Раньше у меня стояло что-то-там-guard (screen guard или типа того). Но оно было глючное: иногда при разблокриовке весило кпк, при нажатии на кнопку suspend разблокировала экран, но при нажатии любой клавиши блокировало его обратно - выглядело это страшно, при нажатии на кнопки разблокировало экран и тутже блокировало обратно. Терпел. А вот недавно перепрошил КПК на новую версию WM5 (кстати стало побыстрее все рисоваться, а в каталог /windows так вообще мгновенно заходит (раньше 10сек)). После перепрошивки обнаружил, что жить без блокировки тяжело - батарея садится быстрее, кнопки нажимаются случайно, когда в кармане КПК лежит... И позавчера вечером не выдержал и решил написать сам. Ну как сказать, получилось вобщем-то. Отличается от прочих (пукнт 1.42) тем, что заточено чисто под Fujitsu-Siemens Loox N560/C550 (может конечно и на 720 заработает, дрова у них похожие), поэтому выключает и экран и клавиатуру в том числе и кнопку suspend(!!). Кстати, выключенный экран (при прослушивании музыки или аудиокниги) экономит более 50мА. С выключенным экраном заряжаться с USB должно быстрее.

Как установить:
Если КПК не Fujitsu-Siemens Loox N560/C550, то очень рекоменудю сделать backup! Если это Fujitsu-Siemens Loox N560/C550, то все равно рекомендую, хотя у меня и жены вроде ничего страшного не произошло пока. В любом случае, я ответственности никакой не несу. :) (Добполнение: У меня работает уже больше чем 2 года без проблем)

Скачать cab. Закинуть на кпк. Запустить установку. Установить. Подождать минуту для верности. Перезагрузить КПК (софтресет). Запустить LooxLight Control и поставить галочку напротив "Permit blocking". Ниже, напротив "Unblock by" два выпадающих списка. В них задается какими кнопками будет происходить РАЗБЛОКИРОВКА. Когда КПК будет заблокирована, эти две кнопки надо будет последовательно нажать. У меня это 2 и 3 кнопки. А по умолчанию почему-то стоит 2 и 4, ну и фик с ним. Теперь идем в Settings/Buttons и там вешаем LooxLight Control на ту кнопку, которой КПК будет БЛОКИРОВАТЬСЯ. У меня это долгое нажатие на кнопку 2. Готово. Можно тестировать.

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

Как реализовано (в общих чертах):
С выключением экрана все просто. А вот с его блокировкой посложнее. LooxLight аттачит себя к gwes.exe. Находит в памяти keybddr.dll и правит в ней таблицу импортированных функций, а именно WaitForSingleObject. Эта функция вызывается нитями keybddr.dll для ожидания прерывания клавиатуры (0x10 и 0x2a), а также для ожидания срабатывания события suspend. Т.е. в таблице импорта keybddr.dll, LooxLight подменяет адрес WaitForSingleObject с адреса функции из библиотеки coredll.dll на собственную функцию. Получается, что в момент, когда keybddr инициирует ожидание нажатия, вызывается функция LooxLight'а, которая в свою очередь дергает оригинальную функцию. Если блокировка не включена, то результат сразу же возвращается в keybddr.dll. Иначе в результате проверяется пара адресов памяти на код нажатой клавиши. По нему решается, разблокировать ли экран или нет (грубо очень). Если нажатие нужно проигнорировать, то сообщаем OS о том, что мы обработали прерывание (InterruptDone) и вызываем оригинальный WaitForSingleObject - тоесть управление обратно в keybddr не передается, пока клавиатуру не разблокируется. Таким образом ни одной нажатие мимо нас в принципе не проходит. Ибо обработчик LooxLight находится первым в цепочке обработки прерывания клавиатуры.

Пока заметил, что если воткнуть КПК в крэдл, когда КПК заблокирована, то экран включается. Правда при этому клавиатура попрежнему заблокирована. Я думаю activesync что-то делает такое не хорошее, например вызывает ExtEscape - функция используемая для выключения/включения питания экрана. Или еще хуже. он дергает какое-то более общее API (варианты: вывести окно на передний план, послать уведомление, включить подсветку), которое как раз и и дергает ExtEscape. Вариантов лечения я вижу два:
1. Простой. Эпизодически запрашивать состояния питания на экран и если блокировка включена, а экран выключен - то выключать экран. Но я так не люблю по таймеру что-то делать. Да и мерцание будет, хоть и очень редко.
2. Сложный. Я уже попробовал пойти по этому пути - убил 3 часа и пока решил отложить. Смысл в том, что можно вклинится в какое-нибудь API какой-нибудь dll. Например можно было бы перехватить ExtEscape, но для этого нужно пробежаться по всем dll'кам в системе и каждую из них происнструментировать так же как и keybddr. Так я еще не пробовал, но есть подозрение, что может не сработать. Ибо у нас есть gwes.exe который и рулит GDI'ем. И если кто-то вызывает API у coredll, тот направляет его еще куда.. короче я к тому, что вызов ExtEscape из coredll может быть проскипан кодом, который напрямую работает с DeviceDriver'ом - а с ним напрямую, я так подозреваю, работает gwes. Было бы просто идиально вклинится в Display Driver. Когда кто-то дергает ExtEscape, тот в свою очередь вызывает DrvEscape callback у драйвера дисплея. Как это вообще работает. Когда gwes.exe грузит драйвер дисплея, он вызывает у него метод DrvDeviceEnable (или типа того). Передавая ему в качестве параметра структуру, которую драйвер должен заполнить. В этой структуре указатели на методы связаные с работой с дисплеем, в том числе и DrvEscape. После того, как драйвер заполнил эту структуру, далее gwes уже работает только с методами прописанными в этой структуре. Было бы клево, найти в памяти адрес этой структура и прописать свой DrvEscape туда. Но полазив по header'ам от wince я пока не нашел цепочку, чтобы добраться до туда. Может имея HDC можно по нему добраться? Но HDC - это указатель на C++ структуру и я этот путь пока не осилил. Другой вариант, изучать gwes.exe и посмотреть куда он копирует адрес заполненной структуры и плясать отсюда. Но блин, gwes.exe как-то криво разобрался IDA (или криво задампился). Все локальные переменные имеют совсем не те адреса, на какие ссылается код. Еще можно было бы посмотреть coredll.dll и как там работает ExtEscape и потыкаться оттуда, но coredll.dll у меня задамлпнного нет (это вообще вроде kernel сам). А дамп всей ОЗУ неплохо так разобранный во времена когда искал как управлять диодами я где-то посеял..

Вобщем я решил пока этот вопрос отложить. Посмотрим как оно будет анноить или нет.

--------
Будущие версии программы можно будет найти здесь.
Другая программа для уменьшения расхода заряда батареи описана здесь.

суббота, 17 ноября 2007 г.

iphone?


Прочитал недавно статью про iphone. Какой он там весь удобный и все такое. Заразился идеей о управлении КПК (WM5) с помощью одной руки и даже одного пальца. Для меня это актуально, так как пользуюсь КПК в метро. Поменял часть софта - менеджер задач и лоунчер. А с клавиатурой пришлось повозится. Перепробовал много вариантов. PocketCM уовлетворила почти всем, кроме того, что была глючная и не давала сделать такую раскладку какую я хочу. И вот я случайно наткнулся на eurokbd. Программа интересная тем, что opensource (хоть и слегка кривовато написана, но да ладно, дареному коню... спасибо автору огромное!). Подправил для нее раскладку, перерисовал скины, подправил исходники и вот, что получилось.

Картинко 1
Картинко 2
Картинко 3
Картинко 4

Первое, что нужно было сделать - придумать раскладку. Раскладка должна была быть такой, чтобы легко попадать по клавишам большим пальцем моей руки (держа КПК той же рукой). Qwerty не подходит. Был соблазн набросать как придется новую раскладку, но это абсолютно не аправданно. Решил сгенерировать оптимальный вариант с учетом частоты следования букв друг за другом. Написал на perl анализатор текста на входе которого текст, на выходе описание для graphwiz (буква -> буква [вес]). Вес являлся частотой появления комбинации букв. На основе такого описания graphwiz построил графики, расположив узлы таким образом, чтобы расстояния между элементами с наибольшими весами было наименьшее. Таким образом минимизируется время набор слов. На графиках линии связей между буквами убраны для простоты картинки.

Инглиш
Русский

Далее нарисовал скины. А саму eurokbd пришлось пропатчить, чтобы она активную кнопку подсвечивала белым фоном (а не черным как это жестко вбито в исходниках), а также не выводила меню переключения раскладки, а переключала ее сразу же с rus на lat и с lat на rus). Итого получилась очень удобная клавиатура! Несмотря на то, что раскладка не qwerty - привыкаешь ОЧЕНЬ быстро. Я уже не могу на стандартную мелкую смотреть без тошноты.

http://toril.ru/pda/eukbak.zip

Для интересующихся, скрипт на perl для анализа текста (textanal.pl):

#!/usr/bin/perl
# Akshaal (C) 2007

# Creates dot graph for link between characters

use warnings;
use strict;

use utf8;
use encoding 'utf8';

my %ass;
my $prev;

my %allow;
for (split '', "йцукенгшщзхъфывапролджэячсмитьбюё ") {
$allow{$_} = 1;
};

sub add ($) {
my $c = shift;

if (defined $prev) {
if (ord($prev) > ord($c)) {
$ass{"$prev$c"} ++;
} else {
$ass{"$c$prev"} ++;
}
}

$prev = $c;
}

# Calc weights
while (<>) {
chop;
for my $c (split '') {
$c = lc $c;

unless ($allow{$c}) {
$c = ' ';
}

add ($c);
}
add (' ');
}

# Create dot
print "graph D {\n";
for my $link (keys %ass) {
my $weight = $ass {$link};
my $a = substr $link, 0, 1;
my $b = substr $link, 1, 1;

print " \"$a\" -- \"$b\" [weight=$weight, style=invis];\n";
}
print "}\n";


А это патч на eurokbd:

--- eurokbd.cpp.old 2007-10-11 20:45:00.000000000 +0300
+++ eurokbd.cpp 2007-11-17 17:58:48.000000000 +0200
@@ -369,10 +369,12 @@

if(vk==_VK_MOD)
{
- if( m_subkeyCurrent!=SUBKEY_PARENT )
- {
- LoadConfig(m_subkeyCurrent->data.pw);
- }
+ if(0==wcscmp(m_pwCurrentConfigName.ptr(), L"rus"))
+ {
+ LoadConfig(L"lat");
+ } else {
+ LoadConfig(L"rus");
+ }
} else if(vk==_VK_FN)
{
// } else if(vk==_VK_CUT)
@@ -788,8 +790,8 @@
COLORREF bgColor, fgColor;
if( m_subkeyCurrent==p )
{
- fgColor = RGB(0xFF,0xFF,0xFF);
- bgColor = RGB(0x00,0x00,0x00);
+ bgColor = RGB(0xFF,0xFF,0xFF);
+ fgColor = RGB(0x00,0x00,0x00);
} else
{
fgColor = m_config->m_colors[p->group][0];
@@ -832,8 +834,8 @@
COLORREF bgColor, fgColor;
if( m_subkeyCurrent==SUBKEY_PARENT )
{
- fgColor = RGB(0xFF,0xFF,0xFF);
- bgColor = RGB(0x00,0x00,0x00);
+ bgColor = RGB(0xFF,0xFF,0xFF);
+ fgColor = RGB(0x00,0x00,0x00);
} else
{
fgColor = m_config->m_colors[m_keyCurrent->group][0];
@@ -987,43 +989,6 @@
}
if(m_hwndPop)
{
- // кнопка Mod
- if(m_keyCurrent->vk==_VK_MOD)
- {
- m_keyCurrent->subkeys.destroy();
-
- WIN32_FIND_DATA ffdata;
- WCHAR* pwMask = new WCHAR[wcslen(g_pwDllDir)+6+1];
- wcscpy(pwMask, g_pwDllDir);
- wcscat(pwMask, L"\\*.txt");
- //UINT iLenMask = wcslen(swMask);
- HANDLE hFind = FindFirstFile(pwMask, &ffdata);
- if( hFind!=INVALID_HANDLE_VALUE )
- {
- int i = 0;
- do
- {
- SUBKEYENTRY* psk = new SUBKEYENTRY;
- psk->vk = _VK_MOD;
- //psk->data.pw= new WCHAR[iLenMask-5+wcslen(ffdata.cFileName)+1];
- //wcscpy(psk->data.pw, swMask);
- //wcscpy(psk->data.pw+iLenMask-5, ffdata.cFileName);
- ffdata.cFileName[wcslen(ffdata.cFileName)-4] = '\0'; // trim .txt
- psk->data.pw= _wcsdup(ffdata.cFileName);
- psk->left = 0;
- psk->top = (i+1)*-16;
- psk->right = 80;
- psk->bottom = (i+0)*-16;
- psk->group = SK_GROUP_LATIN;
- psk->desc.pw= 0;
- m_keyCurrent->subkeys.add_first(psk);
- i ++;
- } while(FindNextFile(hFind, &ffdata));
- FindClose(hFind);
- }
- delete []pwMask;
- }
-
// кнопка Fn
if(m_keyCurrent->vk==_VK_FN)
{
@@ -1148,7 +1113,7 @@
// если нет обоев - рисуем кнопочку, если кнопка текущая - рисуем чёрную кнопочку
if(!m_hbmSkin || p==m_keyCurrent)
{
- COLORREF bgColor = p==m_keyCurrent ? RGB(0x00,0x00,0x00) : m_config->m_colors[p->group][1];
+ COLORREF bgColor = p==m_keyCurrent ? RGB(0xff,0xff,0xff) : m_config->m_colors[p->group][1];
CGdiObj>HBRUSH> hBrushBg2 = CreateSolidBrush(bgColor);
assert(hBrushBg2);
DrawButton(hdc, r0, hBrushBg2, hPenTL, hPenBR);
@@ -1158,13 +1123,13 @@
if (0!=p->data.pw)
{
SelectObject(hdc, m_hFontBig);
- COLORREF fgColor = p==m_keyCurrent ? RGB(0xFF,0xFF,0xFF) : m_config->m_colors[p->group][0];
+ COLORREF fgColor = p==m_keyCurrent ? RGB(0x00,0x00,0x00) : m_config->m_colors[p->group][0];
DrawText(hdc, p->data, &rText, DT_NOPREFIX | DT_BOTTOM | DT_LEFT, fgColor);

if (0!=p->desc.pw) // надстрочный знак
{
SelectObject(hdc, m_hFontIndex);
- COLORREF fgColor2 = p==m_keyCurrent ? RGB(0xFF,0xFF,0xFF) : m_config->m_colorDesc;
+ COLORREF fgColor2 = p==m_keyCurrent ? RGB(0x00,0x00,0x00) : m_config->m_colorDesc;
//if(0!=p->desc2.pw) // есть оба знака - надстрочный двинем чуть левее
// rText.right -= 7*m_vga;
DrawText(hdc, p->desc, &rText, DT_NOPREFIX | DT_RIGHT | DT_TOP, fgColor2);
@@ -1175,13 +1140,13 @@
{
rText.top += 4*m_vga;
SelectObject(hdc, m_hFontIndex);
- COLORREF fgColor2 = p==m_keyCurrent ? RGB(0xFF,0xFF,0xFF) : m_config->m_colorDesc2;
+ COLORREF fgColor2 = p==m_keyCurrent ? RGB(0x00,0x00,0x00) : m_config->m_colorDesc2;
DrawText(hdc, p->desc2, &rText, DT_NOPREFIX | DT_RIGHT | DT_TOP, fgColor2);
}
} else if (0!=p->desc.pw) // текст вроде 'Caps'
{
SelectObject(hdc, m_hFontSmall);
- COLORREF fgColor2 = p==m_keyCurrent ? RGB(0xFF,0xFF,0xFF) : m_config->m_colors[p->group][0];
+ COLORREF fgColor2 = p==m_keyCurrent ? RGB(0x00,0x00,0x00) : m_config->m_colors[p->group][0];
DrawText(hdc, p->desc, &rText, DT_NOPREFIX | DT_CENTER | DT_VCENTER, fgColor2);
}
}
@@ -1205,7 +1170,6 @@
{
m_keyCurrent = pKey;
if(0==m_nPopupDelay ||
- m_keyCurrent->vk==_VK_MOD ||
m_keyCurrent->vk==_VK_FN )
{
PopUp();



Долой стилус!