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:


keyboard.c.diff

message.c.diff


ou ainda o codigo arquivo completo.

keyboard.c

message.c

RPMS


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:

rubinho@dcc.ufmg.br