Como usar deadkeys no wine?
Estou trabalhando com a instalação e configuração do cliente Lotus Notes em linux utilizando o Wine.
Ocorreu tudo bem com a instalação, mas percebi que meu teclado não estava funcionando corretamente.
Utilizo um teclado ABNT2.
Não foi dificil perceber que a o Wine estava configurado para utilizar o teclado US-International e não estava acentuando.
Procurei informações na Internet sobre a configuração do teclado e percebi que existiam dicas de configuração para o teclado ABNT2. Basicamente, estas dicas mandavam alterar o mapa de teclado no arquivo keyboard.c do Wine.
Mesmo com as alterações o Wine enviava uma mensagem avisando que o leiaute do teclado não foi encontrado e estava utilizando uma configuração aproximada que era a ABNT2. A partir daí ele aceitava a configuração do meu teclado mas continuava não acentuando.
Foi então que procurei informações sobre o que estava acontecendo e adivinhe, não encontrei.
Em algumas listas de discussão percebi que várias pessoas tiveram o mesmo problema mas não encontrei nenhuma solução.
O que fazer? O jeito foi perder uma semana de trabalho aqui no laboratório de Universalização de Acesso a Internet – LUAR – do Departamento de Ciência da Computação do UFMG, onde sou estagiário, e tentar resolver o problema.
Estou escrevendo este texto pra explicar os erros que percebi, como fazer para alterar o código do Wine para aceitar as deadkeys e as dúvidas que ainda estou tendo.
1º Detecção de teclado
O Wine faz uma auto-detecção do leiaute do teclado que está configurado no X.
No arquivo wine-20020605/dlls/x11drv/keyboard.c
função: X11DRV_KEYBOARD_DetectLayout
O seguinte for:
for (current = 0; main_key_tab[current].comment; current++) {
Lê todos os caracteres do mapa de teclado configurado no X e compara com todos os mapas de teclado configurados no Wine estaticamente.
Para cada caracter do X não encontrado em um mapa de teclado do Wine é acrescentado um erro e no final o mapa que possuir menos erros é utilizado pelo Wine, se não houver nenhum erro é porque o teclado foi detectado corretamente.
Utilizando um TRACE e alterando o for para procurar apenas no mapa do teclado ABNT percebi o seguinte erro:
// for (current = 0; main_key_tab[current].comment; current++) {
for (current = 18; current < 19; current++) {
Trema não é aspas e acento agudo não é apóstrofe.
Para que o Wine faça a detecção correta do leiatue de teclado ABNT ou do US-International é necessário configurar os seguintes mapas de teclado.
/*** Brazilian ABNT-2 keyboard layout */
static const char main_key_PT_br[MAIN_LEN][4] =
{
"'\"","1!","2@","3#","4$","5%","6¨","7&","8*","9(","0)","-_","=+",
"qQ", "wW","eE","rR","tT","yY","uU","iI","oO","pP","´`","[{",
"aA", "sS","dD","fF","gG","hH","jJ","kK","lL","çÇ","~^","]}",
"zZ", "xX","cC","vV","bB","nN","mM",",<",".>",";:","/?",
"\\|"
};
/*** US international keyboard layout */
static const char main_key_US_intl[MAIN_LEN][4] =
{
"`~", "1!", "2@", "3#", "4$", "5%", "6^", "7&", "8*", "9(", "0)", "-_", "=+", "\\|",
"qQ", "wW", "eE", "rR", "tT", "yY", "uU", "iI", "oO", "pP", "[{", "]}",
"aA", "sS", "dD", "fF", "gG", "hH", "jJ", "kK", "lL", ";:", "´¨",
"zZ", "xX", "cC", "vV", "bB", "nN", "mM", ",<", ".>", "/?"
};
Se você não conseguir digitar o trema ou o acento agudo copie e cole de algum lugar (este texto por exemplo) e cole no mapa de teclado.
Não se esqueça de habilitar o WINE para fazer a detecção de todos os leiaures de teclado (descomentar a alteração do for)
Com estas alterações o Wine passa a detectar corretamente os leiautes de teclado mas algumas teclas, como a barra invertida e o pipe não funionam. Este erro ainda é um mistério pra mim. Fiz algumas tentativas pra tentar corrigi-lo mas ainda não está funcionado 100%, no final eu descrevo.
Agora vou falar das Deadkeys
Deadkeys são teclas que quando pressionadas não produzem um caracter, apenas servem de modificador para o próximo caracter, como os acentos, por exemplo.
No wine estas teclas produzem o caracter automaticamente.
Para solucionar este problema percebi o seguinte:
No código fonte da versão 20020605 do wine fiz as seguintes alterações:
Erro no mapeamento das deadkeys;
A função:
KEYBOARD_MapDeadKeysym
possui algumas opções no case que estão erradas, o que fiz foi comentar estas opções para que a função detecte corretamente as deadkeys. Po exemplo, no trecho:
#ifdef XK_dead_tilde
case XK_dead_tilde :
#endif
// case 0x1000FE7E : /* Xfree's XK_Dtilde */ /* Está errado */
return '~'; /* '? */
#ifdef XK_dead_acute
case XK_dead_acute :
#endif
// case 0x1000FE27 : /* Xfree's XK_Dacute_accent */ /* Está errado */
return 0xb4; /* '' */
Erro na produção do VM_CHAR
Na função:
X11DRV_ToUnicode
O wine espera que o resultado da chamada da função seja ret = 0 para uma deadkey.
ret = TSXLookupString(&e, (LPVOID)lpChar, 2, &keysym, NULL);
Assim ele faz o tratamento da deadkey,
Mas o resultado de ret é 1.
Assim o wine nunca faz o tratamento da deadkey.
Por isso alterei a função e o código ficou o seguinte:
INT X11DRV_ToUnicode(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
LPWSTR bufW, int bufW_size, UINT flags)
{
Display *display = thread_display();
XKeyEvent e;
KeySym keysym;
INT ret;
int keyc;
BYTE lpChar[2];
BYTE dead_char;
if (scanCode & 0x8000)
{
TRACE("Key UP, doing nothing\n" );
return 0;
}
e.display = display;
e.keycode = 0;
e.state = 0;
if (lpKeyState[VK_SHIFT] & 0x80)
{
TRACE("ShiftMask = %04x\n", ShiftMask);
e.state |= ShiftMask;
}
if (lpKeyState[VK_CAPITAL] & 0x01)
{
TRACE("LockMask = %04x\n", LockMask);
e.state |= LockMask;
}
if (lpKeyState[VK_CONTROL] & 0x80)
{
TRACE("ControlMask = %04x\n", ControlMask);
e.state |= ControlMask;
}
if (lpKeyState[VK_NUMLOCK] & 0x01)
{
TRACE("NumLockMask = %04x\n", NumLockMask);
e.state |= NumLockMask;
}
/* Restore saved AltGr state */
TRACE("AltGrMask = %04x\n", AltGrMask);
e.state |= AltGrMask;
TRACE_(key)("(%04X, %04X) : faked state = %X\n",
virtKey, scanCode, e.state);
/* We exit on the first keycode found, to speed up the thing. */
for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
{ /* Find a keycode that could have generated this virtual key */
if ((keyc2vkey[keyc] & 0xFF) == virtKey)
{ /* We filter the extended bit, we don't know it */
e.keycode = keyc; /* Store it temporarily */
if ((EVENT_event_to_vkey(&e) & 0xFF) != virtKey) {
e.keycode = 0; /* Wrong one (ex: because of the NumLock
state), so set it to 0, we'll find another one */
}
}
}
if ((virtKey>=VK_NUMPAD0) && (virtKey<=VK_NUMPAD9))
e.keycode = TSXKeysymToKeycode(e.display, virtKey-VK_NUMPAD0+XK_KP_0);
if (virtKey==VK_DECIMAL)
e.keycode = TSXKeysymToKeycode(e.display, XK_KP_Decimal);
if (!e.keycode)
{
WARN("Unknown virtual key %X !!! \n",virtKey);
return virtKey; /* whatever */
}
else TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
ret = TSXLookupString(&e, (LPVOID)lpChar, 2, &keysym, NULL);
// if (ret == 0)
dead_char = KEYBOARD_MapDeadKeysym(keysym);
if (dead_char)
{
MultiByteToWideChar(main_key_tab[kbd_layout].layout_cp, 0, &dead_char, 1, bufW, bufW_size);
ret = -1;
if (ret == 0)
{
char *ksname;
ksname = TSXKeysymToString(keysym);
if (!ksname)
ksname = "No Name";
if ((keysym >> 8) != 0xff)
{
ERR("Please report: no char for keysym %04lX (%s) :\n",
keysym, ksname);
ERR("(virtKey=%X,scanCode=%X,keycode=%X,state=%X)\n",
virtKey, scanCode, e.keycode, e.state);
}
}
}
else { /* !dead_char */
/* We have a special case to handle : Shift + arrow, shift + home, ...
X returns a char for it, but Windows doesn't. Let's eat it. */
if (!(e.state & NumLockMask) /* NumLock is off */
&& (e.state & ShiftMask) /* Shift is pressed */
&& (keysym>=XK_KP_0) && (keysym<=XK_KP_9))
{
*(char*)lpChar = 0;
ret = 0;
}
/* more areas where X returns characters but Windows does not
CTRL + number or CTRL + symbol*/
if (e.state & ControlMask)
{
if (((keysym>=33) && (keysym < 'A')) ||
((keysym > 'Z') && (keysym < 'a')))
{
*(char*)lpChar = 0;
ret = 0;
}
}
/* We have another special case for delete key (XK_Delete) on an
extended keyboard. X returns a char for it, but Windows doesn't */
if (keysym == XK_Delete)
{
*(char*)lpChar = 0;
ret = 0;
}
else if((lpKeyState[VK_SHIFT] & 0x80) /* Shift is pressed */
&& (keysym == XK_KP_Decimal))
{
*(char*)lpChar = 0;
ret = 0;
}
/* perform translation to unicode */
if(ret)
{
TRACE_(key)("Translating char 0x%02x from code page %d to unicode\n",
*(BYTE *)lpChar, main_key_tab[kbd_layout].layout_cp);
ret = MultiByteToWideChar(main_key_tab[kbd_layout].layout_cp, 0, (LPCSTR)lpChar, ret, bufW, bufW_size);
}
}
TRACE_(key)("ToUnicode about to return %d with char %x %s\n",
ret, bufW ? bufW[0] : 0, bufW ? "" : "(no buffer)");
return ret;
}
Com estas alterações as deadkeys funcioam corretamente.
Mas para o leiaute US_International com deadkeys, ainda havia um erro com o c cedilha.
Por isso fiz a seguinte alteração no arquivo
/wine-20020605/windows/message.c
Alterar a struct
static const struct accent_char accent_chars[] =
{
...
// {'\'', 'C', '\306'}, {'\'', 'c', '\346'},
{'\'', 'C', 'Ç'}, {'\'', 'c', 'ç'},
...
}
Tudo funcionou corretamente.
Probelmas com as VirtualKeys.
MAIN_LEN = 49
Era 48 mas o teclado ABNT possui uma tecla a mais que o teclado americano.
Para fazer meu teclado funcionar corretamente criei o seguinte mapa de teclas:
static const WORD main_key_vkey_qwerty_ABNT[MAIN_LEN] =
{
/* NOTE: this layout must concur with the scan codes layout above */
VK_OEM_3,VK_1,VK_2,VK_3,VK_4,VK_5,VK_6,VK_7,VK_8,VK_9,VK_0,VK_OEM_MINUS,VK_OEM_PLUS,
VK_Q,VK_W,VK_E,VK_R,VK_T,VK_Y,VK_U,VK_I,VK_O,VK_P,VK_OEM_4,VK_OEM_6,
VK_A,VK_S,VK_D,VK_F,VK_G,VK_H,VK_J,VK_K,VK_L,VK_OEM_8,VK_OEM_1,VK_OEM_5,
VK_Z,VK_X,VK_C,VK_V,VK_B,VK_N,VK_M,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_7,VK_OEM_2,
VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */
};
e alterei a chamada do mapa de teclas na tabela de leiautes para o meu mapa de teclas.
{"Brazilian ABNT-2 keyboard layout", 28591, &main_key_PT_br, &main_key_scan_qwerty, &main_key_vkey_qwerty_ABNT},
Não sei o que significam estas virtualKeys, esta alteração foi tentativa e erro mesmo (MARRETA)
Não sei se fiz corretamente, mas agora consigo usar minha barra invertida e meu pipe mas minha tecla DELETE esta funcionando como , se alguem souber como solucionar...
Espero que estas dicas sejam úteis. Principalmente para usuários brasileiros.
Você pode baixar os diffs:
ou ainda o codigo arquivo completo.
até mais.
rubinho/
16/10/2002
Alberto Rubens Beckler.
Mestrando em Ciência da Computação – UFMG
Laboratório de Universalização de Acesso à Internet – LUAR
Projeto do Computador Popular
Críticas, dúvidas e sugestões: