Simpla Tajpi

Nun la plej bona loko por preni la fontokodon de ĉi tiu programo estas: https://bitbucket.org/marcuscf/simplatajpi/src

Por tajpi la esperantajn literojn (eĥoŝanĝo ĉiuĵaŭde), mi uzas la programon Tajpi. Ĝi estas malgranda, malpeza kaj facile uzebla programeto. La nura problemo, kiun mi havis, estis tuj riparita post mia raporto (vidu ĉi tie). La aŭtoro estis tre ĝentila.

Tamen, mi volis ion pli. Verdire, mi volis ion malpli. Tajpi havas plurajn agordeblecojn, sed mi uzas nur la ikso-metodon. Tajpi havas klavkombinon por skribi rusajn literojn, kaj mi neniam uzas tion. En mia komputilo mi uzas mian propran klavaranĝon (verkita per MSKLC) kaj iuj versioj de Tajpi ĝuste detektas ĝin, kaj en aliaj ne. Ekzemple, Tajpi 2.82 uzas la lingvo-kodon por trakti kelkajn senpaŝajn klavojn, kaj tio funkcias bone por mi; sed Tajpi 2.87 uzas la klavar-kodon. Tio ŝajnas teknike pli ĝusta, sed tiumaniere mia memtajlorita klavaranĝo ne estas detektita. Ne indas peti korekton al la aŭtoro de Tajpi por klavararanĝo kiun nur mi uzas (mi nomas ĝin “Plurlingva Brazila ABNT2”). Pro tio, mi volis lerni la bazan funkciadon de Tajpi por kompili mian propran memtajloritan version, sed ĝi estas programita en Visual Basic. Mi ne havas Visual Basic por kompili ĝin.

Tial mi tradukis la kernon de Tajpi al la programlingvo C++ (kun malmultaj ŝanĝoj oni povas igi ĝin pura C), por mia privata uzo. Unu kroma avantaĝo estas ke ekzistas pluraj senpagaj C++kompililoj (kiel GNU GCC, MS Visual C++ Express, ktp), tiel iu ajn povas kompili ĝin, sen dependo de specifa kompililaro. Mi nomis mian version “Simpla Tajpi”. Ĝi ne estas preta por ĝenerala uzado, ĉar nenio estas agordebla, nenio aperas sur la ekrano dum ĝi funkcias, kaj oni devas perforte “mortigi” la programon per la task-administrilo de Windows (ne ekzistas klavkombino por fini la programon). Tamen, mi ŝatas ĝin ĉar ĝi estas tre malgranda kaj mi scias precize tion kion ĝi faras. Mi kreis ruleblan dosieron (.exe) je malpli ol 35 KiB, kaj ĝi uzas pli-malpli 500 KiB da memoro RAM dum ĝi funkcias (aŭ pli-malpli 860 kiB se en 64 bitoj). Per specialaj teknikoj, mi kredas ke eblas igi ĝin eĉ pli malgranda (mi rimarkis ke la .exe enhavas la nomojn de la monatoj, sed mi uzas nenian funkcion pri datoj!). Mi uzis MS VC++ 2010 Express en Windows 7.

Aktualigo 1: Vi povas kompili ĝin per mingw-w64 per ĉi tiu komando:
g++ SimplaTajpi.cpp -o SimplaTajpi.exe -O2 -s -mwindows -municode -DWINVER=0x0400

Aktualigo 2: Mi sukcesis krei ruleblan dosieron je nur 4 KiB kun MS VC++ 2010 Express. Por tio, oni devas forlasi la defaŭltan “entry point” kaj uzi sian propran “entry point”. Tio forlasas ĉiujn aferojn kiujn la defaŭlta biblioteko de C kaj C++ enhavas.

Aktualigo 3: (2011-05-30) Mi kopiis korekton publikitan en Tajpi 2.88 por multfadenaj programoj.

Aktualigo 4: (2013-02-02) Mi faris kelkajn ŝanĝojn: [1] por malĉapeligi vi povas tajpi ikson dufoje, [2] la programo nun pli bone ignoras kelkajn klavojn kiel Shift kaj Ctrl, [3] mi inkluzivis kod-ekzemplon por kompili sen la defaŭltaj bibliotekoj de C kaj C++.

Aktualigo 5: Nun la plej bona loko por preni la fontokodon de ĉi tiu programo estas: https://bitbucket.org/marcuscf/simplatajpi/src

Jen la tuta fontokodo. Laŭ la originala Tajpi, ĝi estas GPL. La ruleblan “.exe” mi ankoraŭ ne disponebligas. Mi ne certas ĉu ĝi estas sufiĉe utila. La programo aŭtomate konvertas de cx al ĉ, ktp. Se vi volas vere tajpi cx, tajpu cxx.

#define WIN32_LEAN_AND_MEAN

/* Cxi tiuj difinoj normale estas parto de la agordoj de la projekto.
Mi preferis kopii ilin cxi tien, tiel ke oni povas vidi la difinojn
kiuj estas necesaj, sen elsxuti aron da dosieroj por la projekto. */
#ifndef WIN32
# define WIN32
#endif

#ifndef _WINDOWS
# define _WINDOWS
#endif

#ifndef _UNICODE
# define _UNICODE
#endif

#ifndef UNICODE
# define UNICODE
#endif

/* La nuraj necesaj bibliotekoj estas user32 kaj kernel32.
Ni ecx ne bezonas la normajn bibliotekojn de C++! */

#include <windows.h>
#include <tchar.h>

// Eble la subaj difinoj estos utilaj, depende de la malnoveco
// de la .h-dosieroj de Windows kiujn vi uzos.
/*
#ifndef MAPVK_VK_TO_VSC
# define MAPVK_VK_TO_VSC 0
#endif

#ifndef MAPVK_VK_TO_CHAR
# define MAPVK_VK_TO_CHAR 2
#endif 
*/

HINSTANCE hInst;
wchar_t bufro;
UINT backScan;
HHOOK kbdHook;

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam);

inline void InstallHook()
{
    kbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInst, 0);
}

inline void UninstallHook()
{
    UnhookWindowsHookEx(kbdHook);
}

// Ofte, oni ja povas enmeti la esperantajn literojn
// en la fontokodon. Malgraux tio, estas pli fortike
// uzi rekte la unikodajn numerojn cxar ili ne dependas
// de la agordoj de la redaktilo nek de la kompililo.
const wchar_t
    CX = 264, cx = 265,
    GX = 284, gx = 285,
    HX = 292, hx = 293,
    JX = 308, jx = 309,
    SX = 348, sx = 349,
    UX = 364, ux = 365;

inline wchar_t KonvertiEo(wchar_t litero)
{
    switch(litero) {
        case L'C': return CX;     case L'c': return cx;
        case L'G': return GX;     case L'g': return gx;
        case L'H': return HX;     case L'h': return hx;
        case L'J': return JX;     case L'j': return jx;
        case L'S': return SX;     case L's': return sx;
        case L'U': return UX;     case L'u': return ux;
        default:   return litero; // mi devus diri "literon", cxu ne? ;-)
    }
}

// 2013-02-02: malkonverti kiam oni tajpas ikson dufoje
inline wchar_t MalkonvertiEo(wchar_t litero)
{
    switch(litero) {
        case CX: return L'C';     case cx: return L'c';
        case GX: return L'G';     case gx: return L'g';
        case HX: return L'H';     case hx: return L'h';
        case JX: return L'J';     case jx: return L'j';
        case SX: return L'S';     case sx: return L's';
        case UX: return L'U';     case ux: return L'u';
        default: return litero; // mi devus diri "literon", cxu ne? ;-)
    }
}

// Sendi BACKSPACEn plus esperantan literon
void SendiEo(wchar_t litero)
{
    const int LEN = 4;
    INPUT input[LEN];
    input[0].type = INPUT_KEYBOARD;
    input[0].ki.wVk = VK_BACK;
    input[0].ki.wScan = backScan;
    input[0].ki.dwFlags = 0;
    input[0].ki.time = 0;
    input[0].ki.dwExtraInfo = 0;

    input[1].type = INPUT_KEYBOARD;
    input[1].ki.wVk = VK_BACK;
    input[1].ki.wScan = backScan;
    input[1].ki.dwFlags = KEYEVENTF_KEYUP;
    input[1].ki.time = 0;
    input[1].ki.dwExtraInfo = 0;

    input[2].type = INPUT_KEYBOARD;
    input[2].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[2].ki.wScan = litero;
    input[2].ki.dwFlags = KEYEVENTF_UNICODE;
    input[2].ki.time = 0;
    input[2].ki.dwExtraInfo = 0;

    input[3].type = INPUT_KEYBOARD;
    input[3].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[3].ki.wScan = litero;
    input[3].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
    input[3].ki.time = 0;
    input[3].ki.dwExtraInfo = 0;

    SendInput(LEN, input, sizeof(INPUT));
}

// 2013-02-02: malkonverti kiam oni tajpas ikson dufoje
// Sendi BACKSPACEn plus litero1 plus litero2 (kutime litero2 estas ikso)
void MalsendiEo(wchar_t litero1, wchar_t litero2)
{
    const int LEN = 6;
    INPUT input[LEN];
    input[0].type = INPUT_KEYBOARD;
    input[0].ki.wVk = VK_BACK;
    input[0].ki.wScan = backScan;
    input[0].ki.dwFlags = 0;
    input[0].ki.time = 0;
    input[0].ki.dwExtraInfo = 0;

    input[1].type = INPUT_KEYBOARD;
    input[1].ki.wVk = VK_BACK;
    input[1].ki.wScan = backScan;
    input[1].ki.dwFlags = KEYEVENTF_KEYUP;
    input[1].ki.time = 0;
    input[1].ki.dwExtraInfo = 0;

    input[2].type = INPUT_KEYBOARD;
    input[2].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[2].ki.wScan = litero1;
    input[2].ki.dwFlags = KEYEVENTF_UNICODE;
    input[2].ki.time = 0;
    input[2].ki.dwExtraInfo = 0;

    input[3].type = INPUT_KEYBOARD;
    input[3].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[3].ki.wScan = litero1;
    input[3].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
    input[3].ki.time = 0;
    input[3].ki.dwExtraInfo = 0;

    input[4].type = INPUT_KEYBOARD;
    input[4].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[4].ki.wScan = litero2;
    input[4].ki.dwFlags = KEYEVENTF_UNICODE;
    input[4].ki.time = 0;
    input[4].ki.dwExtraInfo = 0;

    input[5].type = INPUT_KEYBOARD;
    input[5].ki.wVk = 0; // por unikodo, uzu cxiam 0
    input[5].ki.wScan = litero2;
    input[5].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
    input[5].ki.time = 0;
    input[5].ki.dwExtraInfo = 0;

    SendInput(LEN, input, sizeof(INPUT));
}

// Funkcias por la literoj C, G, H, J, S, U. Tio suficxas.
inline wchar_t SimplaMinuskligo(wchar_t litero)
{
    return litero + 32;
}

bool CxuMajuskle()
{
    bool shiftPremata = (GetKeyState(VK_SHIFT) & 0x80) != 0;
    bool capsLockSxaltita = (GetKeyState(VK_CAPITAL) & 1) != 0;
    return capsLockSxaltita ^ shiftPremata;
}

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    if(code == HC_ACTION) {
        KBDLLHOOKSTRUCT* hookStruct = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);

        // 2013-02-02: Ignori klavojn kiel Shift kaj Ctrl
        switch(hookStruct->vkCode) {
            case VK_LSHIFT:   case VK_RSHIFT:
            case VK_LMENU:    case VK_RMENU:
            case VK_LCONTROL: case VK_RCONTROL:
            case VK_CAPITAL:  case VK_NUMLOCK:
                return CallNextHookEx(kbdHook, code, wParam, lParam);
            // default: dauxrigi;
        }

        // 2013-02-02: Aldone se Ctrl aux Alt estas premataj, mi volas fari nenion
        if((GetKeyState(VK_CONTROL) & 0x80) || (GetKeyState(VK_MENU) & 0x80))
            return CallNextHookEx(kbdHook, code, wParam, lParam);

        if(wParam == WM_KEYDOWN) {

            HWND foregroundWin = GetForegroundWindow();
            /* Aktualigo en 2011-05-30: La auxtoro de Tajpi faris
            korekton en sia programo en la versio 2.88, cxar
            la sekva linio ne suficxas por iuj multfadenaj programoj.
            Mi retradukis liajn sxangxojn, vidu sube. */
            //HKL layout = GetKeyboardLayout(GetWindowThreadProcessId(foregroundWin, 0));

            DWORD remoteThreadId = GetWindowThreadProcessId(foregroundWin, 0);
            DWORD currentThreadId = GetCurrentThreadId();
            AttachThreadInput(remoteThreadId, currentThreadId, TRUE);
            HWND focusedWindow = GetFocus();
            AttachThreadInput(remoteThreadId, currentThreadId, FALSE);
            HKL layout = GetKeyboardLayout(GetWindowThreadProcessId(focusedWindow, 0));

            UINT map = MapVirtualKeyEx(hookStruct->vkCode, MAPVK_VK_TO_CHAR, layout);
            wchar_t signo = map & 0xffff;

            switch(signo) {
                case L'C': case L'G':
                case L'H': case L'J':
                case L'S': case L'U':
                    bufro = CxuMajuskle() ? signo : SimplaMinuskligo(signo);
                    break;

                case L'X':
                    switch(bufro) {
                        case L'C': case L'c':
                        case L'G': case L'g':
                        case L'H': case L'h':
                        case L'J': case L'j':
                        case L'S': case L's':
                        case L'U': case L'u':
                            UninstallHook();
                            bufro = KonvertiEo(bufro);
                            SendiEo(bufro);
                            InstallHook();
                            return 1;

                        // 2013-02-02: malkonverti kiam oni tajpas ikson dufoje
                        case CX: case cx:
                        case GX: case gx:
                        case HX: case hx:
                        case JX: case jx:
                        case SX: case sx:
                        case UX: case ux:
                            UninstallHook();
                            MalsendiEo(MalkonvertiEo(bufro), CxuMajuskle() ? L'X' : L'x');
                            bufro = 0;
                            InstallHook();
                            return 1;
                    }
                    bufro = 0;
                    break;

                default:
                    bufro = 0;
                    break;
            }
        }
    } else {
        bufro = 0;
    }
    return CallNextHookEx(kbdHook, code, wParam, lParam);
}

// Vi povas elekti la normalan wWinMain...
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
    hInst = hInstance;
    backScan = MapVirtualKey(VK_BACK, MAPVK_VK_TO_VSC);
    InstallHook();

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);
    } // Por fini la programon, simple mortigu gxin, per la administrilo de taskoj :-)

    UninstallHook();
    return (int) msg.wParam;
}

// ... aux vi povas agordi vian kompililon kaj "ligilon" (linker)
// por ne uzi la defauxltajn bibliotekojn de C kaj C++ kaj tiukaze
// ankaux agordu ilin por komenci la programon per cxi tiu funkcio:
/*
DWORD CALLBACK SimplaEntryPoint()
{
    // http://blogs.msdn.com/b/oldnewthing/archive/2011/05/25/10168020.aspx
    hInst = GetModuleHandle(0);
    backScan = MapVirtualKey(VK_BACK, MAPVK_VK_TO_VSC);
    InstallHook();

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);
    } // Por fini la programon, simple mortigu gxin, per la administrilo de taskoj :-)

    // Tion ni farus se ekzistus klavkombino por fini la programon.
    UninstallHook();
    ExitProcess(0);
    return 0;
}
*/
Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s