[LINUX] Flux d'entrée japonais XIM (X Input Method)

Créé en mars 2018

"Quelle est l'entrée japonaise du système X Window?" L'auteur qui le pensait a examiné le flux des contributions japonaises. Ceci est une phrase sur le langage C et la programmation X11. OS utilisé: ubuntu16.04

Programmes qui ne prennent pas en charge l'entrée japonaise

Tout d'abord, créez un programme pour saisir des caractères à partir du clavier.

example01.c


#include <stdio.h>
#include <locale.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

int main(void)
{
    Display* dpy = XOpenDisplay(NULL);

    Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, 0);
    XMapRaised(dpy, win);
    XSync(dpy, False);

    XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask);

    XEvent ev = {};
    while(1){
        XNextEvent(dpy, &ev);
        switch(ev.type){
            case KeyPress: {
                KeySym keysym = NoSymbol;
                char text[1024] = {};

				XLookupString((XKeyEvent *)&ev, text, sizeof(text) - 1, &keysym, NULL);
                printf("Got chars: (%s)\n", text);
				
                if(keysym == XK_Escape){
                    puts("Exiting because escape was pressed.");
                    return 0;
                }

            }
			break;
        }
    }

	return 0;
}

Les paramètres de compilation sont les suivants. gcc -o example01 example01.c -lX11

Lorsque vous le compilez et l'exécutez à partir d'un émulateur de terminal (tel qu'un terminal GNOME), les caractères que vous saisissez dans la fenêtre sont simplement imprimés sur l'émulateur de terminal.

Programme prenant en charge l'entrée japonaise

Ce programme est une amélioration du programme ci-dessus pour permettre l'entrée japonaise.

example02.c


#include <stdio.h>
#include <locale.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

int main(void){

#if 1
    setlocale(LC_ALL, "");
#endif

    Display* dpy = XOpenDisplay(NULL);

    Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, 0);
    XMapRaised(dpy, win);
    XSync(dpy, False);

#if 1
    XSetLocaleModifiers("");

    XIM xim = XOpenIM(dpy, 0, 0, 0);
    if(!xim){
        // fallback to internal input method
        XSetLocaleModifiers("@im=none");
        xim = XOpenIM(dpy, 0, 0, 0);
    }

    XIC xic = XCreateIC(xim,
                        XNInputStyle,   XIMPreeditNothing | XIMStatusNothing,
                        XNClientWindow, win,
                        XNFocusWindow,  win,
                        NULL);
    XSetICFocus(xic);
#endif

    XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask);

    XEvent ev = {};
    while(1){
        XNextEvent(dpy, &ev);

#if 1
        if(XFilterEvent(&ev, None) == True) continue;
#endif

        switch(ev.type){
            case KeyPress: {
                KeySym keysym = NoSymbol;
                char text[1024] = {};

#if 1
                Xutf8LookupString(xic, &ev.xkey, text, sizeof(text) - 1, &keysym, NULL);
                printf("Got chars: (%s)\n", text);
#else 
				XLookupString((XKeyEvent *)&ev, text, sizeof(text) - 1, &keysym, NULL);
                printf("Got chars: (%s)\n", text);
#endif
				

                // example of responding to a key
                if(keysym == XK_Escape){
                    puts("Exiting because escape was pressed.");
                    return 0;
                }
            }
            break;
        }
    }

  return 0;

}

Les paramètres de compilation sont les suivants. gcc -o example02 example02.c -lX11

Si vous pouvez saisir le japonais dans le programme X11 (par exemple, xterm), vous pouvez également saisir le japonais dans ce programme. (URL de référence pour ce programme https://gist.github.com/baines/5a49f1334281b2685af5dcae81a6fa8a)

J'ai ajouté quelques fonctions au programme, mais les quatre fonctions suivantes sont directement liées au serveur IM.

  XIM xim = XOpenIM()	//Connectez-vous au serveur IM avec le protocole XIM
  XIC xic = XCreateIC()	//Créer un contexte avec le serveur IM
  XSetICFocus()         //Définir le focus
  XFilterEvent()	//Envoyer l'événement au serveur de messagerie instantanée

Ensuite, l'événement clé est échangé avec le serveur IM des deux manières suivantes.

  XNextEvent()        //Recevoir les événements du serveur de messagerie instantanée
  XFilterEvent()      //Envoyer un événement au serveur de messagerie instantanée

(Le serveur de messagerie instantanée fait référence à fcitx, ibus, etc.)

Flux approximatif d'entrées japonaises

図.gif

Comme illustré, le programme X11 reçoit l'événement clé avec la fonction XNextEvent et décide d'envoyer l'événement au serveur IM avec la fonction XFilterEvent. Lorsque la fonction XFilterEvent envoie l'événement au serveur IM, elle renvoie la valeur de retour True.

Le programme X11 reçoit également les événements clés du serveur IM à l'aide de la fonction XNextEvent. Même si vous transmettez l'événement de ce serveur de messagerie instantanée à la fonction XFilterEvent, la valeur de retour sera renvoyée sous la forme False et ne sera pas renvoyée au serveur de messagerie instantanée.

(L'interaction entre le serveur de messagerie instantanée et IME n'est pas mentionnée dans cet article)

Serveur de messagerie instantanée

Allons un peu plus loin et découvrons le serveur de messagerie instantanée. Le serveur IM est implémenté à l'aide d'une fonction appelée IMdkit. J'ai trouvé le code du programme à l'URL ci-dessous, mais il n'est pas maintenu et j'obtiens une erreur de compilation. https://www.x.org/releases/unsupported/lib/IMdkit/

Après une recherche plus approfondie, j'ai trouvé qu'ibus et fcitx utilisent la fonction IMdkit avec leurs propres modifications, et j'ai pu télécharger la source IMdkit à partir de l'URL suivante. https://github.com/fcitx/fcitx/tree/master/src/frontend/xim/lib

L'exemple de programme de serveur de messagerie instantanée a été téléchargé à partir de l'URL suivante. https://www.x.org/releases/unsupported/lib/IMdkit/Xi18n_sample/

Compilation de sampleIM (serveur IM)

Étant donné que le code du programme téléchargé ne peut pas être compilé tel quel, j'ai préparé un IMdkit et un exemple de serveur IM avec quelques modifications à l'URL suivante. http://ai56go.ivory.ne.jp/sample/sampleIM.zip

Extrayez ce fichier zip et vous trouverez le répertoire de construction. Accédez à ce répertoire de construction et tapez la commande suivante pour créer le programme sampleIM.

chmod 755 build.sh
chmod 755 build2.sh
./build.sh
./build2.sh

(Le code source de sampleIM.c étant ancien, de nombreux messages d'avertissement sont affichés, mais le programme sampleIM est créé.)

Exécution de sampleIM (serveur IM)

export XMODIFIERS=@im=sampleIM
./sampleIM &
xterm

Pour démarrer xterm en tapant dans l'ordre. (Si vous n'avez pas l'indice, xterm, utilisez à la place example02 présenté ci-dessus. Les terminaux GNOME etc. communiquent avec le serveur IM par des méthodes autres que XIM, vous ne pouvez donc pas l'utiliser ici)

Si vous tapez «shift + space» dans xterm, puis tapez une lettre à partir du clavier, Les caractères saisis dans l'émulateur de terminal qui a démarré sampleIM s'affichent. Dans le cas d'un serveur de messagerie instantanée réel, il communique avec l'IME à ce moment pour traiter la conversion japonaise.

Si vous tapez «ctrl + k» «Ceci est une chaîne définie de IM.» «Et elle sera affichée dans xterm. En tapant «shift + space» à la fin, vous pouvez normalement entrer des caractères dans xterm.

Cet échantillonIM Entrée de commutation shift + space ctrl + k confirmé Cependant, comme il ne se convertit pas en japonais, la chaîne de caractères renvoyée par confirmation sera toujours la même.

Pour terminer sampleIM, vérifiez l'ID de processus avec ps et supprimez-le comme indiqué ci-dessous.

ps | grep sampleIM
kill (ID de processus)

Explication de sampleIM

Cet sampleIM a les fonctions minimales en tant que serveur IM. La conversion japonaise est un travail IME, je ne le couvrirai donc pas dans cet article.

En regardant le fonctionnement de sampleIM du point de vue du programme, Tout d'abord, à la ligne 398 de sampleIM.c     ims = IMOpenIM(dpy, Il y en a, et le serveur de messagerie instantanée est en cours d'initialisation. Ligne 401 comme argument de cette fonction IMOpenIM     IMServerName, imname, Il y a. Cette valeur imname est "sampleIM" Il doit correspondre à la valeur de ʻexport XMODIFIERS = @ im = sampleIM` saisie ci-dessus.

Puis à la ligne 425     IMProtocolHandler, MyProtoHandler, Et spécifiez la fonction de rappel (ici, la fonction MyProtoHandler).

Puis de 440 lignes     XNextEvent(dpy, &event); Reçoit un événement clé d'un programme X11 (tel que xterm) et Ligne 441 uniquement si l'événement provenait d'un programme X11     if (XFilterEvent(&event, None) == True)       continue; La fonction XFilterEvent de s'appelle la fonction MyProtoHandler, renvoie True comme valeur de retour et continue;.

249 lignes     MyProtoHandler(ims, call_data) Effectue le traitement en tant que serveur de messagerie instantanée. Si sampleIM est enrichi, cette fonction MyProtoHandler sera enrichie.

L'API d'IMdkit est décrite dans l'URL suivante. https://www.x.org/releases/unsupported/lib/IMdkit/doc/API.text

Les fonctions requises par IMdkit sont les suivantes et peu nombreuses.

XIMS IMOpenIM(Display display,...)
char *IMSetIMValues(XIMS ims,...)
char *IMGetIMValues(XIMS ims,...)
void IMCloseIM(XIMS ims)
int IMPreeditStart(XIMS ims, XPointer im_protocol)
int IMPreeditEnd(XIMS ims, XPointer im_protocol)
void IMForwardEvent(XIMS ims, XPointer im_protocol)
void IMCommitString(XIMS ims, XPointer im_protocol)
int IMCallCallback(XIMS ims, XPointer im_protocol)

finalement

Cette phrase expliquait le flux des entrées japonaises utilisant XIM. -Introduction d'une fonction (exemple de programme) pour la saisie du japonais. -Utilisez la fonction XNextEvent et la fonction XFilterEvent pour échanger des événements. -Le serveur IM utilise la fonction IMdkit. En outre, bien que l'exemple IMdkit d'origine soit dans un état où une erreur de compilation se produit, j'ai créé un exemple IMdkit qui résume afin qu'il n'y ait pas d'erreur.

Recommended Posts

Flux d'entrée japonais XIM (X Input Method)
Entrée japonaise avec pyautogui