vendredi 6 juillet 2018

Chapitre 39 :Modification du curseur de la souris et chargement d’une image BMP


Nous reprenons un chapitre du document xlibbook pour montrer comment modifier la forme du curseur de la souris. J’ai ajouté à l’exemple donné le chargement et l’affichage d’une image au format BMP.
Dans ce programme, j’ai aussi essayé d’améliorer la gestion des évènements en stockant en fin de structure X11 des fenêtres, l’adresse de la procédure de gestion des évènements à appeler pour la fenêtre concernée. Pour cela nous déclarons une table des fenêtres crées avec autant de structure Fenêtre que de fenêtres plus une. La dernière servira de borne d’arrêt lors de la recherche.
Comme dans le programme précédent, nous reprenons les définitions des bitmap du curseur de la souris et de son masque données par le tutoriel sous la forme d’une série de .byte.
Dans le code, nous reprenons comme auparavant les procédures de connexion au serveur, de chargement des polices et des couleurs (même si nous n’en avons pas l’utilité dans ce programme), la création de la fenêtre principale et la création des contextes graphiques. Nous modifions la procédure de création des images pour convertir les 2 images du curseur à partir de leur définition de la .data.
Remarque : dans la création de la fenêtre principale, j’ai voulu tester la possibilité d’avoir une icone de la fenêtre sous la forme d’une image. J’ai donc créée avec le logiciel bitmap une image que j’ai chargée dans la procèdure lectureimage. Puis j’ai passé le pointeur crée à la fonction XSetStandardProperties. Mais cela n’a rien changé lors de l’affichage de la fenêtre (même en réduisant celle-ci). Dès que j’aurais un peu plus de temps, j’essaierai de trouver le problème (ou peut être que le serveur XWing ne prends pas cela en compte !!!)
Nous ajoutons ensuite la création du curseur de la souris par l’appel à la fonction X11 XCreatePixmapCursor à laquelle nous passons les 2 images précédemment créées. Ceci permet de faire apparaitre le curseur sur des fonds différents.
Ensuite, nous créons 3 fenêtres à l’intérieur de la fenêtre principale, les 2 premières comme sur l’exemple du tutoriel, la 3ième en bas à droite servira à afficher l’image de l’icône créée pour la fenêtre principale. Ceci afin de vérifier que l’image lue depuis le fichier et convertie était correcte.
Pour la 4ième fenêtre, nous allons d’abord charger une image au format bmp (Coquelicots.bmp) stockée dans le même répertoire que le programme afin de récupérer ses dimensions. Celles-ci nous serviront à créer la fenêtre exactement à la même taille. Pour la lecture et le chargement d’une image BMP, vous vous souvenez peut être que j’avais déjà fait cela pour l’afficher par l’intermédiaire du frameBuffer. J’ai donc récupéré le code écrit à ce moment là, je l’ai adapté et rendu plus autonome (pour pouvoir être réutilisé dans d’autres programmes ou dans une librairie). Les buffers nécessaires sont adaptées à la taille de l’image lue et crées sur le tas (voir un post précédent sur l’allocation mémoire sur le tas). Ainsi le programme appelant, n’a pas à se préoccuper d’allouer de la place pour l’image. Il faut 2 buffers pour stocker les codes RGB de chaque pixel car le stockage au format BMP est inversé par rapport à l’image réelle. Il y a aussi quelques subtilités dans la longueur de chaque ligne pour ce format d’image. Je vous laisse le soin de lire les commentaires si vous voulez améliorer cette partie !!!!
Vous remarquerez que dans chaque procédure de création de fenêtre, nous stockons dans la structure l’adresse de la fonction à appeler pour chaque évènement.
Ainsi dans la procédure de gestion des évènements, nous nous contentons de balayer la table des fenêtres pour retrouver la structure de la fenêtre concernée, puis de récupérer l’adresse de la fonction dans le registre r2 et nous appelons la procédure par l’utilisation de l’instruction blx r2.  Ensuite dans chaque fonction, nous pouvons tester plus précisément l’événement détecté et réagir en conséquence. Pour les 3 premières fenêtres secondaires, nous ne faisons rien et pour la 4ième, nous testons l’évènement expose pour ré afficher l’image bmp si la fenêtre a été masquée.
A l’exécution, en balayant les différentes fenêtres et le bandeau haut, vous voyez les changement de forme du curseur et sa lisibilité parfaite quelle que soit le fond.
Vous voyez que l’image BMP s’affiche correctement avec les bonnes couleurs mais qu’il y a quand même un certain délai. En lisant les explications du tutoriel, on s’aperçoit que la fonction XCreateImage crée l’image sur le poste client (donc le raspberry) et que c’est la fonction XPutImage qui la transfère sur le serveur ce qui peut prendre un certain temps !!
Exercice : Chercher une image d’un beau bouton, la mettre au format .bmp, puis la charger dans un programme et l’afficher dans une fenêtre bouton.
                Améliorer le code précédent en cherchant une image d’un bouton appuyé et afficher cette image lorsque l’on a cliqué sur le bouton (et bien sûr réafficher l’image du premier bouton lorsque la souris quitte le bouton).
                Dans le tutoriel de Ross Maloney, il montre un exemple de chargement d’une image en couleur avec un format XPM. Je n’ai pas réussi à utiliser cette fonction car il doit falloir une librairie particulière. Pour créer des images de ce format il faut charger sur le Raspberry le package pixmap et utiliser le logiciel pixmap (même principe que bitmap). Donc exercice : chercher la librairie qui permet d’utiliser les fonctions XPM, programmez un exemple d’affichage d’une image de ce format (voir l’exemple du tutoriel).

Je terminerais avec ce programme les exemples d'utilisation de X11. Je vous laisse le soin de découvrir toutes les autres possibilités. 
Je reprendrais ce blog à la fin des vacances.

Chapitre 38 : gestion des images avec X11


Nous allons commencer à partir de l’exemple du tutoriel de Ross Maloney (encore merci pour son document) de créer une image en noir et blanc à partir de sa définition sous la forme d’une suite d’octets. Nous allons aussi y ajouter la lecture d’une image toujours en noir et blanc à partir d’un fichier.
En effet en lisant, le tutoriel je me suis aperçu que l’on pouvait facilement créer des images en noir et blanc avec le logiciel bitmap. En première recherche, ce programme ne figurait pas sur mon raspberry. En fait il fait partie du package x11-apps qu’il faut installer avec la commande sudo apt-get install x11-apps. La prise en main du logiciel est rapide vu sa simplicité. J’ai donc créée une image d’un simple cercle que j’ai enregistré dans un fichier img2 dans le même répertoire que notre programme. J’ai vu dans un document sur internet que cette image pouvait avoir l’extension .btm. C’est peut être une bonne idée pour repérer ce type de fichier !!
Pour la création à partir de sa définition, j’ai repris exactement les codes donnés dans le tutoriel. Je n’ai pas trouvé comment coder une instruction sur plusieurs lignes alors j’ai ajouté autant de .byte que de lignes de codes nécessaires.
Dans notre programme, nous reprenons la séquence de programmation des fonctions X11 : ouverture connexion serveur, chargement des polices, création de la fenêtre principale, création des contextes graphiques et gestion des évènements. Le fond de la fenêtre principale est de couleur rouge, le code RGB est récupéré par appel de la fonction XAllocNamedColor après avoir cherché le colormap par défaut par la fonction XDefaultColormap. Ceci est fait pour montrer l’utilisation de ces fonctions car j’aurais pu donner directement le code RGB de la couleur rouge. Ici le nom de la couleur demandé est « red » et il faudrait trouver aussi le nom de toutes les autres couleurs possibles (en anglais !!). Bonne recherche.
 Nous ajoutons une routine de création de l’image à partir de sa définition en appelant la fonction XCreatePixmapFromBitmapData.
Nous ajoutons aussi une routine de lecture du fichier image img2 en appelant la fonction XReadBitmapFile. Ces 2 fonctions retournent un pointeur vers une structure pixmap qui va nous permettre l’affichage.
Ensuite dans la gestion des évènements, nous ne gérons que la fermeture et le clic des boutons de la souris. Si le bouton 1 (le gauche) est cliqué, nous affichons à l’endroit du clic l’image créée à partir de sa définition (et identique à celle du tutoriel) sinon nous affichons l’image lue à partir du fichier img2. Cette image est un cercle blanc sur fond rouge. Vous remarquerez que si vous cliquez sur une image 1 avec le bouton droit, le cercle blanc est bien dans un petit carré rouge.
Voici le résultat :


Autre remarque : les boutons centre et droit de la souris sont numérotés 2 et 3.
Vous pouvez aussi inverser dans la création des contextes graphiques les valeurs Background et Foreground pour inverser les images. Vous pouvez aussi modifier la couleur de ces 2 paramètres car en effet les images bitmap ne sont constituées que de 0 et de 1 sans signification de couleurs et c’est le contexte graphique qui va donner la valeur des couleurs pour le 0 et pour le 1.
Pour l’explication du codage d’une image, veuillez vous reportez à la documentation disponible sur internet (mais il vaut mieux utiliser le logiciel bitmap sur le  raspberry)

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 :