Un programme d’exemple d’utilisation de « ANSI_Console »

Tester le paquet ANSI_Console

Présentation…

Un paquet ( « package » ) d’utilisation évoluée des écrans et consoles en mode texte, ouvre la porte à la création de bibliothèques plus complexes. Ce programme en Ada est un petit exemple d’utilisation très simple du paquet « ANSI_Console » disponible sur ce site.

Avant-propos

Ce programme est basé sur le paquet ANSI_Console, que vous pouvez obtenir sur cette page : Package Ada pour mise en forme d’écran/console .

Le paquet ANSI_Console se suffit à lui-même, et permet déjà la création d’applications avec une interface intéressante ( déjà mieux que les austères lignes de commande, qui sont trop la règle dans les applications en mode texte ). Bien que le paquet ANSI se suffise déjà à lui-même, on peut tout de même être inspiré(e) de créer d’autres paquets encore plus évolués qui utiliseraient ces fonctionnalités. On peut penser à un système de fenêtrage. Créer un tel système demande du travail, et ce programme de test ne prétend pas en faire tant. Ce programme d’exemple est donc limité : par exemple, il n’y a aucune gestion de la superposition des fenêtres, et il n’y a pas non plus de prise en charge du passage à la ligne automatique pour l’affichage du texte dans une fenêtre, les fenêtres ne peuvent pas être déplacées, etc. Deux autres paquets verront le jour à l’avenir, à une date indéterminée… si vous avez des intérêts dans de tels paquets, toutes propositions de parrainages sont les bienvenues : par mail à les-ziboux@rasama.org  ou par la page de commentaire du site  ( les messages ne sont pas archivés sur le site mais restent privés ).

Le programme de test et d’exemple

Vous trouverez ci-après, pour consultation en ligne, le code du programme de test et d’exemple. Vous pourrez aussi télécharger le source directement par ce lien : « Test_ANSI.adb »  ou même, si vous ne souhaitez pas le compiler vous même, récupérer le binaire Windows 95 ou XP par cet autre lien : « Test_ANSI.exe »  ( non-compressé ).

Configuration éventuelle de la page de code DOS

Ce programme utilise les caractères de cadre ASCII. Par défaut sous Windows 95 ou XP, la page de code DOS n’affiche pas ces caractères de cadre. Si le programme semble ne pas produire l’affichage souhaité, éditez alors votre fichier autoexec.bat, et modifiez la commande mode de manière à ce qu’elle corresponde à ceci ( deux lignes ) :

      mode con codepage prepare=((437) C:\WINDOWS\COMMAND\ega.cpi)
      mode con codepage select=437


        

Sous DOS, c’est la page de code 437 qui doit être active. Si la modification de autoexec.bat est nécessaire, il vous faudra alors redémarrer Windows pour que les modifications prennent effet. Cette modification n’affecte pas l’affichage sous Windows, mais seulement la page de code active sous DOS. De plus, cette page de code, même sous DOS, n’affecte pas les caractères courants ( seulement quelques caractères spéciaux ).

Texte source du programme

      -- Si vous copiez-collez, notez que la tabulation de l'indentation est de 3

      with ANSI_Console;
      use ANSI_Console;

      procedure Test_ANSI is
      -- ----------------------------------------------------------------------------
      -- Programme de test, et exemple d'utilisation du package ANSI_Console
      -- Système de fenêtrage très simple et basique.
      -- ----------------------------------------------------------------------------
      -- Ce programme utilise le package ANSI_Console que vous pouvez obtenir
      -- à l'adresse suivate :
      -- http://www.les-ziboux.rasama.org/ada-commandes-echappement-ansi.html
      --
      -- Ce programme n'est qu'un petit programme de teste, et n'est donc pas
      -- très commenté.
      --
      -- Lundi 27 novembre 2006 - france (quelque part dans l'est)
      -- ----------------------------------------------------------------------------

         -- -------------------------------------------------------------------------
         -- Configuration de la page de code sous DOS
         -- -------------------------------------------------------------------------
         -- Note : ce programme utilise les caractères de cadre ASCII. Par défaut
         -- sous Windows 95/XP, la page de code DOS n'affiche pas ces caractères
         -- de cadre. Si le programme semble ne pas produire l'affichage souhaité,
         -- éditez alors votre fichier autoexec.bat, et modifiez la commande « mode »
         -- de manière à ce qu'elle corresponde à ceci (deux lignes) :
         -- mode con codepage prepare=((437) C:\WINDOWS\COMMAND\ega.cpi)
         -- mode con codepage select=437
         -- Sous DOS, c'est la page de code 437 qui doit être active.
         -- Si la modification d'autoexec.bat est nécessaire, il vous faudra alors
         -- redémarrer Windows pour que les modifications prennent effet. Cette
         -- modification n'affecte pas l'affichage sous Windows, mais seulement la
         -- page de code active sous DOS. De plus, cette page de code, même sous DOS,
         -- n'affecte pas les caractères courants (seulement quelques caractères
         -- spéciaux).

         -- -------------------------------------------------------------------------
         -- À propos de ce programme de test
         -- -------------------------------------------------------------------------
         -- Le package ANSI_Console se suffit à lui-même, et permet déjà la création
         -- d'applications avec une interface intéressante (déjà beaucoup mieux
         -- que les austères lignes de commande, qui sont trop la règle dans les
         -- applications en mode texte). Bien que le package ANSI se suffise déjà
         -- à lui-même, on peut tout de même être inspiré de créer d'autres
         -- package encore plus évolués qui utiliserait ces fonctionnalité. On peut
         -- penser à un système de fenêtrage. Créer un tel système demande du
         -- travail, et ce programme de test ne prétend pas en faire tant. Ce
         -- programme d'exemple et donc limité : par exemple, il n'y a aucune
         -- gestion de la superposition des fenêtre, et il n'y a pas non plus de
         -- prise en charge du passage à la ligne automatique pour l'affichage du
         -- texte dans une fenêtre, les fenêtres ne peuvent pas être déplacées, etc.
         -- Deux autres packages verront le jour à l'avenir (à une date
         -- indéterminée… si vous avez un intérêt dans de tels packages, toutes
         -- propositions de parrainages sont les bienvenues - contact :
         -- les-ziboux@rasama.org).

         -- -------------------------------------------------------------------------
         -- Le code du programme commence ici
         -- -------------------------------------------------------------------------

         -- Note : dans les systèmes graphiques, on a l'habitude avec les
         -- coordonnées, de donner d'abord la coordonnée horizontal, puis ensuite la
         -- vertical (x,y). Mais dans les systèmes en mode texte, on fait l'inverse
         -- (ligne, colonne). C'est ainsi que nous procéderons ici (tout comme dans
         -- le package ANSI_Console).

         -- Les procédures ce programme d'exemple supposent un mode d'écran de
         -- 25 lignes sur 80 colonnes (c'est le mode d'écran standard, mais
         -- mais il est tout de même fixé par la procédure d'initialisation).

         subtype Line_Type   is Natural range 1..25;
         subtype Column_Type is Natural range 1..80;
         subtype Height_Type is Natural range 0..25;
         subtype Width_Type  is Natural range 0..80;

         -- On défini un type fenêtre très basique, qui correspond simplement
         -- au spécification d'un rectangle. Les fenêtre sont un cadre, avec
         -- une pseudo case ferme en haut à droite. La manière de dessiner le cadre
         -- d'une fenêtre impose des restriction sur leurs dimensions possibles. La
         -- largeur d'une fenêtre est de 5 au minimum, et la hauteur minimum est 2.

         subtype Window_Line_Type   is Line_Type;
         subtype Window_Column_Type is Column_Type;
         subtype Window_Height_Type is Height_Type range 2..25;
         subtype Window_Width_Type  is Width_Type  range 5..80;

         -- La zone cliente d'une fenêtre, la zone qui est à l'intérieur, à
         -- des dimensions bien sûre inférieur à celle de la fenêtre elle-même.
         -- La largeur de la zone cliente fait 2 de moins que la largeur de la
         -- fenêtre, idem pour la hauteur.

         subtype Client_Line_Type   is Window_Line_Type   range 1..23;
         subtype Client_Column_Type is Window_Column_Type range 1..78;
         subtype Client_Height_Type is Height_Type range 0..23;
         subtype Client_Width_Type  is Width_Type  range 0..78;

         -- On défini maintenant un type fenêtre.
         -- En plus de ces coordonnées et dimensions, une fenêtre spécifie la
         -- couleur du cadre, et la couleur de la case ferme. On pourra
         -- également spécifier le type de cadre.

         type Frame_Type_Enum is (Single_Line_Frame, Double_Line_Frame);

         type Window_Type is
         record
            L : Window_Line_Type;
            C : Window_Column_Type;
            H : Window_Height_Type;
            W : Window_Width_Type;
            Frame_Type      : Frame_Type_Enum;
            Frame_Color     : Color_Type;
            Close_Box_Color : Color_Type;
         end record; -- Window_Type

         -- Un type zone cliente n'a pas besoin de coordonné, car elle sont
         -- toujours (1,1). Il n'y aura donc que des dimensions.

         type Client_Type is
         record
            H : Client_Height_Type;
            W : Client_Width_Type;
         end record; -- Window_Type

         Metric_Error : exception; -- Positionnement ou dimensionnement incorrect.

         -- Les procédure concernée par les dimensions et les coordonnées
         -- déclenchent Metric_Error dans le cas ou un affichage ne passerait pas
         -- dans l'écran (ou dans une zone préféfinie). C'est le cas si par
         -- exemple, sur un écran de 25 ligne sur 80 colonnes, on essais
         -- d'afficher un texte de 15 caractère de longueur, à la position
         -- (L:3,C:78), car alors le texte ne tient pas entièrement dans l'écran,
         -- et cela est alors considéré comme une faute.

         -- Test si le rectangle défini par (L, C)-(H, W) passe dans l'écran.
         function Fit_In_Screen (
            L : in Line_Type;
            C : in Column_Type;
            H : in Height_Type;
            W : in Width_Type)
         return Boolean
         is
         begin
            if C < 1 then return False;
            end if;

            if (C - 1) + W > 80 then return False;
            end if;

            if L < 1 then return False;
            end if;

            if (L - 1) + H > 25 then return False;
            end if;

            return True;
         end;

         -- Test si le rectangle défini par (L, C)-(H, W) passe dans la fenêtre.
         function Fit_In_Window (
            Window : in Window_Type;
            L : in Line_Type;
            C : in Column_Type;
            H : in Height_Type;
            W : in Width_Type)
         return Boolean
         is
         begin
            if C < 1 then return False;
            end if;

            if (C - 1) + W > Window.W then return False;
            end if;

            if L < 1 then return False;
            end if;

            if (L - 1) + H > Window.H then return False;
            end if;

            return True;
         end;

         -- Test si le rectangle défini par (L, C)-(H, W) passe dans la zone
         -- cliente de la fenêtre. Notez que l'argument est bien une fenêtre, et non
         -- un type Client_Type.
         function Fit_In_Client (
            Window : in Window_Type;
            L : in Line_Type;
            C : in Column_Type;
            H : in Height_Type;
            W : in Width_Type)
         return Boolean
         is
         begin
            if C < 1 then return False;
            end if;

            if (C - 1) + W > Window.W - 2 then return False;
            end if;

            if L < 1 then return False;
            end if;

            if (L - 1) + H > Window.H - 2 then return False;
            end if;

         return True;
         end;

         -- Au lieu d'afficher caractère par caractère,
         -- les lignes affiché sont plutôt préparer dans cette chaîne,
         -- puis afficher d'un bloc. Ceci augmente le confort d'affichage.
         Draw_Buffer : String (Column_Type);

         -- Dessine un fond d'espace de travail. Le rectangle défini par
         -- les coordonnées et les dimensions, est rempli avec un caractère
         -- spécial.
         procedure Draw_Desktop_Background (
            L      : in Line_Type;
            C      : in Column_Type;
            H      : in Height_Type;
            W      : in Width_Type;
            Color  : in Color_Type)
         is
         begin
            if not Fit_In_Screen (L, C, H, W) then raise Metric_Error;
            end if;

            for j in 1..W loop
               Draw_Buffer ((C - 1) + j) := Character'Val(176);
            end loop;
            Set_Text_Color (Color);
            for i in 1..H loop
               Move_Cursor_To ((L - 1) + i, C);
               Put (Draw_Buffer (C..(C- 1) + W));
            end loop;
         end;

         -- Dessine un fenêtre, c'est-à-dire un cadre, un fond rempli d'espace,
         -- et un bouton « ferme » en haut à droite du cadre.
         procedure Draw_Window (W : in Window_Type)
         is
            type Frame_Component_Enum is (
               Top_Left_Corner,
               Top_Right_Corner,
               Bottom_Right_Corner,
               Bottom_Left_Corner,
               Horizontal_Border,
               Vertical_Border
               );
            Frame_Component : constant
               array (Frame_Type_Enum, Frame_Component_Enum) of Character := (
               Double_Line_Frame => (
                  Top_Left_Corner     => Character'Val(201),
                  Top_Right_Corner    => Character'Val(187),
                  Bottom_Right_Corner => Character'Val(188),
                  Bottom_Left_Corner  => Character'Val(200),
                  Horizontal_Border   => Character'Val(205),
                  Vertical_Border     => Character'Val(186)),
               Single_Line_Frame => (
                  Top_Left_Corner     => Character'Val(218),
                  Top_Right_Corner    => Character'Val(191),
                  Bottom_Right_Corner => Character'Val(217),
                  Bottom_Left_Corner  => Character'Val(192),
                  Horizontal_Border   => Character'Val(196),
                  Vertical_Border     => Character'Val(179)));
            Client_Background : constant Character := Character'Val (32);
            Close_Box_Symbol  : constant Character := 'X';
            -- Pourrait être aussi Character'Val(254)
         begin
            if not Fit_In_Screen (W.L, W.C, W.H, W.W) then raise Metric_Error;
            end if;

            if W.W < 5 then raise Metric_Error;
            end if;

            if W.H < 2 then raise Metric_Error;
            end if;

            Set_Text_Color (W.Frame_Color);

            -- Bottom side
            Draw_Buffer (W.C) :=
               Frame_Component (W.Frame_Type, Bottom_Left_Corner);
            for i in 2 .. W.W - 1 loop
               Draw_Buffer ((W.C - 1) + i) :=
                  Frame_Component (W.Frame_Type, Horizontal_Border);
            end loop;
            Draw_Buffer ((W.C - 1) + W.W) :=
               Frame_Component (W.Frame_Type, Bottom_Right_Corner);
            Move_Cursor_To ((W.L - 1) + W.H, W.C);
            Put (Draw_Buffer (W.C..(W.C - 1) + W.W));

            -- Top side
            Draw_Buffer (W.C) :=
               Frame_Component (W.Frame_Type, Top_Left_Corner);
            Draw_Buffer ((W.C - 1) + W.W - 3) := '[';
            Draw_Buffer ((W.C - 1) + W.W - 2) := 'X'; -- Character'Val(254);
            Draw_Buffer ((W.C - 1) + W.W - 1) := ']';
            Draw_Buffer ((W.C - 1) + W.W) :=
               Frame_Component (W.Frame_Type, Top_Right_Corner);
            Move_Cursor_To (W.L, W.C);
            Put (Draw_Buffer (W.C..(W.C - 1) + W.W));

            -- Middle
            Draw_Buffer (W.C) :=
               Frame_Component (W.Frame_Type, Vertical_Border);
            for i in 2 .. W.W - 1 loop
               Draw_Buffer ((W.C - 1) + i) := Client_Background;
            end loop;
            Draw_Buffer ((W.C - 1) + W.W) :=
               Frame_Component (W.Frame_Type, Vertical_Border);
            for i in 2..W.H - 1 loop
               Move_Cursor_To ((W.L - 1) + i, W.C);
               Put (Draw_Buffer (W.C..(W.C - 1) + W.W));
            end loop;

            -- Button
            Set_Text_Color (W.Close_Box_Color);
            Move_Cursor_To (W.L, (W.C - 1) + W.W - 2);
            Put (Close_Box_Symbol);
         end;

         -- Dessine un caractère dans la zone cliente d'une fenêtre, aux coordonnées
         -- spécifiées avec la couleur indiquée.
         procedure Draw (
            W      : in Window_Type;
            L      : in Client_Line_Type;
            C      : in Client_Column_Type;
            Color  : in Color_Type;
            Ch     : in Character)
         is
         begin
            -- ((W.L + 1) - 1) + L = W.L + L
            -- ((W.C + 1) - 1) + C = W.C + C
            if not Fit_In_Screen (W.L + L, W.C + C, 1, 1) then
               raise Metric_Error;
            end if;
            if not Fit_In_Client (W, L, C, 1, 1) then
               raise Metric_Error;
            end if;
            Set_Text_Color (Color);
            Move_Cursor_To (W.L + L, W.C + C);
            Put (Ch);
         end;

         -- Dessine du texte dans la zone cliente d'une fenêtre, aux coordonnées
         -- spécifiées avec la couleur indiquée.
         procedure Draw (
            W      : in Window_Type;
            L      : in Client_Line_Type;
            C      : in Client_Column_Type;
            Color  : in Color_Type;
            Text   : in String)
         is
         begin
            -- ((W.L + 1) - 1) + L = W.L + L
            -- ((W.C + 1) - 1) + C = W.C + C
            if not Fit_In_Screen (W.L + L, W.C + C, 1, Text'Length) then
               raise Metric_Error;
            end if;
            if not Fit_In_Client (W, L, C, 1, Text'Length) then
               raise Metric_Error;
            end if;
            Set_Text_Color (Color);
            Move_Cursor_To (W.L + L, W.C + C);
            Put (Text);
         end;

         -- Dessine du texte centré. Comme c'est le centrage qui donne la coordonnée
         -- horizontal, on ne spécifie bien sûre que la coordonnée vertical.
         procedure Draw_Centered (
            W      : in Window_Type;
            L      : in Client_Line_Type;
            Color  : in Color_Type;
            Text   : in String)
         is
            C2 : Natural;
         begin
            -- (W.L + 1) - 1 = W.L
            if not Fit_In_Screen (W.L + L, W.C + 1, 1, Text'Length) then
               raise Metric_Error;
            end if;
            if not Fit_In_Client (W, L, 1, 1, Text'Length) then
               raise Metric_Error;
            end if;
            if W.W - 2 < Text'Length then raise Metric_Error;
            end if;
            -- (W.C - 1) + 1 = W.C
            C2 := W.C + (W.W - 2 - Text'Length) / 2;
            Set_Text_Color (Color);
            Move_Cursor_To (W.L + L, C2);
            Put (Text);
         end;

         procedure Move_Cursor_To (
            W : in Window_Type;
            L : in Client_Line_Type;
            C : in Client_Column_Type)
         is
         begin
            if L < 1 then raise Metric_Error;
            end if;
            if L > W.H - 2 then raise Metric_Error;
            end if;
            if C < 1 then raise Metric_Error;
            end if;
            if C > W.W - 2 then raise Metric_Error;
            end if;
            Move_Cursor_To (W.L + L, W.C + C);
         end;

      procedure Initialize
      is
      begin
         -- Initialisation
         Set_Screen_Mode (Color_Text_Mode_80x25);
         Disable_Line_Wrapping; -- Pour éviter les mauvaises surprises d'affichage.
         Set_Background_Color (Blue); -- La couleur la plus supportable pour un fond.
         Set_Text_Attributes (Bold_Text); -- Texte en couleur claire.
         Clear_Screen; -- Évidement…
      end;

      procedure Leave_And_Restore_Defaults
      is
      begin
         -- Avant de quitter, on restitue l'environnement
         Enable_Line_Wrapping; -- En condition normal, toujours
         Set_Background_Color (Black); -- Couleur normal la plus courante
         Set_Text_Color (White); -- Couleur normal la plus courante
         Set_Text_Attributes (Default_Text_Attributes);
         Clear_Screen; -- Si on effaçais pas, le reste d'écran défilerait : pas joli.
         -- On ne peut pas restaurer le mode d'écran, car on n'a aucun moyen de
         -- connaître le mode graphique qui était actif au démarrage.
      end;

         W0 : Window_Type := ( -- Fenêtre de test du clavier
            L =>  2,
            C =>  2,
            H =>  4,
            W => 28,
            Frame_Type      => Single_Line_Frame,
            Frame_Color     => White,
            Close_Box_Color => Blue);

         W1 : Window_Type := ( -- Fenêtre qui joue à Windows 3.1
            L =>  7,
            C =>  5,
            H => 10,
            W => 24,
            Frame_Type      => Double_Line_Frame,
            Frame_Color     => Yellow,
            Close_Box_Color => Red);

         W2 : Window_Type := ( -- Fenêtre qui joue à Windows XP
            L =>  4,
            C => 31,
            H => 14,
            W => 34,
            Frame_Type      => Double_Line_Frame,
            Frame_Color     => Blue,
            Close_Box_Color => Cyan);

         W3 : Window_Type := ( -- Fenêtre d'information
            L => 19,
            C =>  2,
            H =>  5,
            W => 78,
            Frame_Type      => Single_Line_Frame,
            Frame_Color     => Yellow,
            Close_Box_Color => Red);

         -- Cette fonction est un petit bricolage.. ne pas y prêter attention.
         function Natural_Image_3 (N : Natural)
         return String
         is
            L : Natural;
         begin
            L := Natural'Image (N)'Length;
            if L = 4 then return Natural'Image (N) (2..4);
            elsif L = 3 then return "0" & Natural'Image (N) (2..3);
            else return "00" & Natural'Image (N) (2..2);
            end if;
         end;

         -- Cette boucle d'événement donne un exemple de l'utilisation
         -- du Get non-bloquant.
         procedure Old_Events_Loop
         is
            -- FIFO des quatre derniers caractères entrés au clavier
            C1        : Character := Character'Val (0);
            C2        : Character := Character'Val (0);
            C3        : Character := Character'Val (0);
            C4        : Character := Character'Val (0);
            -- Pour teste et récupération du résultat d'appuie d'une touche
            C         : Character;
            -- Chaînes pour la représentation numérique des quatre derniers
            -- caractères saisie au clavier
            S1        : String(1..3);
            S2        : String(1..3);
            S3        : String(1..3);
            S4        : String(1..3);
            -- Pour Get non-bloquant
            Available : Boolean;
            -- Est-ce qu'une touche était appuyée à la précédente itération ?
            Previous_Pressed : Boolean;
            -- Code de la touche « Echap »
            Touche_Echap : constant Character := Character'Val (27);
         begin
            -- Affiche le contenu initial
            Draw (W0, 1,  1, Blue, "Touche appuyee :");
            Draw (W0, 1, 17, Cyan, " ---");
            Draw (W0, 2,  1, Cyan, "[--- --- --- ---]");
            Move_Cursor_To (W0, 2, 20);
            -- Previous_Pressed indique si une touche était appuyée à
            -- l'itération précédente.
            Previous_Pressed := False;
            loop
               Get (C, Available);
               if Available and C = Touche_Echap then
                  Beep;
                  exit;
               elsif Available then
                  C1 := C2;
                  C2 := C3;
                  C3 := C4;
                  C4 := C;
                  Draw (W0, 1, 18, Cyan, "Oui");
                  S1 := Natural_Image_3 (Character'Pos (C1));
                  S2 := Natural_Image_3 (Character'Pos (C2));
                  S3 := Natural_Image_3 (Character'Pos (C3));
                  S4 := Natural_Image_3 (Character'Pos (C4));
                  Draw (W0, 2, 2, Cyan, S1 & " " & S2 & " " & S3 & " " & S4);
                  Move_Cursor_To (W0, 2, 20);
                  Previous_Pressed := True;
               elsif Previous_Pressed then
                  -- On ne le fait que si on était pas déjà dans
                  -- cet état lors de l'itération précédente (ceci évite
                  -- de faire flotter l'affichage).
                  Draw (W0, 1, 18, Cyan, "Non");
                  Move_Cursor_To (W0, 2, 20);
                  Previous_Pressed := False;
               end if;
            end loop;
         end;

         subtype Key_Title_Type is String (1..9);

         Key_Title : constant array (Key_Type) of Key_Title_Type := (
            Key_F1                => "F1       ",
            Key_F2                => "F2       ",
            Key_F3                => "F3       ",
            Key_F4                => "F4       ",
            Key_F5                => "F5       ",
            Key_F6                => "F6       ",
            Key_F7                => "F7       ",
            Key_F8                => "F8       ",
            Key_F9                => "F9       ",
            Key_F10               => "F10      ",
            Key_F11               => "F11      ",
            Key_F12               => "F12      ",
            Keypad_Home           => "KP-Home  ",
            Keypad_Up_Arrow       => "KP-Up    ",
            Keypad_Page_Up        => "KP-PgUp  ",
            Keypad_Left_Arrow     => "KP-Left  ",
            Keypad_Right_Arrow    => "KP-Right ",
            Keypad_End            => "KP-End   ",
            Keypad_Down_Arrow     => "KP-Down  ",
            Keypad_Page_Down      => "KP-PgDn  ",
            Keypad_Insert         => "KP-Insert",
            Keypad_Delete         => "KP-Delete",
            Key_Home              => "Home     ",
            Key_Up_Arrow          => "Up       ",
            Key_Page_Up           => "Page-Up  ",
            Key_Left_Arrow        => "Left     ",
            Key_Right_Arrow       => "Right    ",
            Key_End               => "End      ",
            Key_Down_Arrow        => "Down     ",
            Key_Page_Down         => "Page-Down",
            Key_Insert            => "Insert   ",
            Key_Delete            => "Delete   ",
            Key_Print_Screen      => "PrnScreen",
            Key_Pause_Break       => "Pause    ",
            Key_Escape            => "Escape   ",
            Key_Backspace         => "Backspace",
            Key_Enter             => "Enter    ",
            Key_Tab               => "Tab      ",
            Key_Null              => "Null     ",
            Key_A                 => "<A>      ",
            Key_B                 => "<B>      ",
            Key_C                 => "<C>      ",
            Key_D                 => "<D>      ",
            Key_E                 => "<E>      ",
            Key_F                 => "<F>      ",
            Key_G                 => "<G>      ",
            Key_H                 => "<H>      ",
            Key_I                 => "<I>      ",
            Key_J                 => "<J>      ",
            Key_K                 => "<K>      ",
            Key_L                 => "<L>      ",
            Key_M                 => "<M>      ",
            Key_N                 => "<N>      ",
            Key_O                 => "<O>      ",
            Key_P                 => "<P>      ",
            Key_Q                 => "<Q>      ",
            Key_R                 => "<R>      ",
            Key_S                 => "<S>      ",
            Key_T                 => "<T>      ",
            Key_U                 => "<U>      ",
            Key_V                 => "<V>      ",
            Key_W                 => "<W>      ",
            Key_X                 => "<X>      ",
            Key_Y                 => "<Y>      ",
            Key_Z                 => "<Z>      ",
            Key_0                 => "<0>      ",
            Key_1                 => "<1>      ",
            Key_2                 => "<2>      ",
            Key_3                 => "<3>      ",
            Key_4                 => "<4>      ",
            Key_5                 => "<5>      ",
            Key_6                 => "<6>      ",
            Key_7                 => "<7>      ",
            Key_8                 => "<8>      ",
            Key_9                 => "<9>      ",
            Key_Minus             => "<->      ",
            Key_Equal             => "<=>      ",
            Key_Left_Square       => "<[>      ",
            Key_Right_Square      => "<]&gt;      ",
            Key_Space             => "< >      ",
            Key_Semicolon         => "<;>      ",
            Key_Single_Quote      => "<'>      ",
            Key_Comma             => "<,>      ",
            Key_Dot               => "<.>      ",
            Key_Slash             => "</>      ",
            Key_Left_Single_Quote => "<`>      ",
            Keypad_Enter          => "KP-< >   ",
            Keypad_Slash          => "KP-</>   ",
            Keypad_Star           => "KP-<*>   ",
            Keypad_Minus          => "KP-<->   ",
            Keypad_Plus           => "KP-<+>   ",
            Keypad_Middle         => "KP-Middle");

         subtype Modifier_Key_Title_Type is String (1..5);

         Modifier_Key_Title : constant array (Modifier_Key_Type) of
            Modifier_Key_Title_Type := (
            No_Modifier_Key  => "     ",
            Alt_Key          => "  Alt",
            Ctrl_Key         => " Ctrl",
            Shift_Key        => "Shift");

         function Is_Printable (C : in Character)
         return Boolean
         is
         begin
            if Character'Pos (C) in 0..31 then return False;
            else return True;
            end if;
         end;

         procedure Events_Loop
         is
             K                : Keystroke_Input_Type;
            -- Est-ce qu'une touche était appuyée à la précédente itération ?
            Previous_Pressed : Boolean;
         begin
            -- Affiche le contenu initial
            Draw (W0, 1,  1, Blue, "Touche appuyee :");
            Draw (W0, 1, 17, Cyan, " ---");
            Draw (W0, 2,  1, Blue, "[C:  K:     +         ]");
            Draw (W0, 2,  4, Cyan, '-');
            Draw (W0, 2,  8, Cyan, "-----");
            Draw (W0, 2, 13, Blue, '+');
            Draw (W0, 2, 14, Cyan, "---------");
            Move_Cursor_To (W0, 2, 26);
            -- Previous_Pressed indique si une touche était appuyée à
            -- l'itération précédente.
            Previous_Pressed := False;
            loop
               Get_Key (K);
               if K.Character_Available and not Is_Printable (K.C) then
                  K.Character_Available := False;
               end if;
               if K.Key_Available and K.Key = Key_Escape then
                  Beep;
                  exit;
               end if;
               if K.Character_Available then
                  Draw (W0, 2, 4, Cyan, K.C);
                  if not K.Key_Available then
                     Draw (W0, 2, 8, Cyan, "         ");
                  end if;
               end if;
               if K.Key_Available then
                   if K.Modifier_Key /= No_Modifier_Key then
                     Draw (W0, 2,  8, Cyan, Modifier_Key_Title (K.Modifier_Key));
                     Draw (W0, 2, 13, Blue, '+');
                     Draw (W0, 2, 14, Cyan, Key_Title (K.Key));
                  else
                     Draw (W0, 2,  8, Cyan, "     ");
                     Draw (W0, 2, 13, Blue, ' ');
                     Draw (W0, 2, 14, Cyan, Key_Title (K.Key));
                  end if;
                  if not K.Character_Available then
                     Draw (W0, 2, 4, Cyan, ' ');
                  end if;
               end if;
               if K.Key_Available or K.Character_Available then
                  Draw (W0, 1, 18, Cyan, "Oui");
                  Previous_Pressed := True;
                  Move_Cursor_To (W0, 2, 26);
               else
                  if Previous_Pressed then
                     -- On ne le fait que si on était pas déjà dans
                     -- cet état lors de l'itération précédente (ceci évite
                     -- de faire flotter l'affichage).
                     Draw (W0, 1, 18, Cyan, "Non");
                     Move_Cursor_To (W0, 2, 20);
                     Previous_Pressed := False;
                     Move_Cursor_To (W0, 2, 26);
                  end if;
               end if;
            end loop;
         end;

      begin
         Initialize;

         -- Comme avec Windows, on commence par mettre en place le fond du bureau :P
         Draw_Desktop_Background (1, 1, 25, 80, Blue);

         -- Une première fenêtre avec du texte à l'intérieur.
         Draw_Window (W1);
         Draw (W1, 1, 1, White, "Coucou! :)");
         Draw (W1, 2, 1, White, "Je suis Windows 3.1");
         Draw (W1, 3, 1, White, "en mode texte");
         Draw (W1, 4, 1, White, "Coooool!");

         -- Idem… une deuxième.
         Draw_Window (W2);
         Draw (W2, 1, 1, White, "Waaaw, encore mieux");
         Draw (W2, 2, 1, White, "Moi j'suis Windows XP");
         Draw (W2, 3, 1, White, "… en mode texte aussi :P");
         Draw (W2, 4, 1, White, "… le bleu me va si bien");
         Draw (W2, 5, 1, White, "… hihihi");

         -- Messages pour que l'utilisateur(rice) ne se sente pas perdu(e).
         Draw_Window (W3);
         Draw_Centered (W3, 1, Green, "Note : appuyez ALT+Enter pour basculer le plein ecran");
         Draw_Centered (W3, 2, Yellow, "Appuyez sur des touches au hasard pour tester leurs codes");
         Draw_Centered (W3, 3, Yellow, "Appuyez sur Echap pour quitter");

         -- La fenêtre de test du clavier.
         Draw_Window (W0);

         -- Exécution.
         Events_Loop;

         Leave_And_Restore_Defaults;
      end;