# [X11] Contrôler X avec l'extension XTest

## TGL

Bon alors, puisqu'on peut se servir de ce forum pour remettre à plat les conclusions de discussions qu'on a trouvées intéressantes, voilà un petit résumé des évènements de ce topic, initié par LostControl qui se demandait comment zoomer sous Xorg, comme avec un Ctrl+Alt+Plus, mais depuis une simple commande, sans clavier.

L'extension XTest

L'extension XTest est une extension X11 qui permet de générer des évenements, comme des pressions de touches du clavier ou des mouvements de souris. C'est vraiment une extension "de base" sous Xorg ou XFree, et il est à peu près certains qu'elle est déjà prête à l'emploi chez vous. Cette commande vous le confirmera : 

```
grep -i xtest /var/log/Xorg.0.log
```

Les évenements XTest se distinguent de ceux qu'on utilise habituellement avec la Xlib en ça qu'ils ne sont pas destinés à une fenêtre/application particulière, mais au serveur X lui même. Ce sont donc des évenements de bas niveau, en toute chose similaires à ceux provenant d'un vrai périphérique, et qui suivront les même étapes de traitement ensuite.

Cette différence est importante dans l'exemple qui nous intérresse : si on génère, façon Xlib, un "Ctrl+Alt+Plus" à destination par exemple de la fenêtre qui a le focus, ça ne va provoquer un zoom de X, mais juste une tentative de raccourcis clavier pour l'application concernée.

On peut retrouver une documentation officielle de XTest par exemple ici.

Utiliser XTest en C

La première solution pour cette affaire de zoom de X a été trouvée en quelques lignes de C, inspirées par le programme numlockx, qui se sert lui de XTest pour générer un évenement clavier de "vérouillage du pavé numérique". Voilà donc xzoom.c dans sa version écrite par LostControl : 

```
#include <stdio.h>

#include <string.h>

#include <X11/extensions/XTest.h>

#include <X11/keysym.h>

#define NBR_KEYS 3

void executeKeys(Display *disp, unsigned int *keys, int length) {

   int i;

   /* press */

   for (i=0; i<length; i++) {

      XTestFakeKeyEvent(disp, XKeysymToKeycode(disp, keys[i]), True,

                  CurrentTime);

   }

   /* release */

   for (i=length-1; i>=0; i--) {

      XTestFakeKeyEvent(disp, XKeysymToKeycode(disp, keys[i]), False,

                  CurrentTime);

   }

}

int main(int argc, char **argv) {

   Display* disp = XOpenDisplay(NULL);

   unsigned int zoom_in[NBR_KEYS] = {XK_Control_L, XK_Alt_L, XK_KP_Add};

   unsigned int zoom_out[NBR_KEYS] = {XK_Control_L, XK_Alt_L, XK_KP_Subtract};

   if (disp == NULL) return 1;

   if (argc != 2) {

      printf("Please specify a parameter: in|out\n");

      return 1;

   }

   if (strcmp(argv[1], "in") == 0) {

      executeKeys(disp, zoom_in, NBR_KEYS);

   } else if (strcmp(argv[1], "out") == 0) {

      executeKeys(disp, zoom_out, NBR_KEYS);

   } else {

      printf("Unknown parameter. Please specify a parameter: in|out\n");

   }

   XCloseDisplay(disp);

   return 0;

}
```

 Ça se compile comme ça : 

```
% gcc -o xzoom xzoom.c -lX11 -lXtst
```

 ...et ça s'utilise comme ça  : 

```
% ./xzoom in
```

 (avec "in", on a l'équivalent d'un Ctrl+Alt+Plus, et avec "out", on aurait un Ctrl+Alt+Moins). 

Utiliser XTest en Python

Une deuxième solution est d'utiliser le module Python Xlib (dans portage, dev-python/python-xlib). Voilà un petit programme d'exemple, xkeystroke : 

```
#!/usr/bin/python

import sys

from Xlib import display, X, XK

def keystroke(sequence):

    global disp, valid_keys

    down_keycodes = []

    for key in sequence.split('+'):

        if key in valid_keys:

            keycode = disp.keysym_to_keycode(XK.string_to_keysym(key))

            disp.xtest_fake_input(X.KeyPress, keycode)

            down_keycodes.append(keycode)

        else: print >>sys.stderr, "%s: invalid key name" % key

    while down_keycodes:

        keycode = down_keycodes.pop()

        disp.xtest_fake_input(X.KeyRelease, keycode)

valid_keys = {}

for name in dir(XK):

    if name[:3] == 'XK_':

        valid_keys[name[3:]] = None

disp = display.Display()

for sequence in sys.argv[1:]:

    keystroke(sequence)

    disp.sync()
```

 On peut lui demander de simuler n'importe quel séquence de touches clavier : 

```
% ./xkeystroke Shift_L+a b Shift_L+c

AbC
```

 La séquence "AbC" arrive ici jusqu'au xterm depuis lequel on a lancé le programme, parceque le serveur X n'a rien à faire de ces évenements, et donc il transmet au Window Manager, qui n'en a rien à faire non plus, et qui donc les transmet à la fenêtre ayant le focus, ici notre xterm, qui lui enfin les accepte et réagit.

Pour faire un "zoom in", on utiliserait : 

```
% ./xkeystroke Ctrl_L+Alt_L+KP_Add
```

 Notez qu'il faut utiliser des noms de touches valides pour Xorg (pour le "-" du pavé numérique, ça serait "KP_Substract").

Utiliser XTest sans le savoir

Avec un peu de retard (mais c'est tant mieux), on a en fait trouver un programme qui à pour vocation de générer des évennements via l'extension XTest. Il s'agit de x11-misc/xautomation, qui fournie la commande xte. Avec cette commande, notre "zoom in" pourrait s'écrire : 

```
% xte 'keydown Control_L' 'keydown Alt_L' 'key KP_Add' 'keyup Alt_L' 'keyup Control_L'
```

 Oui, c'est un peu long, mais c'est parcequ'on décompose chaque pression puis relâche des touches (sauf pour celle du milieu, 'key KP_Add' étant un raccourcis pour la séquence 'keydown KP_Add' 'keyup KP_Add'). Dans le cas du zoom, on peut bien sûr se faciliter un peu la tâche avec un petit script xzoom.sh qui prendrait juste "in" ou "out" en paramètre, et génèrerait les séquences de touches correspondantes : 

```
#!/bin/bash

prog=$(basename "${0}")

xte=$(which xte 2>/dev/null)

if [[ -z "${xte}" ]] ; then

    echo "This scripts requires \"xte\" from \"x11-misc/xautomation\"" >&2

    exit 1

fi

mod_down[0]="keydown Control_L"

mod_down[1]="keydown Alt_L"

in[0]="key KP_Add"

out[0]="key KP_Substract"

mod_up[0]="keyup Control_L"

mod_up[1]="keyup Alt_L"

if [[ "${1}" == "in" || "${1}" == "out" ]]; then

    eval ${xte} \"\${mod_down[@]}\" \"\${${1}[@]}\" \"\${mod_up[@]}\"

else

    echo "Usage: ${prog} <in|out>" >&2

    exit 2

fi
```

Notez que le programme xte offre vraiment un accès très complet aux fonctionnalités de XTest, et permet aussi de générer par exemple des mouvements du curseur de la souris. Associé à visgrep, une autre commande du même paquet mais non liée à XTest elle et qui permet de trouver l'emplacement d'éléments graphiques à l'écran, on a ainsi une véritable solution pour automatiser des actions sur des GUI sous X.

----------

## Enlight

Ah bah, grande classe les deux!!!

----------

## Dominique_71

Petite question bête et méchante: est-il possible de ne zoomer qu'une fenêtre avec ça? Chaque fois que j'essaie, cela fait la même chose que Ctrl-Alt-+.

Il y a une application qui fait presque cela, façon xmag mais en mieux: xzoom, et ses dépendances sont la glib et X. http://packages.debian.org/unstable/x11/xzoom

----------

## lalebarde

 :Very Happy:   :Very Happy:   :Very Happy:   Mille fois merci TGL !  :Very Happy:   :Very Happy:   :Very Happy:  . 

Ca faisait une vingtaine d'heure cumulées que je recherchait ça sur la toile.

----------

