GEM, être au 7ème ciel
(Programmation GFA 5)

Toujours plus loin, toujours plus haut... Nous allons nous intéresser à l'essence même du multitâche : les lancements d'applications. Mais d'attaquer cette partie noble, nous allons revenir sur certains points car je suis un gros...

Cochon qui s'en dédie

S'il est une chose en informatique de primordiale, c'est bien la remise en question. Une routine qui peut vous paraître belle ne le sera plus le lendemain, une instruction qui marche bien dans tel environement logiciel risquera de planter dans un autre... Et quand on s'aperçoit qu'on pouvait faire plus simple : honte à moi.

Ainsi, au lieu d'appeler le sélecteur de fichier de MagiC à la main, Il suffit de faire :

    Pdomain(n) ou ~GEMDOS(281,W:n)

- Si `n=0`, on est dans le mode TOS normal.
- Si `n=1`, le système d'exploitation va utiliser les fonctions de gestion de fichiers étendues. Donc les noms longs sous Minix ou VFAT. C'est un appel MiNT, donc ça marche sous MiNT et MagiC. Les ouvertures, fermetures de fichiers marchent maintenant avec les noms longs, le fileselector avec les vieux appels AES se trouve être géré avec les noms longs... Le Compendium indique même que FSFIRST et FSNEXT marchent avec les noms longs : là je resterai prudent et utiliserai les fonctions que j'ai décrites dans les numéros 2 et 3 de STimulus.
  Là où c'est cool (Raoul !), c'est qu'on peut sans vergogne utiliser Pdomain() n'importe où dans son programme, et ainsi swapper d'un mode à l'autre pour les noms longs. Merci à Arnaud BERCEGEAY pour ces informations.

  Deuxième remise en cause : la variable d'environnement. Je vous avais expliqué qu'elle était totalement absconse pour OLGA & cie. Et bien ce n'est pas totalement vrai. Faut toujours l'écrire à la main, mais si vous voulez économiser de la mémoire, je vous conseille d'utiliser celle d'OLGA. Cette application a la faculté de quitter elle-même la mémoire quand aucune application la gérant est en mémoire. Par contre, préférez le lancement en AUTO, ACC ou dossier START de MagiC car BubbleGEM n'utilise pas le Daemon avec la variable d'environnement. Donc dans MAGX.INF, écrivez :

      #_ENV OLGAMANAGER=chemin+nom de OLGA

  Dans MINT.CNF, remplacez `#_ENV` par `setenv`.

  Pour récupérer le chemin et nom complet d'OLGA à partir de la variable d'environnement, on utilise la fonction `SHEL_ENVRN(adresse%,chaine$)`. Pour OLGA, ça donnera :

      ~SHEL_ENVRN(adresse%,"OLGAMANAGER=") ! n'oubliez pas le "="
      IF adresse%>0
        nom_olga$=CHAR{adresse%}
      ENDIF

  En C, adresse% est un pointeur sur un pointeur. Vu que j'ai pas trop confiance dans cette fonction GFA, la voici réécrite par votre serviteur :

      get_env("OLGAMANGER="+CHR$(0),nom_olga$)
      '
      PROCEDURE get_env(get_env$,VAR ret_env$)
        '
        ret_env$=CHR$(0)
        '
        IF aa_start%>0  ! aa_start% est l'adresse
          '               d'un buffer mémoire de 256 octets
          INT{ADD(GCONTRL,2)}=0
          INT{ADD(GCONTRL,4)}=1
          INT{ADD(GCONTRL,6)}=2
          INT{ADD(GCONTRL,8)}=0
          '
          LONG{ADDRIN}=aa_start%
          LONG{ADD(ADDRIN,4)}=V:get_env$
          '
          GEMSYS 125
          '
          IF LONG{aa_start%}>0
            ret_env$=CHAR{{aa_start%}}+CHR$(0) ! pointeur sur pointeur !
          ENDIF
        ENDIF
        '
      RETURN

  Ayant récupéré le nom de fichier complet d'OLGA, vous pouvez maintenant faire un SHEL_WRITE. Puis soit attendre que APPL_FIND() trouve OLGA pour se déclarer à OLGA, soit le faire plus loin, avec EVNT_MULTI et MU_TIMER avec un flag.

La question bête...

Et mon SHEL_WRITE, comment je le fais, hein ? Et bien c'est le sujet de ce trimestre. Comme on fait les choses dans l'ordre, on commence par quelques précisions.
Premièrement, c'est bien beau de lancer un programme. Mais il va se trouver perdu si on n'a pas indiqué son répertoire. Comment peut-il par exemple charger son fichier ressource si le répertoire courant est celui de votre application ? Ça donne avant et après chaque lancement :

    ~GEMDOS(14,W:SUB(ASC(nom_prg$),65))
    CHDIR LEFT$(nom_prg$,RINSTR(nom_prg$,"\"))+CHR$(0)
    '
    lancement
    petit délai si SHEL_WRITE
    '
    ~GEMDOS(14,W:SUB(ASC(votre_chemin$),65))
    CHDIR votre_chemin$+CHR$(0)

nom_prg est le nom complet (avec chemin) du programme à lancer, terminé comme d'hab par CHR$(0).
Petite remarque utile et pour ceux qui ne le savent pas, on détermine le chemin de son application lors de l'initialisation avec :

    votre_chemin$=CHR$(ADD(GEMDOS(25),65)+":"+DIR$(SUCC(GEMDOS(25))+"\"

Faites attention, cette chaine ne se termine pas par un null-byte. A vous de le mettre en cas de besoin.

Secondement, il va de soi que vous ne monopolisez pas toute la mémoire avec votre application. Je vous conseille de lire un article d'un certain Féroce Lapin (ST Magazine numéro 63 page 60, Cubase en couverture). Tous mes programmes s'en inspirent. Vous pouvez donc regarder mes sources pour voir ça en réel (par exemple dans le dossier BONUX du numéro 4 de STimulus).

Qu'il est paresseux, l'animal !

Il y a, comme dans n'importe quel problème, une méthode pour se la couler douce. Ici, il s'agit de faire faire les lancements des applications par un programme différent du vôtre. Ça vous dit rien le protocole VA ? On avait vu le VA_START, on va voir le AV_STARTPROG. Il va de soi que le programme à qui vous demandez de lancer une application à votre place comprenne le message : ça tombe bien, MagxDesk, même s'il n'est pas un serveur AV complet, l'accepte sans rechigner.

Après la demande à MagxDesk, et celui-ci va lancer le programme selon les préférences que vous avez définies dans le bureau (Déclarer une application).
Petit délice : si vous avez indiqué le mode Single pour ce programme, MagixDesk va le lancer en mode Single. C'est la seule manière de lancer les programmes en vrai monotache. Il existe un code spécial pour SHEL_WRITE (101) pour le mode Single, mais je n'ai jamais réussi à le faire marcher. Ceci est probablement réservé à l'application dont l'ap_id&=0 et impose quelques manips de plus (justement au niveau de l'ap_id&).

Ça donne :

    CHAR{nom_prg%}=nom_prg$
    CHAR{commande%}=commande$ ! au format Pascal
    '
    INT{buf_adr%}=&H4722   ! AV_STARTPROG
    INT{buf_adr%+2}=ap_id&  ! l'identificateur de votre application
    INT{buf_adr%+4}=0
    LONG{buf_adr%+6}=nom_prg%    ! pointeur vers le chemin+nom
    LONG{buf_adr%+10}=commande%  ! pointeur vers la commande
    INT{buf_adr%+14}=0   ! ? la doc-dev de MagiC indique 0
    '                    ! mais d'autres valeurs sont possibles
    ~APPL_WRITE(0,16,buf_adr%)

`buf_adr%` est un buffer de 16 octets situé en mémoire globale `GEMDOS(68,L:taille%,W:32)` si possible. `nom_prg%` un buffer de 256 octets contenant le nom complet de l'application. A noter que ça peut être n'importe quoi : c'est MagxDesk qui se débrouille après (ACC, TTP, PRG, etc). `commande%` est un buffer de 128 octets qui contiendra une chaîne au format Pascal (`CHR$(SUCC(LEN(commande$)))+commande$+CHR$(0)`) qui ne doit pas dépasser 128 octets (sinon ça va foirer).
Ici, pas besoin de bidouiller de chemin en cours avec les CHDIR, c'est encore MagxDesk qui prend le problème en main. On l'applaudie bien fort !

On vérifie l'aileron arrière et on se lance !

Sans ambages, comme cela, ça donne :

    ret&=EXEC(0,nom_prg$,commande_formate$,env$)

Quoiqu'il arrive, c'est un lancement en monotâche : si vous faites cela en mutlitâche, votre application sera "endormie" jusqu'à ce que vous quittiez le programme que vous avez lancé. Il y a plusieurs modalités à cette fonction (c'est en réalité un GEMDOS(75,W:0,...) ). A vous de voir les doc-dev.

`ret&` est la variable de retour de l'appel au programme. Si par exemple dans le programme lancé vous quittez avec `QUIT 3`, alors vous obtiendrez `ret&=3` dans votre application. Cool non ? Je m'en sert pour interfacer Joe avec ses modules en monotâche : selon la variable de retour du module, Joe va charger ce qu'il y a dans le clipboard sur disque. Et qu'est-ce qu'il y a dans le clipboard sur disque ? Hein ? Les tags que le module a produits !

Il est préférable de terminer vos chaînes par un nullbyte. C'est surtout important pour `commande_formate$` (toujours au format Pascal, cf ci-dessus), et pour `env$`.

`env$` va inscrire des variables d'environnement dans le tampon AES, du style GFALIB=chemin+nom... `env$` n'et pas limitée à 128 caractères, ce qui permet d'en mettre pas mal. Chaque expression est terminée par un nullbyte, la fin de `env$` est terminée par 2 nullbytes. C'est en général pas très utilisé sauf si...

Petit problème : votre commande mise au format Pascal dépasse les 128 caractères. Ça ne marche plus et il faut ruser. Ou va-t-on fourrer les paramètres à donner au programme à lancer ? Bravo ! dans `env$`. Cela donne pour préparer cette chaîne d'environnement (tiré des docs d'ERGO-PRO donc à vérifier) :

    commande$=commande$+" "
    a&=INSTR(commande$," ")  ! séparation des arguments par des nullbytes
    WHILE a&
      MID$(commande$,a&,1)=CHR$(0)
      a&=INSTR(commande$," ")
    WEND
    '
    env$="ARGV=0"+CHR$(0)
    env$=env$+nom_prg$+CHR$(0)
    env$=env$+cmd$+CHR$(0)+CHR$(0)
    '
    commande$=CHR$(127)+CHR$(0) ! pour indiquer qu'on doit aller dans env$

On lance avec ces paramètres, et dans le programme "fils", on va récupérer la commande avec les argv bien connus du C. En GFA, je n'ai jamais fait, mais on pourrait regarder le tampon AES et récupérer avec la fonction get_env$ citée plus haut la commande.

Pour récupérer la commande classiquement, on effectue :

    a&=BYTE{BASEPAGE+128}
    IF a&<127
      commande$=CHAR{BASEPAGE+129}
    ENDIF

Attention, il faut récupéer cette chaîne avant tout appel au niveau des fichiers genre OPEN, FILESELECT et surtout EXIST (cette fonction est à proscrire, utilisez celle que je vous ai donnée dans le numéro 2 de notre si merveilleux Fanzine :) ).

Accélération, le vent fouette le visage (sans parler des mouches qui s'y écrasent).

    ~SHEL_WRITE(mode&,wisgr&,wiscr&,nom_prg$,commande_formate$)

La voici écrite plus décemment :

    PROCEDURE shl_write(mode&,wisgr&,wiscr&,nom_prg$,commande_formate$)
      '
      GCONTRL(0)=121
      GCONTRL(1)=3
      GCONTRL(2)=1
      GCONTRL(3)=2
      GCONTRL(4)=0
      '
      GINTIN(0)=mode&
      GINTIN(1)=wisgr&
      GINTIN(2)=wiscr&
      '
      ~FRE(0) ! petite sécurité, on provoque un garbage collection avant
      '       ! que le GFA ne le fasse pour nous (important quand on fait des V:chaine$)
      '
      IF mode&>256  ! je vais vous expliquer
        ADDRIN(0)=shel_write_buffer%
        ADDRIN(1)=V:commande$
      ELSE
        ADDRIN(0)=V:nom_prg$
        ADDRIN(1)=V:commande$
      ENDIF
      '
      GEMSYS
      '
      IF GINTOUT(0)=0
        ' alerte = problème au lancement
      ENDIF
      '
    RETURN

Comme d'hab les chaine$ sont au format C, voire Pascal. Les `mode&` qui nous intéressent sont :

- 1 = lancement d'un PRG,APP,GTP,TOS,TTP, l'extension importe peu, c'est le type d'éxécutable qui compte. On pourrait donc lancer des PRX.
- 3 = lancement d'accessoire.

Il existe de nombreux autres codes, pour le shutdown, le changement de résolution... Qui c'est qui va chercher ses doc-dev ?
Pour `wisgr&` :

- 1 = programmes GEM (avec les zolies fenêtres)
- 0 = programmes TOS (mode texte)

Pour `wiscr&`, je mets toujours 100. Ne me demandez pas pourquoi, je l'ai oublié moi-même. On peut également mettre 101, ce qui selon la doc de MagiC permettra un lancement en mode Single, mais il faut (d'après ce que j'ai déchiffré) que votre application ait l'`ap_id&=0`, sinon c'est à MagixDesk qu'il faut s'adresser (voir plus haut).

Ici, plus besoin de s'occuper de la taille de la `commande$`, elle a été étendue à 256 caractères voire gérée si trop grande par SHEL_WRITE lui-même.

Comportement de cette fonction ? A vous d'expérimenter... Ça donne des choses magnifiques comme par exemple en mode Single, vous lancez une application fille en multitâche.
A noter que SHEL_WRITE pour le monoTOS marche sans problèmes (sauf les lancements d'accessoires). Seulement il faut quitter de vous-même votre application, et au lieu de retourner sur le bureau, l'application fille sera automatiquement lancée.

Tel un Faucon, je survole et matrîse le sujet.

Ben et le `shel_write_buffer%`, il servait à quoi ? Car voyez-vous, la fonction a été sauvagement étendue en voulant rester compatible. Au lieu d'envoyer le `nom_prg$`, on donne une adresse sur une structure : pointeur sur pointeur ! dont l'adresse est `shel_write_buffer%`.

Si les bits 8,9,10 et 11 de `mode&` sont allumés (c'est à dire `mode&` supérieur à 256), SHEL_WRITE considérera en C qu'il tombe sur un pointeur d'une structure plutôt d'un pointeur d'une chaîne de texte (`nom_prg$`). Cela donne ce petit artifice en GFA.

- Le bit 8 correspond au Psetlimit, quantité de mémoire maximum allouée au programme à lancer (si ce n'est pas précisé : c'est la totalité). On l'indique en Ko.
- Le bit 9 allumé fait intervenir la priorité dans la répartition des tâches (en multitâche évidemment, et sous MiNT. Sous MagiC, le système est différent, mais on le garde pour la compatibilité). Ça va de -20 à +20 en passant par 0 (mode par défaut).
- Le bit 10, si enclenché, indiquera à SHEL_WRITE qu'il faut prendre en compte un chemin par défaut (n'oubliez par le null-byte)
- Le bit 11, pareil, sauf que c'est la chaîne `env$` (tiens, elle me manquait, celle-là).

A noter que Psetlimit et Prenice sont des fonction MiNT, du même groupe que Pexec qui n'est autre que GEMDOS(75...), que le monde est petit :) . La structure sera de la forme :

Elément | Nature | Contenu
0 | LONG{} (32 bits) | Adresse de la chaîne de texte correspondant à `nom_prg$`
1 | WORD{} (16 bits) | Valeur de Psetlimit (en Ko ?)
2 | WORD{} (16 bits) | Valeur de Prenice (-20 à +20)
3 | LONG{} (32 bits) | Adresse de la chaîne contenant le chemin par défaut
4 | LONG{} (32 bits) | Adresse de la chaîne correspondant à `env$`

PS : j'ai un doute mais il semblerait que la variable de retour de shel_write indique l'`ap_id&` de l'application lancée. A vérifier.

On se retourve dans 3 mois !

D'ici là, j'aurais le temps de trouver un bon sujet. Oserai-je vous parler de la VDI, autrement plus complexe que l'AES ? Glurps... Il y a aussi quelques zolies fonctions dans MagiC à explorer, ou peut-être la mise en iconification des fenêtres ? Le Drag&Drop ? Bref, le GEM est une véritable aventure.

Rajah Lone
écrit le 8 Septembre 1998