Obligement - L'Amiga au maximum

Vendredi 23 mai 2025 - 00:56  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : C - Créer un super Shell avec menu Intuition
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - avril 1991)


Je vous propose aujourd'hui d'allier la convivialité du Workbench avec la rigueur du Shell. Comment ? Tout simplement en ajoutant un menu Intuition aux fenêtres Shell (ou CLI, cela s'entend). Ceci nous permettra d'utiliser depuis le Shell tous les raccourcis clavier que l'on voudra, avec en plus une option spécial fainéants : le seul fait d'insérer une disquette dans un lecteur quelconque en affichera le catalogue.

Une fenêtre Shell n'est ni plus ni moins qu'une fenêtre Intuition. Notre programme, lancé depuis cette fenêtre, doit tout d'abord en rechercher le pointeur. C'est très simple : la structure "Process" de notre programme contient un pointeur sur la console_task associée (le Shell). Il nous suffit d'envoyer à cette console_task un DosPacket de type ACTION_DISK_INFO avec en arg1, un pointeur sur une structure InfoData. En retour, on recevra le pointeur de la fenêtre utilisée par la console (pour en savoir plus sur les DosPacket, vous pouvez vous reporter à l'excellent article "Mettez un silencieux dans vos lecteurs de disquette").

Cette fenêtre, comme quelques essais permettent de s'en convaincre, n'utilise aucun drapeau IDCMP et ne comporte donc pas de port utilisateur. Il nous faudra donc d'abord créer un port, puis l'attacher à la fenêtre, modifier les drapeaux IDCMP puis installer le menu. Tout ceci ne mérite pas grandes explications car ce n'est que l'utilisation de routines système bien connues. Voilà, le menu est installé et nous pouvons déjà utiliser les raccourcis clavier ainsi définis, afin d'accéder ultra-rapidement à vos outils préférés (moi c'est Devpac 2 et Genam2/Monam2).

Catalogue automatique

Cela est rendu possible par le fait qu'un message Intuition est généré lorsqu'on insère une disquette. D'abord, il faut savoir que ce message sera envoyé à la fenêtre que celle-ci soit active ou pas. Ce sera donc à notre programme d'effectuer ce test en examinant ActiveWindow d'IntuitionBase.

Les routines qui déterminent dans quel lecteur la disquette a été insérée sont directement inspirées de VirusX de Steve Tibett. Afin d'éviter le sac de noeuds entre les sorties de plusieurs programmes, le catalogue est affiché dans une fenêtre DOS spécialement ouverte à cet effet. L'option catalogue automatique peut être désactivée par le menu.

Rendre la main proprement

Ce programme, écrit avec le Lattice 5.10, doit être relié avec cback.o, de façon à tourner en tâche de fond. Nous avons donc la situation suivante : deux programmes distincts utilisent la même console_task. C'est pour cela que vous ne pourrez pas ajouter une option "endcli" dans le menu.

L'option "quitter" arrête le programme, mais laisse le Shell actif qui redevient un Shell classique. Si vous faites directement un "endcli" depuis le Shell, la fenêtre ne sera toutefois pas fermée pour la même raison. Il vous suffira alors d'actionner "Amiga-Q" (il serait bien sûr possible d'améliorer ceci, mais cela rallongerait considérablement le listing, donc je préfère vous laisser exercer votre sagacité).

Remarquez toutefois qu'il est impératif de supprimer tous les messages "pendants" au port de la fenêtre (fonction SafelyIDCMP) et d'effacer soi-même le port, sinon c'est le gourou assuré.

Utilisation du programme

Il y a deux manières de lier le SuperShell à un Shell standard :
  • Par simple appel depuis le Shell.
  • Mais vous pouvez aussi le placer au début du fichier Shell-Startup (ou Cli-Startup). Ainsi, tout Shell ouvert sera automatiquement un SuperShell.
/**************************************************************************/
/* Programme: SuperShell.c                                                */
/* Fonction: Accroche un menu à une fenêtre Shell (ou CLI)                */
/* Utilisation: - Appel depuis Shell                                      */
/*              - Installation dans le Shell-Startup                      */
/*                ou dans le Cli-Startup                                  */
/* Compilateur: Lattice C 5.10                                            */
/* Compilation: lc -cist -v -y                                            */
/* Linkage: FROM LIB:cback.o+"SuperShell.o"                               */
/*          TO "SuperShell"                                               */
/*          LIB LIB:lc.lib LIB:amiga.lib                                  */
/*          NODEBUG                                                       */
/*          SMALLCODE                                                     */
/*          SMALLDATA                                                     */
/*          DEFINE __main=__tinymain                                      */
/* Auteur: F MAZUE pour ANT le 18/12/90                                   */
/**************************************************************************/

#include <devices/trackdisk.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <string.h>

/**************************************************************************/
/*                      DECLARATION POUR CBACK                            */
/**************************************************************************/

extern BPTR _Backstdout;
char *_procname = "SuperShell";
LONG _BackGroundIO = 1;
LONG _stack = 4000;
LONG _priority = 20;

/**************************************************************************/
/*    définition du menu qui sera ajouté à la fenêtre CLI                 */
/**************************************************************************/

struct TextAttr TOPAZ80 =
{
	(STRPTR) "topaz.font",
	TOPAZ_EIGHTY,
	0,
	0
};

struct IntuiText AutoText[] =
{
	{2, 1, JAM2, 2, 1, NULL, "   List ", NULL}
};

struct MenuItem AutoItem[] =
{
	/* List */
	{
		NULL, 0, 0, 0, 0,
		ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT | MENUTOGGLE | CHECKED, 0,
		(APTR)&AutoText[0], NULL, NULL, NULL, MENUNULL
	}
};

struct IntuiText ToolText[] =
{
	{2, 1, JAM2, 2, 1, NULL, "Genam2", NULL},
	{2, 1, JAM2, 2, 1, NULL, "Monam2", NULL},
	{2, 1, JAM2, 2, 1, NULL, "Dpaint", NULL},
	{2, 1, JAM2, 2, 1, NULL, "Quitter", NULL}
};

struct MenuItem ToolItem[] =
{
	{	/* Genam2 */
		&ToolItem[1], 0, 0, 0, 0,
		ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP, 0,
		(APTR)&ToolText[0], NULL, 'G', NULL, MENUNULL
	},
	
	{	/* Monam2 */
		&ToolItem[2], 0, 0, 0, 0,
		ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP, 0,
		(APTR)&ToolText[1], NULL, 'M', NULL, MENUNULL
	},
	
	{	/* Dpaint */
		&ToolItem[3], 0, 10, 0, 0,
		ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP, 0,
		(APTR)&ToolText[2], NULL, 'P', NULL, MENUNULL
	},
	
	{	/* Quitter */
		NULL, 0, 10, 0, 0,
		ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP, 0,
		(APTR)&ToolText[3], NULL, 'Q', NULL, MENUNULL
	}
};

struct Menu Menus[] =
{
	{&Menus[1], 0, 0, 0, 0, MENUENABLED, "Tools", &ToolItem[0]},
	{NULL, 80, 0, 0, 0, MENUENABLED, "Auto", &AutoItem[0]}
};

struct Menu *FirstMenu = &Menus[0];

struct IntuiText WinText[] =
{
	{3, 0, JAM2, 54, 28, &TOPAZ80, "How to do a Menu", NULL},
	{3, 0, JAM2, 70, 38, &TOPAZ80, "(with Style)", &WinText[0]}
};

struct NewWindow NewWindow =
{
	202, 66, 234, 66, 2, 1, MENUPICK | CLOSEWINDOW,
	WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | ACTIVATE | NOCAREREFRESH,
	NULL, NULL, "Menus", NULL, NULL, 0, 0, -1, -1, WBENCHSCREEN
};

/**************************************************************************/
/*                  fonctions de gestion du menu                          */
/**************************************************************************/

BOOL AdjustMenus(struct Menu *, struct TextAttr *);
VOID AdjustItems(struct RastPort *, struct MenuItem *, struct TextAttr *,
				 USHORT, USHORT, USHORT, USHORT);
VOID AdjustText(struct IntuiText *text, struct TextAttr *attr);
USHORT MaxLength(struct RastPort *, struct MenuItem *, USHORT);

struct IntuiMessage *message;

/**************************************************************************/
/*  Pour ces deux fonctions, se reporter aux                              */
/* RMK Libraries and Devices pages 171                                    */
/**************************************************************************/

VOID StripIntuiMessages(struct MsgPort *mp, struct Window *win);
VOID IDCMPSafely(struct Window *win);
int handleIDCMP(struct Window *win);

/**************************************************************************/
/*        pour recherche du pointeur de fenêtre du CLI courant            */
/**************************************************************************/

#define BTOC(bptr) ((long)(bptr) << 2) /* conversion BCPL -> C */
#define CTOB(cptr) ((long)(cptr) >> 2) /* conversion C -> BCPL */

struct Window *GetWindow();
struct Window *CliWindow;
struct MsgPort *WinPort;

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;

/**************************************************************************/
/*                     pour gestion des drives                           */
/**************************************************************************/

LONG ChangeCount[4];
struct MsgPort *diskport;
struct IOExtTD *diskreq;

VOID InitDrive();
int DriveNum();

VOID CleanExit();

/**************************************************************************/
/*                         PROGRAMME PRINCIPAL                           */
/**************************************************************************/

VOID main()
{
	int signal;
	int signalmask;
	int OK = 0;

	if (!(IntuitionBase =
			  (struct IntuitionBase *)OpenLibrary("intuition.library", 0L)))
		CleanExit();

	if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L)))
		CleanExit();

	CliWindow = (struct Window *)GetWindow();
	if (!CliWindow) CleanExit();

	diskport = (struct MsgPort *)CreatePort(NULL, NULL);
	if (!diskport) CleanExit();

	diskreq = (struct IOExtTD *)CreateExtIO((struct MsgPort *)diskport, sizeof(struct IOExtTD));
	if (!diskreq) CleanExit();

	WinPort = (struct MsgPort *)CreatePort(NULL, NULL);

	/**********************************************************************/
	/* On attache le port nouvellement créé à la fenêtre CLI              */
	/* Puis on modifie les flags IDCMP de la fenêtre                      */
	/**********************************************************************/

	CliWindow->UserPort = WinPort;

	ModifyIDCMP(CliWindow, MENUPICK | DISKINSERTED);

	SetWindowTitles(CliWindow, "SuperShell par F Mazué", (char *)-1);
	RefreshWindowFrame(CliWindow);

	InitDrive();

	/**********************************************************************/
	/* Maintenant, on initialise le menu et on l’attache à la fenêtre CLI */
	/**********************************************************************/

	AdjustMenus(FirstMenu, CliWindow->WScreen->Font);
	SetMenuStrip(CliWindow, FirstMenu);

	signalmask = 1L << CliWindow->UserPort->mp_SigBit;

	do
	{
		signal = Wait(signalmask);
		if (signal & signalmask) OK = handleIDCMP(CliWindow);
	} 
	while (!OK);

	SetWindowTitles(CliWindow, "AmigaShell", (char *)-1);
	RefreshWindowFrame(CliWindow);
	IDCMPSafely(CliWindow);

	ClearMenuStrip(CliWindow);
	CleanExit();
}

struct Window *GetWindow() {
	struct Process *proc;
	struct StandardPacket *packet;
	struct InfoData *infodata;
	long result;
	struct Window *win;
	
	proc = (struct Process *)FindTask(NULL);
	if (!proc->pr_ConsoleTask)
		return (NULL);
	
	packet = (struct StandardPacket *)AllocMem(sizeof(struct StandardPacket), MEMF_CLEAR | MEMF_PUBLIC);
	infodata = (struct InfoData *)AllocMem(sizeof(struct InfoData), MEMF_CLEAR | MEMF_PUBLIC);

	packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
	packet->sp_Pkt.dp_Link = &packet->sp_Msg;
	packet->sp_Pkt.dp_Port = &proc->pr_MsgPort;
	packet->sp_Pkt.dp_Type = ACTION_DISK_INFO;
	packet->sp_Pkt.dp_Arg1 = CTOB(infodata);

	PutMsg((struct MsgPort *)proc->pr_ConsoleTask, (struct Message *)packet);
	WaitPort(&proc->pr_MsgPort);
	GetMsg(&proc->pr_MsgPort);

	result = packet->sp_Pkt.dp_Res1;
	win = (struct Window *)infodata->id_VolumeNode;

	FreeMem(packet, sizeof(struct StandardPacket));
	FreeMem(infodata, sizeof(struct InfoData));
	if (!result) return (NULL);
	return (win);
}

BOOL AdjustMenus(struct Menu *firstmenu, struct TextAttr *attr) {
	struct RastPort textrp = {0};
	struct Menu *menu;
	struct TextFont *font;
	USHORT start, width, height, space;
	BOOL retval = FALSE;
	
	if ((font = (struct TextFont *)OpenFont(attr))) {
		SetFont(&textrp, font);
		width = font->tf_XSize;
		
		height = (font->tf_YSize < 8) ? 8 : font->tf_YSize;
		height++;
		
		start = 2;
		
		menu = firstmenu;
		while (menu)
		{
			menu->LeftEdge = start;
			menu->Width = space =
			TextLength(&textrp, menu->MenuName, (LONG)strlen(menu->MenuName)) +
			width;
			AdjustItems(&textrp, menu->FirstItem, attr, width, height, 0, 0);
			menu = menu->NextMenu;
			start += (space + (width * 2));
		}
		CloseFont(font);
		retval = TRUE;
	}
	return(retval);
}

VOID AdjustItems(struct RastPort *txtrp, struct MenuItem *fi,
				 struct TextAttr *atr, USHORT wdth, USHORT hght, USHORT lv1,
				 USHORT edge)
{
	struct MenuItem *item = fi;
	register USHORT num;
	USHORT strip_width, sub_edge;

	if (fi == NULL) return;

	strip_width = MaxLength(txtrp, item, wdth);
	num = 0;
	while (item)
	{
		item->TopEdge = (num * hght) - lv1;
		item->LeftEdge = edge;
		item->Width = strip_width;
		item->Height = hght;
		sub_edge = strip_width - wdth;
		AdjustText((struct IntuiText *)item->ItemFill, atr);
		AdjustItems(txtrp, item->SubItem, atr, wdth, hght, 1, sub_edge);
		item = item->NextItem;
		num++;
	}
}

USHORT MaxLength(struct RastPort * txtrp, struct MenuItem * fi,
					 USHORT width)
{
	USHORT maxval = 0, textlen;
	struct MenuItem *item = fi;
	struct IntuiText *itext;

	while (item)
	{
		if(item->Flags & COMMSEQ)
		{
			width += (width + COMMWIDTH);
			break;
		}
		item = item->NextItem;
	}
	item = fi;
	while (item)
	{
		itext = (struct IntuiText *)item->ItemFill;
		textlen = itext->LeftEdge +
		TextLength(txtrp, itext->IText, (LONG)strlen(itext->IText)) + width;
		
		maxval = (textlen < maxval) ? maxval : textlen;
		item = item->NextItem;
	}
	return (maxval);
}

VOID AdjustText(struct IntuiText *text, struct TextAttr *attr)
{
	struct IntuiText *nt;
	nt = text;
	while(nt)
	{
		nt->ITextFont = attr;
		nt = nt->NextText;
	}
}

/**************************************************************************/
/* Cette fonction initialise le tableau ChangeCount en:                   */
/* - examinant si les drives sont connectés (non = -1)                    */
/* - si oui, en examinant le status actuel pour remplir                   */
/*   le tableau en conséquence                                            */
/**************************************************************************/
VOID InitDrive()
{
	LONG x;
	BYTE error = 0;

	for (x = 0; x < 4; x++) /* go thru all 4 possible drives */
	{
		error = OpenDevice("trackdisk.device", (LONG)x, (struct IORequest *)diskreq, 0L);
		if (error > 0)
		{
			ChangeCount[x] = -1;
		} 
		else {
			diskreq->iotd_Req.io_Command = TD_CHANGENUM;
			DoIO((struct IORequest *)diskreq);
			ChangeCount[x] = diskreq->iotd_Req.io_Actual;
			CloseDevice((struct IORequest *)diskreq);
		}
	}
}

/**************************************************************************/
/* Cette fonction renvoie dans un entier le numéro                        */
/* du drive dans lequel une disquette vient d'être insérée                */
/**************************************************************************/
int DriveNum()
{
	int RetVal = -1;
	LONG x;
	BYTE error = 0;
	
	for (x = 0; x < 4; x++)
	{
		if (ChangeCount[x] == -1)
			continue; /* pas de lecteur */
		error = OpenDevice("trackdisk.device", (LONG)x,
						   (struct IORequest *)diskreq, 0L);
		if (error > 0)
			continue; /* quelque chose a raté */
		
		diskreq->iotd_Req.io_Command = TD_CHANGESTATE;
		DoIO((struct IORequest *)diskreq);
		if (diskreq->iotd_Req.io_Actual != 0)
		{
			CloseDevice((struct IORequest *)diskreq);
			continue;
		}
		
		diskreq->iotd_Req.io_Command = TD_CHANGENUM;
		DoIO((struct IORequest *)diskreq);
		if (diskreq->iotd_Req.io_Actual != ChangeCount[x]) {
			RetVal = (int)x;
			ChangeCount[x] = diskreq->iotd_Req.io_Actual;
			CloseDevice((struct IORequest *)diskreq);
			break;
		}
		
		CloseDevice((struct IORequest *)diskreq);
	}

	return(RetVal);
}

VOID IDCMPSafely(struct Window *win)
{
	Forbid();
	StripIntuiMessages(win->UserPort, win);
	win->UserPort = NULL;
	ModifyIDCMP(win, NULL);
	Permit();
}

VOID StripIntuiMessages(struct MsgPort *mp, struct Window *win)
{
	struct IntuiMessage *msg, *succ;
	
	msg = (struct IntuiMessage *)mp->mp_MsgList.lh_Head;
	
	while (succ = (struct IntuiMessage *)msg->ExecMessage.mn_Node.ln_Succ)
	{
		if (msg->IDCMPWindow == win)
		{
			Remove((struct Node *)msg);
			ReplyMsg((struct Message *)msg);
		}
		msg = succ;
	}
}

int handleIDCMP(struct Window *win)
{
	int drive;
	int done = 0;
	USHORT code, flag;
	struct IntuiMessage *message = NULL;
	LONG class, itemNum, menuNum, file;
	char cmd[10];
	char titre[22];
	
	strcpy(cmd, "list dfx:");
	strcpy(titre, "CON:1/11/520/200/d£x:");

	while (message = (struct IntuiMessage *)GetMsg(win->UserPort))
	{
		class = message->Class;
		code = message->Code;
		ReplyMsg((struct Message *)message);
		switch (class)
		{
		case DISKINSERTED:
			drive = DriveNum();
			flag = ((struct MenuItem *)ItemAddress(
						FirstMenu, (SHIFTMENU(1) | SHIFTITEM(0))))
					   ->Flags;
			if(CliWindow == IntuitionBase->ActiveWindow) {
				if (flag & CHECKED) /* si auto cd selectionné */
				{
					cmd[7] = (char)(drive + 0x30); /* itoa */
					titre[19] = (char)(drive + 0x30);
					file = Open(titre, MODE_OLDFILE);
					Execute(cmd, NULL, file);
					Delay(150);
					Close(file);
				}
			}
			break;
			
		case MENUPICK:
			itemNum = ITEMNUM(code);
			menuNum = MENUNUM(code);
			switch (menuNum)
			{
			case 0:
				switch (itemNum)
				{
				case 0:
					Execute("genam2", NULL, _Backstdout);
					break;
				case 1:
					Execute("monam2", NULL, _Backstdout);
					break;
				case 2:
					Execute("Deluxe PaintIII: DPaint", NULL, _Backstdout);
					break;
				case 3:
					done = 1;
					break;
				default:
					break;
				}
			default:
				break;
			}
		
		default:
			break;
		}
	}
	return (done);
}

VOID CleanExit() {
	if (WinPort) DeletePort((struct MsgPort *)WinPort);
	if (diskreq) DeleteExtIO((struct IORequest *)diskreq);
	if (diskport) DeletePort((struct MsgPort *)diskport);
	if (GfxBase) CloseLibrary((struct Library *)GfxBase);
	if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	
	if (_Backstdout) {
		Close(_Backstdout); /* on libère le CLI */
		_Backstdout = 0;
	}
	exit(0);
}

Mise à jour de mai 2025 : une archive contenant le listing adapté à vbcc, et avec l'exécutable compilé par vbcc, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur obligement.free.fr/files/antsupershell.lha.


[Retour en haut] / [Retour aux articles]