mercredi 27 juin 2018

Chapitre 37 : gestion des menus avec X11


Ca y est, j’ai trouvé comment centrer les fenêtres principales que nous créons depuis le raspberry en assembleur, sur l’écran du PC. En effet j’avais fait une erreur dans la lecture du tutoriel xlibbook, car je pensais que les paramètres USPosition et USSize concernait des particularités pour les Etats-Unis. En fait US signifie user (utilisateur) et le passage de ces paramètres au gestionnaire d’écran permet le centrage de la fenêtre . Pour cela nous utilisons la fonction XSetWMNormalHints que nous appelons après la création de la fenêtre et avant son affichage. Cela ne change pas grand-chose par rapport à l’appel de XMoveWindow car le résultat sera le même.
Dans ce nouveau programme, nous allons voir la création de menus  qui sont des fenêtres et même qui sont pratiquement identiques à des boutons !!! Donc nous retrouvons, la routine de connexion au serveur X11, la création de la fenêtre principale dans laquelle nous avons corrigé le problème du centrage, le chargement des polices utilisées, la création des contextes graphiques, la création d’une fenêtre d’affichage des messages et son bouton OK. J’ai corrigé une erreur que j’avais faite dans le programme précédent sur le centrage du libellé du bouton. En effet j’avais calculé l’écart entre la taille du bouton en pixel et la taille du libellé en caractères, ce qui est faux. Il faut calculer la taille du texte en pixel et cette taille est fonction de la police utilisée. Il faut appeler la fonction XTextWidth pour la longueur et XTextExtents pour la hauteur. 
Remarque : le chargement des polices de caractères alimente une structure de données. Pour certaines fonctions X11, il faut passer le pointeur sur cette structure, pour d'autres, il faut passer l'identification de la police qui se trouve au déplacement +4 de la structure.
Ensuite nous créons les menus principaux au dessus d’une ligne de séparation puis les sous menus des menus Fichier et Fonction. Les sous menus se distinguent des menus et boutons par le fait qu’ils ne sont pas affichés à la création de la fenêtre mais lors de l’appui sur le menu principal. Vous remarquez que les routines de création se ressemblent toutes et donc qu’il est possible de regrouper tout cela dans les mêmes routines. Je me rends compte aussi que d’avoir créée une structure appelée bouton n’était pas une bonne idée !! Il valait mieux partir de la structure X11 Windows et ajouter les quelques données dont nous avons besoin pour les boutons ou les menus.
Maintenant la partie intéressante !! : la gestion des évènements de toutes ces fenêtres. Pour la fenêtre principale nous retrouvons les évènements de fermeture et d’affichage déjà vus. Nous ajoutons des routines de masquage des sous menus lorsque la souris repasse dans la fenêtre principale.
Ensuite pour chaque menu principal, nous trouvons les routines pour changer la bordure du menu lorsque la souris entre ou sort du menu. Et nous trouvons l’action à entreprendre à chaque clic sur le menu.
Pour le menu Aide, nous nous contentons d’afficher la fenêtre de messages avec un texte d’aide. Hélas, je me suis rendu compte que l’on ne pouvait pas utiliser le retour ligne pour afficher plusieurs lignes de texte. Je vous laisse le soin de trouver une amélioration pour afficher une ou plusieurs lignes dans cette fenêtre.
Pour le clic sur le menu Fonction, nous affichons 2 sous menus qui vont permettre de modifier le code fonction associé à un Contexte graphique. En effet, si nous dessinons sur les mêmes pixels avec le même contexte graphique, il est possible de choisir comme va être le pixel résultat et donc obtenir des effets surprenants au niveau des couleurs des dessins. Ici je n’ai crée que 2 sous menus pour la fonction and et xor et je vous laisse le soin de créer les autres sous menus de toutes les fonctions. J’ai mis au début du programme les constantes de chaque fonction.
Pour le clic sur le menu Fichier, nous affichons 3 sous menus, un pour quitter le programme, un pour dessiner un cercle de couleur verte et un pour dessiner un polygone de couleur bleue. Les 2 dessins se recouvrent partiellement pour voir les résultats des modifications des fonctions du Contexte graphique. Attention, il y a quelques petites astuces pour les dessins : pour le cercle les angles sont comptés en 64 iéme de degré et donc il faut mettre la valeur 64 * 360 pour avoir un cercle complet. Pour les polygones, il faut déclarer les sommets par des coordonnées en 16 bits (directive .hword). Vous remarquerez que dans chaque routine, nous changeons de couleur du dessin par la fonction XSetForeground ce qui est bien commode.
Lors de l’exécution, le passage de la souris sur les menus s’effectuent correctement avec une légère difficulté si la souris passe sur la fenêtre principale avant d’accéder à un sous menu : les sous menus disparaissent !! Peut être il est préférable de les fermer lorsque la souris passe sur un autre menu principal. Les libellés des menus s’effacent aussi dans le cas de recouvrement des fenêtres, il faudrait donc gérer l’évenement Expose pour chaque menu.
Comme les autres programmes, celui-ci peut être grandement amélioré !!! A vous de jouer !!
Voici le résultat sans avoir choisi de fonction and ou xor :

samedi 23 juin 2018

Chapitre 36 : X11 création d'une fenêtre de saisie de texte


Dans ce programme, nous allons créer une zone de saisie de texte car il n’y a pas de fonction dans la librairie X11 pour le faire. Mais au lieu de créer la zone dans la fenêtre principale, nous allons la créer dans une fenêtre autonome puis nous afficherons le résultat dans une autre fenêtre autonome.
Dans ce programme, nous effectuerons aussi quelques modifications pour rendre les routines plus autonomes et permettre leur réutilisation ultérieure. Comme tout programmes X11, nous commençons par ouvrir une connexion au serveur et  à créer la fenêtre principale. Puis nous créons un bouton ok au milieu de la fenêtre principale. L’appui de ce bouton lancera la création d’une fenêtre de saisie qui contiendra une zone de saisie (qui est une fenêtre) et un bouton de validation. Vous remarquez que la zone de saisie est rattachée à la fenêtre de saisie alors que cette dernière est rattachée à la fenêtre racine. Vous remarquez aussi que la zone se positionne bien du premier coup dans la fenêtre de saisie alors que cette dernière nécessite l’appel à la fonction XMoveWindow. Y a un truc qu’il faut que je trouve !!!
Ensuite nous créons une fenêtre d’affichage qui ne contiendra qu’un seul bouton OK. Vous voyez que je passe dans le registre r3 un code de valeur 0. C’est pour une future amélioration, où avec d’autres valeurs, j’afficherais soit un bouton soit 2 boutons OUI/NON soit 3 boutons OUI/NON/ANNULER.
Dans toutes ces routines, les écritures des libellés et texte sont effectuées dans des sous-routines, en effet, il faudra réécrire ces éléments dans le cas où les fenêtres sont masquées par d’autres fenêtres puis redécouvertes (gestion du message Expose).
Et maintenant la partie la plus importante : la gestion des évènements : il nous faut gérer les actions sur 3 fenetres, sur 3 boutons et sur la zone de saisie !!. Dans la boucle des évènements, nous commençons donc à tester quelle fenêtre et quel bouton est concerné par l’évènement. Ensuite pour chaque élément, nous regardons quel évènement arrive (touche, appui souris, etc..) et nous effectuons l’action en conséquence.
Pour la fenêtre principale, nous ne gérons que la fermeture (qui termine le programme) et le masquage de la fenêtre pour réafficher le texte du bouton.
Pour la fenêtre de saisie, nous gérons la fermeture qui détruit la fenêtre ( mais ne termine pas le programme ) et le masquage de la fenêtre.
Pour la fenêtre de message, nous gérons aussi la fermeture et le masquage. Dans ce cas nous réaffichons le texte du bouton OK et le texte du message.
Pour la zone de saisie, nous gérons l’appui sur une touche, et le passage de la souris sur la zone. Dans ce dernier cas, nous augmentons simplement la taille de la bordure pour montrer qu’elle est sélectionnée et que la saisie est possible. Pour l’appui d’une touche, il nous faut tester s’il s’agit d’un caractère ou d’une autre touche. Pour le caractère, il faut l’ajouter à un buffer de saisie et réafficher le texte dans la zone de saisie (Et ben oui, il faut tout faire !!) Ici nous utilisons une autre fonction XDrawText pour écrire le texte. Pour les touches, nous ne gérons que le retour arrière pour effacer le dernier caractère saisi. Et nous gérons aussi un curseur.
Ensuite, pour l’appui sur le bouton Ok de la fenêtre principale, nous appelons la création de la fenêtre de saisie avec sa zone.
Pour l’appui du bouton de la fenêtre de saisie, nous supprimons la fenêtre de saisie et nous créons la fenêtre de message en passant le buffer de saisi pour afficher le texte. Ici on pourrait aussi forcer un zéro binaire pour terminer proprement la chaine de caractère si elle doit être utilisée ultérieurement (par exemple comme paramètre).
Pour l’appui du bouton de la fenêtre de message, nous supprimons cette fenêtre.
Ce programme peut être amélioré en ajoutant d’autres boutons comme annuler, en améliorant les fonds des fenêtres pour les distinguer les unes des autres ainsi que les boutons, agrandir les polices utilisées pour améliorer la lisibilité etc.
Maintenant je vais essayer d'éclaircir le problème du centrage des fenêtres.

jeudi 14 juin 2018

Chapitre 35 : Gestion d’un bouton de la fenêtre, modification police de caractères


Après ces créations de fenêtres, nous allons utiliser toutes les fonctions de création de boutons, de listes déroulantes, de boites de dialogue disponibles dans X11 etc.  Ah Ah Ah, je rigole bien sûr car rien de tout cela n’est fourni, c’est à nous à tout programmer à partir des fonctions de base !!
Donc nous allons ajouter un bouton dans notre fenêtre X11 et voir comment gérer l’appui sur ce bouton pour afficher une ligne de texte. Nous allons aussi modifier la police et la couleur de ce texte.
Pour commencer, j’ai restructuré complétement le programme pour le décomposer en sous-routines plus facilement duplicables en fonction des besoins. Nous trouverons donc une routine pour créer la connexion au serveur X11, une routine pour créer, afficher et configurer la fenêtre, une routine pour créer les contextes graphiques et une routine pour gérer les évènements de la souris. Nous dessinons une ligne avec le contexte graphique simple, ligne horizontale qui traverse toute la fenêtre et dont la longueur variera en fonction de la largeur de la fenêtre. Nous écrivons aussi une ligne de texte avec le contexte simple pour comparaison avec la suite.
A toutes ces routines qui reprennent le code des programmes précédents, nous ajoutons une routine pour créer un bouton. Par rapport aux autres routines, nous lui passons les paramètres de configuration car il est probable que par la suite, les programmes comportent plusieurs boutons. Pour regrouper toutes les informations sur le bouton, j’ai déclaré une structure BT qui n’est pas une structure d’origine X11 et dont on réserve la place en mémoire.
Dans la routine creationBouton, nous créons une fenêtre par la procèdure XCreateSimpleWindow puisque tout bouton est une fenêtre X11. Nous rattachons cette fenêtre à la fenêtre mère créée dans la routine creationfenetre et non pas à la fenêtre racine. Nous autorisons des actions particulières sur ce bouton avec les options ButtonPressMask,  ButtonReleaseMask, LeaveWindowMask, EnterWindowMask qui permettent au gestionnaire d’évènements de recevoir les appuis, relâchements et passages de la souris.
Puis nous créons un contexte graphique spécifique au bouton, puisqu’il faudra bien le distinguer du reste de la fenêtre puis nous l’affichons. En fin nous affichons le libellé du bouton par une sous-routine ecritureTexteBouton. Dans cette dernière, nous calculons le positionnement du texte à partir des données de la structure bouton pour que celui-ci soit centré à l’intérieur du bouton. Nous utilisons une sous-routine car il faudra l’appeler à nouveau en cas de redimensionnement de la fenêtre.
Dans la routine de gestion des évènements nous ajoutons les événements EnterNotify et LeaveNotify et la routine boutonAppel dans la partie de l’évènement ButtonPress. Dans celle-ci, nous commençons à vérifier que l’événement reçu correspond bien au bouton car en effet, il peut y avoir plusieurs boutons sur la fenêtre ou simplement vous avez cliqué en dehors du bouton et dans ce cas, il ne faut rien faire. Si le bouton est ok, on appelle la sous routine ecritureTexte2 pour écrire un texte dans la fenêtre et la sous routine modifBoutonAppui pour modifier l’aspect du bouton : pour faire simple j’inverse les pixels du fond et de la bordure.
Pour le texte, nous utilisons un deuxième contexte graphique (crée dans la routine creationGC) pour avoir une couleur et une police différente. Pour la couleur c’est la même chose que dans l’exemple précèdent pour afficher une ligne colorée. Pour la police, c’est plus compliqué car il nous faut récupérer une police existante dans le système graphique X11 et respecter un nom précis. Ici la police recherchée par la procédure XLoadQueryFont s’appelle
 -*-helvetica-bold-*-normal-*-14-* . Je vous conseille de lire la documentation sur la normalisation des polices X11. En plus, il est très possible que cette police ne soit pas accessible sur votre système et il vous faudra modifier ce nom ou sa taille (ici c’est 14 pixels par pouce). Donc, il vous faudra soit charger sur le raspberry un package supplémentaire des polices X11, soit les polices additionnelles du serveur Xming (en effet à ce jour, j’ai un doute sur qui fournit les polices lors de l’affichage !!). Vouai !! mais comment trouver le nom d’une police ? Et bien, vous disposez sur le raspberry du programme xfontsel qui vous permet de trouver une police valide. Sur mon système ce logiciel trouve plus de 6000 noms !!! et en cochant le bouton select puis les noms de famille, de type et de taille, vous arrivez à trouver une police qui convienne. Dans ce cas, vous reportez le nom complet en respectant bien les tirets et les * du nom. Pour vous familiariser, je vous conseille après un premier fonctionnement correct du programme, d’effectuer plusieurs tests en changeant le nom de cette police et de voir le résultat.
Vous remarquerez que le programme xfontsel, lancé à partir de la session putty X11, affiche le résultat sur votre windows et qu’il ressemble à nos fenêtres X11 !!!
Ensuite, le pointeur de cette police est passé au contexte graphique dans la structure XGCValues comme la couleur.
Il nous reste à gérer la modification de l’apparence du bouton lorsque la souris s’éloigne de celui-ci. C’est dans l’événement LeaveNotify que nous appelons une routine modifBoutonSortie qui se contente de remettre les couleurs du fond et de la bordure comme elles étaient avant l’appui sur le bouton avec la procédure XChangeWindowAttributes. Il faut aussi redessiner le bouton par la procédure XClearArea.
Remarque : dans ce programme, les passages de paramètres par les registres aux routines internes, les sauvegardes des registres ne respectent aucune norme !!!!

Voici le résultat que vous devez retrouver :
 

Chapitre 34 : Création d’une fenêtre avec la fonction XCreateWindow


Pour créer une fenêtre (et donc un bouton, une zone de saisie etc.) il existe une autre fonction que nous allons voir dans ce programme. Nous allons aussi effectuer quelques modifications au programme précèdent pour centrer la fenêtre au milieu de l’écran, changer la couleur et le style de la ligne et gérer les modifications de taille de la fenêtre crée.
Nous allons aussi utiliser les structures de données liées à X11 qui facilite l’écriture de programmes à partit d’exemples en langage C. Ma description des structures se trouvent dans le fichier descStruct.inc qui sera intégré dans le programme par la directive .include. Remarque : si vous voulez utiliser d’autres données de ces structures, il faudra vérifier leur emplacement car j’ai pu faire des erreurs en les traduisant en formalisme GAS.
En plus des descriptions, il faut aussi réserver de la place en mémoire pour 2 structures nécessaires à l’appel de fonctions : XSetWindowAttributes et XGCValues.
Après l’ouverture d’une connexion au serveur et la récupération des informations par l’intermédiaire des structures Display et Screen, nous devons préparer les attributs nécessaires à la création de la fenêtre par XCreateWindow. Pour cela nous alimentons les zones mémoires à l’aide de la structure XSetWindowAttributes. Puis nous calculons les déplacements X et Y pour centrer notre fenêtre sur l’écran. Pour une raison que j’ignore, les valeurs calculées (et vérifiées !!) ne centrent pas la fenêtre. Ensuite nous créons la fenêtre en lui passant 12 paramètres dont 8 par push (voir la documentation ou les commentaires pour les explications détaillées).
Puis nous modifions le nom de la fenêtre par la fonction XSetStandardProperties, nous ajoutons comme auparavant les instructions de correction de l’erreur de fermeture et nous créons les contextes graphiques : le premier très simple et pour le second, nous modifions la couleur la taille et le style de la ligne qui sera dessinée. Pour cela il nous faut passer la position de ces paramètres et leur valeur dans la structure XGCValues.
Puis nous utilisons la fonction XSetWindowBackground pour mettre le fond en gris clair (nous aurions pu passer cette couleur aussi dans la zone Att_background_pixel des attributs crées avant la fenêtre).
Et hop, nous affichons la fenêtre par XMapWindow et nous essayons à nouveau de centrer la fenêtre avec les mêmes calculs mais avec la fonction XMoveWindow et cette fois ci, la fenêtre est bien centrée !!!!!
Puis comme dans le programme précèdent nous affichons le texte, la ligne et le rectangle, les instructions de ces 2 derniers étant reportées dans des sous-routines. En ce qui concerne la ligne, nous la dessinons avec le deuxième contexte graphique ce qui permet de l’avoir en rouge pointillé et plus large  Bravo !!
Dans les autorisations de saisies, nous ajoutons StructureNotifyMask pour que le gestionnaire d’évènements reçoivent les messages de modifications de la fenêtre. Dans la boucle qui géré celui-ci nous ajoutons un test pour le message ConfigureNotify. Si nous recevons ce message, nous comparons la valeur de la hauteur reçue (XConfigureEvent_height) avec la hauteur de la fenêtre en mémoire et si elles sont différentes nous redessinons la ligne et le rectangle (et on pourrait ajouter le texte). Il faut aussi penser à enregistrer la nouvelle hauteur en mémoire. Nous effectuons la même opération pour la largeur. Ici nous nous sommes contentés de redessiner les figures mais dans d’autres cas il faudrait adapter le dessin en calculant un facteur de réduction ou d’agrandissement de chaque figure avant de les redessiner.
Vous remarquerez que toutes les valeurs immédiates ont été remplacées par les valeurs des zones de chaque structure d’évènements.
Exercice : changer la couleur (par exemple bleue) du texte
                Chercher dans la documentation les autres attributs possibles des lignes et rectangles et tester ces attributs.
                Sur un clic du bouton de la souris, afficher le texte « Clic » à l’endroit du clic de la souris.

mercredi 6 juin 2018

Chapitre 33 : écrire et dessiner dans une fenêtre X11


Nous repartons du programme précédent pour le compléter afin de tracer une ligne et un rectangle et d’écrire une courte phrase. En effet, la structure d’un programme avec X11 va être souvent être la même : ouverture d’une connexion, récupération d’informations, création de la fenêtre, affichage, autorisation des saisies et gestion des évènements.
Mais nous utilisons une autre fonction pour changer le nom de la fenêtre : XSetStandardProperties. Cette fonction permet aussi d’associer une icône à notre fenêtre mais nous verrons cela plus tard.
 Et pour dessiner nous devons créer un contexte graphique associé à la fenêtre par la fonction XCreateGC. C’est grâce à ce contexte que nous pourrons modifier la police ou les couleurs des entités à dessiner. Ici nous nous contentons de le créer avec les valeurs par défaut. Remarque : dans la structure screen, il existe un pointeur vers un contexte graphique par défaut. Nous pourrions l’utiliser plutôt que de créer un contexte par nous-mêmes.
Avant d’afficher la fenêtre et pour varier les fonctions, nous modifions le fond de la fenêtre avec un gris léger par la fonction XSetWindowBackground.
Après l’affichage (très important !!!) nous pouvons écrire du texte en utilisant la fonction XDrawString et en lui passant les paramètres suivant, le Display, la fenêtre, le contexte graphique, la position X du début du texte, la position Y du début du texte, l’adresse de la chaine de caractères à afficher et sa longueur. Rien de bien compliqué sauf à penser de respecter la norme d’appel des fonctions.
Ensuite nous dessinons une ligne avec la fonction XDrawLine et en lui passant les paramètres suivant, le Display, la fenêtre, le contexte graphique, les positions X et Y du début de ligne, puis les positions X et Y de la fin de la ligne.
Et enfin nous traçons un rectangle avec la fonction XFillRectangle avec pratiquement les mêmes paramètres.
Le reste du programme est inchangé sauf que l’on ajoute à la fin, la libération des ressources du contexte graphique par la fonction XFreeGC
Dans la documentation X11, vous trouverez les autres fonctions pour dessiner des polygones, des arcs de cercles etc suivant le même principe.
Lors de l’exécution, vous remarquerez que si vous changer les dimensions de la fenêtre avec la souris, tous nos dessins s’effacent !! En effet, par la suite, il nous faudra gérer l’impact de ce redimensionnement sur nos dessins.