Commençons par exécuter à nouveau le programme du chapitre
précédent pour entendre le doux son de Windows. Mais hélas, nous recevons notre
premier message d’erreur :
PuTTY X11 proxy: unable to connect to forwarded X server:
Network error: Connection refused
Serveur X non trouvé.
Soit nous n’avons pas utilisé la connexion créee au chapitre
précédent (Par exemple dans mon Putty, j’ai plusieurs connexions) ou le serveur
XMing sur le PC doit être relancé. En effet vous avez pu éteindre le PC et
oubliez au redémarrage de relancer le serveur XMing. D’ailleurs le mieux c’est
de le lancer systématiquement en début de session en plaçant un raccourci dans
le menu démarrer.
Cette vérification faite, nous pouvons travailler sur notre
deuxième programme où nous allons essayer d’afficher une fenêtre en utilisant
la fonction XCreateSimpleWindow.
Pour cela, nous devons effectuer les étapes suivantes :
Ouvrir
une connexion au serveur d’affichage par XOpenDisplay
Récupérer
des informations dans la structure d’affichage (Display)
Créer
la fenêtre par XCreateSimpleWindow en lui passant 9 paramètres.
Afficher
la fenêtre par XMapWindow
Mettre
en place un gestionnaire d’évènements (et oui il faut bien arriver à fermer la
fenêtre avec la souris) fonction XNextEvent
Fermer
et détruire la fenêtre par XDestroyWindow
Fermer
la connexion au serveur par XCloseDisplay.
Etape 1 : Après l’appel de la fonction XOpenDisplay nous récupérons un pointeur vers la structure
Display dont la description en langage C est la suivante (toutes les structures
sont décrites dans l’entité xlib.h) :
_XDisplay
{
XExtData
*ext_data; /* hook for extension to
hang data */
struct
_XPrivate *private1;
int fd; /* Network
socket. */
int
private2;
int
proto_major_version;/* major version of server's X protocol */
int
proto_minor_version;/* minor version of servers X protocol */
char
*vendor; /* vendor of
the server hardware */
XID private3;
XID
private4;
XID
private5;
int
private6;
XID
(*resource_alloc)( /* allocator function
*/
struct
_XDisplay*
);
int
byte_order; /*
screen byte order, LSBFirst, MSBFirst */
int
bitmap_unit; /* padding and
data requirements */
int
bitmap_pad; /*
padding requirements on bitmaps */
int
bitmap_bit_order; /* LeastSignificant or
MostSignificant */
int
nformats; /* number of
pixmap formats in list */
ScreenFormat
*pixmap_format; /* pixmap
format list */
int
private8;
int
release; /* release
of the server */
struct
_XPrivate *private9, *private10;
int
qlen; /*
Length of input event queue */
unsigned
long last_request_read; /* seq number of last event read */
unsigned
long request; /* sequence
number of last request. */
XPointer
private11;
XPointer
private12;
XPointer
private13;
XPointer
private14;
unsigned
max_request_size; /* maximum number 32 bit words in request*/
struct
_XrmHashBucketRec *db;
int
(*private15)(
struct
_XDisplay*
);
char
*display_name; /*
"host:display" string used on this connect*/
int
default_screen; /* default screen
for operations */
int
nscreens; /* number of
screens on this server*/
Screen
*screens; /* pointer to list of
screens */
unsigned
long motion_buffer; /* size of motion
buffer */
unsigned
long private16;
int
min_keycode; /* minimum defined
keycode */
int
max_keycode; /* maximum defined
keycode */
XPointer
private17;
XPointer
private18;
int
private19;
char
*xdefaults; /* contents of
defaults from server */
/*
there is more to this structure, but it is private to Xlib */
}
Wauoh !!
complexe non !! Pour l’instant, nous allons nous contenter de repérer
les infos intéressantes et de les récupérer par un déplacement à ajouter au pointeur retourné par
xopenDisplay. Par la suite nous utiliserons les descriptions de structures que
j’ai traduites en formalisme assembleur (merci qui ?).
Ici nous avons besoin de récupérer un pointeur vers l’écran (screen)
de l’affichage. Cette information se trouve au déplacement 140. Ce pointeur pointe
sur une structure de type screen dont voici la description en C :
typedef struct {
XExtData
*ext_data; /* hook for extension to
hang data */ 0
struct
_XDisplay *display;/* back pointer to display structure */ 4
Window
root; /* Root window id.
*/ 8
int
width, height; /* width and
height of screen */
12 et 16 ?
int
mwidth, mheight; /* width and height
of in millimeters */ 20
et 24
int
ndepths; /* number of
depths possible */ 28
Depth
*depths; /*
list of allowable depths on the screen */
32
int
root_depth; /*
bits per pixel */
36
Visual
*root_visual; /* root visual */ 40
GC
default_gc; /* GC for the
root root visual */ 44
Colormap
cmap; /*
default color map */
48
unsigned
long white_pixel; 52
unsigned
long black_pixel; /* White and
Black pixel values */ 56
int
max_maps, min_maps; /* max and min
color maps */
int
backing_store; /* Never,
WhenMapped, Always */
Bool
save_unders;
long
root_input_mask; /* initial root input
mask */
} Screen;
Nous trouvons des informations intéressantes comme l’identification
de la fenêtre mère (position8), la taille de l’écran(position 12 et 16), le nombre
de bits de codage d’un pixel (position 36 les codes RGB du pixel blanc et du
pixel noir (position 52 et 56). Nous
mettons ces infos dans les registres r1 r3,r4 et r5 ( affichage du contenu par
la macro d’affichage des registres pour vérification).
Maintenant, nous pouvons créer la fenêtre avec la fonction
XCreateSimpleWindow dont la signature en C est la suivante :
Window
XCreateSimpleWindow(display, parent, x, y, width, height, border_width,
border, background)
Display *display;
Window parent;
int x, y;
unsigned int width, height;
unsigned int border_width;
unsigned long border;
unsigned long background;
display
Specifies the connection to the X server.
parent
Specifies the parent window.
xy
Specify the x and y
coordinates, which are the top-left outside corner of the new
window’s borders and
are relative to the inside of the parent window’s borders.
width
height
Specify the width and height, which are the created window’s
inside dimensions
and do not include the
created window’s borders. The dimensions must be
nonzero, or a BadValue
error results.
border_width
Specifies the width of the created window’s border in pixels.
border
Specifies the border pixel value of the window.
background Specifies
the background pixel value of the window.
Donc nous mettons dans r0, le pointeur du Display, dans r1 l’identification
de la racine récupérée dans la structure écran précédente, zéro dans r2 et r3 pour
la position X et Y de la fenêtre et stop !!
nous ne pouvons pas alimenter les autres registres pour passer les paramètres.
Il nous faut respecter la normalisation des appels de fonctions standards (voir
le chapitre ) ou seuls les 4 premiers registres sont utilisés pour passer ces
paramètres, les autres doivent être passés par la pile. Et il nous faut aussi
respecter que la pile soit toujours alignée sur une frontière de double mot et
donc empiler un nombre de registre pair. Ici, il y a 9 paramètres à passer dont
4 par les registres et il en reste 5 donc il faut faire un push de plus pour
avoir un nombre pair. C’est pourquoi nous commençons par mettre zero dans le
registre r8 et nous effectuons un push de celui-ci. Puis nous passons les
autres paramètres et dans l’ordre inverse demandé dans la signature de la
fonction. Nous passons la valeur du pixel blanc comme valeur du pixel du fond d’écran,
le pixel noir pour la bordure puis la valeur 5 pour la taille de la bordure
(par l’intermèdiaire du registre r8) puis la hauteur et la largeur de la fenêtre.
Ouf !!
Après l’appel de la fonction, nous réalignons la pile par l’instruction
add sp,#24 pour compenser les 6 push de 4 octets et nous testons le code retour
de la fonction. Ceci sera à faire après chaque appel de fonction X11 pour
vérifier si tout c’est bien passé et éviter des recherches laborieuses.
Si tout est OK, nous récupérons un pointeur vers une
structure de type fenêtre (window), pointeur que nous allons utiliser pour
afficher notre fenêtre par XMapWindow.
Puis nous avons une boucle qui va gérer les évènements reçus
dans l’affichage. Ici nous nous contentons d’appeler la fonction XNextEvent en
lui passant le pointeur du Display et un pointeur vers une zone de mémoire qui
stockera l’évènement reçu. Remarque : si cette fonction n’est pas appelée,
la fenêtre ne sera pas affichée. Eventuellement elle peut être affichée en
appelant la fonction Xflush et sans la boucle.
Comme on ne gère aucun évènement, le programme ne fait rien
de plus.
Au lancement du programme, une fenêtre doit apparaitre sur
votre PC avec le titre Xming et les menus système habituels. Vous pouvez
déplacer la fenêtre, la réduire, la redimensionner comme toute fenêtre. Vous la
fermer en cliquant sur la X et vous avez le magnifique message suivant :
XIO: fatal IO error
11 (Resource temporarily unavailable) on X server "localhost:10.0"
after 8 requests
(6 known processed) with 0 events remaining.
Nous verrons au chapitre suivant comment éliminer cette
erreur.
Enfin pour terminer le chapitre, rappelons quelques
principes de X11 :
Toute fenêtre est contenue dans
une fenêtre mère (ou racine)
Toute fenêtre fille est contenue
dans une fenêtre mère ou est tronquée
Une fenêtre parent a toujours un
titre.
Les boutons, menus, boites de
dialogues sont tous des fenêtres.
Les positions et tailles des fenêtres
sont mesurées en pixels.
Chaque fenêtre à son propre système
de coordonnées.
Une autre question se pose : cette fenêtre s’affiche-t-elle
aussi sur l’écran du Raspberry ? Et bien, il faut brancher l’écran, le clavier
, relancer le Raspberry, et à partir de l’écran graphique rechercher dans l’arborescence
de fichier le programme et l’éxecuter. Mais on peut aussi voir une image de l’écran
avec la solution proposée pour les tests du Framebuffer avec le serveur x11vnc
sur le raspberry et le client tightvncViewer sur Windows et ça fonctionne.
Exercice :
modifier la position et la taille de la fenêtre
Modifier
la couleur du fond de la fenêtre.
La
taille de la bordure ne semble pas fonctionner (n’est pas large de 5 pixels
dans notre exemple !!), rechercher pourquoi ?
Dans la
structure Display, il y a un pointeur vers une chaine de caractères qui donne
le nom du vendeur, afficher cette chaine. Vous pouvez aussi afficher les
versions du système X11.