

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Costel+Mincea</id>
	<title>AmigaOS Documentation Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Costel+Mincea"/>
	<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/wiki/Special:Contributions/Costel_Mincea"/>
	<updated>2026-06-04T21:40:47Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Amiga_Hardware_Manufacturer_ID_Registry&amp;diff=12581</id>
		<title>Amiga Hardware Manufacturer ID Registry</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Amiga_Hardware_Manufacturer_ID_Registry&amp;diff=12581"/>
		<updated>2026-03-06T07:29:18Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: /* How to Obtain a Manufacturer Number */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
= How to Obtain a Manufacturer Number =&lt;br /&gt;
&lt;br /&gt;
Manufacturer&#039;s numbers are assigned by the AmigaOS development team. To obtain your unique manufacturer number send a support request to [mailto:support@hyperion-entertainment.com support@hyperion-entertainment.com].&lt;br /&gt;
&lt;br /&gt;
Be sure and include your name and the type of expansion product you are developing.&lt;br /&gt;
&lt;br /&gt;
= Your Manufacturer Number =&lt;br /&gt;
&lt;br /&gt;
Attention hardware manufacturers. If you are developing a hardware expansion product for the Classic Amiga (e.g. 500, 2000, 3000) then you will need to obtain a special manufacturer ID number from the AmigaOS development team to identify your product. This manufacturer number is used by the Amiga to link your hardware with its driver software at boot time.&lt;br /&gt;
&lt;br /&gt;
Your manufacturer number is part of the special protocol that the Amiga uses to automatically configure all expansion devices on the bus without the user having to cut jumpers or adjust dip switches. This is called auto-config.&lt;br /&gt;
&lt;br /&gt;
At start-up time, the system first polls each board in the system and assigns the board its own address space. If it is a memory board, its RAM is linked into the memory free pool. Later in the boot sequence, after DOS is initialized, the binddrivers program is run. Binddrivers will search the directory SYS:Expansion for the drivers that go with the boards.&lt;br /&gt;
&lt;br /&gt;
To do this binddrivers looks in the Tool Type field of all icon files in SYS:Expansion. If the first seven bytes of the Tool Type field are &amp;quot;PRODUCT&amp;quot;, then this is an icon file for a driver.&lt;br /&gt;
&lt;br /&gt;
Binddrivers will then attempt to match the drivers it has found with the boards that were found earlier. This is where your manufacturer number comes in.&lt;br /&gt;
&lt;br /&gt;
Your manufacturer number goes in two places. First, it is burned in hex form into the PAL which is part of ALL auto-config expansion products. Second, it appears in ASCII in the Tool Type field of the icon file for your product&#039;s driver. By matching these two numbers, the Amiga can automatically configure your board and bind it&#039;s software driver into the system as well.&lt;br /&gt;
&lt;br /&gt;
The manufacturer&#039;s number is a 16-bit ID which appears in PAL offsets $10-$17. There is also an 8-bit product number which you assign. The product number is for uniquely identifying different products from the same vendor. The product number will appear in PAL offsets $04-$07. This gives you a total of 24 bits to identify each of your products.&lt;br /&gt;
&lt;br /&gt;
These 24 bits of information in the PAL which identify your product are matched against the information in the Tool Type field of all icon files in the SYS:Expansion drawer. For example, suppose you are manufacturer #1019. You have two products, #1 and #2 which both use the same driver. The icon for your driver for these two products would have a Tool Type set to &amp;quot;PRODUCT=1019/1|1019/2&amp;quot;. This means: I am an icon for a driver that works with product number 1 or 2 from manufacturer 1019, now bind me. Spaces are not legal. Here are two other examples:&lt;br /&gt;
&lt;br /&gt;
; PRODUCT=1208/11&lt;br /&gt;
: is the Tool Type for a driver for product 11 from manufacturer number 1208.&lt;br /&gt;
&lt;br /&gt;
; PRODUCT=1017&lt;br /&gt;
: is the Tool Type for a driver for any product from manufacturer number 1017.&lt;br /&gt;
&lt;br /&gt;
For an informal explanation of the auto-configuration process, see the chapter on Software Expansion Architecture from the 2nd Annual Amiga Developer&#039;s Conference Notes (available on ADCD 2.1). For the details of timing, power, the PAL equations and PAL address specifications and the expansion library documentation, see sections 3.1 to 3.3 of the A500/A2000 Technical Reference Manual. The original auto-config spec appears in the A1000 Schematics and Expansion Specification.&lt;br /&gt;
&lt;br /&gt;
The auto-config process makes the addition of expansion products to the system very easy. All the user has to do is put the board in any slot and copy the driver from the release disk to his own SYS:Expansion drawer. Everything else is automatic. There are no jumpers or dip switches to set. Best of all, you will get a lot less support calls asking how to make your product work with the XYZ-Corp-battery-backed-clock.&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
Some notes on hardware manufacturer ID assignment.&lt;br /&gt;
&lt;br /&gt;
* Manufacturer ID numbers are assigned free of charge.&lt;br /&gt;
* Please provide the following information when requesting an ID:&lt;br /&gt;
*# Name of the company or person&lt;br /&gt;
*# Postal address/contact address&lt;br /&gt;
*# Name of the contact person, with a corresponding e-mail address&lt;br /&gt;
*# A brief description of the kind of products you want to produce&lt;br /&gt;
* All ID numbers smaller than 5000 must be considered unsafe because no information exists on which numbers exactly were taken during the time Commodore, Inc. disintegrated.&lt;br /&gt;
* ID numbers in the range 8192 to 18841 must be considered unsafe because some hardware developers mistook the numbers assigned to them to be given in hexadecimal format. Hence 0x2000 = 8192 and 0x4999 = 18841.&lt;br /&gt;
&lt;br /&gt;
= Example code =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*-----------------------------------------------*/&lt;br /&gt;
/* Here is a short program which will tell you   */&lt;br /&gt;
/* about the expansion boards configured in your */&lt;br /&gt;
/* system without you opening up the machine.    */&lt;br /&gt;
/* Code by Bill Koester of CATS                  */&lt;br /&gt;
/*-----------------------------------------------*/&lt;br /&gt;
#include &amp;lt;libraries/configvars.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/expansion.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct ExpansionBase *ExpansionBase;&lt;br /&gt;
&lt;br /&gt;
void cleanup();&lt;br /&gt;
void printdev();&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   struct ConfigDev *MyConfigDev;&lt;br /&gt;
&lt;br /&gt;
   /*----------------------------------*/&lt;br /&gt;
   /* Open the expansion library,      */&lt;br /&gt;
   /* if there is a problem then exit  */&lt;br /&gt;
   /*----------------------------------*/&lt;br /&gt;
&lt;br /&gt;
   ExpansionBase =(struct ExpansionBase *) OpenLibrary(EXPANSIONNAME,0);&lt;br /&gt;
   if(ExpansionBase==NULL)&lt;br /&gt;
   {&lt;br /&gt;
      printf(&amp;quot;Error opening expansion library!!\n&amp;quot;);&lt;br /&gt;
      cleanup();&lt;br /&gt;
      exit(0);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   /*--------------------------------------------*/&lt;br /&gt;
   /* Use FindConfigDev to get info on the first */&lt;br /&gt;
   /* expansion board on the list maintained by  */&lt;br /&gt;
   /* Exec.  If there are no expansion boards in */&lt;br /&gt;
   /* the system then exit.                      */&lt;br /&gt;
   /*--------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
   MyConfigDev = NULL;&lt;br /&gt;
&lt;br /&gt;
  /*--------------------------------------------------*/&lt;br /&gt;
  /* FindConfigDev(oldConfigDev,manufacturer,product) */&lt;br /&gt;
  /* oldConfigDev = NULL for the top of the list      */&lt;br /&gt;
  /* manufacturer = -1 for any manufacturer           */&lt;br /&gt;
  /* product      = -1 for any product                */&lt;br /&gt;
  /*--------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
   MyConfigDev = FindConfigDev(NULL,-1,-1);&lt;br /&gt;
&lt;br /&gt;
   if(MyConfigDev==NULL)&lt;br /&gt;
   {&lt;br /&gt;
      printf(&amp;quot;No Configured Devices found!!\n&amp;quot;);&lt;br /&gt;
      cleanup();&lt;br /&gt;
      exit(0);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   printdev(MyConfigDev);&lt;br /&gt;
&lt;br /&gt;
   /*-----------------------------------*/&lt;br /&gt;
   /* OK, there is at least one board,  */&lt;br /&gt;
   /* so loop and get the entire list   */&lt;br /&gt;
   /* printing as we go with printdev() */&lt;br /&gt;
   /*-----------------------------------*/&lt;br /&gt;
&lt;br /&gt;
   while(MyConfigDev = FindConfigDev(MyConfigDev,-1,-1))&lt;br /&gt;
   {&lt;br /&gt;
      printdev(MyConfigDev);&lt;br /&gt;
   }&lt;br /&gt;
   cleanup();&lt;br /&gt;
   return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
/* Close up shop...  */&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
void cleanup()&lt;br /&gt;
{&lt;br /&gt;
   if(ExpansionBase)&lt;br /&gt;
      CloseLibrary(ExpansionBase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------*/&lt;br /&gt;
/* Print out the contents of the    */&lt;br /&gt;
/* dev structure for this expansion */&lt;br /&gt;
/* product.                         */&lt;br /&gt;
/*----------------------------------*/&lt;br /&gt;
&lt;br /&gt;
void printdev(dev)&lt;br /&gt;
struct ConfigDev *dev;&lt;br /&gt;
{&lt;br /&gt;
   char buff[200];&lt;br /&gt;
&lt;br /&gt;
   printf(&amp;quot;Flags          = &amp;quot;);&lt;br /&gt;
   if(dev-&amp;gt;cd_Flags==NULL)&lt;br /&gt;
      printf(&amp;quot;NULL  &amp;quot;);&lt;br /&gt;
   if(dev-&amp;gt;cd_Flags&amp;amp;CDF_SHUTUP)&lt;br /&gt;
      printf(&amp;quot;CDF_SHUTUP  &amp;quot;);&lt;br /&gt;
   if(dev-&amp;gt;cd_Flags&amp;amp;CDF_CONFIGME)&lt;br /&gt;
      printf(&amp;quot;CDF_CONFIGME  &amp;quot;);&lt;br /&gt;
   printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
   printf(&amp;quot;Board Address  = %x\n&amp;quot;,dev-&amp;gt;cd_BoardAddr);&lt;br /&gt;
   printf(&amp;quot;Board Size     = %d bytes\n&amp;quot;,dev-&amp;gt;cd_BoardSize);&lt;br /&gt;
   printf(&amp;quot;Slot  Address  = %d\n&amp;quot;,dev-&amp;gt;cd_SlotAddr);&lt;br /&gt;
   printf(&amp;quot;Slot  Size     = %d\n&amp;quot;,dev-&amp;gt;cd_SlotSize);&lt;br /&gt;
   printf(&amp;quot;Driver code at   %x\n&amp;quot;,dev-&amp;gt;cd_Driver);&lt;br /&gt;
&lt;br /&gt;
   printf(&amp;quot;er_Type         = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Type);&lt;br /&gt;
   printf(&amp;quot;er_Product      = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Product);&lt;br /&gt;
   printf(&amp;quot;er_Flags        = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Flags);&lt;br /&gt;
   printf(&amp;quot;er_Reserved03   = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Reserved03);&lt;br /&gt;
   printf(&amp;quot;er_Manufacturer = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Manufacturer);&lt;br /&gt;
   printf(&amp;quot;er_SerialNumber = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_SerialNumber);&lt;br /&gt;
   printf(&amp;quot;er_InitDiagVec  = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_InitDiagVec);&lt;br /&gt;
   printf(&amp;quot;er_Reserved0c   = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Reserved0c);&lt;br /&gt;
   printf(&amp;quot;er_Reserved0d   = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Reserved0d);&lt;br /&gt;
   printf(&amp;quot;er_Reserved0e   = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Reserved0e);&lt;br /&gt;
   printf(&amp;quot;er_Reserved0f   = %x\n&amp;quot;,dev-&amp;gt;cd_Rom.er_Reserved0f);&lt;br /&gt;
&lt;br /&gt;
   printf(&amp;quot;Hit Return to continue:\n&amp;quot;);&lt;br /&gt;
   gets(buff);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Registry =&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ID (decimal)&lt;br /&gt;
! ID (hexadecimal)&lt;br /&gt;
! Manufacturer&lt;br /&gt;
! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 0001&lt;br /&gt;
| Micronik&lt;br /&gt;
|-&lt;br /&gt;
| 211&lt;br /&gt;
| 00D3&lt;br /&gt;
| Pacific Peripherals/Profex&lt;br /&gt;
|-&lt;br /&gt;
| 221&lt;br /&gt;
| 00DD&lt;br /&gt;
| Kupke Computertechnik GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 256&lt;br /&gt;
| 0100&lt;br /&gt;
| Memphis&lt;br /&gt;
| Originally registered to MacroSystems US&lt;br /&gt;
|-&lt;br /&gt;
| 512&lt;br /&gt;
| 0200&lt;br /&gt;
| 3-State Computertechnik&lt;br /&gt;
|-&lt;br /&gt;
| 513&lt;br /&gt;
| 0201&lt;br /&gt;
| Commodore (Braunschweig)&lt;br /&gt;
|-&lt;br /&gt;
| 514&lt;br /&gt;
| 0202&lt;br /&gt;
| Commodore (West Chester)&lt;br /&gt;
|-&lt;br /&gt;
| 515&lt;br /&gt;
| 0203&lt;br /&gt;
| Commodore (West Chester)&lt;br /&gt;
| Originally registered to Combitech/Macrosystem&lt;br /&gt;
|-&lt;br /&gt;
| 756&lt;br /&gt;
| 02F4&lt;br /&gt;
| Progressive Peripherals &amp;amp; Software&lt;br /&gt;
|-&lt;br /&gt;
| 767&lt;br /&gt;
| 02FF&lt;br /&gt;
| Kolff Computer Supplies&lt;br /&gt;
|-&lt;br /&gt;
| 1001&lt;br /&gt;
| 03E9&lt;br /&gt;
| Tecmar&lt;br /&gt;
|-&lt;br /&gt;
| 1002&lt;br /&gt;
| 03EA&lt;br /&gt;
| Telesys&lt;br /&gt;
|-&lt;br /&gt;
| 1003&lt;br /&gt;
| 03EB&lt;br /&gt;
| The Micro-Forge&lt;br /&gt;
|-&lt;br /&gt;
| 1004&lt;br /&gt;
| 03EC&lt;br /&gt;
| Kronos/C Ltd.&lt;br /&gt;
| Originally registered to Card Co. (Supra)&lt;br /&gt;
|-&lt;br /&gt;
| 1005&lt;br /&gt;
| 03ED&lt;br /&gt;
| A-Squared&lt;br /&gt;
|-&lt;br /&gt;
| 1006&lt;br /&gt;
| 03EE&lt;br /&gt;
| Comspec Communications&lt;br /&gt;
|-&lt;br /&gt;
| 1007&lt;br /&gt;
| 03EF&lt;br /&gt;
| HT Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 1008&lt;br /&gt;
| 03F0&lt;br /&gt;
| RDS Software&lt;br /&gt;
|-&lt;br /&gt;
| 1009&lt;br /&gt;
| 03F1&lt;br /&gt;
| Anakin Research&lt;br /&gt;
|-&lt;br /&gt;
| 1010&lt;br /&gt;
| 03F2&lt;br /&gt;
| MicroBotics&lt;br /&gt;
|-&lt;br /&gt;
| 1011&lt;br /&gt;
| 03F3&lt;br /&gt;
| Bob Krauth&lt;br /&gt;
|-&lt;br /&gt;
| 1012&lt;br /&gt;
| 03F4&lt;br /&gt;
| Access Associates (Alegra)&lt;br /&gt;
|-&lt;br /&gt;
| 1013&lt;br /&gt;
| 03F5&lt;br /&gt;
| Mini Comp Systems Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 1014&lt;br /&gt;
| 03F6&lt;br /&gt;
| Cypress Technology&lt;br /&gt;
|-&lt;br /&gt;
| 1015&lt;br /&gt;
| 03F7&lt;br /&gt;
| Fuller Computers&lt;br /&gt;
|-&lt;br /&gt;
| 1016&lt;br /&gt;
| 03F8&lt;br /&gt;
| Galaxy Computers&lt;br /&gt;
|-&lt;br /&gt;
| 1017&lt;br /&gt;
| 03F9&lt;br /&gt;
| ADA Research&lt;br /&gt;
|-&lt;br /&gt;
| 1018&lt;br /&gt;
| 03FA&lt;br /&gt;
| Computer Service Italia&lt;br /&gt;
|-&lt;br /&gt;
| 1019&lt;br /&gt;
| 03FB&lt;br /&gt;
| Amigo&lt;br /&gt;
|-&lt;br /&gt;
| 1020&lt;br /&gt;
| 03FC&lt;br /&gt;
| Micro-Solutions Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1021&lt;br /&gt;
| 03FD&lt;br /&gt;
| Stacar International&lt;br /&gt;
|-&lt;br /&gt;
| 1022&lt;br /&gt;
| 03FE&lt;br /&gt;
| Video Precisions&lt;br /&gt;
|-&lt;br /&gt;
| 1023&lt;br /&gt;
| 03FF&lt;br /&gt;
| ASDG, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1025&lt;br /&gt;
| 0401&lt;br /&gt;
| Ing. Buero Kalawsky&lt;br /&gt;
|-&lt;br /&gt;
| 1026&lt;br /&gt;
| 0402&lt;br /&gt;
| Computer Tuning&lt;br /&gt;
|-&lt;br /&gt;
| 1027&lt;br /&gt;
| 0403&lt;br /&gt;
| Interplan Unternehmensberatung&lt;br /&gt;
|-&lt;br /&gt;
| 1028&lt;br /&gt;
| 0404&lt;br /&gt;
| Imtronics/Memphis&lt;br /&gt;
| Originally registered to Peter Ohlich&lt;br /&gt;
|-&lt;br /&gt;
| 1030&lt;br /&gt;
| 0406&lt;br /&gt;
| Commodore (Lowell University)&lt;br /&gt;
| Originally registered to Productivity Center&lt;br /&gt;
|-&lt;br /&gt;
| 1041&lt;br /&gt;
| 0411&lt;br /&gt;
| Design Labs&lt;br /&gt;
|-&lt;br /&gt;
| 1042&lt;br /&gt;
| 0412&lt;br /&gt;
| MCS&lt;br /&gt;
|-&lt;br /&gt;
| 1043&lt;br /&gt;
| 0413&lt;br /&gt;
| B. J. Freeman&lt;br /&gt;
|-&lt;br /&gt;
| 1044&lt;br /&gt;
| 0414&lt;br /&gt;
| Side Effects Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1045&lt;br /&gt;
| 0415&lt;br /&gt;
| Oklahoma Personal Comp.&lt;br /&gt;
|-&lt;br /&gt;
| 1046&lt;br /&gt;
| 0416&lt;br /&gt;
| Advanced Micro Innovations&lt;br /&gt;
|-&lt;br /&gt;
| 1047&lt;br /&gt;
| 0417&lt;br /&gt;
| Industrial Support Services&lt;br /&gt;
|-&lt;br /&gt;
| 1048&lt;br /&gt;
| 0418&lt;br /&gt;
| Technisoft&lt;br /&gt;
|-&lt;br /&gt;
| 1049&lt;br /&gt;
| 0419&lt;br /&gt;
| Prolific, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1050&lt;br /&gt;
| 041A&lt;br /&gt;
| Softeam, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1051&lt;br /&gt;
| 041B&lt;br /&gt;
| GRC Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 1052&lt;br /&gt;
| 041C&lt;br /&gt;
| David Lai&lt;br /&gt;
|-&lt;br /&gt;
| 1053&lt;br /&gt;
| 041D&lt;br /&gt;
| Ameristar Technologies&lt;br /&gt;
|-&lt;br /&gt;
| 1054&lt;br /&gt;
| 041E&lt;br /&gt;
| Cline Refrigeration&lt;br /&gt;
|-&lt;br /&gt;
| 1055&lt;br /&gt;
| 041F&lt;br /&gt;
| Cardiac Pacemakers, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1056&lt;br /&gt;
| 0420&lt;br /&gt;
| Supra Corp. (Creative Microsystems)&lt;br /&gt;
|-&lt;br /&gt;
| 1057&lt;br /&gt;
| 0421&lt;br /&gt;
| Wayne Diener&lt;br /&gt;
|-&lt;br /&gt;
| 1058&lt;br /&gt;
| 0422&lt;br /&gt;
| Computer Systems Associates (CSA)&lt;br /&gt;
|-&lt;br /&gt;
| 1059&lt;br /&gt;
| 0423&lt;br /&gt;
| Trionix, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 1060&lt;br /&gt;
| 0424&lt;br /&gt;
| David Lucas&lt;br /&gt;
|-&lt;br /&gt;
| 1061&lt;br /&gt;
| 0425&lt;br /&gt;
| Analog Precision&lt;br /&gt;
| Also assigned to D &amp;amp; L Distributing&lt;br /&gt;
|-&lt;br /&gt;
| 1267&lt;br /&gt;
| 04F3&lt;br /&gt;
| RBM Digitaltechnik&lt;br /&gt;
|-&lt;br /&gt;
| 1282&lt;br /&gt;
| 0502&lt;br /&gt;
| M-TEC Hardware Design&lt;br /&gt;
|-&lt;br /&gt;
| 1337&lt;br /&gt;
| 0539&lt;br /&gt;
| Thomas Stenzel (DaFR34K)&lt;br /&gt;
|-&lt;br /&gt;
| 1576&lt;br /&gt;
| 0628&lt;br /&gt;
| Boris Križma&lt;br /&gt;
|-&lt;br /&gt;
| 1761&lt;br /&gt;
| 06E1&lt;br /&gt;
| Great Valley Products&lt;br /&gt;
|-&lt;br /&gt;
| 1803&lt;br /&gt;
| 070B&lt;br /&gt;
| UAE Amiga Emulator&lt;br /&gt;
|-&lt;br /&gt;
| 2002&lt;br /&gt;
| 07D2&lt;br /&gt;
| Mimetics Corp.&lt;br /&gt;
|-&lt;br /&gt;
| 2003&lt;br /&gt;
| 07D3&lt;br /&gt;
| ACDA&lt;br /&gt;
|-&lt;br /&gt;
| 2004&lt;br /&gt;
| 07D4&lt;br /&gt;
| Finn R. Jacobsen&lt;br /&gt;
|-&lt;br /&gt;
| 2005&lt;br /&gt;
| 07D5&lt;br /&gt;
| Elthen Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2006&lt;br /&gt;
| 07D6&lt;br /&gt;
| Nine Tiles Computer Systems Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2007&lt;br /&gt;
| 07D7&lt;br /&gt;
| Analog Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2008&lt;br /&gt;
| 07D8&lt;br /&gt;
| Bell &amp;amp;amp; Howell&lt;br /&gt;
|-&lt;br /&gt;
| 2009&lt;br /&gt;
| 07D9&lt;br /&gt;
| Roland Kochler&lt;br /&gt;
|-&lt;br /&gt;
| 2010&lt;br /&gt;
| 07DA&lt;br /&gt;
| Byte Corp.&lt;br /&gt;
|-&lt;br /&gt;
| 2011&lt;br /&gt;
| 07DB&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 2012&lt;br /&gt;
| 07DC&lt;br /&gt;
| DKB, Inc.&lt;br /&gt;
| Previously named Michigan Software&lt;br /&gt;
|-&lt;br /&gt;
| 2013&lt;br /&gt;
| 07DD&lt;br /&gt;
| Pacific Peripherals&lt;br /&gt;
|-&lt;br /&gt;
| 2014&lt;br /&gt;
| 07DE&lt;br /&gt;
| Sysaphus Software&lt;br /&gt;
|-&lt;br /&gt;
| 2015&lt;br /&gt;
| 07DF&lt;br /&gt;
| Digitronics&lt;br /&gt;
|-&lt;br /&gt;
| 2016&lt;br /&gt;
| 07E0&lt;br /&gt;
| Akron Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2017&lt;br /&gt;
| 07E1&lt;br /&gt;
| Great Valley Products&lt;br /&gt;
|-&lt;br /&gt;
| 2018&lt;br /&gt;
| 07E2&lt;br /&gt;
| Calmos&lt;br /&gt;
|-&lt;br /&gt;
| 2019&lt;br /&gt;
| 07E3&lt;br /&gt;
| Dover Research&lt;br /&gt;
|-&lt;br /&gt;
| 2020&lt;br /&gt;
| 07E4&lt;br /&gt;
| David Krehbiel&lt;br /&gt;
|-&lt;br /&gt;
| 2021&lt;br /&gt;
| 07E5&lt;br /&gt;
| Synergy Peripheral Systems&lt;br /&gt;
| Also known as California Access&lt;br /&gt;
|-&lt;br /&gt;
| 2022&lt;br /&gt;
| 07E6&lt;br /&gt;
| Xetec&lt;br /&gt;
|-&lt;br /&gt;
| 2023&lt;br /&gt;
| 07E7&lt;br /&gt;
| Micron Technology&lt;br /&gt;
|-&lt;br /&gt;
| 2024&lt;br /&gt;
| 07E8&lt;br /&gt;
| CH Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2025&lt;br /&gt;
| 07E9&lt;br /&gt;
| American Liquid Light&lt;br /&gt;
|-&lt;br /&gt;
| 2026&lt;br /&gt;
| 07EA&lt;br /&gt;
| Progressive Peripherals &amp;amp;amp; Software&lt;br /&gt;
| Also used by Ateo&lt;br /&gt;
|-&lt;br /&gt;
| 2027&lt;br /&gt;
| 07EB&lt;br /&gt;
| Wicat Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2028&lt;br /&gt;
| 07EC&lt;br /&gt;
| Applied Systems &amp;amp;amp; Peripherals&lt;br /&gt;
|-&lt;br /&gt;
| 2029&lt;br /&gt;
| 07ED&lt;br /&gt;
| Delaware Valley Software&lt;br /&gt;
|-&lt;br /&gt;
| 2030&lt;br /&gt;
| 07EE&lt;br /&gt;
| Palomax&lt;br /&gt;
|-&lt;br /&gt;
| 2031&lt;br /&gt;
| 07EF&lt;br /&gt;
| Incognito Software&lt;br /&gt;
|-&lt;br /&gt;
| 2032&lt;br /&gt;
| 07F0&lt;br /&gt;
| Jadesign&lt;br /&gt;
|-&lt;br /&gt;
| 2033&lt;br /&gt;
| 07F1&lt;br /&gt;
| BVR&lt;br /&gt;
|-&lt;br /&gt;
| 2034&lt;br /&gt;
| 07F2&lt;br /&gt;
| Spirit Technology&lt;br /&gt;
|-&lt;br /&gt;
| 2035&lt;br /&gt;
| 07F3&lt;br /&gt;
| Spirit Technology&lt;br /&gt;
|-&lt;br /&gt;
| 2036&lt;br /&gt;
| 07F4&lt;br /&gt;
| Atronic&lt;br /&gt;
|-&lt;br /&gt;
| 2037&lt;br /&gt;
| 07F5&lt;br /&gt;
| Scott Karlin&lt;br /&gt;
|-&lt;br /&gt;
| 2038&lt;br /&gt;
| 07F6&lt;br /&gt;
| Howitch&lt;br /&gt;
|-&lt;br /&gt;
| 2039&lt;br /&gt;
| 07F7&lt;br /&gt;
| Sullivan Brothers Visual Engineers&lt;br /&gt;
|-&lt;br /&gt;
| 2040&lt;br /&gt;
| 07F8&lt;br /&gt;
| G I T&lt;br /&gt;
|-&lt;br /&gt;
| 2041&lt;br /&gt;
| 07F9&lt;br /&gt;
| Amigo Business Computers&lt;br /&gt;
|-&lt;br /&gt;
| 2042&lt;br /&gt;
| 07FA&lt;br /&gt;
| Micro E Ab&lt;br /&gt;
|-&lt;br /&gt;
| 2043&lt;br /&gt;
| 07FB&lt;br /&gt;
| Ralph Kruse&lt;br /&gt;
|-&lt;br /&gt;
| 2044&lt;br /&gt;
| 07FC&lt;br /&gt;
| Clearpoint Research&lt;br /&gt;
|-&lt;br /&gt;
| 2045&lt;br /&gt;
| 07FD&lt;br /&gt;
| Kodiak&lt;br /&gt;
|-&lt;br /&gt;
| 2046&lt;br /&gt;
| 07FE&lt;br /&gt;
| BSC&lt;br /&gt;
| Originally registered to Phoenix Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2047&lt;br /&gt;
| 07FF&lt;br /&gt;
| No Name Shown&lt;br /&gt;
|-&lt;br /&gt;
| 2048&lt;br /&gt;
| 0800&lt;br /&gt;
| Commodore Braunschweig&lt;br /&gt;
|-&lt;br /&gt;
| 2049&lt;br /&gt;
| 0801&lt;br /&gt;
| BSC&lt;br /&gt;
| Originally registered to Elaborate Bytes&lt;br /&gt;
|-&lt;br /&gt;
| 2050&lt;br /&gt;
| 0802&lt;br /&gt;
| Kronos/C Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2051&lt;br /&gt;
| 0803&lt;br /&gt;
| Spartanics&lt;br /&gt;
|-&lt;br /&gt;
| 2052&lt;br /&gt;
| 0804&lt;br /&gt;
| Jochheim Computer Tuning&lt;br /&gt;
|-&lt;br /&gt;
| 2053&lt;br /&gt;
| 0805&lt;br /&gt;
| Trans Data Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2054&lt;br /&gt;
| 0806&lt;br /&gt;
| Applied Systems &amp;amp;amp; Peripherals Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2055&lt;br /&gt;
| 0807&lt;br /&gt;
| Checkpoint Technologies&lt;br /&gt;
| Originally registered to Amiga Solutions&lt;br /&gt;
|-&lt;br /&gt;
| 2056&lt;br /&gt;
| 0808&lt;br /&gt;
| Adept Development&lt;br /&gt;
|-&lt;br /&gt;
| 2057&lt;br /&gt;
| 0809&lt;br /&gt;
| Advanced Computer Design&lt;br /&gt;
|-&lt;br /&gt;
| 2058&lt;br /&gt;
| 080A&lt;br /&gt;
| Sir Netics&lt;br /&gt;
|-&lt;br /&gt;
| 2059&lt;br /&gt;
| 080B&lt;br /&gt;
| Expert Services&lt;br /&gt;
|-&lt;br /&gt;
| 2060&lt;br /&gt;
| 080C&lt;br /&gt;
| Digital Art Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2061&lt;br /&gt;
| 080D&lt;br /&gt;
| Adept Development&lt;br /&gt;
|-&lt;br /&gt;
| 2062&lt;br /&gt;
| 080E&lt;br /&gt;
| Expansion Technologies (Expansion Systems)&lt;br /&gt;
|-&lt;br /&gt;
| 2063&lt;br /&gt;
| 080F&lt;br /&gt;
| Alphatech&lt;br /&gt;
|-&lt;br /&gt;
| 2064&lt;br /&gt;
| 0810&lt;br /&gt;
| Edotronik GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 2065&lt;br /&gt;
| 0811&lt;br /&gt;
| California Access/Synergy&lt;br /&gt;
| Originally registered to Logical Design Works&lt;br /&gt;
|-&lt;br /&gt;
| 2066&lt;br /&gt;
| 0812&lt;br /&gt;
| Bowden, Williams, Full &amp;amp; Assoc.&lt;br /&gt;
|-&lt;br /&gt;
| 2067&lt;br /&gt;
| 0813&lt;br /&gt;
| NES, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2068&lt;br /&gt;
| 0814&lt;br /&gt;
| Amdev&lt;br /&gt;
|-&lt;br /&gt;
| 2069&lt;br /&gt;
| 0815&lt;br /&gt;
| Big Brother Security Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2070&lt;br /&gt;
| 0816&lt;br /&gt;
| Active Circuits Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2071&lt;br /&gt;
| 0817&lt;br /&gt;
| ICD, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2072&lt;br /&gt;
| 0818&lt;br /&gt;
| Multi-Meg Electronique&lt;br /&gt;
|-&lt;br /&gt;
| 2073&lt;br /&gt;
| 0819&lt;br /&gt;
| Kupke Computertechnik GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 2074&lt;br /&gt;
| 081A&lt;br /&gt;
| The Checkered Ball&lt;br /&gt;
|-&lt;br /&gt;
| 2075&lt;br /&gt;
| 081B&lt;br /&gt;
| Hi Tension Computer Services Ltd. (UK)&lt;br /&gt;
|-&lt;br /&gt;
| 2076&lt;br /&gt;
| 081C&lt;br /&gt;
| Alfa Data&lt;br /&gt;
| Originally assigned to Elmtech Research, Ltd. (UK)&lt;br /&gt;
|-&lt;br /&gt;
| 2077&lt;br /&gt;
| 081D&lt;br /&gt;
| Great Valley Products&lt;br /&gt;
| Originally registered to Clartscreen, Ltd. (UK)&lt;br /&gt;
|-&lt;br /&gt;
| 2078&lt;br /&gt;
| 081E&lt;br /&gt;
| Interworks&lt;br /&gt;
|-&lt;br /&gt;
| 2079&lt;br /&gt;
| 081F&lt;br /&gt;
| Galysh Enterprises&lt;br /&gt;
|-&lt;br /&gt;
| 2080&lt;br /&gt;
| 0820&lt;br /&gt;
| Hardital Synthesis&lt;br /&gt;
| Originally registered to Realtime Games Software Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2081&lt;br /&gt;
| 0821&lt;br /&gt;
| GBS&lt;br /&gt;
|-&lt;br /&gt;
| 2082&lt;br /&gt;
| 0822&lt;br /&gt;
| Circum Design Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2083&lt;br /&gt;
| 0823&lt;br /&gt;
| Alberta Micro Electronic Center&lt;br /&gt;
|-&lt;br /&gt;
| 2084&lt;br /&gt;
| 0824&lt;br /&gt;
| Bestech&lt;br /&gt;
|-&lt;br /&gt;
| 2085&lt;br /&gt;
| 0825&lt;br /&gt;
| Lasar Fantasy&lt;br /&gt;
|-&lt;br /&gt;
| 2086&lt;br /&gt;
| 0826&lt;br /&gt;
| Pulsar&lt;br /&gt;
|-&lt;br /&gt;
| 2087&lt;br /&gt;
| 0827&lt;br /&gt;
| Ivis&lt;br /&gt;
|-&lt;br /&gt;
| 2088&lt;br /&gt;
| 0828&lt;br /&gt;
| Applied Engineering&lt;br /&gt;
|-&lt;br /&gt;
| 2089&lt;br /&gt;
| 0829&lt;br /&gt;
| Solid-State Design &amp;amp;amp; Development&lt;br /&gt;
|-&lt;br /&gt;
| 2090&lt;br /&gt;
| 082A&lt;br /&gt;
| Vison Quest&lt;br /&gt;
|-&lt;br /&gt;
| 2091&lt;br /&gt;
| 082B&lt;br /&gt;
| Seaview Software&lt;br /&gt;
|-&lt;br /&gt;
| 2092&lt;br /&gt;
| 082C&lt;br /&gt;
| BSC&lt;br /&gt;
| Mistakenly used by ADS (Advanced Development Software)&lt;br /&gt;
|-&lt;br /&gt;
| 2093&lt;br /&gt;
| 082D&lt;br /&gt;
| Bernd Culenfeld&lt;br /&gt;
|-&lt;br /&gt;
| 2094&lt;br /&gt;
| 082E&lt;br /&gt;
| American Liquid Light&lt;br /&gt;
|-&lt;br /&gt;
| 2095&lt;br /&gt;
| 082F&lt;br /&gt;
| CEGITES&lt;br /&gt;
|-&lt;br /&gt;
| 2096&lt;br /&gt;
| 0830&lt;br /&gt;
| Quadlite Computers Ltd.&lt;br /&gt;
| Originally registered to EV Industries&lt;br /&gt;
|-&lt;br /&gt;
| 2097&lt;br /&gt;
| 0831&lt;br /&gt;
| Silicon Peace&lt;br /&gt;
|-&lt;br /&gt;
| 2098&lt;br /&gt;
| 0832&lt;br /&gt;
| Black Belt Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2099&lt;br /&gt;
| 0833&lt;br /&gt;
| Village Tronic&lt;br /&gt;
| Originally registerd to Steve Yaeger&lt;br /&gt;
|-&lt;br /&gt;
| 2100&lt;br /&gt;
| 0834&lt;br /&gt;
| ReadySoft&lt;br /&gt;
|-&lt;br /&gt;
| 2101&lt;br /&gt;
| 0835&lt;br /&gt;
| Phoenix Micro Technologies&lt;br /&gt;
|-&lt;br /&gt;
| 2102&lt;br /&gt;
| 0836&lt;br /&gt;
| Advanced Systems &amp;amp; Software&lt;br /&gt;
| Orignally assigned to Preferred Technology&lt;br /&gt;
|-&lt;br /&gt;
| 2103&lt;br /&gt;
| 0837&lt;br /&gt;
| Rombo Productions&lt;br /&gt;
|-&lt;br /&gt;
| 2104&lt;br /&gt;
| 0838&lt;br /&gt;
| Impulse Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2105&lt;br /&gt;
| 0839&lt;br /&gt;
| Beta Unlimited&lt;br /&gt;
|-&lt;br /&gt;
| 2106&lt;br /&gt;
| 083A&lt;br /&gt;
| Memory Expansion System, Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2107&lt;br /&gt;
| 083B&lt;br /&gt;
| Vortex Computer Systems GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 2108&lt;br /&gt;
| 083C&lt;br /&gt;
| Platypus Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2109&lt;br /&gt;
| 083D&lt;br /&gt;
| Gigatron OHG&lt;br /&gt;
|-&lt;br /&gt;
| 2110&lt;br /&gt;
| 083E&lt;br /&gt;
| PG Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2111&lt;br /&gt;
| 083F&lt;br /&gt;
| New Technologies Group&lt;br /&gt;
|-&lt;br /&gt;
| 2112&lt;br /&gt;
| 0840&lt;br /&gt;
| Interactive Video Systems (IVS)&lt;br /&gt;
| Pacific Peripherals&lt;br /&gt;
|-&lt;br /&gt;
| 2113&lt;br /&gt;
| 0841&lt;br /&gt;
| Vector&lt;br /&gt;
| Originally registered to H. K. Computer&lt;br /&gt;
|-&lt;br /&gt;
| 2114&lt;br /&gt;
| 0842&lt;br /&gt;
| Pacific Digital&lt;br /&gt;
| Originally registered to C. H. Helfrich Elektronik&lt;br /&gt;
|-&lt;br /&gt;
| 2115&lt;br /&gt;
| 0843&lt;br /&gt;
| Xanadu&lt;br /&gt;
|-&lt;br /&gt;
| 2116&lt;br /&gt;
| 0844&lt;br /&gt;
| Pacific Digital&lt;br /&gt;
| Originally registered to AMS&lt;br /&gt;
|-&lt;br /&gt;
| 2117&lt;br /&gt;
| 0845&lt;br /&gt;
| X-Pert&lt;br /&gt;
|-&lt;br /&gt;
| 2118&lt;br /&gt;
| 0846&lt;br /&gt;
| The Amiga Centre&lt;br /&gt;
|-&lt;br /&gt;
| 2119&lt;br /&gt;
| 0847&lt;br /&gt;
| Digital Pacific&lt;br /&gt;
|-&lt;br /&gt;
| 2120&lt;br /&gt;
| 0848&lt;br /&gt;
| Solid State Leisure&lt;br /&gt;
|-&lt;br /&gt;
| 2121&lt;br /&gt;
| 0849&lt;br /&gt;
| Hydra Systems&lt;br /&gt;
| Originally registered to Analog Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2122&lt;br /&gt;
| 084A&lt;br /&gt;
| Cumana&lt;br /&gt;
|-&lt;br /&gt;
| 2123&lt;br /&gt;
| 084B&lt;br /&gt;
| KAPS 2C Conception&lt;br /&gt;
|-&lt;br /&gt;
| 2124&lt;br /&gt;
| 084C&lt;br /&gt;
| Mike Mason&lt;br /&gt;
|-&lt;br /&gt;
| 2125&lt;br /&gt;
| 084D&lt;br /&gt;
| For Your Eyes&lt;br /&gt;
|-&lt;br /&gt;
| 2126&lt;br /&gt;
| 084E&lt;br /&gt;
| Volkmar Breitfeld Computersysteme&lt;br /&gt;
|-&lt;br /&gt;
| 2127&lt;br /&gt;
| 084F&lt;br /&gt;
| Sunrize Industries&lt;br /&gt;
|-&lt;br /&gt;
| 2128&lt;br /&gt;
| 0850&lt;br /&gt;
| Scott Advanced Micro Designs&lt;br /&gt;
|-&lt;br /&gt;
| 2129&lt;br /&gt;
| 0851&lt;br /&gt;
| Digital Micronics&lt;br /&gt;
|-&lt;br /&gt;
| 2130&lt;br /&gt;
| 0852&lt;br /&gt;
| Alfa-Laval&lt;br /&gt;
|-&lt;br /&gt;
| 2131&lt;br /&gt;
| 0853&lt;br /&gt;
| Multigros A/S&lt;br /&gt;
|-&lt;br /&gt;
| 2132&lt;br /&gt;
| 0854&lt;br /&gt;
| Archos&lt;br /&gt;
|-&lt;br /&gt;
| 2133&lt;br /&gt;
| 0855&lt;br /&gt;
| Icom Simulations&lt;br /&gt;
|-&lt;br /&gt;
| 2134&lt;br /&gt;
| 0856&lt;br /&gt;
| Commodore Test Engineering Group&lt;br /&gt;
|-&lt;br /&gt;
| 2135&lt;br /&gt;
| 0857&lt;br /&gt;
| Microcreations&lt;br /&gt;
|-&lt;br /&gt;
| 2136&lt;br /&gt;
| 0858&lt;br /&gt;
| Shoestring Productions&lt;br /&gt;
|-&lt;br /&gt;
| 2137&lt;br /&gt;
| 0859&lt;br /&gt;
| Faberushi&lt;br /&gt;
|-&lt;br /&gt;
| 2138&lt;br /&gt;
| 085A&lt;br /&gt;
| Evesham Micro Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2139&lt;br /&gt;
| 085B&lt;br /&gt;
| Panagolin Laser Software&lt;br /&gt;
|-&lt;br /&gt;
| 2140&lt;br /&gt;
| 085C&lt;br /&gt;
| Thomas Rudloff&lt;br /&gt;
|-&lt;br /&gt;
| 2141&lt;br /&gt;
| 085D&lt;br /&gt;
| Daniel Hohabir&lt;br /&gt;
|-&lt;br /&gt;
| 2142&lt;br /&gt;
| 085E&lt;br /&gt;
| GfxBase, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2143&lt;br /&gt;
| 085F&lt;br /&gt;
| Axellabs&lt;br /&gt;
|-&lt;br /&gt;
| 2144&lt;br /&gt;
| 0860&lt;br /&gt;
| Roctec Electronics Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2145&lt;br /&gt;
| 0861&lt;br /&gt;
| Omega Datentechnik&lt;br /&gt;
|-&lt;br /&gt;
| 2146&lt;br /&gt;
| 0862&lt;br /&gt;
| Atlantis&lt;br /&gt;
|-&lt;br /&gt;
| 2147&lt;br /&gt;
| 0863&lt;br /&gt;
| Skytec Computers&lt;br /&gt;
|-&lt;br /&gt;
| 2148&lt;br /&gt;
| 0864&lt;br /&gt;
| Protar Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2149&lt;br /&gt;
| 0865&lt;br /&gt;
| ACS&lt;br /&gt;
|-&lt;br /&gt;
| 2150&lt;br /&gt;
| 0866&lt;br /&gt;
| Software Results Enterprises&lt;br /&gt;
| Originally registered to University of Illinois&lt;br /&gt;
|-&lt;br /&gt;
| 2151&lt;br /&gt;
| 0867&lt;br /&gt;
| Infinity Systems Design Group&lt;br /&gt;
|-&lt;br /&gt;
| 2152&lt;br /&gt;
| 0868&lt;br /&gt;
| Trade It&lt;br /&gt;
|-&lt;br /&gt;
| 2153&lt;br /&gt;
| 0869&lt;br /&gt;
| Suntec, Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2154&lt;br /&gt;
| 086A&lt;br /&gt;
| DJW Micro Systems&lt;br /&gt;
| Originally registered to Tritec Marketing&lt;br /&gt;
|-&lt;br /&gt;
| 2155&lt;br /&gt;
| 086B&lt;br /&gt;
| Power Computing Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2156&lt;br /&gt;
| 086C&lt;br /&gt;
| MacroSystems&lt;br /&gt;
|-&lt;br /&gt;
| 2157&lt;br /&gt;
| 086D&lt;br /&gt;
| Masoboshi GmbH (DCE)&lt;br /&gt;
|-&lt;br /&gt;
| 2158&lt;br /&gt;
| 086E&lt;br /&gt;
| HAL Software Hardware Handel&lt;br /&gt;
|-&lt;br /&gt;
| 2159&lt;br /&gt;
| 086F&lt;br /&gt;
| Mainhattan Data&lt;br /&gt;
| Originally registered to Michael Lamm Computersysteme&lt;br /&gt;
|-&lt;br /&gt;
| 2160&lt;br /&gt;
| 0870&lt;br /&gt;
| Digital Processing System&lt;br /&gt;
| Originally registered to bbdp Electronics&lt;br /&gt;
|-&lt;br /&gt;
| 2161&lt;br /&gt;
| 0871&lt;br /&gt;
| Blue Ribbon Soundworks&lt;br /&gt;
| Originally registered to Design Computer Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2162&lt;br /&gt;
| 0872&lt;br /&gt;
| XPert&lt;br /&gt;
| Originally registered to The Station&lt;br /&gt;
|-&lt;br /&gt;
| 2163&lt;br /&gt;
| 0873&lt;br /&gt;
| DelaComp&lt;br /&gt;
| Originally registered to Bryan Williams&lt;br /&gt;
|-&lt;br /&gt;
| 2164&lt;br /&gt;
| 0874&lt;br /&gt;
| Superformance Computer Engineering GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 2165&lt;br /&gt;
| 0875&lt;br /&gt;
| Overland Engineering&lt;br /&gt;
|-&lt;br /&gt;
| 2166&lt;br /&gt;
| 0876&lt;br /&gt;
| Thomas Hamren&lt;br /&gt;
|-&lt;br /&gt;
| 2167&lt;br /&gt;
| 0877&lt;br /&gt;
| Village Tronic&lt;br /&gt;
|-&lt;br /&gt;
| 2168&lt;br /&gt;
| 0878&lt;br /&gt;
| Toolbox Design&lt;br /&gt;
|-&lt;br /&gt;
| 2169&lt;br /&gt;
| 0879&lt;br /&gt;
| Digital Processing System&lt;br /&gt;
|-&lt;br /&gt;
| 2170&lt;br /&gt;
| 087A&lt;br /&gt;
| Superformance&lt;br /&gt;
|-&lt;br /&gt;
| 2171&lt;br /&gt;
| 087B&lt;br /&gt;
| Utilities Unlimited&lt;br /&gt;
|-&lt;br /&gt;
| 2172&lt;br /&gt;
| 087C&lt;br /&gt;
| phase 5&lt;br /&gt;
|-&lt;br /&gt;
| 2173&lt;br /&gt;
| 087D&lt;br /&gt;
| Juergen Kommos&lt;br /&gt;
|-&lt;br /&gt;
| 2174&lt;br /&gt;
| 087E&lt;br /&gt;
| Electronic Design&lt;br /&gt;
|-&lt;br /&gt;
| 2175&lt;br /&gt;
| 087F&lt;br /&gt;
| James Cook University of North Queensland&lt;br /&gt;
|-&lt;br /&gt;
| 2176&lt;br /&gt;
| 0880&lt;br /&gt;
| AmiTrix Development&lt;br /&gt;
|-&lt;br /&gt;
| 2177&lt;br /&gt;
| 0881&lt;br /&gt;
| Ferranti&lt;br /&gt;
|-&lt;br /&gt;
| 2178&lt;br /&gt;
| 0882&lt;br /&gt;
| Leviathan Development&lt;br /&gt;
|-&lt;br /&gt;
| 2179&lt;br /&gt;
| 0883&lt;br /&gt;
| United Video Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2180&lt;br /&gt;
| 0884&lt;br /&gt;
| GPSoft Pty. Ltd.&lt;br /&gt;
| Originally registered to Juergen Kommos&lt;br /&gt;
|-&lt;br /&gt;
| 2181&lt;br /&gt;
| 0885&lt;br /&gt;
| ArMAX&lt;br /&gt;
| Oliver Bausch&lt;br /&gt;
|-&lt;br /&gt;
| 2182&lt;br /&gt;
| 0886&lt;br /&gt;
| CP Computer&lt;br /&gt;
|-&lt;br /&gt;
| 2183&lt;br /&gt;
| 0887&lt;br /&gt;
| AMOK - Amiga Module &amp;amp;amp; Oberon Klub&lt;br /&gt;
|-&lt;br /&gt;
| 2184&lt;br /&gt;
| 0888&lt;br /&gt;
| ITEK Neser &amp;amp;amp; Sieber GbR&lt;br /&gt;
|-&lt;br /&gt;
| 2185&lt;br /&gt;
| 0889&lt;br /&gt;
| Phillip C. Lello&lt;br /&gt;
|-&lt;br /&gt;
| 2186&lt;br /&gt;
| 088A&lt;br /&gt;
| Cyborg Design Services&lt;br /&gt;
|-&lt;br /&gt;
| 2187&lt;br /&gt;
| 088B&lt;br /&gt;
| G2 Systems&lt;br /&gt;
|-&lt;br /&gt;
| 2188&lt;br /&gt;
| 088C&lt;br /&gt;
| Pro System Computersysteme&lt;br /&gt;
|-&lt;br /&gt;
| 2189&lt;br /&gt;
| 088D&lt;br /&gt;
| ZEUS Electronic&lt;br /&gt;
| Originally registered to MSPI (Markt &amp;amp;amp; Technik)&lt;br /&gt;
|-&lt;br /&gt;
| 2190&lt;br /&gt;
| 088E&lt;br /&gt;
| Altatech&lt;br /&gt;
|-&lt;br /&gt;
| 2191&lt;br /&gt;
| 088F&lt;br /&gt;
| NewTek&lt;br /&gt;
|-&lt;br /&gt;
| 2192&lt;br /&gt;
| 0890&lt;br /&gt;
| M-TEC Hardware Design&lt;br /&gt;
| Originally registered to Hardware Design Udo Neuroth&lt;br /&gt;
|-&lt;br /&gt;
| 2193&lt;br /&gt;
| 0891&lt;br /&gt;
| Great Valley Products&lt;br /&gt;
| Originally registered to Viona Development&lt;br /&gt;
|-&lt;br /&gt;
| 2194&lt;br /&gt;
| 0892&lt;br /&gt;
| Amitek&lt;br /&gt;
| Originally assigned to Marpet Developments&lt;br /&gt;
|-&lt;br /&gt;
| 2195&lt;br /&gt;
| 0893&lt;br /&gt;
| Ingenieurbuero Helfrich&lt;br /&gt;
|-&lt;br /&gt;
| 2196&lt;br /&gt;
| 0894&lt;br /&gt;
| The Neo Group&lt;br /&gt;
|-&lt;br /&gt;
| 2197&lt;br /&gt;
| 0895&lt;br /&gt;
| Cyon&lt;br /&gt;
|-&lt;br /&gt;
| 2198&lt;br /&gt;
| 0896&lt;br /&gt;
| Bob Research Group&lt;br /&gt;
|-&lt;br /&gt;
| 2199&lt;br /&gt;
| 0897&lt;br /&gt;
| Richmond Sound Design Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2200&lt;br /&gt;
| 0898&lt;br /&gt;
| US Cybernetics&lt;br /&gt;
|-&lt;br /&gt;
| 2201&lt;br /&gt;
| 0899&lt;br /&gt;
| Fulvio Ieva&lt;br /&gt;
|-&lt;br /&gt;
| 2202&lt;br /&gt;
| 089A&lt;br /&gt;
| Silicon Studio&lt;br /&gt;
|-&lt;br /&gt;
| 2203&lt;br /&gt;
| 089B&lt;br /&gt;
| MacroSystems (USA)&lt;br /&gt;
| Was named Micro System Devices&lt;br /&gt;
|-&lt;br /&gt;
| 2204&lt;br /&gt;
| 089C&lt;br /&gt;
| Conspector Entertainment&lt;br /&gt;
|-&lt;br /&gt;
| 2205&lt;br /&gt;
| 089D&lt;br /&gt;
| Laserforum&lt;br /&gt;
|-&lt;br /&gt;
| 2206&lt;br /&gt;
| 089E&lt;br /&gt;
| Elbox Computer&lt;br /&gt;
| Mistakenly used by Index Information Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2207&lt;br /&gt;
| 089F&lt;br /&gt;
| Applied Magic Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 2208&lt;br /&gt;
| 08A0&lt;br /&gt;
| SDL Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 2560&lt;br /&gt;
| 0A00&lt;br /&gt;
| Harms&lt;br /&gt;
|-&lt;br /&gt;
| 2588&lt;br /&gt;
| 0A1C&lt;br /&gt;
| A1K.org Community&lt;br /&gt;
|-&lt;br /&gt;
| 2640&lt;br /&gt;
| 0A50&lt;br /&gt;
| Micronik&lt;br /&gt;
|-&lt;br /&gt;
| 3084&lt;br /&gt;
| 0C0C&lt;br /&gt;
| Team 4&lt;br /&gt;
|-&lt;br /&gt;
| 3643&lt;br /&gt;
| 0E3B&lt;br /&gt;
| E3B&lt;br /&gt;
| Michael Boehmer&lt;br /&gt;
|-&lt;br /&gt;
| 3855&lt;br /&gt;
| 0F0F&lt;br /&gt;
| Micronik&lt;br /&gt;
|-&lt;br /&gt;
| 4096&lt;br /&gt;
| 1000&lt;br /&gt;
| MegaMicro&lt;br /&gt;
|-&lt;br /&gt;
| 4110&lt;br /&gt;
| 100E&lt;br /&gt;
| DigiFeX&lt;br /&gt;
|-&lt;br /&gt;
| 4136&lt;br /&gt;
| 1028&lt;br /&gt;
| Imtronics/Memphis&lt;br /&gt;
|-&lt;br /&gt;
| 4149&lt;br /&gt;
| 1035&lt;br /&gt;
| PROTAR&lt;br /&gt;
|-&lt;br /&gt;
| 4369&lt;br /&gt;
| 1111&lt;br /&gt;
| Frank Strauß Elektronik&lt;br /&gt;
| Also used by Kupke&lt;br /&gt;
|-&lt;br /&gt;
| 4626&lt;br /&gt;
| 1212&lt;br /&gt;
| Individual Computers&lt;br /&gt;
|-&lt;br /&gt;
| 4648&lt;br /&gt;
| 1228&lt;br /&gt;
| Flesch Hornemann Computer Elec.&lt;br /&gt;
|-&lt;br /&gt;
| 4680&lt;br /&gt;
| 1248&lt;br /&gt;
| Kupke Computertechnik GmbH&lt;br /&gt;
|-&lt;br /&gt;
| 4711&lt;br /&gt;
| 1267&lt;br /&gt;
| RBM digitaltechnik&lt;br /&gt;
|-&lt;br /&gt;
| 4754&lt;br /&gt;
| 1292&lt;br /&gt;
| MacroSystems&lt;br /&gt;
|-&lt;br /&gt;
| 5000&lt;br /&gt;
| 1388&lt;br /&gt;
| ITH&lt;br /&gt;
|-&lt;br /&gt;
| 5001&lt;br /&gt;
| 1389&lt;br /&gt;
| VMC&lt;br /&gt;
|-&lt;br /&gt;
| 5010&lt;br /&gt;
| 1392&lt;br /&gt;
| Ambience Creation Technology&lt;br /&gt;
|-&lt;br /&gt;
| 5011&lt;br /&gt;
| 1393&lt;br /&gt;
| Creative Development&lt;br /&gt;
|-&lt;br /&gt;
| 5012&lt;br /&gt;
| 1394&lt;br /&gt;
| Georg Braun&lt;br /&gt;
|-&lt;br /&gt;
| 5013&lt;br /&gt;
| 1395&lt;br /&gt;
| Swedish User Group of Amiga&lt;br /&gt;
|-&lt;br /&gt;
| 5014&lt;br /&gt;
| 1396&lt;br /&gt;
| Jakub Bednarski&lt;br /&gt;
|-&lt;br /&gt;
| 5015&lt;br /&gt;
| 1397&lt;br /&gt;
| KryoFlux, Ltd.&lt;br /&gt;
|-&lt;br /&gt;
| 5016&lt;br /&gt;
| 1398&lt;br /&gt;
| Igor Majstorovic&lt;br /&gt;
|-&lt;br /&gt;
| 5017&lt;br /&gt;
| 1399&lt;br /&gt;
| Alastair M. Robinson&lt;br /&gt;
|-&lt;br /&gt;
| 5018&lt;br /&gt;
| 139A&lt;br /&gt;
| Austex Software&lt;br /&gt;
|-&lt;br /&gt;
| 5019&lt;br /&gt;
| 139B&lt;br /&gt;
| Sören Gust&lt;br /&gt;
|-&lt;br /&gt;
| 5020&lt;br /&gt;
| 139C&lt;br /&gt;
| Rok Krajnc&lt;br /&gt;
|-&lt;br /&gt;
| 5030&lt;br /&gt;
| 13A6&lt;br /&gt;
| Tim Tashpulatov&lt;br /&gt;
|-&lt;br /&gt;
| 5040&lt;br /&gt;
| 13B0&lt;br /&gt;
| 7-bit&lt;br /&gt;
|-&lt;br /&gt;
| 5050&lt;br /&gt;
| 13BA&lt;br /&gt;
| Sakura IT&lt;br /&gt;
|-&lt;br /&gt;
| 5060&lt;br /&gt;
| 13C4&lt;br /&gt;
| FPGAArcade&lt;br /&gt;
|-&lt;br /&gt;
| 5070&lt;br /&gt;
| 13CE&lt;br /&gt;
| CancerSoft Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 5080&lt;br /&gt;
| 13D8&lt;br /&gt;
| Stephen Leary&lt;br /&gt;
|-&lt;br /&gt;
| 5090&lt;br /&gt;
| 13E2&lt;br /&gt;
| DMA Softlab LLC&lt;br /&gt;
|-&lt;br /&gt;
| 5100&lt;br /&gt;
| 13EC&lt;br /&gt;
| Brookhouse Engineering&lt;br /&gt;
|-&lt;br /&gt;
| 5110&lt;br /&gt;
| 13F6&lt;br /&gt;
| Eduardo Arana&lt;br /&gt;
|-&lt;br /&gt;
| 5120&lt;br /&gt;
| 1400&lt;br /&gt;
| CS-LAB&lt;br /&gt;
|-&lt;br /&gt;
| 5130&lt;br /&gt;
| 140A&lt;br /&gt;
| Robert Miranda&lt;br /&gt;
|-&lt;br /&gt;
| 5140&lt;br /&gt;
| 1414&lt;br /&gt;
| RastPort&lt;br /&gt;
|-&lt;br /&gt;
| 5132&lt;br /&gt;
| 140C&lt;br /&gt;
| UAS Interface Ltd6&lt;br /&gt;
|-&lt;br /&gt;
| 5150&lt;br /&gt;
| 141E&lt;br /&gt;
| Amiga Kit&lt;br /&gt;
|-&lt;br /&gt;
| 5160&lt;br /&gt;
| 1428&lt;br /&gt;
| Central Texas Commodore User Group&lt;br /&gt;
|-&lt;br /&gt;
| 5170&lt;br /&gt;
| 1432&lt;br /&gt;
| Confusion Research Center&lt;br /&gt;
|-&lt;br /&gt;
| 5180&lt;br /&gt;
| 143C&lt;br /&gt;
| Solar Soyuz Zaibatsu&lt;br /&gt;
|-&lt;br /&gt;
| 5500&lt;br /&gt;
| 157C&lt;br /&gt;
| Inhouse Information&lt;br /&gt;
|-&lt;br /&gt;
| 5768&lt;br /&gt;
| 1688&lt;br /&gt;
| Bio Con&lt;br /&gt;
|-&lt;br /&gt;
| 6148&lt;br /&gt;
| 1804&lt;br /&gt;
| HK-Computer&lt;br /&gt;
|-&lt;br /&gt;
| 6502&lt;br /&gt;
| 1966&lt;br /&gt;
| Cloanto&lt;br /&gt;
|-&lt;br /&gt;
| 6520&lt;br /&gt;
| 1978&lt;br /&gt;
| Oliver Gantert&lt;br /&gt;
|-&lt;br /&gt;
| 7777&lt;br /&gt;
| 1E61&lt;br /&gt;
| Rafal Gabriel Chyla&lt;br /&gt;
|-&lt;br /&gt;
| 8215&lt;br /&gt;
| 2017&lt;br /&gt;
| Vortex&lt;br /&gt;
|-&lt;br /&gt;
| 8244&lt;br /&gt;
| 2034&lt;br /&gt;
| Spirit Technology&lt;br /&gt;
|-&lt;br /&gt;
| 8290&lt;br /&gt;
| 2062&lt;br /&gt;
| Expansion Systems&lt;br /&gt;
|-&lt;br /&gt;
| 8448&lt;br /&gt;
| 2100&lt;br /&gt;
| ReadySoft&lt;br /&gt;
|-&lt;br /&gt;
| 8512&lt;br /&gt;
| 2140&lt;br /&gt;
| Phase 5 Digital Products&lt;br /&gt;
|-&lt;br /&gt;
| 8553&lt;br /&gt;
| 2169&lt;br /&gt;
| Digital Processing Systems Inc.&lt;br /&gt;
|-&lt;br /&gt;
| 8704&lt;br /&gt;
| 2200&lt;br /&gt;
| ACT Elektronik&lt;br /&gt;
|-&lt;br /&gt;
| 8738&lt;br /&gt;
| 2222&lt;br /&gt;
| ACT Elektronik&lt;br /&gt;
|-&lt;br /&gt;
| 9512&lt;br /&gt;
| 2528&lt;br /&gt;
| Tower Technologies&lt;br /&gt;
|-&lt;br /&gt;
| 10676&lt;br /&gt;
| 29B4&lt;br /&gt;
| Electronic Design&lt;br /&gt;
|-&lt;br /&gt;
| 14195&lt;br /&gt;
| 3773&lt;br /&gt;
| Media-net-Point&lt;br /&gt;
|-&lt;br /&gt;
| 14501&lt;br /&gt;
| 38A5&lt;br /&gt;
| Petsoff, Finland&lt;br /&gt;
|-&lt;br /&gt;
| 16375&lt;br /&gt;
| 3FF7&lt;br /&gt;
| Uwe Gerlach&lt;br /&gt;
|-&lt;br /&gt;
| 16707&lt;br /&gt;
| 4143&lt;br /&gt;
| Ateo Concepts&lt;br /&gt;
|-&lt;br /&gt;
| 16708&lt;br /&gt;
| 4144&lt;br /&gt;
| ALiENDESiGN&lt;br /&gt;
|-&lt;br /&gt;
| 16945&lt;br /&gt;
| 4231&lt;br /&gt;
| A.C.T.&lt;br /&gt;
|-&lt;br /&gt;
| 17740&lt;br /&gt;
| 454C&lt;br /&gt;
| HK-Computer (ELSAT)&lt;br /&gt;
|-&lt;br /&gt;
| 18260&lt;br /&gt;
| 4754&lt;br /&gt;
| MacroSystems (Germany)&lt;br /&gt;
|-&lt;br /&gt;
| 19796&lt;br /&gt;
| 4D54&lt;br /&gt;
| Markt &amp;amp; Technik&lt;br /&gt;
|-&lt;br /&gt;
| 22359&lt;br /&gt;
| 5757&lt;br /&gt;
| Markt &amp;amp; Technik&lt;br /&gt;
|-&lt;br /&gt;
| 26464&lt;br /&gt;
| 6760&lt;br /&gt;
| Combitec&lt;br /&gt;
|-&lt;br /&gt;
| 26470&lt;br /&gt;
| 6766&lt;br /&gt;
| Combitec&lt;br /&gt;
|-&lt;br /&gt;
| 28014&lt;br /&gt;
| 6D6E&lt;br /&gt;
| MNT Media and Technology UG&lt;br /&gt;
|-&lt;br /&gt;
| 32768&lt;br /&gt;
| 8000&lt;br /&gt;
| M.A.S.T.&lt;br /&gt;
|-&lt;br /&gt;
| 42240&lt;br /&gt;
| A500&lt;br /&gt;
| Aethreum Digital&lt;br /&gt;
|-&lt;br /&gt;
| 43437&lt;br /&gt;
| A9AD&lt;br /&gt;
| Reis-Ware&lt;br /&gt;
|-&lt;br /&gt;
| 43521&lt;br /&gt;
| AA01&lt;br /&gt;
| Cameron&lt;br /&gt;
|-&lt;br /&gt;
| 43537&lt;br /&gt;
| AA11&lt;br /&gt;
| Reis-Ware&lt;br /&gt;
|-&lt;br /&gt;
| 44359&lt;br /&gt;
| AD47&lt;br /&gt;
| Matay&lt;br /&gt;
|-&lt;br /&gt;
| 46504&lt;br /&gt;
| B5A8&lt;br /&gt;
| Phoenix&lt;br /&gt;
|-&lt;br /&gt;
| 49160&lt;br /&gt;
| C008&lt;br /&gt;
| Combitec&lt;br /&gt;
|-&lt;br /&gt;
| 61453&lt;br /&gt;
| F00D&lt;br /&gt;
| Forefront Technologies Inc.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For any changes/additions to this registry please use the [http://www.amigaos.net/contact AmigaOS web site contact form].&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Using_the_Editors&amp;diff=12580</id>
		<title>AmigaOS Manual: AmigaDOS Using the Editors</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Using_the_Editors&amp;diff=12580"/>
		<updated>2026-02-12T07:21:56Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: /* EDIT */ Set the defaults of PREVIOUS and WIDTH to new 4.1 Final Edition Update 3 values&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A text editor or word processing program is necessary for creating or editing text files and script files. Amiga Workbench software comes with three text editors. This chapter describes them in the following order:&lt;br /&gt;
&lt;br /&gt;
* ED&lt;br /&gt;
* MEmacs&lt;br /&gt;
* EDIT&lt;br /&gt;
&lt;br /&gt;
Each of the Amiga editors can be used separately for editing AmigaDOS scripts and programs; ED and MEmacs can be used for creating these files. If you are comfortable with the UNIX Emacs editor, you may prefer using the MEmacs editor. If you need to edit files containing binary code or you need to edit files too large to fit into memory, use EDIT. If you are not familiar with any of the editors, we recommend that you use the ED editor.&lt;br /&gt;
&lt;br /&gt;
Each editor has the basic functionality of a word processor, however, none of these editors support style formatting options, such as italics, page numbering, or different fonts. If you need these features, you can purchase third party word processing software containing such features for your Amiga.&lt;br /&gt;
&lt;br /&gt;
= ED =&lt;br /&gt;
&lt;br /&gt;
ED is a full screen ASCII text editor that uses menus and function keys to access its features. It is easy to use and is suitable for editing scripts, startup files, MountLists, and other simple files. Use either a mouse or the keyboard to perform operations with ED. Although ED&#039;s menus are preprogrammed, when you are familiar with the program, you can reconfigure them as needed.&lt;br /&gt;
&lt;br /&gt;
{{Note|ED does not accept files containing binary code. To edit this type of file, use EDIT or MEmacs.}}&lt;br /&gt;
&lt;br /&gt;
The bottom line of the ED window is the status line used for displaying messages, prompts, and commands. Error messages displayed on the status line remain until you enter another ED command. Figure 4-1 illustrates the ED window showing the status line.&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-1.png|none|frame|ED Window with Status Line]]&lt;br /&gt;
&lt;br /&gt;
== Starting ED ==&lt;br /&gt;
&lt;br /&gt;
Start ED from a Shell or with the Workbench Execute Command menu item. Open ED at the prompt by entering ED and a new or existing file name, as follows:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ED &amp;lt;filename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;lt;filename&amp;gt; is the name of an existing file or a new file to be used for saving your work. If the file name specified cannot be found in the current directory, ED opens a blank window and displays the message Creating new file.&lt;br /&gt;
&lt;br /&gt;
The format for ED is the following:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;ED [FROM] &amp;lt;filename&amp;gt; [SIZE &amp;lt;n&amp;gt;] [WITH &amp;lt;filename&amp;gt;]&lt;br /&gt;
 [WINDOW &amp;lt;window specification&amp;gt;] [TABS &amp;lt;n&amp;gt;] [WIDTH | COLS &amp;lt;n&amp;gt;]&lt;br /&gt;
 [HEIGHT | ROWS &amp;lt;n&amp;gt;]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The FROM argument specifies the source file to be edited. This argument is required, but the FROM keyword is optional.&lt;br /&gt;
&lt;br /&gt;
The SIZE argument changes the ED buffer size. ED has a default text buffer size of 40,000 bytes. For example:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ED Script SIZE 55000&lt;br /&gt;
&lt;br /&gt;
increase the size of the buffer to 55,000 bytes.&lt;br /&gt;
&lt;br /&gt;
The WITH argument specifies an ED command file that can contain any sequence of ED extended mode commands. When WITH is specified, ED executes the commands contained in the command file. The WITH argument&#039;s keywords is required if you use WITH.&lt;br /&gt;
&lt;br /&gt;
The WINDOW argument specifies the console type, such as RAX:0/0/640/256/EdWindow or CONSOLE:. The WINDOW argument&#039;s keyword is required if you use WINDOW.&lt;br /&gt;
&lt;br /&gt;
TABS sets the tab stop interval, which is the number of spaces to the right that the cursor moves when you press the Tab key. The default value for TABS is 3.&lt;br /&gt;
&lt;br /&gt;
The WIDTH and HEIGHT arguments adjust the size of the ED window by specifying the number of characters to display horizontally and vertically. By default the ED window is 640 x 200 picels or approximately 88 characters wide by 21 lines high.&lt;br /&gt;
&lt;br /&gt;
== Using ED ==&lt;br /&gt;
&lt;br /&gt;
All ED commands have key sequences and many are available through menus. You can enter ED commands in either of the following ways:&lt;br /&gt;
&lt;br /&gt;
* By choosing the command&#039;s menu item.&lt;br /&gt;
* By typing in the command&#039;s key sequence and pressing Return.&lt;br /&gt;
&lt;br /&gt;
In addition, you can use the mouse to perform some commands, such as those that control cursor movement.&lt;br /&gt;
&lt;br /&gt;
You can work on files in ED with the following two command modes:&lt;br /&gt;
&lt;br /&gt;
; Immediate&lt;br /&gt;
: Commands are executed as soon as typed. EP opens in immediate mode.&lt;br /&gt;
&lt;br /&gt;
; Extended&lt;br /&gt;
: Commands are not executed until you press Return or Esc.&lt;br /&gt;
&lt;br /&gt;
=== Immediate Commands ===&lt;br /&gt;
&lt;br /&gt;
In immediate mode, ED executes commands right away. Specify an immediate command by pressing a single key or Ctrl+key combination or by using the mouse. All immediate commands have corresponding extended versions.&lt;br /&gt;
&lt;br /&gt;
Immediate commands control the following:&lt;br /&gt;
&lt;br /&gt;
* Cursor movement&lt;br /&gt;
* Text scrolling&lt;br /&gt;
* Text insertion&lt;br /&gt;
* Text deletion&lt;br /&gt;
* Repetition of commands&lt;br /&gt;
&lt;br /&gt;
=== Moving the Cursor in Immediate Mode ===&lt;br /&gt;
&lt;br /&gt;
The cursor can be positioned anywhere in text by moving the pointer to the desired spot and clicking the selection button. To move the cursor with the keyboard, use the arrow keys, Tab, and Ctrl+key combinations.&lt;br /&gt;
&lt;br /&gt;
{{Note|In ED, the Tab key only moves the cursor. It does not insert Tab characters or spaces in a line.}}&lt;br /&gt;
&lt;br /&gt;
Move the cursor one position in any direction by pressing the appropriate arrow key. If the cursor is on the right edge of the screen, ED scrolls the text to the left do display the rest of the line. ED scrolls the text vertically one line at a time and horizontally ten characters at a time. You cannot move the cursor beyond the left edge of the line. If you try to move the cursor beyond the top or bottom of the file, ED displays a Top of File or Bottom of File message.&lt;br /&gt;
&lt;br /&gt;
Additional key combinations that control cursor movement are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Shift+up arrow || Top of the file.&lt;br /&gt;
|-&lt;br /&gt;
| Shift+down arrow || Bottom of the file.&lt;br /&gt;
|-&lt;br /&gt;
| Shift+left arrow || Left edge of the ED window (regardless of the margin setting).&lt;br /&gt;
|-&lt;br /&gt;
| Sift+right arrow || End of the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+] || Right edge of current line (if cursor is already there, it is moved to the left edge).&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+E || Start of the first line on the screen (if cursor is already there, it is moved to the end of the last line on the screen).&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+T || Start of the next word.&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+R || Space following the previous word.&lt;br /&gt;
|-&lt;br /&gt;
| Tab || The next tab position (multiple of the TABS value; 3 by default).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If your file has more lines than can fit in the ED window, you can scroll through the file vertically. Scroll one line at a time by pressing the up or down cursor key to move in the corresponding direction. Move the text in jumps by pressing:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Ctrl+D || Moves 12 lines down through the file.&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+U || Moves 12 lines up through the file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These commands do not move the cursor position in the window; they redraw the text in the window with the new line at the cursor position.&lt;br /&gt;
&lt;br /&gt;
If something disturbs your screen, such as an alert from another program appearing in the ED window or message remarks in the status line, press:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Ctrl+V || Refreshes the window display.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Inserting Text in Immediate Mode ===&lt;br /&gt;
&lt;br /&gt;
Any characters typed in immediate mode are inserted at the current cursor position and the cursor is shifted to the right. Any characters to the right of the cursor are shifted to make room for new text. If the line is wider than the width of the window, the window scrolls to the right to show what you are typing. If you move the cursor beyond the end of the line, ED inserts spaces between the end of the line and any new characters inserted.&lt;br /&gt;
&lt;br /&gt;
There is maximum limit of 255 characters in a line. If you add more characters, ED displays a Line Too Long message.&lt;br /&gt;
&lt;br /&gt;
To split the current line at the cursor, press Return. Any text to the left of the cursor remains on the original line. All text under and to the right of the cursor moves down onto a new line. Pressing Return at the end of the line creates a new blank line.&lt;br /&gt;
&lt;br /&gt;
=== Deleting Text in Immediate Mode ===&lt;br /&gt;
&lt;br /&gt;
ED has no type over mode. To replace a word or line, you must delete the existing words and insert new information with the following keys and key combinations:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Backspace || Deletes the character to the left of the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Del || Deletes the character highlighted by the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+O || If the cursor is over a space, all spaces up to the next character are deleted. If the cursor is over a character, all characters up to the next space are deleted.&lt;br /&gt;
|-&lt;br /&gt;
| Ctrl+Y || Deletes all characters from the cursor to the end of the line.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
When text is deleted, any characters remaining on the line shift to the left and any text beyond the right edge of the screen becomes visible.&lt;br /&gt;
&lt;br /&gt;
=== Changing Case in Immediate Mode ===&lt;br /&gt;
&lt;br /&gt;
You can change the case of text by positioning the cursor and pressing Ctrl+F. If the letter is lower case, it becomes upper case and vice versa. Ctrl+F does not change non-alphabetic characters or symbols.&lt;br /&gt;
&lt;br /&gt;
After you press Ctrl+F, the cursor moves to the right. You can hold down Ctrl+F to repeat the command until you change all the letters on the line.&lt;br /&gt;
&lt;br /&gt;
=== Extended Commands ===&lt;br /&gt;
&lt;br /&gt;
In extended mode, commands are displayed on the command line - or status line - at the bottom of the window. ED does not execute these commands until you press Return or Esc. If you use Esc to execute extended commands, ED remains in extended mode. If you use Return to execute extended commands, ED returns to immediate mode.&lt;br /&gt;
&lt;br /&gt;
Extended commands manage the following:&lt;br /&gt;
&lt;br /&gt;
* Program control&lt;br /&gt;
* Cursor movement&lt;br /&gt;
* Text modification&lt;br /&gt;
* Block control&lt;br /&gt;
* Searching and exchanging text&lt;br /&gt;
&lt;br /&gt;
To enter extended mode, press Esc. An asterisk appears as a prompt in the status line. Extended commands consist of one or two characters. Multiple extended commands can be typed on a single command line by separating them with a semicolon. Commands can be grouped together for ED to repeat automatically. Use Backspace to correct mistakes.&lt;br /&gt;
&lt;br /&gt;
You can also execute commands through the programmable menu and function keys. Reconfigure the menus and functions keys by assigning a command to the key or menu item of your choice as described on page 4-21.&lt;br /&gt;
&lt;br /&gt;
=== Using String Delimiters ===&lt;br /&gt;
&lt;br /&gt;
In some cases, commands require arguments, such as a number or a text strig. A string argument for an ED command must be enclosed in a pair of identical delimiter characters. In unambiguous situations you may omit the trailing delimiter. Valid delimiters include &amp;quot;, /, \, !, :, +, -, and %. You cannot use the same delimiter character inside your string. Invalid delimiter characters include letters, numbers, spaces, semicolons, question marks, brackets, and control characters.&lt;br /&gt;
&lt;br /&gt;
=== Using a File Requester ===&lt;br /&gt;
&lt;br /&gt;
You can also ask ED to use a file requester, allowing you to view the contents of the drives and directories in your system.&lt;br /&gt;
&lt;br /&gt;
To invoke a file requester for a load or save command, you must place a question mark (?) before the required string argument. Be sure to include a space before the question mark (for example, sa ?/Text/). Normally, when a command is followed by a string. ED treats the string as the file to be loaded or saved and attempts the operation immediately. However, the question mark indicates that you want to specify the file through a file requester. You must still specify a string after the question mark, but the string becomes the text that appears in the file requester title bar.&lt;br /&gt;
&lt;br /&gt;
== ED Menus ==&lt;br /&gt;
&lt;br /&gt;
ED has two sets of command menu assignments: default and expanded. The default menu assignments, as illustrated in Figure 4-2, are set up by the S:Ed-startup file, which is automatically executed each time you run ED. The S:Ed-startup file is a command file of ED extended mode commands, without the Escape characters. You can edit this file to set up custom menus, as described on page 4-21, or define preprogrammed function key assignments with the Set FN Key menu item.&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-2.png|none|frame|Default Menu Assignments]]&lt;br /&gt;
&lt;br /&gt;
=== Enabling Expanded Menus ===&lt;br /&gt;
&lt;br /&gt;
The expanded command menu assignments, as illustrated in Figure 4-3, can be enabled by renaming or deleting the default S:Ed-startup file. If ED cannot find a file named S:Ed-startup, it opens with the expanded set of menus, providing more options.&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-3.png|none|frame|Expanded Menu Assignments]]&lt;br /&gt;
&lt;br /&gt;
Rather then deleting your S-Ed-startup file, we recommend that you rename it as follows:&lt;br /&gt;
&lt;br /&gt;
# In the Workbench window, go to the Window menu and select Show All Files.&lt;br /&gt;
# Double-click on the S drawer icon.&lt;br /&gt;
# Click on the Ed-startup icon.&lt;br /&gt;
# Go to the Icons menu and choose Rename.&lt;br /&gt;
# Delete the name in the Rename requester&#039;s New Name text gadget and enter a new name for Ed-startup.&lt;br /&gt;
# Select OK.&lt;br /&gt;
&lt;br /&gt;
You can also create your own customized file of startup options. Avoid including Quit commands in the S:Ed-startup file since they can cause ED to quit immediately after opening.&lt;br /&gt;
&lt;br /&gt;
The menu items in both the default and expanded menus have the same function regardless of which set you use. All of the ED commands are available through the keyboard using extended mode commands even if they do not appear in any menu.&lt;br /&gt;
&lt;br /&gt;
The following sections describe the menu items found in the expanded menus and their corresponding extended and immediate mode commands. An ellipsis (...) indicates that an argument is required or that a menu item opens a requester or prompt.&lt;br /&gt;
&lt;br /&gt;
=== Project Menu ===&lt;br /&gt;
&lt;br /&gt;
The following are the expanded Project menu items:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| New || Esc,N,W || Creates a new file, replacing the existing file. The message Edits will be lost-type Y to confirm: is displayed. Press any key (except Y) to abort the command.&lt;br /&gt;
|-&lt;br /&gt;
| Open... || Esc,O,P... || Opens a file. Specify the file by entering the path to the file as a properly delimited string. (If slashes appear in the path to a file, do not use the slash as a delimiter.) The message Edits will be lost-type Y to confirm: reminds you that you are replacing the current file.&lt;br /&gt;
|-&lt;br /&gt;
| Insert File... || Esc,I,F... || Inserts a file into the current file. ED reads into memory the specified file at the point immediately following the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Write Block... || Esc,W,B... || Writes the currently marked block to a specified file. ED overwrites any other files with that name and copies the block to the file.&lt;br /&gt;
|-&lt;br /&gt;
| Save || Esc,S,A || Saves the text to the current file, overwriting the existing text in the file. Use Save As to save to different file. SA followed by Q is equivalent to the X command.&lt;br /&gt;
|-&lt;br /&gt;
| Save As... || Esc,S,A... || Saves the text to the specified file name.&lt;br /&gt;
|-&lt;br /&gt;
| Save &amp;amp; Exit || Esc,X || Exits ED, saving the current file to the designated file name. ED writes the text it is holding in memory to the file that was specified when ED was opened and then terminates.&lt;br /&gt;
|-&lt;br /&gt;
| About || Esc,S,H || Shows the current state of the editor. The screen display information, such as the value of tab stops, current margins, block marks, and the name of the file being edited.&lt;br /&gt;
|-&lt;br /&gt;
| Quit || Esc,Q || Exits ED without saving changes. If you made any changes to the file, ED asks if you want to quit. If you press Y, ED terminates immediately without saving the changes to the file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Edit Menu ===&lt;br /&gt;
&lt;br /&gt;
The following are commands used for editing:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Undo Line || Esc,U || Reverses changes made to the current line. However, ED cannot undo a line deletion. Once you have moved from the current line, the U command cannot undo a change.&lt;br /&gt;
|-&lt;br /&gt;
| Star Block || Esc,B,S || Identify the beginning and end of a block of text. To specify a block of text to be moved, inserted, or deleted, place the cursor on the first line that you want in the block and enter the BS command. Move the cursor to the last line that you want in the block and enter the BE command.&amp;lt;br/&amp;gt;You cannot start or finish a block in the middle of a line.&lt;br /&gt;
|-&lt;br /&gt;
| Show Block || Esc,S,B || Redraws the display so the block is at the top of the screen.&lt;br /&gt;
|-&lt;br /&gt;
| Insert Block || Esc,I,B || Inserts a copy of the block after the current line. The block remains defined until you change the text. Use IB to insert copies of the block throughout the document.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Block || Esc,D,B || Deletes a block.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Line || Esc,D&amp;lt;br/&amp;gt;Ctrl+B || Deletes the entire line.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Movement Menu ===&lt;br /&gt;
&lt;br /&gt;
The following commands move the cursor around the screen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Top || Esc,T || Top of the file; first line of the file is brought to the top of the window.&lt;br /&gt;
|-&lt;br /&gt;
| Bottom || Esc,B || Bottom of the file; last line of the file is brought to the bottom of the window.&lt;br /&gt;
|-&lt;br /&gt;
| Go To Line... || Esc,M... || Move the cursor to the specified line. Enter the line number on the status line and press Return. The line specified is brought to the top of the window. If no number is given, the cursor goes to the top of the window.&lt;br /&gt;
|-&lt;br /&gt;
| Next Page || Esc,P,D&amp;lt;br/&amp;gt;Ctrl+D || Go to next page.&lt;br /&gt;
|-&lt;br /&gt;
| Previous Page || Esc,P,U&amp;lt;br/&amp;gt;Ctrl+U || Go to previous page.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Search Menu ===&lt;br /&gt;
&lt;br /&gt;
The following commands let you search through the file for specific instances of text. You can substitute one pattern of text with another (search and replace) and have ED request confirmation of (query) each replace. If the specified text is not found or there are no more instances of the text, the message Search failed is displayed. When using the Find and Replace menu commands, ED prompts for the text strings. Enter the text without delimiters. When using extended mode, include delimited strings with the command.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Find...&amp;lt;br/&amp;gt;Find Next || Esc,s... || Finds the next occurrence of the specified string of text. The search starts one character beyond the current cursor position and continues forward through the file. If the string is found, the cursor moves to the start of the located string. The search is case-sensitive, unless the Ignore Case command is used. Find Next repeats the command.&lt;br /&gt;
|-&lt;br /&gt;
| Reverse Find...&amp;lt;br/&amp;gt;Reverse Find&amp;lt;br/&amp;gt;Next || Esc,B,F... || Searches backwards through the file for the specified string. This command finds the last occurrence of the string before the current cursor position. The search continues through to the beginning of the file. Reverse Find Next repeats the command.&lt;br /&gt;
|-&lt;br /&gt;
| Replace... || Esc,E... || Exchanges one occurrence of text with another.&amp;lt;br/&amp;gt;In extended mode, enter the strings enclosed by three delimiters. For example, to replace the word to with too, enter &amp;quot;to&amp;quot;too&amp;quot;. Specify empty strings by typing two delimiters with nothing between them. If the first string is empty, ED inserts the second string at the current cursor position. If the second string is empty, ED searches for the next occurrence of the first string and then deletes it. Note that ED ignores margin settings when exchanging text.&lt;br /&gt;
|-&lt;br /&gt;
| Global Replace || Esc,R,P,E... || Exchanges all occurrences of text.&lt;br /&gt;
|-&lt;br /&gt;
| Query-Replace... || Esc,E,Q... || Searches for the text to be exchanged and requests verification by displaying Exchange?. Enter Y to exchange or another other key to abort.&lt;br /&gt;
|-&lt;br /&gt;
| Global Query-Replace... || Esc,R,P,E,Q... || Searches for all occurrences of the text to be exchanged and requests verification for each. Enter Y to exchange or any other key to abort.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Settings Menu ===&lt;br /&gt;
&lt;br /&gt;
The following commands are used for setting up your ED environment:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Set FN Key... || Esc,S,F... || Defines the function keys and other programmable keys. Defining function key and Ctrl+key commands is similar to defining menu items. See page 4-15 for instructions for defining function keys and an example of the Set FN Key command.&lt;br /&gt;
|-&lt;br /&gt;
| Show FN Key... || Esc,D,F&amp;lt;br/&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;key&amp;gt;&amp;lt;/nowiki&amp;gt; || Displays the setting for the function key specified by &amp;lt;nowiki&amp;gt;&amp;lt;key&amp;gt;&amp;lt;/nowiki&amp;gt;. Enter a space and a key slot number for &amp;lt;nowiki&amp;gt;&amp;lt;key&amp;gt;&amp;lt;/nowiki&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Reset Keys || Esc,R,K || Resets the key definitions to the default. See page 4-16 for a table of special key mappings.&lt;br /&gt;
|-&lt;br /&gt;
| Right Margin... || Esc,S,R... || Sets the right margin. Use the SR command followed by a number indicating the column position.&lt;br /&gt;
|-&lt;br /&gt;
| Left Margin... || Esc,S,L... || Sets the left margin. Use the SL command followed by a number indicating the column position. The left margin should not be set beyond the right edge of the screen.&lt;br /&gt;
|-&lt;br /&gt;
| Ignore Case || Esc,U,C || Specifies a case-insensitive search. UC instructs all subsequent searches not to make any distinction between upper and lower case next. To make searches case-sensitive again, use the LC command.&lt;br /&gt;
|-&lt;br /&gt;
| Case Sensitive || Esc,L,C || Specifies a case-sensitive search.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Set FN Key ===&lt;br /&gt;
&lt;br /&gt;
Set FN Key is used to define function keys and other programmable keys. There are 57 immediate command key slots ranging from 1 to 57. Any slot number can be redefined and any numbers within the range that do not appear in the special key mappings on page 4-16 are not defined.&lt;br /&gt;
&lt;br /&gt;
The following is the syntax for the Set FN Key command:&lt;br /&gt;
&lt;br /&gt;
 SF &amp;lt;slot number&amp;gt; /command string/&lt;br /&gt;
&lt;br /&gt;
Define Ctrl+key combinations by substituting a caret (^) and the other character for the slot number.&lt;br /&gt;
&lt;br /&gt;
==== Example Script ====&lt;br /&gt;
&lt;br /&gt;
This example script assigns function keys to cursor control commands. You can also enter these as a series of extended mode commands. The Top of File, Bottom of File, End of Page, Next Page, Next Line, and Previous Line commands are assigned to the F1 through F6 keys, respectively. Quotation marks are used as delimiters.&lt;br /&gt;
&lt;br /&gt;
 SF 1 &amp;quot;t&amp;quot;&lt;br /&gt;
 SF 2 &amp;quot;b&amp;quot;&lt;br /&gt;
 SF 3 &amp;quot;ep&amp;quot;&lt;br /&gt;
 SF 4 &amp;quot;pd&amp;quot;&lt;br /&gt;
 SF 5 &amp;quot;n&amp;quot;&lt;br /&gt;
 SF 6 &amp;quot;p&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Special Key Mappings ===&lt;br /&gt;
&lt;br /&gt;
The following table shows the default key definitions used in the Reset Keys command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Slot# !! Key/Key Sequence !! Function&lt;br /&gt;
|-&lt;br /&gt;
| 1-10 || F1 through F10 || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 11-20 || Shift+F1 to Shift+F10 || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 21 || Shift+left arrow || Move to beginning of line&lt;br /&gt;
|-&lt;br /&gt;
| 22 || Shift+right arrow || Move to end of line&lt;br /&gt;
|-&lt;br /&gt;
| 23 || Shift+up arrow || Move to top of document&lt;br /&gt;
|-&lt;br /&gt;
| 24 || Shift+down arrow || Move to bottom of document&lt;br /&gt;
|-&lt;br /&gt;
| 25 || Del || Delete character at cursor&lt;br /&gt;
|-&lt;br /&gt;
| 26 || Not defined || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 27 || Ctrl+A || Insert line&lt;br /&gt;
|-&lt;br /&gt;
| 28 || Ctrl+B || Delete line&lt;br /&gt;
|-&lt;br /&gt;
| 29 || Ctrl+C || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 30 || Ctrl+D || Move down 12 lines&lt;br /&gt;
|-&lt;br /&gt;
| 31 || Ctrl+E || Move to top or bottom of screen&lt;br /&gt;
|-&lt;br /&gt;
| 32 || Ctrl+F || Change case&lt;br /&gt;
|-&lt;br /&gt;
| 33 || Ctrl+G || Repeat last extended command line&lt;br /&gt;
|-&lt;br /&gt;
| 34 || Ctrl+H || Delete character left of cursor&lt;br /&gt;
|-&lt;br /&gt;
| 35 || Ctrl+I || Move cursor to next tab position&lt;br /&gt;
|-&lt;br /&gt;
| 36 || Ctrl+J || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 37 || Ctrl+K || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 38 || Ctrl+L || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 39 || Ctrl+M || Return&lt;br /&gt;
|-&lt;br /&gt;
| 40 || Ctrl+N || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 41 || Ctrl+O || Delete word or spaces&lt;br /&gt;
|-&lt;br /&gt;
| 42 || Ctrl+P || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 43 || Ctrl+Q || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 44 || Ctrl+R || Move to end of previous word&lt;br /&gt;
|-&lt;br /&gt;
| 45 || Ctrl+S || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 46 || Ctrl+T || Move start of next word&lt;br /&gt;
|-&lt;br /&gt;
| 47 || Ctrl+U || Move up 12 lines&lt;br /&gt;
|-&lt;br /&gt;
| 48 || Ctrl+V || Redisplay window&lt;br /&gt;
|-&lt;br /&gt;
| 49 || Ctrl+W || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 50 || Ctrl+X || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 51 || Ctrl+Y || Delete to end of line&lt;br /&gt;
|-&lt;br /&gt;
| 52 || Ctrl+Z || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 53 || Ctrl+[ || Esc (enter extended command mode)&lt;br /&gt;
|-&lt;br /&gt;
| 54 || Not defined || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 55 || Ctrl+] || Move to end or start of line, depending on cursor position&lt;br /&gt;
|-&lt;br /&gt;
| 56 || Not defined || Not defined&lt;br /&gt;
|-&lt;br /&gt;
| 57 || Not defined || Not defined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Command Menu ===&lt;br /&gt;
&lt;br /&gt;
The following commands are for manipulating files:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Extended Command... || Esc,C,M... || Enters extended command mode; equivalent to pressing Ctrl+[ or Esc.&lt;br /&gt;
|-&lt;br /&gt;
| Repeat Last || Esc,R,E || Attempts to repeat the last command.&lt;br /&gt;
|-&lt;br /&gt;
| Run File... || Esc,R,F... || Loads and executes a command file of extended mode commands.&lt;br /&gt;
|-&lt;br /&gt;
| ARexx Command... || Esc,R,X... || Runs the specified ARexx program.&lt;br /&gt;
|-&lt;br /&gt;
| Redisplay || Esc,V,W || Redraws the ED window and clears the status line; equivalent to pressing Ctrl+V.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Other ED Commands ===&lt;br /&gt;
&lt;br /&gt;
There are also ED commands that do not appear in menus. These commands are listed here in functional groups. Use them in extended mode by entering the following key sequences.&lt;br /&gt;
&lt;br /&gt;
==== Program Control ====&lt;br /&gt;
&lt;br /&gt;
The following are program control commands:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Extend Margins || Esc,E,X || Extends the margins for the current line. Once you enter the EX command, ED ignores the right margin on the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Status Line Message || Esc,S,M... || Prints a given string on the status line.&lt;br /&gt;
|-&lt;br /&gt;
| Exit with Query || Esc,X,Q || Exits ED unless changes were made to the file. If changes have been made, the message File has been changed-type Y to save and exit: is displayed. Press any key (except Y) to abort the exit. XQ is equivalent to clicking the close gadget on the ED window.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Cursor Control ====&lt;br /&gt;
&lt;br /&gt;
The following commands are used for controlling the cursor:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| End Page || Esc,E,P || End of a page.&lt;br /&gt;
|-&lt;br /&gt;
| Previous || Esc,P || Start of the previous line.&lt;br /&gt;
|-&lt;br /&gt;
| Character Left || Esc,C,L || One place to the left.&lt;br /&gt;
|-&lt;br /&gt;
| Character Right || Esc,C,R || One place to the right.&lt;br /&gt;
|-&lt;br /&gt;
| Current End || Esc,C,E || End of the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Current Start || Esc,C,S || Start of the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Tab || Esc,T,B || Next tab position.&lt;br /&gt;
|-&lt;br /&gt;
| Word Next || Esc,W,N || Start of the next word.&lt;br /&gt;
|-&lt;br /&gt;
| Word Previous || Esc,W,P || Space after previous word.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Modifying Text ====&lt;br /&gt;
&lt;br /&gt;
The following commands edit text on the screen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Insert Before || Esc,I || Inserts the specified string on the line before the cursor. Specify a new line&#039;s string after the I command to insert text before the current line containing the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Insert After || Esc,A || Inserts the specified string on the line after the cursor. This command works in the same way as I, except that the string is inserted on a new line beneath the current cursor position.&lt;br /&gt;
|-&lt;br /&gt;
| Split || Esc,S || Splits the current line at the cursor position.&lt;br /&gt;
|-&lt;br /&gt;
| Join || Esc,J || Joins the next line to the end of the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Delete || Esc,D || Deletes the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Character || Esc,D,C || Deletes the character under the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Left || Esc,D,L || Deletes the character to the left of the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Word || Esc,D,W || Deletes to the end of the current word.&lt;br /&gt;
|-&lt;br /&gt;
| End Line || Esc,E,L || Deletes to the end of the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Flip Case || Esc,F,C || Switches the case of the selected letters, one at a time.&lt;br /&gt;
|-&lt;br /&gt;
| Set Tab || Esc,S,T || Sets the tab stop. To change the current setting of tabs, use the ST command followed by a number.&lt;br /&gt;
|-&lt;br /&gt;
| Next || Esc,N || Start of the next line.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Repeating Commands in Extended Mode ==&lt;br /&gt;
&lt;br /&gt;
Pressing Ctrl+G repeats a command line. You can set up execute complex sets of editing commands many times.&lt;br /&gt;
&lt;br /&gt;
You can repeat a command a specified number of times by entering the number before the command. For example:&lt;br /&gt;
&lt;br /&gt;
 4 E/rename/copy/&lt;br /&gt;
&lt;br /&gt;
exchanges the next four occurrences of &amp;quot;rename&amp;quot; to &amp;quot;copy&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Use the RP (Repeat) extended command to repeat a command until ED returns an error, such as reaching the end of the file. For example:&lt;br /&gt;
&lt;br /&gt;
 T;RP E/rename/copy/&lt;br /&gt;
&lt;br /&gt;
moves the cursor to the top of the file, then exchanges all occurrences of &amp;quot;rename&amp;quot; with &amp;quot;copy&amp;quot;. The T command (Top of File) changes all occurrences of Rename in the whole file. Otherwise, only the occurrences after the current cursor position are changed.&lt;br /&gt;
&lt;br /&gt;
To execute command groups repeatedly, you can group the commands together in parentheses. You can also nest command groups. For example:&lt;br /&gt;
&lt;br /&gt;
 RP (F/Workbench/;3A//)&lt;br /&gt;
&lt;br /&gt;
inserts three blank lines (the null string//) after every line containing Workbench.&lt;br /&gt;
&lt;br /&gt;
To interrupt any sequence of extended commands, press any key during execution. If an error occurs, ED abandons the command sequence.&lt;br /&gt;
&lt;br /&gt;
== Customizing ED ==&lt;br /&gt;
&lt;br /&gt;
You can customize ED with commands that change the menus and function key setup. These commands can be entered individually within ED. They can also be saved as a script, such as S:Ed-startup, or as a file specified using the WITH argument. To execute the file from within ED, use the Run File (Esc,R,F) extended command. For information about changing the function keys, see page 4-15.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
\ Set Menu Item || Esc,S,I || Defines the menu headings and items. There are 120 menu item slots ranging from 0 to 119. The slot type identifies the contents of the slot and is a number from 0 to 4. The 0 slot type must be the last defined slot. Do not create a menu without items; if you specify a menu heading, include menu items after it. See below for the syntax of the Set Menu Item command and a table of the slot types.&lt;br /&gt;
|-&lt;br /&gt;
| Enable Menu || Esc,E,M || Enables menus. You must follow the Set Menu Item commands with EM to enable the menu commands. See page 4-22 for an example script using the Enable Menu command.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Set Menu Item ===&lt;br /&gt;
&lt;br /&gt;
The following is the syntax for the Set Menu Item command:&lt;br /&gt;
&lt;br /&gt;
 SI &amp;lt;slot number&amp;gt; &amp;lt;slot type&amp;gt; /string1/string2/&lt;br /&gt;
&lt;br /&gt;
The following table shows the slot types and functions used with the Set Menu Item command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Type !! Function !! String Input&lt;br /&gt;
|-&lt;br /&gt;
| 0 || End of Menus || No arguments&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Menu Heading || String1 = heading name&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Menu Item || String1 = item name&amp;lt;br/&amp;gt;String2 = command string&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Submenu Heading || String1 = heading name&amp;lt;br/&amp;gt;String2 = command string&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Separator bar || No arguments&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Example Script ===&lt;br /&gt;
&lt;br /&gt;
The following is an example script using the Set Menu Item and Enable Menu commands. Quotation marks are used as the delimiters.&lt;br /&gt;
&lt;br /&gt;
 SI 0 1 &amp;quot;Project&amp;quot;&lt;br /&gt;
 SI 1 2 &amp;quot;Open . . . &amp;quot; &amp;quot;op ? /Open file:/&amp;quot;&lt;br /&gt;
 SI 2 2 &amp;quot;Save ... &amp;quot; &amp;quot;sa&amp;quot;&lt;br /&gt;
 SI 3 4&lt;br /&gt;
 SI 4 2 &amp;quot;Quit!&amp;quot; &amp;quot;q&amp;quot;&lt;br /&gt;
 SI 5 1 &amp;quot;Move&amp;quot;&lt;br /&gt;
 SI 6 2 &amp;quot;Top&amp;quot; &amp;quot;t&amp;quot;&lt;br /&gt;
 SI 7 2 &amp;quot;Bottom&amp;quot; &amp;quot;b&amp;quot;&lt;br /&gt;
 SI 8 0&lt;br /&gt;
 EM&lt;br /&gt;
&lt;br /&gt;
This script produces the menus illustrated in Figure 4-4:&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-4.png|none|frame|ED Custom Menu Example]]&lt;br /&gt;
&lt;br /&gt;
== Printing From ED ==&lt;br /&gt;
&lt;br /&gt;
Use the following steps to print a file that is open in the current ED window:&lt;br /&gt;
&lt;br /&gt;
# Choose the Save As menu item to display a file requester.&lt;br /&gt;
# Enter prt: in the Drawer field.&lt;br /&gt;
# Select OK.&lt;br /&gt;
&lt;br /&gt;
This prints the file, but does not save it to disk. If you wish to save the file you must select the Save menu item or Save As and a file name.&lt;br /&gt;
&lt;br /&gt;
== Quitting ED ==&lt;br /&gt;
&lt;br /&gt;
You can exit ED in one of the following three ways:&lt;br /&gt;
&lt;br /&gt;
* Esc,X. This method exits ED and saves the current file to a designated file name that was specified when ED was opened.&lt;br /&gt;
* Esc,X,Q or click on the ED window&#039;s close gadget. This method exits ED if no changes were made to the file. If changes were made to the file, ED lets you save the changes or exit without saving.&lt;br /&gt;
* Esc,Q or select the Quit item in the Project menu. This method exits ED without saving any changes if you confirm the status line warning that changes will be lost.&lt;br /&gt;
&lt;br /&gt;
== ARexx Support ==&lt;br /&gt;
&lt;br /&gt;
You can also control ED from ARexx by sending and receiving commands through ED&#039;s ARexx port. Each copy of ED running concurrently has an individual ARexx port name that must be specified to handle information for the correct session. The ARexx port names are assigned as follows:&lt;br /&gt;
&lt;br /&gt;
* The first session&#039;s port name is ED&lt;br /&gt;
* The second session&#039;s is ED_1&lt;br /&gt;
* The third session&#039;s is Ed_2 and so on&lt;br /&gt;
&lt;br /&gt;
Many of ED&#039;s extended commands can be used from ARexx. By using ED&#039;s RV command in ARexx programs, you can send information from ED to ARexx. This gives information about the status of ED, such as the current line number of the name of the file being edited.&lt;br /&gt;
&lt;br /&gt;
The RV command accepts the name of the ARexx stem variable to store its argument information. For example, in ARexx:&lt;br /&gt;
&lt;br /&gt;
 address &#039;Ed&#039; &#039;RV /stem/&#039;&lt;br /&gt;
&lt;br /&gt;
assigns values to the following variables:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| stem.LEFT || Current left margin&lt;br /&gt;
|-&lt;br /&gt;
| stem.RIGHT || Current right margin&lt;br /&gt;
|-&lt;br /&gt;
| stem.TABSTOP || Current tab stop setting&lt;br /&gt;
|-&lt;br /&gt;
| stem.LMAX || Maximum number of lines visible on screen&lt;br /&gt;
|-&lt;br /&gt;
| stem.WIDTH || Width of the screen in characters&lt;br /&gt;
|-&lt;br /&gt;
| stem.X || Cursor X position in the ED window (1 is the left edge)&lt;br /&gt;
|-&lt;br /&gt;
| stem.Y || Cursor Y position in the ED window (1 is the top line)&lt;br /&gt;
|-&lt;br /&gt;
| stem.BASE || Window base (normally 0, but non-zero when the screen is shifted to the right)&lt;br /&gt;
|-&lt;br /&gt;
| stem.EXTEND || Extended margin value (Extend Margins command)&lt;br /&gt;
|-&lt;br /&gt;
| stem.FORCECASE || Case sensitivity flag (Ignore Case = 1, Case Sensitive = 0)&lt;br /&gt;
|-&lt;br /&gt;
| stem.LINE || Current line number in the file (1 is the first line)&lt;br /&gt;
|-&lt;br /&gt;
| stem.FILENAME || Name of the file being edited&lt;br /&gt;
|-&lt;br /&gt;
| stem.CURRENT || Text of the current line&lt;br /&gt;
|-&lt;br /&gt;
| stem.LASTCMD || Last extended command issued&lt;br /&gt;
|-&lt;br /&gt;
| stem.SEARCH || Lst string searched for&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any valid ARexx symbol can be substituted for &amp;quot;stem&amp;quot;. Enclose the name in proper delimiters. These variables can be treated as ordinary ARexx stem variables.&lt;br /&gt;
&lt;br /&gt;
== ED/ARexx Example Program ==&lt;br /&gt;
&lt;br /&gt;
The example program, Transpose.ed, illustrates the use of several extended commands from ARexx. This program transposes two characters when launched from ED. For example, if a line contains the string 123 and the cursor is highlighted the 3, Transpose.ed changes the string to 213.&lt;br /&gt;
&lt;br /&gt;
Enter this program and save it as REXX:Transpose.ed. Then, open ED and edit an existing file or create a new one. Place the cursor one character to the right of the ones to be transposed, press Esc, and enter:&lt;br /&gt;
&lt;br /&gt;
 RX /transpose.ed/&lt;br /&gt;
&lt;br /&gt;
The program executes and the characters are transposed if ARexx is running and everything is entered correctly. The entire file name, including the extension, must be specified to run the program.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*Transpose.ed: An example program to transpose two characters. */&lt;br /&gt;
/*Given string &#039;123&#039;, if cursor is on 3, this macro converts */&lt;br /&gt;
/*string into &#039;213&#039;. */&lt;br /&gt;
HOST = address () /*find out which ED session invoked this program*/&lt;br /&gt;
address VALUE HOST /*...and talk to that session */&lt;br /&gt;
&#039;rv&#039;&#039;/CURR/&#039; /*Ask ED to store info in stem variable CURR */&lt;br /&gt;
             /*Obtain two pieces of information: */&lt;br /&gt;
currpos = CURR.X /*1.position of cursor on line */&lt;br /&gt;
currling = CURR.CURRENT /*2. Contents of current line */&lt;br /&gt;
if /currpos &amp;gt;2) then /*Work only on the current line */&lt;br /&gt;
  currpos = currpos - 1&lt;br /&gt;
else do /*Otheriwse, report error and exit */&lt;br /&gt;
&#039;sm/Cusor must be at position 2 or further to the right/&#039;&lt;br /&gt;
exit 10&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
/*Next the code needs to reverse the CURRPOSth and CURRPOSth-1 */&lt;br /&gt;
/*characters and then replace the current line with the new one. */&lt;br /&gt;
/*drop CURR. CURR is no longer needed; dropping it saves some */&lt;br /&gt;
/*memory. */&lt;br /&gt;
&#039;d&#039; /*Tell ED to delete current line */&lt;br /&gt;
currlin = swapch (currpos,currlin) /*Swap the two characters */&lt;br /&gt;
&#039;i/&#039;||currlin||&#039;/&#039; /*Insert modified line */&lt;br /&gt;
do i = 1 to currpos /*Place cursor back where it started */&lt;br /&gt;
  &#039;cr&#039; /*ED&#039;s `cursor right&#039; command */&lt;br /&gt;
  end&lt;br /&gt;
exit /*Program has finished */&lt;br /&gt;
&lt;br /&gt;
/*Function to swap two characters */&lt;br /&gt;
Swapch: procedure */&lt;br /&gt;
parse arg cpos,clin&lt;br /&gt;
  ch1 = substr(clin,cpos1) /*Get character */&lt;br /&gt;
  clin = delstr(clin,cpos1) /*Delete it from string */&lt;br /&gt;
  clin = insert(ch1,clin,cpos-2,1) /*Insert to create transposition */&lt;br /&gt;
return clin /*Return modified string */ &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= MEmacs =&lt;br /&gt;
&lt;br /&gt;
MEmacs (MicroEmacs), which is similar to the UNIX-based Emacs editor, is a screen-oriented editor in which you can edit multiple files at the same time. MEmacs performs all operations on memory-resident text, requiring that entire text files be able to fit into memory at once.&lt;br /&gt;
&lt;br /&gt;
Line length, generally 80 characters long, is limited to the right edge of the screen. You can enter characters beyond the limit, however, they are not displayed. To see these characters, break the line or delete some of the displayed characters. A dollar sign ($) at the right edge of the screen indicates that there are characters beyond what is displayed.&lt;br /&gt;
&lt;br /&gt;
The format for the MEmacs command is the following:&lt;br /&gt;
&lt;br /&gt;
 MEMACS [&amp;lt;filename&amp;gt;] [GOTO &amp;lt;n&amp;gt;] [OPT W]&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;filename&amp;gt; argument is optional.&lt;br /&gt;
&lt;br /&gt;
The GOTO &amp;lt;n&amp;gt; option specifies the line on which the cursor is to appear when the file is opened.&lt;br /&gt;
&lt;br /&gt;
Specifying OPT W opens MEmacs in a Workbench window rather than on its own screen, which saves memory.&lt;br /&gt;
&lt;br /&gt;
== Starting MEmacs ==&lt;br /&gt;
&lt;br /&gt;
MEmacs can be run from either the Workbench or the Shell. From the Workbench, double-click on the MEmacs icon in the Tools window of the Extras disk. If you have a hard disk, the Tools drawer is in your Workbench window.&lt;br /&gt;
&lt;br /&gt;
From the Shell, enter:&lt;br /&gt;
&lt;br /&gt;
 MEmacs &amp;lt;filename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;lt;filename&amp;gt; specifies the file to read into MEmacs. If a file with that name does not previously exist, a new file is created when you save your work.&lt;br /&gt;
&lt;br /&gt;
== MEmacs Commands ==&lt;br /&gt;
&lt;br /&gt;
The line at the bottom of the MEmacs screen identifies either the current file name or the name of the current buffer if no file name is specified. Figure 4-5 illustrates the MEmacs opening screen .&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-5.png|none|frame|MEmacs Opening Screen]]&lt;br /&gt;
&lt;br /&gt;
Several buffers can be in use at the same time and one or more can be displayed on the screen simultaneously. Menu options switch between them. At all times, the screen displays what is actually in the buffer.&lt;br /&gt;
&lt;br /&gt;
MEMacs has two conditions of operation:&lt;br /&gt;
&lt;br /&gt;
; Normal&lt;br /&gt;
: When you enter and manipulate text directly in the file without special functions.&lt;br /&gt;
&lt;br /&gt;
; Command&lt;br /&gt;
: When you enter a command through a menu selection or the keyboard shortcut for it. In the command condition, the cursor jumps to the bottom line of the display and waits for you to supply additional information following the prompt. You cannot return to the normal condition until you satisfy or cancel the command by pressing Return.&lt;br /&gt;
&lt;br /&gt;
In the MEmacs normal condition, you can:&lt;br /&gt;
&lt;br /&gt;
* Move the cursor using the arrow keys.&lt;br /&gt;
* Move the cursor to the edge of the window by holding down Shift and pressing the appropriate arrow key.&lt;br /&gt;
* Move the cursor by clicking the left mouse button at the desired place on the screen.&lt;br /&gt;
* Insert characters at the current cursor position by typing them.&lt;br /&gt;
* Delete the character at the current cursor position by pressing Del.&lt;br /&gt;
* Delete the character to the left of the cursor by pressing Backspace.&lt;br /&gt;
* Perform other special menu and command functions.&lt;br /&gt;
&lt;br /&gt;
When using MEmacs, you should be familiar with the following special terms:&lt;br /&gt;
&lt;br /&gt;
; Buffer&lt;br /&gt;
: A memory area that MEmacs controls. There is always at least one buffer used by MEmacs containing zero or more text characters.&lt;br /&gt;
&lt;br /&gt;
; Dot&lt;br /&gt;
: The current cursor position.&lt;br /&gt;
&lt;br /&gt;
; Mark&lt;br /&gt;
: A specified cursor position. (Each buffer has its own dot and mark.) The Set-mark menu item marks the current cursor position (described on page 4-33). You can move forward or backward in the file, adding or deleting text. To return to the marked place, select the Swap-dot&amp;amp;mark menu item (described on page 4-36).&lt;br /&gt;
&lt;br /&gt;
: You can also set a mark to indicate the beginning of a block of text that you want to duplicate, move, or delete. The block encompasses all the characters starting with the mark and continuing to the current cursor position.&lt;br /&gt;
&lt;br /&gt;
; Kill&lt;br /&gt;
: Kill commands remove text from the screen to save in a kill buffer. This text can be retrieved and inserted into your document by using the Yank command. Issuing successive Kill commands (without selecting Yank in between) adds each block of the text to the existing text in the kill buffer. If you select Yank, the next block of killed text otherwise the current block.&lt;br /&gt;
&lt;br /&gt;
; Window&lt;br /&gt;
: MEmacs screens can be split into multiple layers for editing and displaying more than one buffer or two or more portions of the same buffer. Each layer is a MEmacs window.&lt;br /&gt;
&lt;br /&gt;
; Modified Buffers&lt;br /&gt;
: Buffers are marked as modified when any changes are made. The modified status is removed when the buffer is saved.&lt;br /&gt;
&lt;br /&gt;
: To see modified buffers, use the List-buffers command (described on page 4-33); modified buffers are identified with an asterisk (*). If you exit MEmacs without saving any changes, a prompt tells you that modified buffers exist and asks if you really want to quit.&lt;br /&gt;
&lt;br /&gt;
== Menu Commands ==&lt;br /&gt;
&lt;br /&gt;
MEmacs has the following menus:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Project || Contains system and file-oriented items.&lt;br /&gt;
|-&lt;br /&gt;
| Edit || Contains buffer editing commands.&lt;br /&gt;
|-&lt;br /&gt;
| Window || Controls the characteristics of the MEmacs windows.&lt;br /&gt;
|-&lt;br /&gt;
| Move || Controls the placement of the cursor.&lt;br /&gt;
|-&lt;br /&gt;
| Line || Controls line-oriented operations.&lt;br /&gt;
|-&lt;br /&gt;
| Word || Controls word-oriented operations.&lt;br /&gt;
|-&lt;br /&gt;
| Search || Controls search and search/replace options.&lt;br /&gt;
|-&lt;br /&gt;
| Extras || Controls the numerical value of arguments and lets you execute a series of operations as though it were a single special command.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Figure 4-6 illustrates the MEmacs expanded menu bar.&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig4-6.png|none|frame|Expanded Menus]]&lt;br /&gt;
&lt;br /&gt;
=== Project Menu ===&lt;br /&gt;
&lt;br /&gt;
The commands in the Project menu, except for Visit-file, affect the buffer associated with the current cursor position.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Rename || Ctrl+X,F || Changes the name of the file associated with the current buffer. Pressing Return without specifying a file name disassociates the buffer from any file name.&lt;br /&gt;
|-&lt;br /&gt;
| Read-file || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+R || Replaces the contents of the current buffer with the contents of a file. Enter a complete file path. Press Return without specifying a file name to ignore the request and return to normal mode.&lt;br /&gt;
|-&lt;br /&gt;
| Visit-file || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+V || Allows you to work with additional files other than the one you are currently editing. Enter the complete file path.&lt;br /&gt;
|-&lt;br /&gt;
| Insert-file || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+I || Insert the contents of a file into the current buffer at a point one line above the current cursor position. Enter the complete file path.&lt;br /&gt;
|-&lt;br /&gt;
| Save-file || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+S || Writes the contents of the current buffer to the file name associated with that buffer. Issues the file&#039;s line count following a successful save. MEmacs does not save the file if no name is provided; it displays this error message: No file name.&lt;br /&gt;
|-&lt;br /&gt;
| Save-as-file || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+W || Allows you to specify the name and path of a file associated with a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Save-mod || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+M || Writes the contents of all modified buffers to the disk. Do not accidentally modify a buffer that you did not intend to change.&lt;br /&gt;
|-&lt;br /&gt;
| Save-exit || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+F || Saves all modified buffers and exits MEmacs.&lt;br /&gt;
|-&lt;br /&gt;
| New-Cli || Ctrl+- || Opens a new Shell window known as a Spawn Window. Enter AmigaDOS commands in the Spawn Window without interfering with MEmacs. Close the window with ENDSHELL.&lt;br /&gt;
|-&lt;br /&gt;
| Cli-Command || Ctrl+X,! || Lets you execute an AmigaDOS command while in MEmacs. Enter a command following the ! prompt at the bottom of the screen. Command output is placed in the spawn.output buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Quit || Ctrl+C || Exits MEmacs. You are given an opportunity to save modified buffers or quit without saving. Alternative keyboard shortcuts: Ctrl+X,Ctrl+C Esc,Ctrl+C.&lt;br /&gt;
|-&lt;br /&gt;
| About... || || Gives program copyright information&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Edit Menu ===&lt;br /&gt;
&lt;br /&gt;
The commands in the Edit menu affect the editing of your buffers and their associated files.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Kill-region || Ctrl+W || Deletes block of text from the current buffer and saves them in a kill buffer. Text can be retrieved with the Yank command. Make copies of a block by immediately selecting Yank without changing the cursor position after killing the block. This restores the block to its position and leaves a copy in the kill buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Yank || Ctrl+Y || Copies the contents of the kill buffer to the current cursor location on the current line. Reverses the action of Kill-region without changing the contents of the kill buffer. Used with Kill-region for moving text or for repeatedly copying a single block of text.&lt;br /&gt;
|-&lt;br /&gt;
| Set-mark || Ctrl+@ || Marks the cursor position in a buffer. The subsequent cursor position is referred to as a dot. Move between the mark and the dot using the Swap-dot&amp;amp;mark command in the Move menu. Used for marking blocks of text. Alternative keyboard shortcut: Esc,-.&lt;br /&gt;
|-&lt;br /&gt;
| Copy-region || Esc,W || Copies the contents of the marked region to the kill buffer without deleting it, replacing any previous contents.&lt;br /&gt;
|-&lt;br /&gt;
| Upper-region || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+U || Changes the text of the entire marked region to upper case.&lt;br /&gt;
|-&lt;br /&gt;
| Lower-region || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+L || Changes the text of the entire marked region to lower case.&lt;br /&gt;
|-&lt;br /&gt;
| List-buffers || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+B || Splits the current buffer&#039;s window and displays a list of the buffers MEmacs is maintaining. To redisplay the current buffer, select the One-window command or press Ctrl+X,1. The List-Buffer fields are:&amp;lt;br/&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| C || Displays an asterisk if the buffer has been modified since it was last saved to a file. (Stands for changed.)&lt;br /&gt;
|-&lt;br /&gt;
| Size || Shows how many characters are in the buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || Shows the name given to the buffer. If you read in a file, this is the name of the file without the full path.&lt;br /&gt;
|-&lt;br /&gt;
| File || Shows the full path to the file. This is the file to which MEmacs writes the buffer if you Save-file or Save-exit while the cursor is in that buffer.&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| Select-buffer || Ctrl+X,B || Allows you to select the buffer to edit in the current window. Replaces the contents of the window with the selected buffer or new buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Insert-buffer || Esc,&amp;lt;br/&amp;gt;Ctrl+Y || Insert the contents of a named buffer into the current buffer at the line above the current cursor position.&lt;br /&gt;
|-&lt;br /&gt;
| Kill-buffer || Ctrl+X,K || Deletes the contents of one or more chosen buffers, returning the memory to the memory manager to reuse. You must specify the buffer to be deleted; a buffer cannot be killed if its contents are currently displayed.&lt;br /&gt;
|-&lt;br /&gt;
| Justify-buffer || Ctrl+X,J || Removes all blank spaces and tabs from the left edge of all the lines in the current buffer. The text realigns with the current margins.&lt;br /&gt;
|-&lt;br /&gt;
| Redisplay || Ctrl+L || Redraws the screen.&lt;br /&gt;
|-&lt;br /&gt;
| Quote-char || Ctrl+Q || Allows insertion of any literal character in the text file, except the Tab key. Alternative keyboard shortcut: Ctrl+X,Q.&lt;br /&gt;
|-&lt;br /&gt;
| Indent || Ctrl+J || Moves the cursor to the next line, automatically indenting the same amount of spaces as the previous line. Alternative keyboard shortcuts: Help or Enter on the numeric keypad.&lt;br /&gt;
|-&lt;br /&gt;
| Transpose || Ctrl+T || Swaps the positions of two adjacent characters. Place the cursor on the right-most of the two characters and execute the command.&lt;br /&gt;
|-&lt;br /&gt;
| Cancel || Ctrl+G || Ends an ongoing menu command, such as query search and replace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Window Menu ===&lt;br /&gt;
&lt;br /&gt;
The Window menu controls how to view your buffers on the screen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| One-window || Ctrl+X,1 || Makes the current buffer a single, full-sized window on the MEmacs screen.&lt;br /&gt;
|-&lt;br /&gt;
| Split-window || Ctrl+X,2 || Splits the current window in half, positioning the current buffer identically in both windows. Any changes you make in either window affect the whole buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Next-window || Ctrl+X,N || Moves the cursor to the next window and makes that window available for editing.&lt;br /&gt;
|-&lt;br /&gt;
| Prev-window || Ctrl+X,P || Moves the cursor to the previous window and makes that window available for editing.&lt;br /&gt;
|-&lt;br /&gt;
| Expand-window || Ctrl+X,Z || Adds a line to the height of the current window and simultaneously subtracts a line from the height of the adjacent window.&lt;br /&gt;
|-&lt;br /&gt;
| Shrink-window || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+Z || Substracts a line from the height of the current window and adds a line to the height of the adjacent window.&lt;br /&gt;
|-&lt;br /&gt;
| Next-w-page || Esc,&amp;lt;br/&amp;gt;Ctrl+V || Displays the next page of the adjacent window. This does not make the window available for editing.&lt;br /&gt;
|-&lt;br /&gt;
| Prev-w-page || Ctrl+X,V || Displays the previous page of the adjacent window. If only one window is displayed, it displays the previous page of that window.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Move Menu ===&lt;br /&gt;
&lt;br /&gt;
These commands move the cursor rapidly through the current buffer.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Top-of-buffer || Esc,&amp;lt; || Moves the cursor to the top line of the current buffer.&lt;br /&gt;
|-&lt;br /&gt;
| End-of-buffer || Esc,&amp;gt; || Moves the cursor to the bottom line of the current buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Top-of-window || Esc,, || Moves the cursor to the top of the current window.&lt;br /&gt;
|-&lt;br /&gt;
| End-of-window || Esc,. || Moves the cursor to the bottom of the current window.&lt;br /&gt;
|-&lt;br /&gt;
| Goto-line || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+G || Moves the cursor to a specified line number. Specifying a line number larger than the total number of lines in a buffer moves the cursor to the last line of the buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Swap-dot&amp;amp;mark || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+X || Marks the current cursor position (dot) and moves the cursor to the previously marked setting.&lt;br /&gt;
|-&lt;br /&gt;
| Next-page || Ctrl+V || Moves the cursor toward the end of the buffer by one full window, less one line.&lt;br /&gt;
|-&lt;br /&gt;
| Prev-page || Esc,V || Moves the cursor towards the beginning of the buffer by one full window, less one line.&lt;br /&gt;
|-&lt;br /&gt;
| Next-word || Esc,F || Moves the cursor forward to the next non-alphabetic character, such as a space or punctuation mark, following the current word.&lt;br /&gt;
|-&lt;br /&gt;
| Previous-word || Esc,B || Moves the cursor back to the first letter of the previous word.&lt;br /&gt;
|-&lt;br /&gt;
| Scroll-up || Ctrl+Z || Moves the text up one line.&lt;br /&gt;
|-&lt;br /&gt;
| Scroll-down || Esc,Z || Moves the text down one line.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Line Menu ===&lt;br /&gt;
&lt;br /&gt;
These commands move the cursor within or between lines and perform operations involving entire lines.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Open-line || Ctrl+O || Splits the line containing the cursor, forcing the character at the current cursor position to become the first character of the following line. The cursor remains on the original line. Pressing the Del key cancels an accidental Open-line.&lt;br /&gt;
|-&lt;br /&gt;
| Kill-line || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+D || Deletes the line containing the cursor and places the text in the kill buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Kill-to-eol || Ctrl+K || Deletes the text between the current cursor position and the end of the line and places the text in the kill buffer.&lt;br /&gt;
|-&lt;br /&gt;
| Start-of-line || Ctrl+A || Moves the cursor to the first position on a line.&lt;br /&gt;
|-&lt;br /&gt;
| End-of-line || Ctrl+E || Moves the cursor to the last position on a line.&lt;br /&gt;
|-&lt;br /&gt;
| Next-line || Ctrl+N || Moves the cursor down one line.&lt;br /&gt;
|-&lt;br /&gt;
| Previous-line || Ctrl+P || Moves the cursor up one line.&lt;br /&gt;
|-&lt;br /&gt;
| Line-to-top || Esc,! || Moves the line containing the cursor to the top of the window.&lt;br /&gt;
|-&lt;br /&gt;
| Delete-blanks || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+O || Deletes blank lines, proceeding forward from the current cursor position until MEmacs reaches the next line on which there is text. Does not delete single blank lines.&lt;br /&gt;
|-&lt;br /&gt;
| Show-Line# || Ctrl+X,= || Displays information on the present cursor position.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Word Menu ===&lt;br /&gt;
&lt;br /&gt;
The Word menu contains word-associated operations.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Delete-forw ||  Esc,D || Deletes the character on which the cursor is positioned and all remaining characters to the right until the next non-alphanumeric character is found, such as a blank space, tab, or punctuation.&lt;br /&gt;
|-&lt;br /&gt;
| Delete-back || Esc,H || Delets all characters to the left of the cursor until it finds the first character of a word. The character at the cursor position is not deleted. Alternative keyboard shortcut: Esc,Del.&lt;br /&gt;
|-&lt;br /&gt;
| Upper-word || Esc,U || Changes a word to upper case, starting at the cursor position and proceeding to the last character of the word.&lt;br /&gt;
|-&lt;br /&gt;
| Lower-word || Esc,L || Changes a word to lower case, starting at the cursor position and proceeding to the last character of the word.&lt;br /&gt;
|-&lt;br /&gt;
| Cap-word || Esc,C || Changes the character at the cursor position to upper case. Also changes the characters to the right of the cursor, up to the end of the word, to lower case.&lt;br /&gt;
|-&lt;br /&gt;
| Switch-case || Esc,^ || Changes the case of a word, starting at the current cursor position and proceeding to the right until it reaches the end of the word. If a word is upper case or has mixed text, it changes upper to lowr case and vice versa.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Search Menu ===&lt;br /&gt;
&lt;br /&gt;
These commands search through the current buffer for specific text strings. The case (upper or lower) of the string is not significant in the search. However, if you are using text substitution (search and replace), the text is replaced in the case of the replacement string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Search-forward || Ctrl+S || Searches through the text starting at the current cursor position and moving forward to the end of the buffer. At the prompt, enter the character string for the search. Alternative keyboard shortcut: Ctrl+X,S.&lt;br /&gt;
|-&lt;br /&gt;
| Search-backward || Ctrl+R || Searches through the text from the current cursor position backwards to the beginning to the buffer. Alternative keyboard shortcut: Ctrl+X,R.&lt;br /&gt;
|-&lt;br /&gt;
| Search-replace || Esc,R || Searches the same as Search-forward, allowing you to replace the string with different text. At the prompt, enter the replacement string of characters.&lt;br /&gt;
|-&lt;br /&gt;
| Query-s-r || Esc,Q || Operates the same as Search-replace, except it asks for confirmation to replace each time it finds the specified string. The options are Y (yes), N (no), C (change all occurrences), and Ctrl+G (abort).&lt;br /&gt;
|-&lt;br /&gt;
| Fence-match || Esc,&amp;lt;br/&amp;gt;Ctrl+F || Finds the closest occurrence of the fence character to match the one at the current cursor position. Fence characters are parenthesis, brackets, braces, and angle brackets.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Extras Menu ===&lt;br /&gt;
&lt;br /&gt;
These commands are MEmacs operational commands and macro commands. Specific numeric arguments may be required before selecting a command; an * indicate that an argument is required. Macro commands are executed by selecting the Execute-macro menu item.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Set-arg || Ctrl+U || Lets you specify a numeric argument for a command.&lt;br /&gt;
|-&lt;br /&gt;
| Set || Esc,S || Lets you set the following MEmacs parameters:&lt;br /&gt;
 {| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 | Screen || Places the MEmacs display in a Workbench window or back onto a custom screen.&lt;br /&gt;
 |-&lt;br /&gt;
 | Interlace || Turns the interlace mode on or off.&lt;br /&gt;
 |-&lt;br /&gt;
 | Mode || Results in a second prompt &amp;quot;Mode:&amp;quot;; you can enter Cmode (for editing C programs) or Wrap (to enable automatic word-wrap when the text reaches a set cursor position). Cmode provides automatic fence matching. Use +mode or -mode to add or subtract a mode.&lt;br /&gt;
 |-&lt;br /&gt;
 | Left || Determines the left margin. Prompts for a numerical argument if not provided with the entry.&lt;br /&gt;
 |-&lt;br /&gt;
 | Right || Determines the right margin. Prompts for a numerical argument if not provided with the entry.&lt;br /&gt;
 |-&lt;br /&gt;
 | Tab || Sets the increment for tab spacing. Prompts for a numerical argument if not provided with the entry.&lt;br /&gt;
 |-&lt;br /&gt;
 | Indent || Determines how far to indent each level of nesting (used in Cmode). Prompts for a numerical argument if not provided with the entry.&lt;br /&gt;
 |-&lt;br /&gt;
 | Case || Turns case-sensitive searches on of off; default is off.&lt;br /&gt;
 |-&lt;br /&gt;
 | Backup || Turns the MEmacs backup function on or off. Your options are: ON (renames the current file &amp;lt;filename&amp;gt;.bak and saves that bakkup file to the T: directory); SAFE (this option checks to see if a file already exists for the buffer - if so, it displays an error and does not overwrite the existing file, Ctrl+X clears the display); and OFF (this is the default option - MEmacs does not perform any backup).&lt;br /&gt;
 |}&lt;br /&gt;
|-&lt;br /&gt;
| Start-macro || Ctrl+X,( || Tells MEmacs to start recording any subsequent keystrokes. Used with the Stop-macro and Execute-macro commands.&lt;br /&gt;
|-&lt;br /&gt;
| Stop-macro || Ctrl+X,) || Tells MEmacs to stop recording keystrokes.&lt;br /&gt;
|-&lt;br /&gt;
| Execute-macro || Ctrl+X,E || Repeats keystrokes that were entered between Start-macro and Stop-macro.&lt;br /&gt;
|-&lt;br /&gt;
| Set-key || Ctrl+X,&amp;lt;br/&amp;gt;Ctrl+K || Allows you to redefine all of the function keys, the Shifted function keys, the Help key, or any key on the numeric keypad as keyboard macros. You cannot use the menu shortcut of Ctrl+@ to insert the Set-mark command into any keyboard macro definitions.&lt;br /&gt;
|-&lt;br /&gt;
| Reset-keys || Esc,K || Returns any keys defined by Set-keys to their original default state.&lt;br /&gt;
|-&lt;br /&gt;
| Execute-file || Esc,E || Allows you to execute a program file within MEmacs.&lt;br /&gt;
|-&lt;br /&gt;
| Execute-line || Ctrl+[,&amp;lt;br/&amp;gt;Ctrl+[ || Sets MEmacs to command mode. At the prompt enter any menu command and its parameters. Alternate keyboard shortcut: Esc,Esc.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The following table contains the default values of the Set-key/function keys when used in macro commands.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key !! Assignment !! Key Sequence&lt;br /&gt;
|-&lt;br /&gt;
| F1 || Clone line || Ctrl+A,Ctrl+K,Ctrl+Y,Ctrl+M,Ctrl+Y&lt;br /&gt;
|-&lt;br /&gt;
| F2 || Delete line || Ctrl+X,Ctrl+D&lt;br /&gt;
|-&lt;br /&gt;
| F3 || Execute keyboard macro || Ctrl+X,E&lt;br /&gt;
|-&lt;br /&gt;
| F4 || Next screen || Ctrl+V&lt;br /&gt;
|-&lt;br /&gt;
| F5 || Previous screen || Esc,V&lt;br /&gt;
|-&lt;br /&gt;
| F6 || Split window || Ctrl+X,2&lt;br /&gt;
|-&lt;br /&gt;
| F7 || One window || Ctrl+X,1&lt;br /&gt;
|-&lt;br /&gt;
| F8 || Scroll window up || Ctrl+Z&lt;br /&gt;
|-&lt;br /&gt;
| F9 || Scroll window down || Esc,Z&lt;br /&gt;
|-&lt;br /&gt;
| F10 || Save file and exit || Ctrl+X,Ctrl+F&lt;br /&gt;
|-&lt;br /&gt;
| Help || Insert line || Ctrl+J&lt;br /&gt;
|-&lt;br /&gt;
| Enter (keypad) || Insert line || Ctrl+J&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Commands Not in Menus ==&lt;br /&gt;
&lt;br /&gt;
The following commands are only accessible through the keyboard.&lt;br /&gt;
&lt;br /&gt;
Keys are bound when they can be used to perform a function. For example, any key or key sequence that can be used as a shortcut for a menu item is bound to that menu item.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Describe Key || Esc,&amp;lt;br/&amp;gt;Ctrl+D || Tells you if any functions are bound to a key or key sequence. At the prompt enter the specific key or key sequence.&lt;br /&gt;
|-&lt;br /&gt;
| Bind Key || Esc,&amp;lt;br/&amp;gt;Ctrl+B || Allows you to bind a key to a function. At the prompt, enter the key or key sequence.&lt;br /&gt;
|-&lt;br /&gt;
| Unbind Key || Esc,&amp;lt;br/&amp;gt;Ctrl+U || Allows you to return a bound key to an unbound state. At the prompt, enter the key or key sequence. Standard bound keys cannot be unbound.&lt;br /&gt;
|-&lt;br /&gt;
| Echo || Esc,&amp;lt;br/&amp;gt;Ctrl+E || Displays the string entered in the command line.&lt;br /&gt;
|-&lt;br /&gt;
| Move to Edge of Window || Shift+Arrow || Moves the cursor to the top, bottom, left, or right edge of the screen.&lt;br /&gt;
|-&lt;br /&gt;
| Delete the Next Character || Ctrl+D || Deletes the character at the current position. Same as pressing Del.&lt;br /&gt;
|-&lt;br /&gt;
| Delete the Previous Character || Ctrl+H || Deletes the character to the left of the current cursor position. Same as pressing Backspace.&lt;br /&gt;
|-&lt;br /&gt;
| Move to Next Line || Ctrl+M || Inserts a newline character after the current cursor position and moves the cursor to the start of the new line.&lt;br /&gt;
|-&lt;br /&gt;
| Move x number of Characters || Ctrl+F,&amp;lt;br/&amp;gt;Ctrl+B || Allows you to move the cursor forward or backward a specified number of spaces. Providing no value moves the cursor only one character.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MEmacs ==&lt;br /&gt;
&lt;br /&gt;
MEmacs looks for an Emacs_pro file when it is opened to see if there are any command or local files that it should automatically execute. You can customize the Emacs_pro file by adding commands to it that you use often, command sequences, or text strings. If an Emacs_pro file does not already exist, you can create one.&lt;br /&gt;
&lt;br /&gt;
To create a global file of commands, place the Emacs_pro file in the S: directory. Local files can be out in any directory. If that directory is the current directory when MEmacs is opened, the commands in that particular local file are executed.&lt;br /&gt;
&lt;br /&gt;
When both local and global Emacs_pro files are present, the local file overrides the global file.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
 Set Case On&lt;br /&gt;
 Set-Key F11 &amp;quot;Dear Sirs:&amp;quot;&lt;br /&gt;
 Set-Key F12 &amp;quot;^S Workbench&amp;quot;&lt;br /&gt;
 Set-Key F13 &amp;quot;^X^B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
makes the following assignments:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Shift+F1 || Enter the text string &amp;quot;Dear Sirs:&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Shift+F2 || Search forward for the next occurrence of the word Workbench. (The Set Case On commands make any text searches case-sensitive.)&lt;br /&gt;
|-&lt;br /&gt;
| Shift+F3 || Display the list of buffers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You must use Ctrl+Q to enter a Ctrl+key sequence. For example, to enter the ^S character shown in the example, press Ctrl+Q, Ctrl+S.&lt;br /&gt;
&lt;br /&gt;
== Quitting MEmacs ==&lt;br /&gt;
&lt;br /&gt;
You can exit MEmacs by selecting the Quit menu item in the Project menu or by entering Ctrl+C. MEmacs lets you save any modified buffers or quit without saving.&lt;br /&gt;
&lt;br /&gt;
= EDIT =&lt;br /&gt;
&lt;br /&gt;
EDIT is a line editor designed for the automated editing of files, particularly binary files or files that are larger than available memory. You cannot create a new file with EDIT.&lt;br /&gt;
&lt;br /&gt;
EDIT processes files line by line. As EDIT moves through the input, or source file, each line is passed after alteration to a sequential output file, the destination file.&lt;br /&gt;
&lt;br /&gt;
EDIT processes the lines in files in a forward direction; however, you can move backward a limited number of lines. EDIT holds the lines in an output queue before writing them to the destination file. The size of this queue depends on the amount of memory available. You can increase the size of the queue with the OPT P and OPT W options.&lt;br /&gt;
&lt;br /&gt;
The format for EDIT is the following:&lt;br /&gt;
&lt;br /&gt;
 EDIT [FROM] &amp;lt;filename&amp;gt; [[TO] &amp;lt;filename&amp;gt;] [WITH &amp;lt;filename&amp;gt;] [VER &amp;lt;filename&amp;gt;]&lt;br /&gt;
 [OPT P &amp;lt;lines&amp;gt; | W &amp;lt;chars&amp;gt; | P&amp;lt;lines&amp;gt;W&amp;lt;chars&amp;gt;] [WIDTH &amp;lt;chars&amp;gt;] [PREVIOUS &amp;lt;lines&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
The FROM argument specifies the source file to be edited. You must specify a source file with EDIT, although the FROM keyword is optional.&lt;br /&gt;
&lt;br /&gt;
The TO argument specifies the destination file to which EDIT sends its output, including editing changes. If you omit the TO argument, EDIT uses a temporary file. This temporary file is renamed with the name of the FROM file and overwrites the FROM file when editing is complete.&lt;br /&gt;
&lt;br /&gt;
The WITH keyword specifies a file containing editing commands.&lt;br /&gt;
&lt;br /&gt;
The VER keyword specifies the file to which EDIT sends error messages and line verifications. If the VER argument is not given, EDIT uses the screen.&lt;br /&gt;
&lt;br /&gt;
Use OPT P &amp;lt;n&amp;gt; and OPT W &amp;lt;n&amp;gt; to specify the PREVIOUS and WIDTH options. However, do nit use the OPT keyword with PREVIOUS and WIDTH.&lt;br /&gt;
&lt;br /&gt;
You can use the PREVIOUS and WIDTH options to increase or decrease the amount of available memory. The PREVIOUS option sets the number of previous lines available to EDIT to the integer &amp;lt;n&amp;gt;. The WIDTH option sets the maximum number of characters allowed on a line to &amp;lt;n&amp;gt;. EDIT multiples the number of previous lines by the maximum number of characters (PREVIOUS * WIDTH) to determine the available memory. The default values are PREVIOUS 100 WIDTH 1200.&lt;br /&gt;
&lt;br /&gt;
== Starting EDIT ==&lt;br /&gt;
&lt;br /&gt;
Start EDIT through a Shell using the following command:&lt;br /&gt;
&lt;br /&gt;
 1.&amp;lt; EDIT &amp;lt;filename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;filename&amp;gt; is the name of an existing file to be edited.&lt;br /&gt;
&lt;br /&gt;
== EDIT Commands ==&lt;br /&gt;
&lt;br /&gt;
The following list provides background information about EDIT commands:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Current line || Refers to the line that EDIT is working on at any time. Every command entered refers to the current line, all text changes are made to the current line, and new lines are inserted before the current line.&lt;br /&gt;
|-&lt;br /&gt;
| Original lines || The lines of the source file. Lines retain their original number until you renumber them with the REWIND or = commands.&lt;br /&gt;
|-&lt;br /&gt;
| Non-original lines || Any lines that are inserted into the source file or original lines that are split. These are not assigned line numbers.&lt;br /&gt;
|-&lt;br /&gt;
| Line verification || When using commands that change information in a line, EDIT displays the revised line after the command is executed.&lt;br /&gt;
|-&lt;br /&gt;
| Arguments || Strings, qualified strings, numbers, and switch values used with EDIT commands.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Enter commands in one of the following three ways:&lt;br /&gt;
&lt;br /&gt;
* Enter the commands, then press Return&lt;br /&gt;
* Enter the final command argument, then press Return&lt;br /&gt;
* Enter a semicolon or closing parenthesis&lt;br /&gt;
&lt;br /&gt;
The text conventions used in the command description are the following:&lt;br /&gt;
&lt;br /&gt;
* Command names are shown in upper case, although EDIT is not case-sensitive.&lt;br /&gt;
* Angle brackets indicate that information must be substituted. For example, &amp;lt;string&amp;gt; indicates that the command takes a string argument.&lt;br /&gt;
* An &amp;lt;n&amp;gt; represents a numeric argument.&lt;br /&gt;
* Square brackets indicate that the argument is optional. For example, [&amp;lt;n&amp;gt;] indicates that the command can take an optional numeric argument.&lt;br /&gt;
* Slashes are used as delimiters for strings; use one slash between two strings.&lt;br /&gt;
* Periods are used as delimiters for file names (slashes cannot be used since they are used to separate strings).&lt;br /&gt;
&lt;br /&gt;
== Selecting the Current Line ==&lt;br /&gt;
&lt;br /&gt;
The following commands let you move through the file and select the current line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Move to a specific line number || M &amp;lt;n&amp;gt; || Specify a new current line by entering its line number, a period, and an asterisk as M&#039;s argument. Only original lines can be accessed by line number.&lt;br /&gt;
|-&lt;br /&gt;
| Move to next line in the source file || N || Move forward one line. Entering a number and N indicates the number of lines to move forard. When used as the last line of the source file, EDIT creates an extra line at the end of the file. If you are already on this extra line, using N causes an error message to be displayed.&lt;br /&gt;
|-&lt;br /&gt;
| Move to the previous line in the source file || P || Moves back one line. Entering P repeatedly moves more than one line. Entering a number + P indicates how many lines to move back. You can only move back to previous lines that have not yet been written to the destination file. The default is 100 lines, which can be changed with the PREVIOUS option.&lt;br /&gt;
|-&lt;br /&gt;
| Find || F&amp;lt;string&amp;gt; || Lets you select a current line by specifying some of its content.&lt;br /&gt;
|-&lt;br /&gt;
| Search Backward || B,F&amp;lt;string&amp;gt; || Looks backward starting from the current line through the source file for a line containing the specified string.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Editing the Current Line ==&lt;br /&gt;
&lt;br /&gt;
The following commands add new material or replace material on the current line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Insert &amp;lt;string2&amp;gt; after &amp;lt;string1&amp;gt; || A &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt; || Insert &amp;lt;string2&amp;gt; after the first occurrence of &amp;lt;string1&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Insert &amp;lt;string2&amp;gt; before &amp;lt;string1&amp;gt; || B &amp;lt;string2&amp;gt; &amp;lt;string1&amp;gt; || Insert &amp;lt;string2&amp;gt; before the first occurrence of &amp;lt;string1&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Exchange &amp;lt;string2&amp;gt; for &amp;lt;string1&amp;gt; || E &amp;lt;string2&amp;gt; &amp;lt;string1&amp;gt; || Replaces the first occurrence of &amp;lt;string1&amp;gt; with &amp;lt;string2&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inserting and Deleting Lines ==&lt;br /&gt;
&lt;br /&gt;
The following commands insert new material (non-original lines) and delete lines from the source file. You can also insert complete files into the source file.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Insert one or more lines || I [&amp;lt;n&amp;gt;] || If given alone or with a line number, inserts text before the current line.&amp;lt;br/&amp;gt;&lt;br /&gt;
If given with an asterisk, the text is inserted at the end of the file.&amp;lt;br/&amp;gt;&lt;br /&gt;
Indicate the end of the insertion by pressing Return, Z, and Return.&lt;br /&gt;
|-&lt;br /&gt;
| Delete one or more lines || D [&amp;lt;n&amp;gt;] || Deletes the current line if entered with no arguments. Deletes a specific line if entered with a line number. Deletes a set of lines if entered with a range of line numbers; do not use punctuation between the numbers. Deletes everything from the current line through the end of the source file of entered with a period and an asterisk as arguments.&lt;br /&gt;
|-&lt;br /&gt;
| Delete all lines until the specified string is found || D,F&amp;lt;string&amp;gt; || Deletes successive lines from the source file until the line containing the matching string is found. If no argument is specified, it deletes all lines until it finds the last string specified.&lt;br /&gt;
|-&lt;br /&gt;
| Delete existing lines and replace with new text || R [&amp;lt;n&amp;gt;] || Lets you delete line and then insert new ones. Entering a line number following R indicates a specific line to replace.&lt;br /&gt;
|-&lt;br /&gt;
| Change the terminator || Z &amp;lt;string&amp;gt; || Tells EDIT that it has reached the end of any new text that is being inserted. Entering a string after Z changes it from the default.&lt;br /&gt;
|-&lt;br /&gt;
| Show current information about EDIT || S,H,D || Displays saved information values, such as the last string searched for, the last command entered, and the input terminator.&lt;br /&gt;
|-&lt;br /&gt;
| Turn trailing spaces on/off || &amp;lt;nowiki&amp;gt;T,R,+|-&amp;lt;/nowiki&amp;gt; || Preserves any blanks that fall at the end of lines.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Editing Line Windows ==&lt;br /&gt;
&lt;br /&gt;
You can define subsections of the line, called line windows, on which EDIT executes all subsequent commands. In the descriptions of EDIT qualifiers, the beginning of the line always indicates the beginning of the line window.&lt;br /&gt;
&lt;br /&gt;
Whenever EDIT verifies a current line, it indicates the position of the line window by displaying a &amp;gt; character directly beneath the line. EDIT omits the pointer if the line window begins at the start of the line.&lt;br /&gt;
&lt;br /&gt;
The following commands control the position of the character pointer:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &amp;gt; || Moves the pointer one character to the right.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt; || Moves the pointer one character to the left.&lt;br /&gt;
|-&lt;br /&gt;
| PR || Resets the pointer to the start of the line.&lt;br /&gt;
|-&lt;br /&gt;
| PA &amp;lt;string&amp;gt; || Moves the pointer to the first character after the specified string.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;string&amp;gt; || Moves the pointer to the first character before the specified string.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The following commands change the character at the current pointer, then move the pointer.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| $ || Makes the character at the pointer lower case, then moves the pointer one character to the right.&lt;br /&gt;
|-&lt;br /&gt;
| % || Makes the character at the pointer upper case, then moves the pointer one character to the right.&lt;br /&gt;
|-&lt;br /&gt;
| _ || The _ (underscore) command deletes the character at the pointer, turning it into a space, then moves the pointer one character to the right.&lt;br /&gt;
|-&lt;br /&gt;
| # || Deletes the character at the pointer, then moves the rest of the line one character to the left. To delete several characters, specify a number before the #. For example, 5# deletes the next five characters in the window.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A combination of these commands can be used to edit a line character by character.&lt;br /&gt;
&lt;br /&gt;
The following commands insert and exchange text on the current line, similar to the A, B, and E commands; however, the character pointer is moved on completion.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Insert &amp;lt;string2&amp;gt; after &amp;lt;string1&amp;gt; || A,P &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt; || Inserts &amp;lt;string2&amp;gt; after the first occurrence of &amp;lt;string1&amp;gt;. The pointer is then positioned after &amp;lt;string2&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Insert &amp;lt;string2&amp;gt; before &amp;lt;string1&amp;gt; || B,P &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt; || Inserts &amp;lt;string2&amp;gt; before the first occurrence of &amp;lt;string1&amp;gt;. The pointer is then positioned after &amp;lt;string2&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Exchange &amp;lt;string1&amp;gt; with &amp;lt;string2&amp;gt; || E,P, &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt; || Replaces the first occurrence of &amp;lt;string1&amp;gt; with &amp;lt;string2&amp;gt;. The pointer is then positioned after &amp;lt;string2&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Till After || D,T,A &amp;lt;string&amp;gt; || Deletes all text from the beginning of the line or the character pointer to the end of the specified string.&lt;br /&gt;
|-&lt;br /&gt;
| Delete Till Before || D,T,B &amp;lt;string&amp;gt; || Deletes all text from the beginning of the line or the character pointer; stops before the specified string.&lt;br /&gt;
|-&lt;br /&gt;
| Delete From After || D,F,A &amp;lt;string&amp;gt; || Deletes all text starting after a specified string to the end of the line.&lt;br /&gt;
|-&lt;br /&gt;
| Delete From Before || D,F,B &amp;lt;string&amp;gt; || Deletes all text starting with the specified string to the end of the line.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Splitting and Joining Lines ==&lt;br /&gt;
&lt;br /&gt;
These commands split a line into more than one line and join together two or more successive lines.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Split line before &amp;lt;string&amp;gt; || S,B &amp;lt;string&amp;gt; || Splits the current line before the specified string. The first part of the line is sent to the output queue; the second part is made into a new non-original current line. Qualifiers can be used to restrict the context of the string.&lt;br /&gt;
|-&lt;br /&gt;
| Split line after &amp;lt;string&amp;gt; || S,A &amp;lt;string&amp;gt; || Splits the current line after the specified string. The first part of the line is sent to the output queue; the remainder of the line becomes the new current line. Qualifiers can be used to restrict the context of the string.&lt;br /&gt;
|-&lt;br /&gt;
| Join two lines || C,L [&amp;lt;string&amp;gt;] || Joins the current line with the next line of the source file. The string argument is optional; however, if a string is specified it is added to the end of the current line and that whole line is joined with the next line in the source file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Renumbering Lines ==&lt;br /&gt;
&lt;br /&gt;
These commands renumber the source file&#039;s lines to include non-original lines and to update a file that has been edited.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Renumber source lines || = &amp;lt;n&amp;gt; || Sets the current line number to &amp;lt;n&amp;gt;. All subsequent original and non-original lines below &amp;lt;n&amp;gt; are renumbered you move to them.&lt;br /&gt;
|-&lt;br /&gt;
| Return to the beginning source file || REWIND || Moves back through the source file to make line 1 the current line. EDIT scans the rest of the source file and writes the lines to the destination file. This file is closed and reopened as a new source file. Non-original lines are now recognized as original lines. Can be entered as REWI.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verifying Lines ==&lt;br /&gt;
&lt;br /&gt;
These commands describe different ways of verifying lines.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Turn Verification on/off || V + &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; - || Turns line verification on or off. If off, the lines are not displayed on the screen. To turn off, enter V -. To turn on, enter V +.&lt;br /&gt;
|-&lt;br /&gt;
| Verify the current line || ? || Verifies the current line by displaying the line number and the contents of the line.&lt;br /&gt;
|-&lt;br /&gt;
| Verify the current line with character indicators || ! || Produces two lines of verification. In the first line all non-graphic characters are replaced with the first character of their hexadecimal value In the second line, a minus sign is displayed under all positions corresponding to upper case letters and the second hexadecimal digit corresponds to non-graphic characters. All other positions contain spaces. In binary files, non-graphic characters are represented with question marks (??).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inspecting the Source File ==&lt;br /&gt;
&lt;br /&gt;
These commands advance through the source file, sending the lines it passes to the verification file, as well as to the normal output. They are known as Type commands because they display lines on the screen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Type &amp;lt;n&amp;gt; lines to the screen || T &amp;lt;n&amp;gt; || Types the specified number of lines to the screen. The first line typed is the current line. Omitting the &amp;lt;n&amp;gt; continues typing to the end of the source file. Interrupt the command with Ctrl+C.&lt;br /&gt;
|-&lt;br /&gt;
| Type the lines in the output queue || T,P || Displays the lines currently held in the output queue.&lt;br /&gt;
|-&lt;br /&gt;
| Type until EDIT has replaced all the lines in the output queue || T,N || Types from the current line forward until all the lines in the output queue are replaced. The previous contents are sent to the destination file.&lt;br /&gt;
|-&lt;br /&gt;
| Type with line numbers || T,L &amp;lt;n&amp;gt; || Similar to the T command. Types a specified number of lines, displaying the line numbers. EDIT displays + + + + for inserted or split lines since they do not have line numbers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Making Global Changes ==&lt;br /&gt;
&lt;br /&gt;
These commands start and stop global changes. Global changes take place automatically as EDIT scans the source file in forward direction. These commands automatically apply an A, B, or E command, as appropriate, to any occurrence of &amp;lt;string1&amp;gt; in a new current line. They also apply to the current line that is in effect when the command is given.&lt;br /&gt;
&lt;br /&gt;
 GA [qualifier] &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt;&lt;br /&gt;
 GB [qualifier] &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt;&lt;br /&gt;
 GE [qualifier] &amp;lt;string1&amp;gt; &amp;lt;string2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, if you want to change DF0: to DF2: throughout an entire file, enter:&lt;br /&gt;
&lt;br /&gt;
 GE /DF0:/DF2:/&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Cancel a global command || CG [&amp;lt;id number&amp;gt;] || Cancels a global command. The identification number set with a GA, GB, or GE command is output to the verification file or the serene if EDIT is interactive. If no argument is specified, all global operations are cancelled. To cancel a specific operation, enter the identification number of the CG command.&lt;br /&gt;
|-&lt;br /&gt;
| Suspend a global command || SG [&amp;lt;id number&amp;gt;] || Suspends a global command. All global operations are suspended if no argument is given. Enter the identification number to suspend a specific operation.&lt;br /&gt;
|-&lt;br /&gt;
| Enable a global command || EG [&amp;lt;id number&amp;gt;] || Resumes global operation that had been suspended with the SG command. Unless a specific identification number is provided, all global commands are resumed.&lt;br /&gt;
|-&lt;br /&gt;
| Show global commands || SHG || Displays the current global commands and their identification numbers. Also provides the number of times each global string was matched.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Changing Command, Input, and Output Files ==&lt;br /&gt;
&lt;br /&gt;
These commands change the files set up when you started EDIT from the Shell. These files are:&lt;br /&gt;
&lt;br /&gt;
* the command file - started with the WITH option.&lt;br /&gt;
* the input file - the source file specified with FROM.&lt;br /&gt;
* the output file - the destination file specified with TO.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Changing the Command File || C &amp;lt;filename&amp;gt; || Reads EDIT commands from a specified file. Delimit the file using a character other than a slash (/) since AmigaDOS uses these characters to separate file names. When all commands in the specified file are executed, the file is closed. You can then enter the commands through the keyboard.&lt;br /&gt;
|-&lt;br /&gt;
| Changing the Input File || FROM &amp;lt;filename&amp;gt; || Reads lines from another source file. EDIT does not close the original source file. Reselect the source file by entering the FROM command without an argument. For example of the FROM command, see page 4-54.&lt;br /&gt;
|-&lt;br /&gt;
| Closing a File || CF &amp;lt;filename&amp;gt; || Closes the destination file that was originally specified with the TO command. You can then open that file for input. CF can also close a new input file that is open. For examples of the CF command, see page 4-54.&lt;br /&gt;
|-&lt;br /&gt;
| Changing the Output File || TO &amp;lt;filename&amp;gt; || Specifies a different file as the destination file. The TO command writes the existing queue of output lines to the new TO file. The new TO file is used until another file is specified. Reselect the original destination file by using the TO command with no argument. The alternate output file remains open, but unused. For examples of the TO command, see page 4-54.&lt;br /&gt;
|-&lt;br /&gt;
| Stop executing the command file || Q || Stops EDIT from executing the current command file specified with the WITH keyword or with the C command. EDIT reverts to any previous command file. Using Q at the outermost level is equivalent to using the W command.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The FROM, CF, and TO commands are used as follows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command !! Action&lt;br /&gt;
|-&lt;br /&gt;
| M10 || Pass lines 1-9 in the original source file to the output queue.&lt;br /&gt;
|-&lt;br /&gt;
| FROM .XYZ. || Select the XYZ file for new input; line 10 of the original source file remains current.&lt;br /&gt;
|-&lt;br /&gt;
| M6 || Pass line 10 from the original file, then pass lines 1-5 from the XYZ file to the output queue. Line 6 of XYZ is the new current line.&lt;br /&gt;
|-&lt;br /&gt;
| FROM || Reselect the original source file.&lt;br /&gt;
|-&lt;br /&gt;
| M14 || Pass line 6 from XYZ, then lines 11-13 from the original source file to the output queue. Line 14 of the source file is the new current line.&lt;br /&gt;
|-&lt;br /&gt;
| FROM .XYZ. || Reselect file XYZ. Line 14 of the source file is still the current line.&lt;br /&gt;
|-&lt;br /&gt;
| M* || Pass line 14 of the source file and all remaining lines of file XYZ to the output queue. An extra line is added to the end of file XYZ. That line is the new current line.&lt;br /&gt;
|-&lt;br /&gt;
| FROM || Reselect the original source file. The extra line added to file XYZ is still the current line.&lt;br /&gt;
|-&lt;br /&gt;
| CF .XYZ. || Close file XYZ.&lt;br /&gt;
|-&lt;br /&gt;
| M* || Pass the remaining lines of the source file (lines 15 to the end of the file) to the output queue.&lt;br /&gt;
|-&lt;br /&gt;
| M11 || Pass lines 1-10 of the source file to the original destination file.&lt;br /&gt;
|-&lt;br /&gt;
| TO .XYZ. || Make XYZ the new output file.&lt;br /&gt;
|-&lt;br /&gt;
| M21 || Pass lines 11-20 to file XYZ.&lt;br /&gt;
|-&lt;br /&gt;
| TO M31 || Make the original destination file current, and pass lines 21 to 30 to it.&lt;br /&gt;
|-&lt;br /&gt;
| TO .XYZ. || Make XYZ the current output file.&lt;br /&gt;
|-&lt;br /&gt;
| M41 || Pass lines 31 to 40 to XYZ.&lt;br /&gt;
|-&lt;br /&gt;
| TO || Make the original destination file current.&lt;br /&gt;
|-&lt;br /&gt;
| TO .XYZ. || Send the output queue to file XYZ.&lt;br /&gt;
|-&lt;br /&gt;
| 1000N || Advance through the next 1000 lines of the source file.&lt;br /&gt;
|-&lt;br /&gt;
| TO || Select the original destination file.&lt;br /&gt;
|-&lt;br /&gt;
| CF .XYZ. || Close the XYZ file.&lt;br /&gt;
|-&lt;br /&gt;
| I2000 .XYZ. || Insert the 1000 lines from the source file that were sent to file XYZ back into the source file above line 2000.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ending EDIT ==&lt;br /&gt;
&lt;br /&gt;
These commands exit EDIT.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Exit, saving changes || W || Exits EDIT, saving all changes to the destination file specified by TO. EDIT exits after reaching the end of the source, closing all the files, and relinquishing memory. If you started EDIT without specifying a destination file, it renames the temporary destination with the same name as the original source file, which is renamed. T/Edit-backup. This backup is only available until the next time you run EDIT.&lt;br /&gt;
|-&lt;br /&gt;
| Exit, without saving changes || STOP || Stops EDIT immediately without saving any changes to the source file. Prevents EDIT from overwriting to original source file, ensuring that no changes are made to the original input information.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12578</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12578"/>
		<updated>2025-09-12T05:33:18Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12577</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12577"/>
		<updated>2025-09-12T05:32:44Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums (*)&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum (*)&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK (*)&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment (*)&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site (*)&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums (*)&lt;br /&gt;
* (*) external&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12576</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12576"/>
		<updated>2025-09-12T05:32:10Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums (*)&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum (*)&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK (*)&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment*&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site*&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums*&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12575</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12575"/>
		<updated>2025-09-12T05:31:49Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums[*]&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum[*]&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK[*]&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment*&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site*&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums*&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12574</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12574"/>
		<updated>2025-09-12T05:31:09Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums*&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum*&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK*&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment*&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site*&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums*&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;br /&gt;
&lt;br /&gt;
(*) external site&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12573</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12573"/>
		<updated>2025-09-12T05:29:20Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** https://www.amigaos.net/viewforum.php?f=25|AmigaOS Support Forums (ext)&lt;br /&gt;
** https://os4coding.net/forum|OS4Coding Forum (ext)&lt;br /&gt;
** https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30|Get the SDK (ext)&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** https://www.hyperion-entertainment.com/|Hyperion Entertainment (ext)&lt;br /&gt;
** https://www.amigaos.net/|AmigaOS Web Site (ext)&lt;br /&gt;
** http://support.amigaos.net/|AmigaOS Support Forums (ext)&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12572</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=MediaWiki:Sidebar&amp;diff=12572"/>
		<updated>2025-09-12T05:26:33Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** recentchanges-url|recentchanges&lt;br /&gt;
* Developers&lt;br /&gt;
** Autodocs:Main|Autodocs&lt;br /&gt;
** Tutorials:Main|Tutorials&lt;br /&gt;
** DeveloperDoc:Registries|Registries&lt;br /&gt;
** DeveloperDoc:Standards|Standards&lt;br /&gt;
** DeveloperDoc:Main|Reference&lt;br /&gt;
** [https://www.amigaos.net/viewforum.php?f=25 AmigaOS Support Forums]&lt;br /&gt;
** [https://os4coding.net/forum OS4Coding Forum]&lt;br /&gt;
** [https://www.hyperion-entertainment.com/index.php/downloads?view=files&amp;amp;parent=30 SDK]&lt;br /&gt;
* Users&lt;br /&gt;
** AmigaOS_Manual:_Workbench|Workbench&lt;br /&gt;
** AmigaOS_Manual:_AmigaDOS|DOS&lt;br /&gt;
** AmigaOS_Manual:_ARexx|ARexx&lt;br /&gt;
** Bars_%26_Pipes_Professional|Bars &amp;amp; Pipes Professional&lt;br /&gt;
* Links&lt;br /&gt;
** [https://www.hyperion-entertainment.com/ Hyperion Entertainment]&lt;br /&gt;
** [https://www.amigaos.net/ AmigaOS Web Site]&lt;br /&gt;
** [http://support.amigaos.net/ AmigaOS Support Forums]&lt;br /&gt;
* SEARCH&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Writing_Datatype_Classes&amp;diff=12569</id>
		<title>Writing Datatype Classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Writing_Datatype_Classes&amp;diff=12569"/>
		<updated>2025-01-26T19:46:12Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Subclassing =&lt;br /&gt;
&lt;br /&gt;
For each of the DataType categories, there is a class that handles the data. Handlers for explicit data are subclasses under the appropriate class. For example, a class that handles ILBM pictures would be a subclass of the Picture class.&lt;br /&gt;
&lt;br /&gt;
In order to fully understand the class concept used by DataTypes it helps to have a basic understanding of BOOPSI.&lt;br /&gt;
&lt;br /&gt;
A subclass must provide an OM_NEW method that converts the source data into the data format required by the superclass.  The subclass must optionally have a OM_DISPOSE method that discards any of the data constructed in the OM_NEW method. All other methods must be passed to the superclass.&lt;br /&gt;
&lt;br /&gt;
Following is a listing of a example class dispatcher for a subclass of the picture class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 Dispatch(Class *cl, Object *o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ClassBase *cb = (struct ClassBase *) cl-&amp;gt;cl_UserData;&lt;br /&gt;
    uint32 retval;&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;MethodID)&lt;br /&gt;
    {&lt;br /&gt;
        case OM_NEW:&lt;br /&gt;
            if (retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg))&lt;br /&gt;
            {&lt;br /&gt;
                /* Convert the source data to required data format */&lt;br /&gt;
                if (!GetObjectData (cb, cl, (Object *)retval,&lt;br /&gt;
                             ((struct opSet *) msg)-&amp;gt;ops_AttrList))&lt;br /&gt;
                {&lt;br /&gt;
                    /* Force disposal of the object */&lt;br /&gt;
                    IIntuition-&amp;gt;ICoerceMethod (cl, (Object *) retval, OM_DISPOSE);&lt;br /&gt;
                    retval = NULL;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* Let the superclass handle everything else */&lt;br /&gt;
        default:&lt;br /&gt;
            retval = (uint32) IIntuition-&amp;gt;IDoSuperMethodA (cl, o, msg);&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (retval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obtaining a Handle to the Source Data ==&lt;br /&gt;
&lt;br /&gt;
The first step that a class has to perform in order to convert the source data, is to get a handle on the data. This is obtained by passing the DTA_Handle tag to the superclass using the OM_GET method. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
Object *o;&lt;br /&gt;
BPTR fh;&lt;br /&gt;
&lt;br /&gt;
IDataTypes-&amp;gt;GetDTAttrs(o, DTA_Handle, &amp;amp;fh, TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the source Type is DTF_IFF, then DTA_Handle points to a struct IFFHandle that is already initialized and opened for reading, otherwise the handle points to a BPTR file handle.&lt;br /&gt;
&lt;br /&gt;
== Handling Errors ==&lt;br /&gt;
&lt;br /&gt;
Whenever an error occurs and the class is unable to continue converting the data, then it must set an appropriate error using the DOS SetIoErr() function and the &amp;lt;dos/dos.h&amp;gt; error codes or the error codes defined in &amp;lt;datatypes/datatypes.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For example, if the class is unable to allocate memory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (buf = IExec-&amp;gt;AllocVecTags(size, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
	... continue with conversion ...&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
	IDOS-&amp;gt;SetIoErr (ERROR_NO_FREE_STORE);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Data Format ==&lt;br /&gt;
&lt;br /&gt;
The following sections outline the required data format for each of the main object types.&lt;br /&gt;
&lt;br /&gt;
=== Picture Class ===&lt;br /&gt;
&lt;br /&gt;
The picture class is the superclass for any static graphic classes. The structures and tags used by this class are defined in &amp;lt;datatypes/pictureclass.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
A picture subclass must fill out a BitMapHeader structure as well as provide a Mode ID, a BitMap and a ColorMap during the OM_NEW method of the class.  There is no need to provide any other methods, as the remainder is handled by the picture class itself.&lt;br /&gt;
&lt;br /&gt;
The picture subclass needs to fill in any fields of the BitMapHeader structure that the class has data for. This will ensure that the picture data can then be saved to a file or copied to the clipboard.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct BitMapHeader *bmh;&lt;br /&gt;
&lt;br /&gt;
/* Obtain a pointer to the BitMapHeader structure from the picture class */&lt;br /&gt;
if (IDataTypes-&amp;gt;GetDTAttrs (dto, PDTA_BitMapHeader, &amp;amp;bmh, TAG_END) &amp;amp;&amp;amp; bmh)&lt;br /&gt;
{&lt;br /&gt;
  /* Fill in some fields */&lt;br /&gt;
  bmh-&amp;gt;bmh_Width  = 640;&lt;br /&gt;
  bmh-&amp;gt;bmh_Height = 200;&lt;br /&gt;
  bmh-&amp;gt;bmh_Depth  = 2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before manipulating any color information, the picture subclass must first tell the picture class how many colors it has, so that the information required for color remapping can be established.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IDataTypes-&amp;gt;SetDTAttrs (dto, PDTA_NumColors, ncolors, TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the number of colors have been established, the palette information can be filled in for the picture. The following fragment illustrates filling in the palette information.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct ColorRegister *cmap;&lt;br /&gt;
int16 n, ncolors;&lt;br /&gt;
int32 *cregs;&lt;br /&gt;
&lt;br /&gt;
/* Get a pointer to the color registers that need to be&lt;br /&gt;
 * filled in */&lt;br /&gt;
IDataTypes-&amp;gt;GetDTAttrs (dto,&lt;br /&gt;
  PDTA_ColorRegisters, &amp;amp;cmap,&lt;br /&gt;
  PDTA_CRegs, &amp;amp;cregs,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Set the color information */&lt;br /&gt;
for (n = 0; n &amp;lt; ncolors; n++)&lt;br /&gt;
{&lt;br /&gt;
  if (IDOS-&amp;gt;Read (fh, &amp;amp;rgb, QSIZE) == QSIZE)&lt;br /&gt;
  {&lt;br /&gt;
    /* Set the master color table */&lt;br /&gt;
    cmap-&amp;gt;red   = rgb.rgbRed;&lt;br /&gt;
    cmap-&amp;gt;green = rgb.rgbGreen;&lt;br /&gt;
    cmap-&amp;gt;blue  = rgb.rgbBlue;&lt;br /&gt;
    cmap++;&lt;br /&gt;
&lt;br /&gt;
    /* Set the color table used for remapping */&lt;br /&gt;
    cregs[n * 3 + 0] = rgb.rgbRed   &amp;lt;&amp;lt; 24;&lt;br /&gt;
    cregs[n * 3 + 1] = rgb.rgbGreen &amp;lt;&amp;lt; 24;&lt;br /&gt;
    cregs[n * 3 + 2] = rgb.rgbBlue  &amp;lt;&amp;lt; 24;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    /* Indicate that we encountered an error. DOS Read&lt;br /&gt;
     * will have already filled in the IoErr() value */&lt;br /&gt;
    return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The picture class must get the actual picture information in standard Amiga bitmap format. If the bitmap is allocated using the graphics AllocBitMap() function, then the picture class can dispose of the bitmap at OM_DISPOSE time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct BitMap *bm;&lt;br /&gt;
&lt;br /&gt;
if (bm = IGraphics-&amp;gt;AllocBitMap (bmh-&amp;gt;bmh_Width, bmh-&amp;gt;bmh_Height, bmh-&amp;gt;bmh_Depth, BMF_CLEAR, NULL))&lt;br /&gt;
{&lt;br /&gt;
  /* Tell the picture class about the picture data */&lt;br /&gt;
  IDataTypes-&amp;gt;SetDTAttrsA (dto, PDTA_BitMap, bm, TAG_END);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
  /* Indicate the error and that we encountered an error */&lt;br /&gt;
  IDOS-&amp;gt;SetIoErr (ERROR_NO_FREE_STORE);&lt;br /&gt;
  return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A picture subclass needs to provide the following fields to the superclass, during the OM_NEW method:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| DTA_NominalHoriz || (int32) Set to the width of the picture.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_NominalVert || (int32) Set to the height of the picture.&lt;br /&gt;
|-&lt;br /&gt;
| PDTA_ModeID || (uint32) A valid mode ID for the BitMap.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjName || (STRPTR) The name, or title, of the picture&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjAuthor || (STRPTR) The author of the picture.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjAnnotation || (STRPTR) Notes on the picture.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjCopyright || (STRPTR) Copyright notice for the picture.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjVersion || (STRPTR) Version of the picture.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If a picture subclass uses something other than AllocBitMap() to allocate the bitmap, then it must free the bitmap itself. This is done by implementing an OM_DISPOSE method for the subclass.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 Dispatch (Class *cl, Object *o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
  struct ClassBase *cb = (struct ClassBase *) cl-&amp;gt;cl_UserData;&lt;br /&gt;
  struct localData *lod;&lt;br /&gt;
  uint32 retval;&lt;br /&gt;
&lt;br /&gt;
  switch (msg-&amp;gt;MethodID)&lt;br /&gt;
  {&lt;br /&gt;
      case OM_NEW:&lt;br /&gt;
        /* ... */&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case OM_DISPOSE:&lt;br /&gt;
        /* Get a pointer to our object data */&lt;br /&gt;
        lod = INST_DATA (cl, o);&lt;br /&gt;
&lt;br /&gt;
        /* Tell the picture class that it doesn&#039;t have a&lt;br /&gt;
         * bitmap to free any more */&lt;br /&gt;
        IDataTypes-&amp;gt;SetDTAttrs (o, PDTA_BitMap, NULL, TAG_END);&lt;br /&gt;
&lt;br /&gt;
        /* Free the bitmap ourself */&lt;br /&gt;
        myfreebitmap (lod-&amp;gt;lod_BitMap);&lt;br /&gt;
&lt;br /&gt;
      /* Let the superclass handle everything else */&lt;br /&gt;
      default:&lt;br /&gt;
        retval = (uint32) IIntuition-&amp;gt;DoSuperMethodA (cl, o, msg);&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return retval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Class ===&lt;br /&gt;
&lt;br /&gt;
The sound class is the superclass for any sampled audio classes. The structures and tags used by this class are defined in&lt;br /&gt;
&amp;lt;datatypes/soundclass.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
A sound subclass needs to provide the following fields to the superclass, during the OM_NEW method:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| SDTA_Sample || (uint8 *) 8-bit sound data. The sound class will FreeVec() the sample data.&lt;br /&gt;
|-&lt;br /&gt;
| SDTA_SampleLength || (uint32) Number of 8-bit bytes in the sound data.&lt;br /&gt;
|-&lt;br /&gt;
| SDTA_Volume || (uint16) Number ranging from 0 being the quietest to 64 being the loudest.&lt;br /&gt;
|-&lt;br /&gt;
| SDTA_Period || (uint16) Amount of time to play the sound.&lt;br /&gt;
|-&lt;br /&gt;
| SDTA_Cycles || (uint16) Number of times to play the sound. 0 being an infinite loop.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjName || (STRPTR) The name, or title, of the sound&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjAuthor || (STRPTR) The author of the sound.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjAnnotation || (STRPTR) Notes on the sound.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjCopyright || (STRPTR) Copyright notice for the sound.&lt;br /&gt;
|-&lt;br /&gt;
| DTA_ObjVersion || (STRPTR) Version of the sound.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
No methods other OM_NEW are need, as the remainder is handled by the sound class itself.&lt;br /&gt;
&lt;br /&gt;
The sound subclass also needs to fill in any fields of the VoiceHeader structure that the class has data for. This will ensure that the sound data can then be saved to a file or copied to the clipboard.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct VoiceHeader *vh;&lt;br /&gt;
&lt;br /&gt;
/* Obtain a pointer to the VoiceHeader structure from the sound class */&lt;br /&gt;
if (IDataTypes-&amp;gt;GetDTAttrs (dto, SDTA_VoiceHeader, &amp;amp;vh, TAG_END) &amp;amp;&amp;amp; vh)&lt;br /&gt;
{&lt;br /&gt;
  /* Fill in some fields */&lt;br /&gt;
  vh-&amp;gt;vh_Octaves     = 1;&lt;br /&gt;
  vh-&amp;gt;vh_Compression = 0;&lt;br /&gt;
  vh-&amp;gt;vh_Volume      = 63;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a sound subclass uses something other than AllocVec() to allocate the sound data, then it must free the sample itself. This is done by implementing an OM_DISPOSE method for the subclass.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 Dispatch (Class *cl, Object *o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
  struct ClassBase *cb = (struct ClassBase *) cl-&amp;gt;cl_UserData;&lt;br /&gt;
  struct localData *lod;&lt;br /&gt;
  uint32 retval;&lt;br /&gt;
&lt;br /&gt;
  switch (msg-&amp;gt;MethodID)&lt;br /&gt;
  {&lt;br /&gt;
    case OM_NEW:&lt;br /&gt;
      /* ... */&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case OM_DISPOSE:&lt;br /&gt;
      /* Get a pointer to our object data */&lt;br /&gt;
      lod = INST_DATA (cl, o);&lt;br /&gt;
&lt;br /&gt;
      /* Tell the sound class that it doesn&#039;t have a&lt;br /&gt;
       * sample to free any more */&lt;br /&gt;
      IDataTypes-&amp;gt;SetDTAttrs (o, SDTA_Sample, NULL, TAG_END);&lt;br /&gt;
&lt;br /&gt;
      /* Free the sample ourself */&lt;br /&gt;
      IExec-&amp;gt;FreeVec(lod-&amp;gt;lod_Sample);&lt;br /&gt;
&lt;br /&gt;
      /* Let the superclass handle everything else */&lt;br /&gt;
    default:&lt;br /&gt;
      retval = (uint32) IIntuition-&amp;gt;IDoSuperMethodA (cl, o, msg);&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return retval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Text Class ===&lt;br /&gt;
&lt;br /&gt;
The text class is the superclass for any formatted or non-formatted text classes. The structures and tags used by this class are defined in &amp;lt;datatypes/textclass.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The text class provides the subclass with a buffer that contains the text data. The subclass must then provide the text class with a list of Line segments during the layout method. This Line list should only be created at gpl_Initial time, unless the TDTA_WordWrap attribute is TRUE.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Line&lt;br /&gt;
{&lt;br /&gt;
  struct MinNode ln_Link;&lt;br /&gt;
  STRPTR         ln_Text;&lt;br /&gt;
  ULONG		 ln_TextLen;&lt;br /&gt;
  UWORD		 ln_XOffset;&lt;br /&gt;
  UWORD		 ln_YOffset;&lt;br /&gt;
  UWORD		 ln_Width;&lt;br /&gt;
  UWORD		 ln_Height;&lt;br /&gt;
  UWORD		 ln_Flags;&lt;br /&gt;
  BYTE		 ln_FgPen;&lt;br /&gt;
  BYTE		 ln_BgPen;&lt;br /&gt;
  ULONG		 ln_Style;&lt;br /&gt;
  APTR		 ln_Data;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Line structure fields are as follows:&lt;br /&gt;
&lt;br /&gt;
;ln_Link&lt;br /&gt;
: MinNode used to link to the line list.&lt;br /&gt;
&lt;br /&gt;
; ln_Text&lt;br /&gt;
: Pointer to the text for this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_TextLen&lt;br /&gt;
: Number of bytes of text in this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_XOffset&lt;br /&gt;
: Left pixel offset from the left edge of the object for this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_YOffset&lt;br /&gt;
: Top pixel offset from the top edge of the object for this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_Width&lt;br /&gt;
: Width of the line segment in pixels.&lt;br /&gt;
&lt;br /&gt;
; ln_Height&lt;br /&gt;
: Height of the line segment in pixels.&lt;br /&gt;
&lt;br /&gt;
; ln_Flags&lt;br /&gt;
: Control flags for this line segment.&lt;br /&gt;
:: LNF_LF -- Used to indicate that this segment is the end of a line.&lt;br /&gt;
&lt;br /&gt;
; ln_FgPen&lt;br /&gt;
: Pen to use for the foreground (the text color) for this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_BgPen&lt;br /&gt;
: Pen to use for the background color for this line segment.&lt;br /&gt;
&lt;br /&gt;
; ln_Style&lt;br /&gt;
: Text attribute soft style to use for this line segment.&lt;br /&gt;
&lt;br /&gt;
As each Line segment is allocated it must be added to the Line list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct List *linelist;&lt;br /&gt;
struct Line *line;&lt;br /&gt;
&lt;br /&gt;
/* Get a pointer to the line list */&lt;br /&gt;
if (IDataTypes-&amp;gt;GetDTAttrs (o, TDTA_LineList, (uint32) &amp;amp;linelist, TAG_END) &amp;amp;&amp;amp; linelist)&lt;br /&gt;
{&lt;br /&gt;
  /* Create a Line segment */&lt;br /&gt;
  if (line = AllocVecTags(sizeof (struct Line), AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
  {&lt;br /&gt;
    /* Add it to the list */&lt;br /&gt;
    IExec-&amp;gt;AddTail (linelist, (struct Node *)&amp;amp;line-&amp;gt;ln_Link);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Currently, this is the hardest class to subclass, due to the number of methods that must be implemented. Following is the shell for a dispatcher and the layout method for a text subclass.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct localData&lt;br /&gt;
{&lt;br /&gt;
  VOID  *lod_Pool;&lt;br /&gt;
  uint32 lod_Flags;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint32 Dispatch (Class * cl, Object * o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ClassBase *cb = (struct ClassBase *) cl-&amp;gt;cl_UserData;&lt;br /&gt;
    struct localData *lod;&lt;br /&gt;
    struct List *linelist;&lt;br /&gt;
    uint32G retval = 0;&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;MethodID)&lt;br /&gt;
    {&lt;br /&gt;
        case OM_NEW:&lt;br /&gt;
            if (retval = IIntuition-&amp;gt;IDoSuperMethodA (cl, o, msg))&lt;br /&gt;
            {&lt;br /&gt;
                uint32 len, estlines, poolsize;&lt;br /&gt;
                BOOL success = FALSE;&lt;br /&gt;
                STRPTR buffer;&lt;br /&gt;
&lt;br /&gt;
                /* Get a pointer to the object data */&lt;br /&gt;
                lod = INST_DATA (cl, (Object *) retval);&lt;br /&gt;
&lt;br /&gt;
                /* Get the attributes that we need to determine memory pool size */&lt;br /&gt;
                IDataTypes-&amp;gt;GetDTAttrs ((Object *) retval,&lt;br /&gt;
                             TDTA_Buffer,    &amp;amp;buffer,&lt;br /&gt;
                             TDTA_BufferLen, &amp;amp;len,&lt;br /&gt;
                             TAG_END);&lt;br /&gt;
&lt;br /&gt;
                 /* Make sure we have a text buffer */&lt;br /&gt;
                 if (buffer &amp;amp;&amp;amp; len)&lt;br /&gt;
                 {&lt;br /&gt;
                     /* Estimate the pool size that we will need */&lt;br /&gt;
                     estlines = (len / 80) + 1;&lt;br /&gt;
                     estlines = (estlines &amp;gt; 200) ? 200 : estlines;&lt;br /&gt;
                     poolsize = sizeof (struct Line) * estlines;&lt;br /&gt;
&lt;br /&gt;
                     /* Create a memory pool for the line list */&lt;br /&gt;
                     lod-&amp;gt;lod_Pool = IExec-&amp;gt;AllocSysObjectTags(ASOT_MEMPOOL,&lt;br /&gt;
                         ASOPOOL_MFlags, MEMF_SHARED | MEMF_CLEAR,&lt;br /&gt;
                         ASOPOOL_Puddle, poolsize,&lt;br /&gt;
                         ASOPOOL_Threshold, poolsize,&lt;br /&gt;
                         TAG_END);&lt;br /&gt;
                     if (lod-&amp;gt;lod_Pool != NULL)&lt;br /&gt;
                         success = TRUE;&lt;br /&gt;
                     else&lt;br /&gt;
                         IDOS-&amp;gt;SetIoErr (ERROR_NO_FREE_STORE);&lt;br /&gt;
                 }&lt;br /&gt;
                 else&lt;br /&gt;
                 {&lt;br /&gt;
                     /* Indicate that something was missing that we needed */&lt;br /&gt;
                     IDOS-&amp;gt;SetIoErr (ERROR_REQUIRED_ARG_MISSING);&lt;br /&gt;
                 }&lt;br /&gt;
&lt;br /&gt;
                 if (!success)&lt;br /&gt;
                 {&lt;br /&gt;
                     IIntuition-&amp;gt;ICoerceMethod (cl, (Object *) retval, OM_DISPOSE);&lt;br /&gt;
                     retval = NULL;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
         case OM_UPDATE:&lt;br /&gt;
         case OM_SET:&lt;br /&gt;
             /* Pass the attributes to the text class and force a refresh if we need it */&lt;br /&gt;
             if ((retval = IIntuition-&amp;gt;IDoSuperMethodA (cl, o, msg)) &amp;amp;&amp;amp; (OCLASS (o) == cl))&lt;br /&gt;
             {&lt;br /&gt;
                 struct RastPort *rp;&lt;br /&gt;
&lt;br /&gt;
                 /* Get a pointer to the rastport */&lt;br /&gt;
                 if (rp = IGraphics-&amp;gt;ObtainGIRPort (((struct opSet *) msg)-&amp;gt;ops_GInfo))&lt;br /&gt;
                 {&lt;br /&gt;
                     IIntuition-&amp;gt;DoRender(o, ((struct opSet *) msg)-&amp;gt;ops_GInfo, GREDRAW_UPDATE);&lt;br /&gt;
&lt;br /&gt;
                     /* Release the temporary rastport */&lt;br /&gt;
                     IGraphics-&amp;gt;ReleaseGIRPort (rp);&lt;br /&gt;
                 }&lt;br /&gt;
                 retval = 0;&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
         case GM_LAYOUT:&lt;br /&gt;
             /* Tell everyone that we are busy doing things */&lt;br /&gt;
             notifyAttrChanges (o, ((struct gpLayout *) msg)-&amp;gt;gpl_GInfo, NULL,&lt;br /&gt;
                                GA_ID, G(o)-&amp;gt;GadgetID,&lt;br /&gt;
                                DTA_Busy,  TRUE,&lt;br /&gt;
                                TAG_END);&lt;br /&gt;
&lt;br /&gt;
             /* Let the superclass partake */&lt;br /&gt;
             retval = (uint32) IIntuition-&amp;gt;IDoSuperMethodA (cl, o, msg);&lt;br /&gt;
&lt;br /&gt;
             /* We need to do this one asynchronously */&lt;br /&gt;
             retval += IDataTypes-&amp;gt;DoAsyncLayout (o, (struct gpLayout *) msg);&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
         case DTM_PROCLAYOUT:&lt;br /&gt;
             /* Tell everyone that we are busy doing things */&lt;br /&gt;
             notifyAttrChanges (o, ((struct gpLayout *) msg)-&amp;gt;gpl_GInfo, NULL,&lt;br /&gt;
                                GA_ID, G(o)-&amp;gt;GadgetID,&lt;br /&gt;
                                DTA_Busy,  TRUE,&lt;br /&gt;
                                TAG_END);&lt;br /&gt;
&lt;br /&gt;
             /* Let the superclass partake and then fall through to our layout method */&lt;br /&gt;
             retval = (uint32) IDataTypes-&amp;gt;DoSuperMethodA (cl, o, msg);&lt;br /&gt;
&lt;br /&gt;
         case DTM_ASYNCLAYOUT:&lt;br /&gt;
             /* Layout the text */&lt;br /&gt;
             retval = layoutMethod (cb, cl, o, (struct gpLayout *) msg);&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
         case OM_DISPOSE:&lt;br /&gt;
             /* Get a pointer to our object data */&lt;br /&gt;
             lod = INST_DATA (cl, o);&lt;br /&gt;
&lt;br /&gt;
             /* Don&#039;t let the super class free the line list */&lt;br /&gt;
             if (IIntuition-&amp;gt;GetDTAttrs (o, TDTA_LineList, &amp;amp;linelist, TAG_END) &amp;amp;&amp;amp; linelist)&lt;br /&gt;
                 IExec-&amp;gt;NewList (linelist);&lt;br /&gt;
&lt;br /&gt;
             /* Delete the line pool */&lt;br /&gt;
             IExec-&amp;gt;FreeSysObject(ASOT_MEMPOOL, lod-&amp;gt;lod_Pool);&lt;br /&gt;
&lt;br /&gt;
         /* Let the superclass handle everything else */&lt;br /&gt;
         default:&lt;br /&gt;
             retval = (uint32) IIntuition-&amp;gt;DoSuperMethodA (cl, o, msg);&lt;br /&gt;
             break;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     return (retval);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 uint32 layoutMethod (struct ClassBase *cb, Class * cl, Object * o, struct gpLayout * gpl)&lt;br /&gt;
 {&lt;br /&gt;
     struct DTSpecialInfo *si = (struct DTSpecialInfo *) G (o)-&amp;gt;SpecialInfo;&lt;br /&gt;
     struct localData *lod = INST_DATA (cl, o);&lt;br /&gt;
     uint32 visible = 0, total = 0;&lt;br /&gt;
     struct RastPort trp;&lt;br /&gt;
     uint32 hunit = 1;&lt;br /&gt;
     uint32 bsig = 0;&lt;br /&gt;
&lt;br /&gt;
     /* Switches */&lt;br /&gt;
     BOOL linefeed = FALSE;&lt;br /&gt;
     BOOL newseg = FALSE;&lt;br /&gt;
     BOOL abort = FALSE;&lt;br /&gt;
&lt;br /&gt;
     /* Attributes obtained from superclass */&lt;br /&gt;
     struct TextAttr *tattr;&lt;br /&gt;
     struct TextFont *font;&lt;br /&gt;
     struct List *linelist;&lt;br /&gt;
     struct IBox *domain;&lt;br /&gt;
     uint32 wrap = FALSE;&lt;br /&gt;
     uint32 bufferlen;&lt;br /&gt;
     STRPTR buffer;&lt;br /&gt;
     STRPTR title;&lt;br /&gt;
&lt;br /&gt;
     /* Line information */&lt;br /&gt;
     uint32 num, offset, swidth;&lt;br /&gt;
     uint32 anchor, newanchor;&lt;br /&gt;
     uint32 style = FS_NORMAL;&lt;br /&gt;
     struct Line *line;&lt;br /&gt;
     uint32 yoffset = 0;&lt;br /&gt;
     uint8 fgpen = 1;&lt;br /&gt;
     uint8 bgpen = 0;&lt;br /&gt;
     uint32 tabspace;&lt;br /&gt;
     uint32 numtabs;&lt;br /&gt;
     uint32 i, j;&lt;br /&gt;
&lt;br /&gt;
     uint32 nomwidth, nomheight;&lt;br /&gt;
&lt;br /&gt;
     /* Get all the attributes that we are going to need for a successful layout */&lt;br /&gt;
     if (IDataTypes-&amp;gt;GetDTAttrs (o,&lt;br /&gt;
                          DTA_TextAttr,  &amp;amp;tattr,&lt;br /&gt;
                          DTA_TextFont,  &amp;amp;font,&lt;br /&gt;
                          DTA_Domain,    &amp;amp;domain,&lt;br /&gt;
                          DTA_ObjName,   &amp;amp;title,&lt;br /&gt;
                          TDTA_Buffer,   &amp;amp;buffer,&lt;br /&gt;
                          TDTA_BufferLen,&amp;amp;bufferlen,&lt;br /&gt;
                          TDTA_LineList, &amp;amp;linelist,&lt;br /&gt;
                          TDTA_WordWrap, &amp;amp;wrap,&lt;br /&gt;
                          TAG_END) == 8)&lt;br /&gt;
     {&lt;br /&gt;
         /* Lock the global object data so that nobody else can manipulate it */&lt;br /&gt;
         IExec-&amp;gt;ObtainSemaphore (&amp;amp;(si-&amp;gt;si_Lock));&lt;br /&gt;
&lt;br /&gt;
         /* Make sure we have a buffer */&lt;br /&gt;
         if (buffer)&lt;br /&gt;
         {&lt;br /&gt;
             /* Initialize the temporary RastPort */&lt;br /&gt;
             IGraphics-&amp;gt;InitRastPort (&amp;amp;trp);&lt;br /&gt;
             IGraphics-&amp;gt;SetFont (&amp;amp;trp, font);&lt;br /&gt;
&lt;br /&gt;
             /* Calculate the nominal size */&lt;br /&gt;
             nomheight = (ULONG) (24 * font-&amp;gt;tf_YSize);&lt;br /&gt;
             nomwidth  = (ULONG) (80 * font-&amp;gt;tf_XSize);&lt;br /&gt;
&lt;br /&gt;
             /* Calculate the tab space */&lt;br /&gt;
             tabspace = font-&amp;gt;tf_XSize * 8;&lt;br /&gt;
&lt;br /&gt;
             /* We only need to perform layout if we are doing word wrap, or this&lt;br /&gt;
              * is the initial layout call */&lt;br /&gt;
             if (wrap || gpl-&amp;gt;gpl_Initial)&lt;br /&gt;
             {&lt;br /&gt;
                 /* Delete the old line list */&lt;br /&gt;
                 while (line = (struct Line *) IExec-&amp;gt;RemHead (linelist))&lt;br /&gt;
                     IExec-&amp;gt;FreePooled (lod-&amp;gt;lod_Pool, line, sizeof (struct Line));&lt;br /&gt;
&lt;br /&gt;
                 /* Step through the text buffer */&lt;br /&gt;
                 for (i = offset = num = numtabs = 0;&lt;br /&gt;
                      (i &amp;lt;= bufferlen) &amp;amp;&amp;amp; (bsig == 0) &amp;amp;&amp;amp; !abort;&lt;br /&gt;
                      i++)&lt;br /&gt;
                 {&lt;br /&gt;
                     /* Check for end of line */&lt;br /&gt;
                     if (buffer[i]==13 &amp;amp;&amp;amp; buffer[i+1]==10)&lt;br /&gt;
                     {&lt;br /&gt;
                         newseg = linefeed = TRUE;&lt;br /&gt;
                         newanchor = i + 2;&lt;br /&gt;
                         i++;&lt;br /&gt;
                     }&lt;br /&gt;
                     /* Check for end of page */&lt;br /&gt;
                     else if (buffer[i] == 12)&lt;br /&gt;
                     {&lt;br /&gt;
                         newseg = linefeed = TRUE;&lt;br /&gt;
                         newanchor = i + 1;&lt;br /&gt;
                     }&lt;br /&gt;
                     /* Check for tab */&lt;br /&gt;
                     else if (buffer[i] == 9)&lt;br /&gt;
                     {&lt;br /&gt;
                         /* See if we need to terminate a line segment */&lt;br /&gt;
                         if ((numtabs == 0) &amp;amp;&amp;amp; num)&lt;br /&gt;
                             newseg = TRUE;&lt;br /&gt;
                         numtabs++;&lt;br /&gt;
                     }&lt;br /&gt;
                     else&lt;br /&gt;
                     {&lt;br /&gt;
                         /* See if we have any TABs that we need to finish out */&lt;br /&gt;
                         if (numtabs)&lt;br /&gt;
                         {&lt;br /&gt;
                             offset += (((offset / tabspace) + 1) * tabspace) - offset;&lt;br /&gt;
                             num = numtabs = 0;&lt;br /&gt;
                             anchor = i;&lt;br /&gt;
                         }&lt;br /&gt;
&lt;br /&gt;
                         /* Compute the width of the line. */&lt;br /&gt;
                         swidth = IGraphics-&amp;gt;TextLength (&amp;amp;trp, &amp;amp;buffer[anchor], num+1);&lt;br /&gt;
                         if (offset + swidth &amp;gt; domain-&amp;gt;Width)&lt;br /&gt;
                         {&lt;br /&gt;
                             /* Search for a whitespace character */&lt;br /&gt;
                             for (j = i; (j &amp;gt;= anchor) &amp;amp;&amp;amp; !newseg; j--)&lt;br /&gt;
                             {&lt;br /&gt;
                                 if (buffer[j] == &#039; &#039;)&lt;br /&gt;
                                 {&lt;br /&gt;
                                     num -= (i - j);&lt;br /&gt;
                                     newseg = TRUE;&lt;br /&gt;
                                     i = j + 1;&lt;br /&gt;
                                 }&lt;br /&gt;
                             }&lt;br /&gt;
&lt;br /&gt;
                             newseg = linefeed = TRUE;&lt;br /&gt;
                             newanchor = i;&lt;br /&gt;
                             i--;&lt;br /&gt;
                         }&lt;br /&gt;
                         else&lt;br /&gt;
                         {&lt;br /&gt;
                             num++;&lt;br /&gt;
                         }&lt;br /&gt;
                     }&lt;br /&gt;
&lt;br /&gt;
                     /* Time for a new text segment yet? */&lt;br /&gt;
                     if (newseg)&lt;br /&gt;
                     {&lt;br /&gt;
                         /* Allocate a new line segment from our memory pool */&lt;br /&gt;
                         if (line = IExec-&amp;gt;AllocPooled (lod-&amp;gt;lod_Pool, sizeof (struct Line)))&lt;br /&gt;
                         {&lt;br /&gt;
                             swidth = IGraphics-&amp;gt;TextLength (&amp;amp;trp, &amp;amp;buffer[anchor], num);&lt;br /&gt;
                             line-&amp;gt;ln_Text    = &amp;amp;buffer[anchor];&lt;br /&gt;
                             line-&amp;gt;ln_TextLen = num;&lt;br /&gt;
                             line-&amp;gt;ln_XOffset = offset;&lt;br /&gt;
                             line-&amp;gt;ln_YOffset = yoffset + font-&amp;gt;tf_Baseline;&lt;br /&gt;
                             line-&amp;gt;ln_Width   = swidth;&lt;br /&gt;
                             line-&amp;gt;ln_Height  = font-&amp;gt;tf_YSize;&lt;br /&gt;
                             line-&amp;gt;ln_Flags   = (linefeed) ? LNF_LF : NULL;&lt;br /&gt;
                             line-&amp;gt;ln_FgPen   = fgpen;&lt;br /&gt;
                             line-&amp;gt;ln_BgPen   = bgpen;&lt;br /&gt;
                             line-&amp;gt;ln_Style   = style;&lt;br /&gt;
                             line-&amp;gt;ln_Data    = NULL;&lt;br /&gt;
&lt;br /&gt;
                             /* Add the line to the list */&lt;br /&gt;
                             IExec-&amp;gt;AddTail (linelist, (struct Node *) line);&lt;br /&gt;
&lt;br /&gt;
                             /* Increment the line count */&lt;br /&gt;
                             if (linefeed)&lt;br /&gt;
                             {&lt;br /&gt;
                                 yoffset += font-&amp;gt;tf_YSize;&lt;br /&gt;
                                 offset = 0;&lt;br /&gt;
                                 total++;&lt;br /&gt;
                             }&lt;br /&gt;
                             else&lt;br /&gt;
                             {&lt;br /&gt;
                                 /* Increment the offset */&lt;br /&gt;
                                 offset += swidth;&lt;br /&gt;
                             }&lt;br /&gt;
                         }&lt;br /&gt;
                         else&lt;br /&gt;
                         {&lt;br /&gt;
                             abort = TRUE;&lt;br /&gt;
                         }&lt;br /&gt;
&lt;br /&gt;
                         /* Clear the variables */&lt;br /&gt;
                         newseg = linefeed = FALSE;&lt;br /&gt;
                         anchor = newanchor;&lt;br /&gt;
                         num = 0;&lt;br /&gt;
&lt;br /&gt;
                         /* Check to see if layout has been aborted */&lt;br /&gt;
                         bsig = IDOS-&amp;gt;CheckSignal (SIGBREAKF_CTRL_C);&lt;br /&gt;
                     }&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
             else&lt;br /&gt;
             {&lt;br /&gt;
                 /* No layout to perform */&lt;br /&gt;
                 total = si-&amp;gt;si_TotVert;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
         /* Compute the lines and columns type information */&lt;br /&gt;
         si-&amp;gt;si_VertUnit  = font-&amp;gt;tf_YSize;&lt;br /&gt;
         si-&amp;gt;si_VisVert   = visible = domain-&amp;gt;Height / si-&amp;gt;si_VertUnit;&lt;br /&gt;
         si-&amp;gt;si_TotVert   = total;&lt;br /&gt;
&lt;br /&gt;
         si-&amp;gt;si_HorizUnit = hunit = 1;&lt;br /&gt;
         si-&amp;gt;si_VisHoriz  = (int32) domain-&amp;gt;Width / hunit;&lt;br /&gt;
         si-&amp;gt;si_TotHoriz  = domain-&amp;gt;Width;&lt;br /&gt;
&lt;br /&gt;
         /* Release the global data lock */&lt;br /&gt;
         IExec-&amp;gt;ReleaseSemaphore (&amp;amp;si-&amp;gt;si_Lock);&lt;br /&gt;
&lt;br /&gt;
         /* Were we aborted? */&lt;br /&gt;
         if (bsig == 0)&lt;br /&gt;
         {&lt;br /&gt;
             /* Not aborted, so tell the world of our newest attributes */&lt;br /&gt;
             notifyAttrChanges (o, gpl-&amp;gt;gpl_GInfo, NULL,&lt;br /&gt;
                                GA_ID,                   G(o)-&amp;gt;GadgetID,&lt;br /&gt;
&lt;br /&gt;
                                DTA_VisibleVert,         visible,&lt;br /&gt;
                                DTA_TotalVert,           total,&lt;br /&gt;
                                DTA_NominalVert,         nomheight,&lt;br /&gt;
                                DTA_VertUnit,            font-&amp;gt;tf_YSize,&lt;br /&gt;
&lt;br /&gt;
                                DTA_VisibleHoriz,        (uint32) (domain-&amp;gt;Width / hunit),&lt;br /&gt;
                                DTA_TotalHoriz,          domain-&amp;gt;Width,&lt;br /&gt;
                                DTA_NominalHoriz,        nomwidth,&lt;br /&gt;
                                DTA_HorizUnit,           hunit,&lt;br /&gt;
&lt;br /&gt;
                                DTA_Title,               title,&lt;br /&gt;
                                DTA_Busy,                FALSE,&lt;br /&gt;
                                DTA_Sync,                TRUE,&lt;br /&gt;
                                TAG_END);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     return total;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Writing a Superclass =&lt;br /&gt;
&lt;br /&gt;
(relevant text to be provided)&lt;br /&gt;
&lt;br /&gt;
= Defining a DataType Descriptor =&lt;br /&gt;
&lt;br /&gt;
DataTypes uses a simple descriptor to determine what type of data a file contains and what class, if any, is used to handle that data.&lt;br /&gt;
&lt;br /&gt;
The DTDesc utility is used to define a DataType descriptor. The following steps describe how to use this utility to define a descriptor.&lt;br /&gt;
&lt;br /&gt;
1. Load several sample files of the type being defined. This can be done by dropping their icons into the DTDesc window or by using the &amp;quot;Extras/Load Samples...&amp;quot; menu item.&lt;br /&gt;
&lt;br /&gt;
2. The view area in the bottom right side of the DTDesc window will show the first 64 characters of the files. This area is used to define the Mask. The characters that don&#039;t match will be blotted out and only the similar characters will be shown. If more characters are shown as similar than really are, then they can easily be blotted out by rubbing over them.&lt;br /&gt;
&lt;br /&gt;
3. DataTypes can be broken out into several different categories. Use the Group menu to select the category that the DataType descriptor belongs in.&lt;br /&gt;
&lt;br /&gt;
4. The remaining fields must be filled out. Following is a table describing the fields and the information they require.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| File Type || User description of the DataType.&lt;br /&gt;
|-&lt;br /&gt;
| Base Name || The base name of the DataType. The class library name is derived from this.&lt;br /&gt;
|-&lt;br /&gt;
| Name Pattern || The name of the file can be used to indicate the DataType. AmigaDOS wildcards can be used to specify the file name.&lt;br /&gt;
|-&lt;br /&gt;
| Function || A function can be used to further define a data type. This function must be a stand-alone executable that uses the DataType Descriptor Function Interface.&lt;br /&gt;
|-&lt;br /&gt;
| Case Sensitive? || The Mask can be either case sensitive or not.&lt;br /&gt;
|-&lt;br /&gt;
| Priority || Descriptors are sorted by Type, Function, Name Pattern and Mask. The priority field allows a DataType descriptor to be assigned a different priority than a similar DataType descriptor.&lt;br /&gt;
|-&lt;br /&gt;
| Type || This is a read-only field and is used to indicate what basic type a DataType is. Types include IFF, Binary and ASCII.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
5. Once a DataType descriptor has been defined, it must be saved. Select the &amp;quot;Project/Save As...&amp;quot; menu item for the file requester used to save a DataType descriptor. The default name for a DataType descriptor is the text entered into File Type field.&lt;br /&gt;
&lt;br /&gt;
6. In order for a new DataType descriptor to be loaded, the datatypes.library must be flushed from the system. This can either be done with the [[SDK_Developer_Tools#Expunge|Expunge]] command included in the SDK. Another way that the new DataType descriptor can be loaded is by using the AddDataTypes command with the REFRESH option.&lt;br /&gt;
&lt;br /&gt;
== DataType Descriptor Function Interface ==&lt;br /&gt;
&lt;br /&gt;
Sometimes the fields within the DTDesc utility are not enough to define a DataType descriptor. In this case it is necessary to use a Function to&lt;br /&gt;
further narrow down a DataType.&lt;br /&gt;
&lt;br /&gt;
A Function is a stand-alone executable that expects the following arguments.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL retval = Function(struct DTHookContext *dthc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function must return TRUE if the data matches, FALSE if it doesn&#039;t.&lt;br /&gt;
&lt;br /&gt;
The DTHookContext structure contains the fields that are necessary for narrowing down the DataType. Following is a listing of the DTHookContext&lt;br /&gt;
structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DTHookContext&lt;br /&gt;
{&lt;br /&gt;
    struct Library        *dthc_SysBase;&lt;br /&gt;
    struct Library        *dthc_DOSBase;&lt;br /&gt;
    struct Library        *dthc_IFFParseBase;&lt;br /&gt;
    struct Library        *dthc_UtilityBase;&lt;br /&gt;
&lt;br /&gt;
    /* File context */&lt;br /&gt;
    BPTR                   dthc_Lock;          /* Lock on the file */&lt;br /&gt;
    struct FileInfoBlock  *dthc_FIB;           /* Pointer to a FileInfoBlock */&lt;br /&gt;
    BPTR                   dthc_FileHandle;    /* Pointer to the file handle (may be NULL) */&lt;br /&gt;
    struct IFFHandle      *dthc_IFF;           /* Pointer to an IFFHandle (may be NULL) */&lt;br /&gt;
    STRPTR                 dthc_Buffer;        /* Buffer */&lt;br /&gt;
    ULONG                  dthc_BufferLength;  /* Length of the buffer */&lt;br /&gt;
&lt;br /&gt;
    ULONG                  dthc_Length;       /* size of this structure */&lt;br /&gt;
    struct ExecIFace *     dthc_IExec;&lt;br /&gt;
    struct DOSIFace  *     dthc_IDOS;&lt;br /&gt;
    struct IFFParseIFace * dthc_IIFFParse;&lt;br /&gt;
    struct UtilityIFace *  dthc_IUtility;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The DTHookContext structure fields are as follows:&lt;br /&gt;
&lt;br /&gt;
; dthc_SysBase&lt;br /&gt;
; dthc_DOSBase&lt;br /&gt;
; dthc_IFFParseBase&lt;br /&gt;
; dthc_UtilityBase&lt;br /&gt;
: These are library bases that your function can utilize. It will need to open any other libraries that it needs.&lt;br /&gt;
&lt;br /&gt;
; dthc_Lock&lt;br /&gt;
: If the source data is a DOS file, then this is a lock on the file.&lt;br /&gt;
&lt;br /&gt;
; dthc_FIB&lt;br /&gt;
: If the source data is a DOS file, then this is a filled in FileInfoBlock for the file.&lt;br /&gt;
&lt;br /&gt;
; dthc_FileHandle&lt;br /&gt;
: If the source data is a DOS file, then this is the file handle for the file otherwise the handle will be NULL. The file is guaranteed to be at the beginning.&lt;br /&gt;
&lt;br /&gt;
; dthc_IFF&lt;br /&gt;
: If the source data is IFF, then this is the IFFHandle for accessing the data, otherwise the handle will be NULL. The position is guaranteed to be at the beginning of the data. The DOS file fields must not be accessed if the data is IFF.&lt;br /&gt;
&lt;br /&gt;
; dthc_Buffer&lt;br /&gt;
: This buffer contains the first dthc_BufferLength bytes of data.&lt;br /&gt;
&lt;br /&gt;
; dthc_BufferLength&lt;br /&gt;
: Indicates the number of bytes in dthc_Buffer, up to 64.&lt;br /&gt;
&lt;br /&gt;
; dthc_Length&lt;br /&gt;
: Size in bytes of struct DTHookContext&lt;br /&gt;
&lt;br /&gt;
; dthc_IExec&lt;br /&gt;
; dthc_IDOS&lt;br /&gt;
; dthc_IIFFParse&lt;br /&gt;
; dthc_IUtility&lt;br /&gt;
: These are interface pointers that your function can utilize.&lt;br /&gt;
&lt;br /&gt;
The following example shows how to write a simple DataTypes Descriptor Function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* This example must not be linked with any startup code so that&lt;br /&gt;
 * DTHook is the entry point for the executable.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL DTHook (struct DTHookContext * dthc)&lt;br /&gt;
{&lt;br /&gt;
    BOOL retval = FALSE;&lt;br /&gt;
    uint32 i;&lt;br /&gt;
    uint8 ch;&lt;br /&gt;
&lt;br /&gt;
    /* Make sure we have a buffer */&lt;br /&gt;
    if (dthc-&amp;gt;dthc_Buffer)&lt;br /&gt;
    {&lt;br /&gt;
        for (i = 0; (i &amp;lt; dthc-&amp;gt;dthc_BufferLength) &amp;amp;&amp;amp; !retval; i++)&lt;br /&gt;
        {&lt;br /&gt;
            ch = dthc-&amp;gt;dthc_Buffer[i];&lt;br /&gt;
&lt;br /&gt;
            /* Look at the data... */&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The next example shows how to write a DataTypes Descriptor that looks into an IFF file for needed chunk information.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* This example must not be linked with any startup code&lt;br /&gt;
 * so that DTHook is the entry point for the executable.&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/pictureclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BMH_SIZE (sizeof (struct BitMapHeader))&lt;br /&gt;
&lt;br /&gt;
BOOL DTHook(struct DTHookContext * dthc)&lt;br /&gt;
{&lt;br /&gt;
  struct BitMapHeader bmh;&lt;br /&gt;
  struct ContextNode *cn;&lt;br /&gt;
  struct IFFHandle *iff;&lt;br /&gt;
  BOOL retval = FALSE;&lt;br /&gt;
&lt;br /&gt;
  /* Make sure that this is an IFF data type */&lt;br /&gt;
  if (iff = dthc-&amp;gt;dthc_IFF)&lt;br /&gt;
&lt;br /&gt;
    /* Stop on the BitMapHeader (type, id) */&lt;br /&gt;
    if (dthc-&amp;gt;dthc_IIFFParse-&amp;gt;StopChunk (iff, ID_ILBM, ID_BMHD) == 0)&lt;br /&gt;
&lt;br /&gt;
      /* Scan through the IFF handle */&lt;br /&gt;
      if (dthc-&amp;gt;dthc_IIFFParse-&amp;gt;ParseIFF (iff, IFFPARSE_SCAN) == 0L)&lt;br /&gt;
&lt;br /&gt;
        /* Make sure we have a current chunk */&lt;br /&gt;
        if (cn = dthc-&amp;gt;dthc_IIFFParse-&amp;gt;CurrentChunk (iff))&lt;br /&gt;
&lt;br /&gt;
          /* Make sure the current chunk is ILBM BMHD */&lt;br /&gt;
          if ((cn-&amp;gt;cn_Type == ID_ILBM) &amp;amp;&amp;amp; (cn-&amp;gt;cn_ID == ID_BMHD))&lt;br /&gt;
&lt;br /&gt;
            /* Read the chunk data */&lt;br /&gt;
            if (dthc-&amp;gt;dthc_IIFFParse-&amp;gt;ReadChunkBytes (iff, &amp;amp;bmh, BMH_SIZE) == BMH_SIZE)&lt;br /&gt;
&lt;br /&gt;
              /* See if the depth is set to 24 */&lt;br /&gt;
              if (bmh.bmh_Depth == 24)&lt;br /&gt;
                retval = TRUE;&lt;br /&gt;
&lt;br /&gt;
  return (retval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=12568</id>
		<title>Exec Messages and Ports</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=12568"/>
		<updated>2025-01-26T19:40:03Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Messages and Ports ==&lt;br /&gt;
&lt;br /&gt;
For inter-process communication, Exec provides a consistent, high-performance mechanism of messages and ports. This mechanism is used to pass message structures of arbitrary sizes from task to task, interrupt to task, or task to software interrupt. In addition, messages are often used to coordinate operations between cooperating tasks. This section describes many of the details of using messages and ports that the casual Amiga programmer won&#039;t need. See [[Introduction_to_Exec|Introduction to Exec]] for a general introduction to using messages and ports.&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;message&#039;&#039; data structure has two parts: system linkage and message body. The system linkage is used by Exec to attach a given message to its destination. The message body contains the actual data of interest. The message body is any arbitrary data up to 64K bytes in size. The message body data can include pointers to other data blocks of any size.&lt;br /&gt;
&lt;br /&gt;
Messages are always sent to a predetermined destination &#039;&#039;port&#039;&#039;. At a port, incoming messages are queued in a first-in-first-out (FIFO) order. There are no system restrictions on the number of ports or the number of messages that may be queued to a port (other than the amount of available system memory).&lt;br /&gt;
&lt;br /&gt;
Messages are always queued by &#039;&#039;reference&#039;&#039;, i.e., by a pointer to the message. For performance reasons message copying is not performed. In essence, a message between two tasks is a temporary license for the receiving task to use a portion of the memory space of the sending task; that portion being the message itself. This means that if task A sends a message to task B, the message is still part of the task A context. Task A, however, should not access the message until it has been &#039;&#039;replied&#039;&#039;; that is, until task B has sent the message back, using the ReplyMsg() function. This technique of message exchange imposes important restrictions on message access.&lt;br /&gt;
&lt;br /&gt;
== Message Ports ==&lt;br /&gt;
&lt;br /&gt;
Message ports are rendezvous points at which messages are collected. A port may contain any number of outstanding messages from many different originators. When a message arrives at a port, the message is appended to the end of the list of messages for that port, and a pre-specified arrival action is invoked. This action may do nothing, or it may cause a predefined task signal or software interrupt (see [[Exec_Interrupts|Exec Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
Like many Exec structures, ports may be given a symbolic name. Such names are particularly useful for tasks that must rendezvous with dynamically created ports. They are also useful for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
A message port consists of a MsgPort structure as defined in the &amp;amp;lt;exec/ports.h&amp;amp;gt; and &amp;amp;lt;exec/ports.i&amp;amp;gt; include files. The C structure for a port is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct MsgPort {&lt;br /&gt;
    struct Node  mp_Node;&lt;br /&gt;
    UBYTE        mp_Flags;&lt;br /&gt;
    UBYTE        mp_SigBit;&lt;br /&gt;
    struct Task *mp_SigTask;&lt;br /&gt;
    struct List  mp_MsgList;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mp_Node&lt;br /&gt;
: is a standard Node structure. This is useful for tasks that might want to rendezvous with a particular message port by name.&lt;br /&gt;
&lt;br /&gt;
; mp_FLags&lt;br /&gt;
: are used to indicate message arrival actions. See the explanation below.&lt;br /&gt;
&lt;br /&gt;
; mp_SigBit&lt;br /&gt;
: is the signal bit &#039;&#039;number&#039;&#039; when a port is used with the task signal arrival action.&lt;br /&gt;
&lt;br /&gt;
; mp_SigTask&lt;br /&gt;
: is a pointer to the task to be signaled. If a software interrupt arrival action is specified, this is a pointer to the interrupt structure.&lt;br /&gt;
&lt;br /&gt;
; mp_MsgList&lt;br /&gt;
: is the list header for all messages queued to this port. (See [[Exec_Lists_and_Queues|Exec Lists and Queues]]).&lt;br /&gt;
&lt;br /&gt;
The mp_Flags field contains a subfield indicated by the PF_ACTION mask. This sub-field specifies the message arrival action that occurs when a port receives a new message.&lt;br /&gt;
&lt;br /&gt;
The possibilities are as follows:&lt;br /&gt;
&lt;br /&gt;
; PA_SIGNAL&lt;br /&gt;
: This flag tells Exec to signal the mp_SigTask using signal number mp_SigBit on the arrival of a new message. Every time a message is put to the port another signal will occur regardless of how many messages have been queued to the port.&lt;br /&gt;
&lt;br /&gt;
; PA_SOFTINT&lt;br /&gt;
: This flag tells Exec to Cause() a software interrupt when a message arrives at the port. In this case, the mp_SigTask field must contain a pointer to a struct Interrupt rather than a Task pointer. The software interrupt will be Caused every time a message is received.&lt;br /&gt;
&lt;br /&gt;
; PA_IGNORE&lt;br /&gt;
: This flag tells Exec to perform no operation other than queuing the message. This action is often used to stop signaling or software interrupts without disturbing the contents of the mp_SigTask field.&lt;br /&gt;
&lt;br /&gt;
It is important to realize that a port&#039;s arrival action will occur for each new message queued, and that there is not a one-to-one correspondence between messages and signals. Task signals are only single-bit flags so there is no record of how many times a particular signal occurred. There may be many messages queued and only a single task signal; sometimes however there may be a signal, but no messages. All of this has certain implications when designing code that deals with these actions. Your code should not depend on receiving a signal for every message at your port. All of this is also true for software interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message Port ===&lt;br /&gt;
&lt;br /&gt;
To create a new message port use AllocSysObject() with an object type of ASOT_PORT. If you want to make the port &#039;&#039;public&#039;&#039;, you will need to use the ASOPORT_Name tag. Don&#039;t make a port public when it is not necessary for it to be so.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, functions such as CreatePort() and CreateMsgPort() were often used. Some older applications may even have created their own static message ports by hand. Although all of the older methods still function for backwards compatibility, they should no longer be used.&lt;br /&gt;
&lt;br /&gt;
Using dynamic message ports with ASOT_PORT is the only way to ensure your applications will remain compatible and its message ports automatically freed by the system when required.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message Port ===&lt;br /&gt;
&lt;br /&gt;
Before a message port is deleted, all outstanding messages from other tasks must be returned. This is done by getting and replying to all messages at the port until message queue is empty. Of course, there is no need to reply to messages owned by the current task (the task performing the port deletion). Public ports must be removed from the system properly before deallocation. If a signal was allocated for the message port, it must also be freed. FreeSysObject() handles all of this automatically.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, message ports must be freed using the correct corresponding function such as DeletePort() or DeleteMsgPort(). The FreeSysObject() function must only be used on ports which were allocated with AllocSysObject().&lt;br /&gt;
&lt;br /&gt;
=== How to Rendezvous at a Message Port ===&lt;br /&gt;
&lt;br /&gt;
The FindPort() function provides a means of finding the address of a public port given its symbolic name. For example, FindPort(&amp;amp;quot;Griffin&amp;amp;quot;) will return either the address of the message port named &amp;quot;Griffin&amp;quot; or NULL indicating that no such public port exists. Since FindPort() does not do any arbitration over access to public ports, the usage of FindPort() must be protected with Forbid()/Permit(). Names should be unique to prevent collisions among multiple applications. It is a good idea to use your application name as a prefix for your port name. FindPort() does not arbitrate for access to the port list. The owner of a port might remove it at any time. For these reasons a Forbid()/Permit() pair is required for the use of FindPort(). The port address can no longer be regarded as being valid after Permit() unless your application knows that the port cannot go away (for example, if your application created the port).&lt;br /&gt;
&lt;br /&gt;
The following is an example of how to safely put a message to a specific port:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL)&lt;br /&gt;
        IExec-&amp;gt;PutMsg(port,message);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
    return(port ? TRUE : FALSE);      /* If FALSE, the port was not found */&lt;br /&gt;
&lt;br /&gt;
    /* Once we&#039;ve done a Permit(), the port might go away and leave us with&lt;br /&gt;
       an invalid port address. So we return just a BOOL to indicate whether&lt;br /&gt;
       the message has been sent or not. */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, a message contains both system header information and the actual message content. The system header is of the Message form defined in &amp;lt;exec/ports.h&amp;gt;. This structure is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Message {&lt;br /&gt;
    struct Node     mn_Node;&lt;br /&gt;
    struct MsgPort *mn_ReplyPort;&lt;br /&gt;
    UWORD           mn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mn_Node&lt;br /&gt;
: is a standard Node structure used for port linkage.&lt;br /&gt;
&lt;br /&gt;
; mn_ReplyPort&lt;br /&gt;
: is used to indicate a port to which this message will be returned when a reply is necessary.&lt;br /&gt;
&lt;br /&gt;
; mn_Length&lt;br /&gt;
: indicates the total length of the message, including the Message structure itself.&lt;br /&gt;
&lt;br /&gt;
This structure is always attached to the head of all messages. For example, if you want a message structure that contains the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; coordinates of a point on the screen, you could define it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    UWORD          xy_X;&lt;br /&gt;
    UWORD          xy_Y;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this structure, the mn_Length field should be set to sizeof(struct XYMessage).&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are allocated using AllocSysObject() with an object type of ASOT_MESSAGE. The ASOMSG_ReplyPort tag is set to the reply port if desired. Some messages may be sent in one direction in which case the ASOMSG_ReplyPort tag is not used. The size of the message is indicated with ASOMSG_Length and includes the size of the payload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct XYMessage xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
  ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
  ASOMSG_ReplyPort, replyPort,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are freed using FreeSysObject(). Programmers should be careful not to free a message which is still in use or queues in a message port.&lt;br /&gt;
&lt;br /&gt;
=== Putting a Message ===&lt;br /&gt;
&lt;br /&gt;
A message is delivered to a given destination port with the PutMsg() function. The message is queued to the port, and that port&#039;s arrival action is invoked. If the action specifies a task signal or a software interrupt, the originating task may temporarily lose the processor while the destination processes the message. If a reply to the message is required, the mn_ReplyPort field must be set up prior to the call to PutMsg().&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment for putting a message to a public port. A complete example is at the end of the article.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    uint16         xy_X;&lt;br /&gt;
    uint16         xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyport, *xyreplyport;&lt;br /&gt;
    struct XYMessage *msg;&lt;br /&gt;
    BOOL   foundport;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate the message we&#039;re going to send. */&lt;br /&gt;
    struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
      ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xymsg != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* The replyport we&#039;ll use to get response */&lt;br /&gt;
        if (xyreplyport = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)) {&lt;br /&gt;
&lt;br /&gt;
            xymsg-&amp;gt;xy_Msg.mn_ReplyPort = xyreplyport;&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            /* Now try to send that message to a public port named &amp;quot;xyport&amp;quot;.&lt;br /&gt;
             * If foundport eq 0, the port isn&#039;t out there.&lt;br /&gt;
             */&lt;br /&gt;
            if (foundport = SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
&lt;br /&gt;
            . . .                /* Now let&#039;s wait till the someone responds... */&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t find &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory for xymessage\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Waiting for a Message ===&lt;br /&gt;
&lt;br /&gt;
A task may go to sleep waiting for a message to arrive at one or more ports. This technique is widely used on the Amiga as a general form of event notification. For example, it is used extensively by tasks for I/O request completion.&lt;br /&gt;
&lt;br /&gt;
The MsgPort.mp_SigTask field contains the address of the task to be signaled and mp_SigBit contains a preallocated signal number (as described in [[Exec_Tasks|Exec Tasks]]).&lt;br /&gt;
&lt;br /&gt;
You can call the WaitPort() function to wait for a message to arrive at a port. This function will return the first message (it may not be the only) queued to a port. Note that your application must still call GetMsg() to remove the message from the port. If the port is empty, your task will go to sleep waiting for the first message. If the port is not empty, your task will not go to sleep. It is possible to receive a signal for a port without a message being present yet. The code processing the messages should be able to handle this. The following code illustrates WaitPort().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport == NULL)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct XYMessage *xy_msg = IExec-&amp;gt;WaitPort(xyport);  /* go to sleep until message arrives */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A more general form of waiting for a message involves the use of the Wait() function (see [[Exec_Signals|Exec Signals]]). This function waits for task event signals directly. If the signal assigned to the message port occurs, the task will awaken. Using the Wait() function is more general because you can wait for more than one signal. By combining the signal bits from each port into one mask for the Wait() function, a loop can be set up to process all messages at all ports.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example using Wait():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct XYMessage *xy_msg;&lt;br /&gt;
BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport != NULL)&lt;br /&gt;
{&lt;br /&gt;
    uint32 portsig = 1 &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;&lt;br /&gt;
    uint32 usersig = SIGBREAKF_CTRL_C;            /* User can break with CTRL-C.  */&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 signal = IExec-&amp;gt;Wait(portsig | usersig);  /* Sleep till someone signals.  */&lt;br /&gt;
&lt;br /&gt;
        if (signal &amp;amp; portsig)              /* Got a signal at the msgport. */&lt;br /&gt;
        {   .  .  .&lt;br /&gt;
        }&lt;br /&gt;
        if (signal &amp;amp; usersig)              /* Got a signal from the user.  */&lt;br /&gt;
        {&lt;br /&gt;
            ABORT = TRUE;                  /* Time to clean up.            */&lt;br /&gt;
             . . .&lt;br /&gt;
        }&lt;br /&gt;
        if (ABORT) break;&lt;br /&gt;
    }&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=WaitPort() only returns a pointer to the first message in a port. It does not actually remove the message from the port queue.|title=WaitPort() Does Not Remove A Message}}&lt;br /&gt;
&lt;br /&gt;
=== Getting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are usually removed from ports with the GetMsg() function. This function removes the next message at the head of the port queue and returns a pointer to it. If there are no messages in a port, this function returns a zero.&lt;br /&gt;
&lt;br /&gt;
The example below illustrates the use of GetMsg() to print the contents of all messages in a port:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certain messages may be more important than others. Because ports impose FIFO ordering, these important messages may get queued behind other messages regardless of their priority. If it is necessary to recognize more important messages, it is easiest to create another port for these special messages.&lt;br /&gt;
&lt;br /&gt;
=== Replying ===&lt;br /&gt;
&lt;br /&gt;
When the operations associated with receiving a new message are finished, it is usually necessary to send the message back to the originator. The receiver replies the message by returning it to the originator using the ReplyMsg() function. This is important because it notifies the originator that the message can be reused or deallocated.&lt;br /&gt;
&lt;br /&gt;
The ReplyMsg() function serves this purpose. It returns the message to the port specified in the mn_ReplyPort field of the message. If this field is zero, no reply is returned.&lt;br /&gt;
&lt;br /&gt;
The previous example can be enhanced to reply to each of its messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport)) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg(xymsg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that the reply does not occur until &#039;&#039;after&#039;&#039; the message values have been used.&lt;br /&gt;
&lt;br /&gt;
Often the operations associated with receiving a message involve returning &#039;&#039;results&#039;&#039; to the originator. Typically this is done within the message itself. The receiver places the results in fields defined (or perhaps reused) within the message body before replying the message back to the originator. Receipt of the replied message at the originator reply port indicates it is once again safe for the originator to use or change the values found within the message.&lt;br /&gt;
&lt;br /&gt;
The following are two short example tasks that communicate by sending, waiting for and replying to messages. Run these two programs together.&lt;br /&gt;
&lt;br /&gt;
==== Port1.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// port1.c - port and message example, run at the same time as port2.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xym_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
      ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 portsig = 1U &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;       /* Give user a `break&#039; signal. */&lt;br /&gt;
        uint32 usersig = SIGBREAKF_CTRL_C;&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Start port2 in another shell.  CTRL-C here when done.\n&amp;quot;);&lt;br /&gt;
        do&lt;br /&gt;
        {                                                   /* port1 will wait forever and reply   */&lt;br /&gt;
            uint32 signal = IExec-&amp;gt;Wait(portsig | usersig); /* to messages, until the user breaks. */&lt;br /&gt;
            struct XYMessage *xymsg = 0;&lt;br /&gt;
&lt;br /&gt;
                                   /* Since we only have one port that might get messages we     */&lt;br /&gt;
            if (signal &amp;amp; portsig)  /* have to reply to, it is not really necessary to test for   */&lt;br /&gt;
            {                      /* the portsignal. If there is not message at the port, xymsg */&lt;br /&gt;
&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* simply will be NULL. */&lt;br /&gt;
                {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 received: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&lt;br /&gt;
                    xymsg-&amp;gt;xy_X += 50;       /* Since we have not replied yet to the owner of    */&lt;br /&gt;
                    xymsg-&amp;gt;xy_Y += 50;       /* xymsg, we can change the data contents of xymsg. */&lt;br /&gt;
&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 replying with: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            if (signal &amp;amp; usersig)  /* The user wants to abort. */&lt;br /&gt;
            {&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* Make sure port is empty. */&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                ABORT = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while (ABORT == FALSE);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
        }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Port2.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// port2.c - port and message example, run at the same time as port1.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyreplyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyreplyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        struct XYMessage *reply = NULL;&lt;br /&gt;
&lt;br /&gt;
        struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
          ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
          ASOMSG_ReplyPort, xyreplyport,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (xymsg != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;   /* our special message information. */&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Sending to port1: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
            &lt;br /&gt;
                                                                   /* port2 will simply try to put  */&lt;br /&gt;
            if (SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))  /* one message to port1 wait for */&lt;br /&gt;
            {                                                      /*  the reply, and then exit     */&lt;br /&gt;
                IExec-&amp;gt;WaitPort(xyreplyport);&lt;br /&gt;
                if (reply = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyreplyport))&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Reply contains: x = %d y = %d\n&amp;quot;,   /* We don&#039;t ReplyMsg since   */&lt;br /&gt;
                            xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);                /* WE initiated the message. */&lt;br /&gt;
&lt;br /&gt;
                      /* Since we only use this private port for receiving replies, and we sent     */&lt;br /&gt;
                      /* only one and got one reply there is no need to cleanup. For a public port, */&lt;br /&gt;
                      /* or if you pass a pointer to the port to another process, it is a very good */&lt;br /&gt;
                      /* habit to always handle all messages at the port before you delete it.      */&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t find &#039;xyport&#039;; start port1 in a separate shell\n&amp;quot;);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory\n&amp;quot;);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyreplyport\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL) IExec-&amp;gt;PutMsg(port, message);&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
    return(port ? TRUE : FALSE); /* FALSE if the port was not found */&lt;br /&gt;
&lt;br /&gt;
         /* Once we&#039;ve done a Permit(), the port might go away and leave us with an invalid port    */&lt;br /&gt;
}        /* address. So we return just a BOOL to indicate whether the message has been sent or not. */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control inter-task communication with messages and ports. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AddPort()&lt;br /&gt;
| Add a public message port to the system list.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_PORT)&lt;br /&gt;
| Allocate and initialize a new message port.&lt;br /&gt;
|-&lt;br /&gt;
| FindPort()&lt;br /&gt;
| Find a public message port in the system list.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_PORT)&lt;br /&gt;
| Free a message port.&lt;br /&gt;
|-&lt;br /&gt;
| GetMsg()&lt;br /&gt;
| Get next message from the message port.&lt;br /&gt;
|-&lt;br /&gt;
| PutMsg()&lt;br /&gt;
| Put a message to a message port.&lt;br /&gt;
|-&lt;br /&gt;
| RemPort()&lt;br /&gt;
| Remove a message port from the system list.&lt;br /&gt;
|-&lt;br /&gt;
| ReplyMsg()&lt;br /&gt;
| Reply to a message on its reply port.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Audio_Device&amp;diff=12567</id>
		<title>Audio Device</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Audio_Device&amp;diff=12567"/>
		<updated>2025-01-26T19:37:57Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Devices|Audio]]{{CodeReview}}&lt;br /&gt;
== Audio Device ==&lt;br /&gt;
&lt;br /&gt;
The Amiga has four hardware audio channels—two of the channels produce audio output from the left audio connector, and two from the right. These channels can be used in many ways. You can combine a right and a left channel for stereo sound, use a single channel, or play a different sound through each of the channels to create four-part harmony.&lt;br /&gt;
&lt;br /&gt;
== About Amiga Audio ==&lt;br /&gt;
&lt;br /&gt;
Most personal computers that produce sound have hardware designed for one &#039;&#039;specific&#039;&#039; synthesis technique. The Amiga computer uses a very general method of digital sound synthesis that is quite similar to the method used in digital hi-fi components and state-of-the-art keyboard and drum synthesizers.&lt;br /&gt;
&lt;br /&gt;
For programs that can afford the memory, playing sampled sounds gives you a simple and very CPU-efficient method of sound synthesis. A sampled sound is a table of numbers which represents a sound digitally. When the sound is played back by the Amiga, the table is fed by a DMA channel into one of the four digital-to-analog converters in the custom chips. The digital-to-analog converter converts the samples into voltages that can be played through amplifiers and loudspeakers, reproducing the sound.&lt;br /&gt;
&lt;br /&gt;
On the Amiga you can create sound data in many other ways. For instance, you can use trigonometric functions in your programs to create the more traditional sounds—sine waves, square waves, or triangle waves—by using tables that describe their shapes. Then you can combine these waves for richer sound effects by adding the tables together. Once the data are entered, you can modify them with techniques described below. For information about the limitations of the audio hardware and suggestions for improving system efficiency and sound quality, refer to the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Some commands enable your program to co-reside with other programs using the audio device at the same time. Programs can co-reside because the audio device handles allocation of audio channels and arbitrates among programs competing for the same resources. When properly used, this allows many programs to use the audio device simultaneously.&lt;br /&gt;
&lt;br /&gt;
The audio device commands help isolate the programmer from the idiosyncrasies of the custom chip hardware and make it easier to use. But you can also produce sound on the Amiga by directly accessing the hardware registers if you temporarily lock out other users first. For certain types of sound synthesis, this is more CPU-efficient.&lt;br /&gt;
&lt;br /&gt;
=== Definitions ===&lt;br /&gt;
&lt;br /&gt;
Terms used in the following discussions may be unfamiliar. Some of the more important ones are defined below.&lt;br /&gt;
;Amplitude&lt;br /&gt;
:The height of a waveform, which corresponds to the amount of voltage or current in the electronic circuit.&lt;br /&gt;
;Amplitude modulation&lt;br /&gt;
:A means of producing special audio effects by using one channel to alter the amplitude of another.&lt;br /&gt;
;Channel&lt;br /&gt;
:One “unit” of the audio device.&lt;br /&gt;
;Cycle&lt;br /&gt;
:One repetition of a waveform.&lt;br /&gt;
;Frequency&lt;br /&gt;
:The number of times per second a cycle repeats.&lt;br /&gt;
;Frequency modulation&lt;br /&gt;
:A means of producing special audio effects by using one channel to affect the period of the waveform produced by another channel.&lt;br /&gt;
;Period&lt;br /&gt;
:The time elapsed between the output of successive sound samples, in units of system clock ticks.&lt;br /&gt;
;Precedence&lt;br /&gt;
:Priority of the user of a sound channel.&lt;br /&gt;
;Sample&lt;br /&gt;
:Unit of audio data, one of the fixed-interval points on the waveform.&lt;br /&gt;
;Waveform&lt;br /&gt;
:Graph that shows a model of how the amplitude of a sound varies over time—usually over one cycle.&lt;br /&gt;
&lt;br /&gt;
== Audio Device Commands and Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Command!!Command Operation&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_ALLOCATE&#039;&#039;&#039;||Allocate one or more of the four audio channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_FINISH&#039;&#039;&#039;||Abort the current write request on one or more of the channels. Can be done immediately or at the end of the current cycle.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_FREE&#039;&#039;&#039;||Free one or more audio channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_LOCK&#039;&#039;&#039;||Lock one or more audio channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_PERVOL&#039;&#039;&#039;||Change the period and volume for writes in progress. Can be done immediately or at the end of the cycle.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_SETPREC&#039;&#039;&#039;||Set the allocation precedence of one or more channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ADCMD_WAITCYCLE&#039;&#039;&#039;||Wait for the current write cycle to complete on a single channel. Returns at the end of the cycle or immediately if no cycle is active on the channel.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_FLUSH&#039;&#039;&#039;||Purge all write cycles and waitcycles (in-progress and queued) for one or more channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_READ&#039;&#039;&#039;||Return a pointer to the I/O block currently writing on a single channel.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_RESET&#039;&#039;&#039;||Reset one or more channels their initialized state. All active and queued requests will be aborted.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_START&#039;&#039;&#039;||Resume writes to one or more channels that were stopped.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_STOP&#039;&#039;&#039;||Stop any write cycle in progress on one or more channels.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;CMD_WRITE&#039;&#039;&#039;||Start a write cycle on a single channel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device Interface ==&lt;br /&gt;
&lt;br /&gt;
The audio device operates like the other Amiga I/O devices. To make sound, you first open the audio device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.&lt;br /&gt;
&lt;br /&gt;
Audio device commands use an extended I/O request block named IOAudio to send commands to the audio device. This is the standard IORequest block with some extra fields added at the end.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IOAudio&lt;br /&gt;
{&lt;br /&gt;
    struct IORequest ioa_Request;   /* I/O request block.  See exec/io.h.     */&lt;br /&gt;
    WORD   ioa_AllocKey;            /* Alloc. key filled in by audio device   */&lt;br /&gt;
    UBYTE  *ioa_Data;               /* Pointer to a sample or allocation mask */&lt;br /&gt;
    ULONG  ioa_Length;              /* Length of sample or allocation mask.   */&lt;br /&gt;
    UWORD  ioa_Period;              /* Sample playback speed                  */&lt;br /&gt;
    UWORD  ioa_Volume;              /* Volume of sound                        */&lt;br /&gt;
    UWORD  ioa_Cycles;              /* # of times to play sample. 0=forever.  */&lt;br /&gt;
    struct Message ioa_WriteMsg;    /* Filled in by device - usually not used */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file devices/audio.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Audio Device ===&lt;br /&gt;
&lt;br /&gt;
Before you can use the audio device, you must first open it with a call to OpenDevice(). Four primary steps are required to open the audio device:&lt;br /&gt;
&lt;br /&gt;
* Create a message port using CreatePort. Reply messages from the device must be directed to a message port.&lt;br /&gt;
* Allocate memory for an extended I/O request structure of type IOAudio using AllocMem().&lt;br /&gt;
* Fill in io_Message.mn_ReplyPort with the message port created by CreatePort().&lt;br /&gt;
* Open the audio device. Call OpenDevice(), passing IOAudio.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MsgPort  *AudioMP;          /* Define storage for port pointer */&lt;br /&gt;
struct IOAudio  *AudioIO;          /* Define storage for IORequest pointer */&lt;br /&gt;
&lt;br /&gt;
if (AudioMP = CreatePort(0,0) )&lt;br /&gt;
    {&lt;br /&gt;
    AudioIO = (struct IOAudio *)&lt;br /&gt;
               AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR);&lt;br /&gt;
    if (AudioIO)&lt;br /&gt;
        {&lt;br /&gt;
        AudioIO-&amp;amp;gt;ioa_Request.io_Message.mn_ReplyPort  = AudioMP;&lt;br /&gt;
        AudioIO-&amp;amp;gt;ioa_AllocKey                         = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    if (OpenDevice(AUDIONAME,0L,(struct IORequest *)AudioIO,0L) )&lt;br /&gt;
        printf(&amp;amp;quot;%s did not open\n&amp;amp;quot;,AUDIONAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A special feature of the OpenDevice() function with the audio device allows you to automatically allocate channels for your program to use when the device is opened. This is convenient since you &#039;&#039;must&#039;&#039; allocate one or more channels before you can produce sound.&lt;br /&gt;
&lt;br /&gt;
This is done by setting the value of ioa_AllocKey to zero, setting the value of ioa_Request.io_Message.mn_Node.ln_Pri to the appropriate precedence, setting io_Data to the address of a channel combination array, and setting ioa_Request.ioa_Length to a non-zero value (the length of the channel combination array). The audio device will attempt to allocate channels just as if you had sent the ADCMD_ALLOCATE command (see below). If the allocation fails, the OpenDevice() call will return immediately.&lt;br /&gt;
&lt;br /&gt;
If you want to allocate channels at some later time, set the value of the ioa_Request.ioa_Length field of the IOAudio block to zero when you call OpenDevice(). For more on channel allocation and the ADCMD_ALLOCATE command, see the section on “Allocation and Arbitration” below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
UBYTE chans[] = {1,2,4,8};  /* get any of the four channels */&lt;br /&gt;
&lt;br /&gt;
if (AudioIO)&lt;br /&gt;
    {&lt;br /&gt;
    AudioIO-&amp;amp;gt;ioa_Request.io_Message.mn_ReplyPort  = AudioMP;&lt;br /&gt;
    AudioIO-&amp;amp;gt;ioa_AllocKey                         = 0;&lt;br /&gt;
    AudioIO-&amp;amp;gt;ioa_Request.io_Message.mn_Node.ln_Pri= 120;&lt;br /&gt;
    AudioIO-&amp;amp;gt;ioa_Data                             = chans;&lt;br /&gt;
    AudioIO-&amp;amp;gt;ioa_Length                           = sizeof(chans);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
if (OpenDevice(AUDIONAME,0L,(struct IORequest *)AudioIO,0L) )&lt;br /&gt;
    printf(&amp;quot;%s did not open\n&amp;quot;, AUDIONAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Audio Device Command Types ===&lt;br /&gt;
&lt;br /&gt;
Commands for audio use can be divided into two categories: allocation/arbitration commands and hardware control commands.&lt;br /&gt;
&lt;br /&gt;
There are four allocation/arbitration commands. These do not actually produce any sound. Instead they manage and arbitrate the audio resources for the many tasks that may be using audio in the Amiga’s multitasking environment.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_ALLOCATE||Reserves an audio channel for your program to use.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_FREE||Frees an audio channel.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_SETPREC||Changes the precedence of a sound in progress.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_LOCK||Tells if a channel has been stolen from you.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The hardware control commands are used to set up, start, and stop sounds on the audio device:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|CMD_WRITE||The main command. Starts a sound playing.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_FINISH||Aborts a sound in progress.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_PERVOL||Changes the period (speed) and volume of a sound in progress.&lt;br /&gt;
|-&lt;br /&gt;
|CMD_FLUSH||Clears the audio channels.&lt;br /&gt;
|-&lt;br /&gt;
|CMD_RESET||Resets and initializes the audio device.&lt;br /&gt;
|-&lt;br /&gt;
|ADCMD_WAITCYCLE||Signals you when a cycle finishes.&lt;br /&gt;
|-&lt;br /&gt;
|CMD_STOP||Temporarily stops a channel from playing.&lt;br /&gt;
|-&lt;br /&gt;
|CMD_START||Restarts an audio channel that was stopped.&lt;br /&gt;
|-&lt;br /&gt;
|CMD_READ||Returns a pointer to the current IOAudio request.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Scope of Audio Commands ===&lt;br /&gt;
&lt;br /&gt;
Most audio commands can operate on multiple channels. The exceptions are ADCMD_WAITCYCLE, CMD_WRITE and CMD_READ, which can only operate on one channel at a time. You specify the channel(s) that you want to use by setting the appropriate bits in the ioa_Request.io_Unit field of the IOAudio block. If you send a command for a channel that you do not own, your command will be ignored. For more details, see the section on “Allocation and Arbitration” below.&lt;br /&gt;
&lt;br /&gt;
=== Audio and System I/O Functions ===&lt;br /&gt;
&lt;br /&gt;
==== BeginIO() ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All the commands that you can give to the audio device should be sent by calling the BeginIO() function. This differs from other Amiga devices which generally use SendIO() or DoIO(). You should not use SendIO() or DoIO() with the audio device because these functions clear some special flags used by the audio device; this might cause audio to work incorrectly under certain circumstances. To be safe, you should always use BeginIO() with the audio device.&lt;br /&gt;
&lt;br /&gt;
==== Wait() and WaitPort() ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These functions can be used to put your task to sleep while a sound plays. Wait() takes a wake-up mask as its argument. The wake-up mask is usually the mp_SigBit of a MsgPort that you have set up to get replies back from the audio device.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WaitPort()&#039;&#039;&#039; will put your task to sleep while a sound plays. The argument to WaitPort() is a pointer to a MsgPort that you have set up to get replies back from the audio device.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wait()&#039;&#039;&#039; and WaitPort() will not remove the message from the reply port. You must use GetMsg() to remove it.&lt;br /&gt;
&lt;br /&gt;
You must always use Wait() or WaitPort() to wait for I/O to finish with the audio device.&lt;br /&gt;
&lt;br /&gt;
==== AbortIO() ====&lt;br /&gt;
&lt;br /&gt;
This function can be used to cancel requests for ADCMD_ALLOCATE, ADCMD_LOCK, CMD_WRITE, or ADCMD_WAITCYCLE. When used with the audio device, AbortIO() always succeeds.&lt;br /&gt;
&lt;br /&gt;
=== Closing the Audio Device ===&lt;br /&gt;
&lt;br /&gt;
An OpenDevice() must eventually be matched by a call to CloseDevice().&lt;br /&gt;
&lt;br /&gt;
All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;AbortIO((struct IORequest *)AudioIO);  /* Abort any pending requests */&lt;br /&gt;
WaitPort(AudioMP);                     /* Wait for abort message */&lt;br /&gt;
GetMsg(AudioMP);                       /* Get abort message */&lt;br /&gt;
CloseDevice((struct IORequest *)AudioIO);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;CloseDevice()&#039;&#039;&#039; performs an ADCMD_FREE command on any channels selected by the ioa_Request.io_Unit field of the IOAudio request. This means that if you close the device with the same IOAudio block that you used to allocate your channels (or a copy of it), the channels will be automatically freed.&lt;br /&gt;
&lt;br /&gt;
If you allocated channels with multiple allocation commands, you cannot use this function to close all of them at once. Instead, you will have to issue one ADCMD_FREE command for each allocation that you made. After issuing the ADCMD_FREE commands for each of the allocations, you can call CloseDevice().&lt;br /&gt;
&lt;br /&gt;
== A Simple Audio Example ==&lt;br /&gt;
&lt;br /&gt;
The Amiga’s audio software has a complex allocation and arbitration system which is described in detail in the sections below. At this point, though, it may be helpful to look at a simple audio example:&lt;br /&gt;
&lt;br /&gt;
== Audio Allocation and Arbitration ==&lt;br /&gt;
&lt;br /&gt;
The first command you send to the audio device should always be ADCMD_ALLOCATE. You can do this when you open the device, or at a later time. You specify the channels you want in the &#039;&#039;&#039;ioa_Data&#039;&#039;&#039; field of the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block. If the allocation succeeds, the audio device will return the channels that you now own in the lower four bits of the &#039;&#039;&#039;ioa_Request.io_Unit&#039;&#039;&#039; field of your &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block. For instance, if the &#039;&#039;&#039;io_Unit&#039;&#039;&#039; field equals 5 (binary 0101) then you own channels 2 and 0. If the &#039;&#039;&#039;io_Unit&#039;&#039;&#039; field equals 15 (binary 1111) then you own all the channels.&lt;br /&gt;
&lt;br /&gt;
When you send the ADCMD_ALLOCATE command, the audio device will also return a unique allocation key in the &#039;&#039;&#039;ioa_AllocKey&#039;&#039;&#039; of the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block. You must use this allocation key for all subsequent commands that you send to the audio device. The audio device uses this unique key to identify which task issued the command. If you do not use the correct allocation key assigned to you by the audio device when you send a command, your command will be ignored.&lt;br /&gt;
&lt;br /&gt;
When you request a channel with ADCMD_ALLOCATE, you specify a precedence number from -128 to 127 in the &#039;&#039;&#039;ioa_Request.io_Message.mn_Node.ln_Pri&#039;&#039;&#039; field of the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block. If a channel you want is being used and you have specified a higher precedence than the current user, ADCMD_ALLOCATE will “steal” the channel from the other user. Later on, if your precedence is lower than that of another user who is performing an allocation, the channel may be stolen from you.&lt;br /&gt;
&lt;br /&gt;
If you set the precedence to 127 when you open the device or raise the precedence to 127 with the ADCMD_SETPREC command, no other tasks can steal a channel from you. When you have finished with a channel, you must relinquish it with the ADCMD_FREE command to make it available for other users.&lt;br /&gt;
&lt;br /&gt;
The following table shows suggested precedence values.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Suggested Precedences for Channel Allocation&lt;br /&gt;
!Predecence!!Type of Sound&lt;br /&gt;
|-&lt;br /&gt;
|127||&#039;&#039;Unstoppable&#039;&#039;. Sounds first allocated at lower precedence, then set to this highest level.&lt;br /&gt;
|-&lt;br /&gt;
|90 – 100||&#039;&#039;Emergencies&#039;&#039;. Alert, urgent situation that requires immediate action.&lt;br /&gt;
|-&lt;br /&gt;
|80 – 90||&#039;&#039;Annunciators&#039;&#039;. Attention, bell (Ctrl-G).&lt;br /&gt;
|-&lt;br /&gt;
|75||&#039;&#039;Speech&#039;&#039;. Synthesized or recorded speech (narrator.device).&lt;br /&gt;
|-&lt;br /&gt;
|50 – 70||&#039;&#039;Sonic cues&#039;&#039;. Sounds that provide information that is not provided by graphics. Only the beginning of each sound (enough to recognize it) should be at this level; the rest should be set to sound effects level.&lt;br /&gt;
|-&lt;br /&gt;
| -50 – 50||&#039;&#039;Music program&#039;&#039;. Musical notes in music-oriented program. The higher levels should be used for the attack portions of each note.&lt;br /&gt;
|-&lt;br /&gt;
| -70 – -50||&#039;&#039;Sound effects&#039;&#039;. Sounds used in conjunction with graphics. More important sounds should use higher levels.&lt;br /&gt;
|-&lt;br /&gt;
| -100 – -80||&#039;&#039;Background&#039;&#039;. Theme music and restartable background sounds.&lt;br /&gt;
|-&lt;br /&gt;
| -128||&#039;&#039;Silence&#039;&#039;. Lowest level (freeing the channel completely is preferred)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you attempt to perform a command on a channel that has been stolen from you by a higher priority task, an AUDIO_NOALLOCATION error is returned and the bit in the ioa_Request.io_Unit field corresponding to the stolen channel is cleared so you know which channel was stolen.&lt;br /&gt;
&lt;br /&gt;
If you want to be warned before a channel is stolen so that you have a chance to stop your sound gracefully, then you should use the ADCMD_LOCK command after you open the device. This command is also useful for programs which write directly to the audio hardware. For more on ADCMD_LOCK, see the section below.&lt;br /&gt;
&lt;br /&gt;
== Allocation and Arbitration Commands ==&lt;br /&gt;
&lt;br /&gt;
These commands allow the audio channels to be shared among different tasks and programs. None of these commands can be called from interrupt code.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_ALLOCATE ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This command gives your program a channel to use and should be the first command you send to the audio device. You specify the channels you want by setting a pointer to an array in the &#039;&#039;&#039;ioa_Data&#039;&#039;&#039; field of the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; structure. This array uses a value of 1 to allocate channel 0, 2 for channel 1, 4 for channel 2, and 8 for channel 3. For multiple channels, add the values together. For example, if you want to allocate all channels, use a value of 15.&lt;br /&gt;
&lt;br /&gt;
If you want a pair of stereo channels and you have no preference about which of the left and right channels the system will choose for the allocation, you can pass a pointer to an array containing 3, 5, 10, and 12. Channels 1 and 2 produce sound on the left side and channels 0 and 3 on the right side. The table below shows how this array corresponds to all the possible combinations of a right and a left channel.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Possible Channel Combinations&lt;br /&gt;
!Channel 3&lt;br /&gt;
right&lt;br /&gt;
!Channel 2&lt;br /&gt;
left&lt;br /&gt;
!Channel 1&lt;br /&gt;
left&lt;br /&gt;
!Channel 0&lt;br /&gt;
right&lt;br /&gt;
!Decimal Value &lt;br /&gt;
of&lt;br /&gt;
Allocation Mask&lt;br /&gt;
|-&lt;br /&gt;
|0||0||1||1||3&lt;br /&gt;
|-&lt;br /&gt;
|0||1||0||1||5&lt;br /&gt;
|-&lt;br /&gt;
|1||0||1||0||10&lt;br /&gt;
|-&lt;br /&gt;
|1||1||0||0||12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== How ADCMD_ALLOCATE Operates ====&lt;br /&gt;
&lt;br /&gt;
The ADCMD_ALLOCATE command tries the first combination, 3, to see if channels 0 and 1 are not being used. If they are available, the 3 is copied into the &#039;&#039;&#039;io_Unit&#039;&#039;&#039; field and you get an allocation key for these channels in the &#039;&#039;&#039;ioa_AllocKey&#039;&#039;&#039; field. You copy the key into other I/O blocks for any other commands you may want to perform on these channels.&lt;br /&gt;
&lt;br /&gt;
If channels 0 and 1 are being used, ADCMD_ALLOCATE tries the other combinations in turn. If all the combinations are in use, ADCMD_ALLOCATE checks the precedence number of the users of the channels and finds the combination that requires it to steal the channel or channels of the lowest precedence. If all the combinations require stealing a channel or channels of equal or higher precedence, the ADCMD_ALLOCATE request fails. Precedence is in the &#039;&#039;&#039;ln_Pri&#039;&#039;&#039; field of the &#039;&#039;&#039;io_Message&#039;&#039;&#039; in the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block you pass to ADCMD_ALLOCATE; it has a value from -128 to 127.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== The ADIOF_NOWAIT Flag ====&lt;br /&gt;
&lt;br /&gt;
If you need to produce a sound right now and otherwise don’t want to allocate, set the ADIOF_NOWAIT flag to 1. This will cause the command to return an IOERR_ALLOCFAILED error if it cannot allocate any of the channels. If you are producing a non-urgent sound and you can wait, set the ADIOF_NOWAIT flag to 0. Then, the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block returns only when you get the allocation. If ADIOF_NOWAIT is set to 0, the audio device will continue to retry the allocation request whenever channels are freed until it is successful. If the program decides to cancel the request, &#039;&#039;&#039;AbortIO&#039;&#039;&#039;() can be used.&lt;br /&gt;
&lt;br /&gt;
==== ADCMD_ALLOCATE Examples ====&lt;br /&gt;
&lt;br /&gt;
The following are more examples of how to tell ADCMD_ALLOCATE your channel preferences. If you want any channel, but want a right channel first, use an array containing 1, 8, 2, and 4:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|0001&lt;br /&gt;
|-&lt;br /&gt;
|1000&lt;br /&gt;
|-&lt;br /&gt;
|0010&lt;br /&gt;
|-&lt;br /&gt;
|0100&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you only want a right channel, use 1 and 8 (channels 0 and 3):&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|0001&lt;br /&gt;
|-&lt;br /&gt;
|1000&lt;br /&gt;
|}&lt;br /&gt;
If you want only a left channel, use 2 and 4 (channels 1 and 2):&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|0010&lt;br /&gt;
|-&lt;br /&gt;
|0100&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you want to allocate a channel and keep it for a sound that can be interrupted and restarted, allocate it at a certain precedence. If it is stolen, allocate it again with the ADIOF_NOWAIT flag set to 0. When the channel is relinquished, you will get it again.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== The Allocation Key ====&lt;br /&gt;
&lt;br /&gt;
If you want to perform multi-channel commands, all the channels must have the same key since the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block has only one allocation key field. The channels must all have that same key even when they were not allocated simultaneously. If you want to use a key you already have, you can pass that key in the &#039;&#039;&#039;ioa_AllocKey&#039;&#039;&#039; field and ADCMD_ALLOCATE can allocate other channels with that existing key. The ADCMD_ALLOCATE command returns a new and unique key only if you pass it a zero in the allocation key field.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_FREE ===&lt;br /&gt;
&lt;br /&gt;
ADCMD_FREE is the opposite of ADCMD_ALLOCATE. When you perform ADCMD_FREE on a channel, it does a CMD_RESET command on the hardware and “unlocks” the channel. It also checks to see if there are other pending allocation requests. You do not need to perform ADCMD_FREE on channels stolen from you. If you want channels back after they have been stolen, you must reallocate them with the same allocation key.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_SETPREC ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This command changes the precedence of an allocated channel. As an example of the use of ADCMD_SETPREC, assume that you are making the sound of a chime that takes a long time to decay. It is important that user hears the chime but not so important that he hears it decay all the way. You could lower precedence after the initial attack portion of the sound to let another program steal the channel. You can also set the precedence to maximum (127) if you do not want the channel(s) stolen from you.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_LOCK ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The ADCMD_LOCK command performs the “steal verify” function. When another application is attempting to steal a channel or channels, ADCMD_LOCK gives you a chance to clean up before the channel is stolen. You perform a ADCMD_LOCK command right after the ADCMD_ALLOCATE command. ADCMD_LOCK does not return until a higher-priority user attempts to steal the channel(s) or you perform an ADCMD_FREE command. If someone is attempting to steal, you must finish up and ADCMD_FREE the channel as quickly as possible.&lt;br /&gt;
&lt;br /&gt;
You must use ADCMD_LOCK if you want to write directly to the hardware registers instead of using the device commands. If your channel is stolen, you are not notified unless the ADCMD_LOCK command is present. This could cause problems for the task that has stolen the channel and is now using it at the same time as your task. ADCMD_LOCK sets a switch that is not cleared until you perform an ADCMD_FREE command on the channel. Canceling an ADCMD_LOCK request with &#039;&#039;&#039;AbortIO()&#039;&#039;&#039; will not free the channel.&lt;br /&gt;
&lt;br /&gt;
The following outline describes how ADCMD_LOCK works when a channel is stolen and when it is not stolen.&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;User A&#039;&#039; allocates a channel.&lt;br /&gt;
# &#039;&#039;User A&#039;&#039; locks the channel.&lt;br /&gt;
#:If &#039;&#039;User B&#039;&#039; allocates the channel with a higher precedence:&lt;br /&gt;
## &#039;&#039;User B&#039;&#039;’s ADCMD_ALLOCATE command is suspended (regardless of the setting of the ADIOF_NOWAIT flag).&lt;br /&gt;
## &#039;&#039;User A&#039;&#039;’s lock command is replied to with an error (this will be ADIOERR_CHANNELSTOLEN).&lt;br /&gt;
## &#039;&#039;User A&#039;&#039; does whatever is needed to finish up when a channel is stolen.&lt;br /&gt;
## &#039;&#039;User A&#039;&#039; frees the channel with ADCMD_FREE.&lt;br /&gt;
## &#039;&#039;User B&#039;&#039;’s ADCMD_ALLOCATE command is replied to. Now user B has the channel.&lt;br /&gt;
#:If the channel is not allocated by another user:&lt;br /&gt;
## &#039;&#039;User A&#039;&#039; finishes the sound.&lt;br /&gt;
## &#039;&#039;User A&#039;&#039; performs the ADCMD_FREE command.&lt;br /&gt;
## &#039;&#039;User A&#039;&#039;’s ADCMD_LOCK command is replied to.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Never make the freeing of a channel (if the channel is stolen) dependent on allocating another channel. This may cause a deadlock. If you want channels back after they have been stolen, you must reallocate them with the same allocation key. To keep a channel and never let it be stolen, set precedence to maximum (127). Do not use a lock for this purpose.&lt;br /&gt;
&lt;br /&gt;
== Hardware Control Commands ==&lt;br /&gt;
&lt;br /&gt;
The following commands change hardware registers and affect the actual sound output.&lt;br /&gt;
&lt;br /&gt;
=== CMD_WRITE ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is a single-channel command and is the main command for making sounds. You pass the following to CMD_WRITE:&lt;br /&gt;
&lt;br /&gt;
* A pointer to the waveform to be played (must start on a word boundary and must be in memory accessible by the custom chips, MEMF_CHIP)&lt;br /&gt;
* The length of the waveform in bytes (must be an even number)&lt;br /&gt;
* A count of how many times you want to play the waveform&lt;br /&gt;
&lt;br /&gt;
If the count is 0, CMD_WRITE will play the waveform from beginning to end, then repeat the waveform continuously until something aborts it.&lt;br /&gt;
&lt;br /&gt;
If you want period and volume to be set at the start of the sound, set the WRITE command’s ADIOF_PERVOL flag. If you do not do this, the previous volume and period for that channel will be used. This is one of the flags that is cleared by &#039;&#039;&#039;DoIO()&#039;&#039;&#039; and &#039;&#039;&#039;SendIO()&#039;&#039;&#039;. The &#039;&#039;&#039;ioa_WriteMsg&#039;&#039;&#039; field in the &#039;&#039;&#039;IOAudio&#039;&#039;&#039; block is an extra message field that can be replied to at the start of the CMD_WRITE. This second message is used only to tell you when the CMD_WRITE command &#039;&#039;starts&#039;&#039; processing, and it is used only when the ADIOF_WRITEMESSAGE flag is set to 1.&lt;br /&gt;
&lt;br /&gt;
If a CMD_STOP has been performed, the CMD_WRITE requests are queued up. The CMD_WRITE command does not make its own copy of the waveform, so any modification of the waveform before the CMD_WRITE command is finished may affect the sound. This is sometimes desirable for special effects. To splice together two waveforms without clicks or pops, you must send a separate, second CMD_WRITE command while the first is still in progress. This technique is used in double-buffering, which is described below.&lt;br /&gt;
&lt;br /&gt;
By using two waveform buffers and two CMD_WRITE requests you can compute a waveform continuously. This is called double-buffering. The following describes how you use double-buffering.&lt;br /&gt;
&lt;br /&gt;
# Compute a waveform in memory buffer A.&lt;br /&gt;
# Issue CMD_WRITE A with &#039;&#039;&#039;io_Data&#039;&#039;&#039; pointing to buffer A.&lt;br /&gt;
# Continue the waveform in memory buffer B.&lt;br /&gt;
# Issue CMD_WRITE B with &#039;&#039;&#039;io_Data&#039;&#039;&#039; pointing to Buffer B.&lt;br /&gt;
# Wait for CMD_WRITE A to finish.&lt;br /&gt;
# Continue the waveform in memory buffer A.&lt;br /&gt;
# Issue CMD_WRITE A with &#039;&#039;&#039;io_Data&#039;&#039;&#039; pointing to Buffer A.&lt;br /&gt;
# Wait for CMD_WRITE B to finish.&lt;br /&gt;
# Loop back to step 3 until the waveform is finished.&lt;br /&gt;
# At the end, remember to wait until both CMD_WRITE A and B are finished.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_FINISH ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The ADCMD_FINISH command aborts (calls &#039;&#039;&#039;AbortIO()&#039;&#039;&#039;) the current write request on a channel or channels. This is useful if you have something playing, such as a long buffer or some repetitions of a buffer, and you want to stop it.&lt;br /&gt;
&lt;br /&gt;
ADCMD_FINISH has a flag you can set (ADIOF_SYNCCYCLE) that allows the waveform to finish the current cycle before aborting it. This is useful for splicing together sounds at zero crossings or some other place in the waveform where the amplitude at the end of one waveform matches the amplitude at the beginning of the next. Zero crossings are positions within the waveform at which the amplitude is zero. Splicing at zero crossings gives you fewer clicks and pops when the audio channel is turned off or the volume is changed.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_PERVOL ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ADCMD_PERVOL lets you change the volume and period of a CMD_WRITE that is in progress. The change can take place immediately or you can set the ADIOF_SYNCCYCLE flag to have the change occur at the end of the cycle. This is useful to produce vibratos, glissandos, tremolos, and volume envelopes in music or to change the volume of a sound.&lt;br /&gt;
&lt;br /&gt;
=== CMD_FLUSH ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CMD_FLUSH aborts (calls AbortIO()) all CMD_WRITEs and all ADCMD_WAITCYCLEs that are queued up for the channel or channels. It does not abort ADCMD_LOCKs (only ADCMD_FREE clears locks).&lt;br /&gt;
&lt;br /&gt;
=== CMD_RESET ===&lt;br /&gt;
&lt;br /&gt;
CMD_RESET restores all the audio hardware registers. It clears the attach bits, restores the audio interrupt vectors if the programmer has changed them, and performs the CMD_FLUSH command to cancel all requests to the channels. CMD_RESET also unstops channels that have had a CMD_STOP performed on them. CMD_RESET does not unlock channels that have been locked by ADCMD_LOCK.&lt;br /&gt;
&lt;br /&gt;
=== ADCMD_WAITCYCLE ===&lt;br /&gt;
&lt;br /&gt;
This is a single-channel command. ADCMD_WAITCYCLE is replied to when the current cycle has completed, If there is no CMD_WRITE in progress, it returns immediately.&lt;br /&gt;
&lt;br /&gt;
=== CMD_STOP ===&lt;br /&gt;
&lt;br /&gt;
This command stops the current write cycle immediately. If there are no CMD_WRITEs in progress, it sets a flag so any future CMD_WRITEs are queued up and do not begin processing (playing).&lt;br /&gt;
&lt;br /&gt;
=== CMD_START ===&lt;br /&gt;
&lt;br /&gt;
CMD_START undoes the CMD_STOP command. Any cycles that were stopped by the CMD_STOP command are actually lost because of the impossibility of determining exactly where the DMA ceased. If the CMD_WRITE command was playing two cycles and the first one was playing when CMD_STOP was issued, the first one is lost and the second one will be played.&lt;br /&gt;
&lt;br /&gt;
This command is also useful when you are playing the same wave form with the same period out of multiple channels. If the channels are stopped when the CMD_WRITE commands are issued, CMD_START exactly synchronizes them, avoiding cancellation and distortion. When channels are allocated, they are effectively started by the CMD_START command.&lt;br /&gt;
&lt;br /&gt;
=== CMD_READ ===&lt;br /&gt;
&lt;br /&gt;
CMD_READ is a single-channel command. Its only function is to return a pointer to the current CMD_WRITE command. It enables you to determine which request is being processed.&lt;br /&gt;
&lt;br /&gt;
== Double Buffered Sound Example ==&lt;br /&gt;
&lt;br /&gt;
The program listed below demonstrates double buffering with the audio device. Run the program from the CLI. It takes one parameter—the name of an IFF 8SVX sample file to play on the Amiga’s audio device. The maximum size for a sample on the Amiga is 128K. However, by using double-buffering and queueing up requests to the audio device, you can play longer samples smoothly and without breaks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
 * Audio_8SVX.c&lt;br /&gt;
 *&lt;br /&gt;
 * 8SVX example - double buffers &amp;amp;gt;128K samples&lt;br /&gt;
 *&lt;br /&gt;
 * Compile with SAS C 5.10  lc -b1 -cfistq -v -y -L&lt;br /&gt;
 *&lt;br /&gt;
 * Run from CLI only&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/audio.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dosextens.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;iff/iff.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;iff/8svx.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/dos_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/graphics_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdlib.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void) { return(0); }     /* Disable SAS CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define VHDR MakeID(&#039;V&#039;,&#039;H&#039;,&#039;D&#039;,&#039;R&#039;)&lt;br /&gt;
#define BODY MakeID(&#039;B&#039;,&#039;O&#039;,&#039;D&#039;,&#039;Y&#039;)&lt;br /&gt;
#define MY8S MakeID(&#039;8&#039;,&#039;S&#039;,&#039;V&#039;,&#039;X&#039;)&lt;br /&gt;
&lt;br /&gt;
void              kill8svx(char *);&lt;br /&gt;
void              kill8(void);&lt;br /&gt;
&lt;br /&gt;
/*--------------------*/               /* These globals are needed */&lt;br /&gt;
/*   G L O B A L S    */               /* by the clean up routines */&lt;br /&gt;
/*--------------------*/&lt;br /&gt;
struct IOAudio     *AIOptr1,           /* Pointers to Audio IOBs      */&lt;br /&gt;
                   *AIOptr2,&lt;br /&gt;
                   *Aptr;&lt;br /&gt;
struct Message     *msg;               /* Msg, port and device for    */&lt;br /&gt;
struct MsgPort     *port,              /* driving audio               */&lt;br /&gt;
                   *port1,*port2;&lt;br /&gt;
       ULONG        device;&lt;br /&gt;
       UBYTE       *sbase,*fbase;      /* For sample memory allocation */&lt;br /&gt;
       ULONG        fsize,ssize;       /* and freeing                  */&lt;br /&gt;
&lt;br /&gt;
struct FileHandle  *v8handle;&lt;br /&gt;
       UBYTE        chan1[]  = {  1 }; /* Audio channel allocation arrays */&lt;br /&gt;
       UBYTE        chan2[]  = {  2 };&lt;br /&gt;
       UBYTE        chan3[]  = {  4 };&lt;br /&gt;
       UBYTE        chan4[]  = {  8 };&lt;br /&gt;
       UBYTE       *chans[] = {chan1,chan2,chan3,chan4};&lt;br /&gt;
&lt;br /&gt;
       BYTE         oldpri,c;          /* Stuff for bumping priority */&lt;br /&gt;
&lt;br /&gt;
struct Task        *mt=0L;&lt;br /&gt;
struct GfxBase     *GfxBase = NULL;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*-----------*/&lt;br /&gt;
/*  M A I N  */&lt;br /&gt;
/*-----------*/&lt;br /&gt;
&lt;br /&gt;
void main(int argc,char **argv)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
/*-------------*/&lt;br /&gt;
/* L O C A L S */&lt;br /&gt;
/*-------------*/&lt;br /&gt;
&lt;br /&gt;
       char         *fname;               /* File name and data pointer*/&lt;br /&gt;
       UBYTE        *p8data;              /* for file read.            */&lt;br /&gt;
       ULONG         clock;               /* Clock constant            */&lt;br /&gt;
       ULONG         length[2];           /* Sample lengths            */&lt;br /&gt;
       BYTE          iobuffer[8],         /* Buffer for 8SVX header    */&lt;br /&gt;
                    *psample[2];          /* Sample pointers           */&lt;br /&gt;
       Chunk        *p8Chunk;             /* Pointers for 8SVX parsing */&lt;br /&gt;
       Voice8Header *pVoice8Header;&lt;br /&gt;
       ULONG         y,rd8count,speed;    /* Counters, sampling speed   */&lt;br /&gt;
       ULONG         wakebit;             /* A wakeup mask              */&lt;br /&gt;
&lt;br /&gt;
/*-------------*/&lt;br /&gt;
/*   C O D E   */&lt;br /&gt;
/*-------------*/&lt;br /&gt;
&lt;br /&gt;
/*------------------------------*/&lt;br /&gt;
/* Check Arguments, Initialize  */&lt;br /&gt;
/*------------------------------*/&lt;br /&gt;
&lt;br /&gt;
fbase=0L;&lt;br /&gt;
sbase=0L;&lt;br /&gt;
AIOptr1=0L;&lt;br /&gt;
AIOptr2=0L;&lt;br /&gt;
port=0L;&lt;br /&gt;
port1=0L;&lt;br /&gt;
port2=0L;&lt;br /&gt;
v8handle=0L;&lt;br /&gt;
device=1L;&lt;br /&gt;
&lt;br /&gt;
if (argc &amp;amp;lt; 2)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No file name given.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
fname=argv[1];&lt;br /&gt;
&lt;br /&gt;
/*---------------------------*/&lt;br /&gt;
/* Initialize Clock Constant */&lt;br /&gt;
/*---------------------------*/&lt;br /&gt;
&lt;br /&gt;
GfxBase=(struct GfxBase *)OpenLibrary(&amp;amp;quot;graphics.library&amp;amp;quot;,0L);&lt;br /&gt;
if (GfxBase==0L)&lt;br /&gt;
    {&lt;br /&gt;
    puts(&amp;amp;quot;Can&#039;t open graphics library\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
if (GfxBase-&amp;amp;gt;DisplayFlags &amp;amp;amp; PAL)&lt;br /&gt;
    clock=3546895L;        /* PAL clock */&lt;br /&gt;
else&lt;br /&gt;
    clock=3579545L;        /* NTSC clock */&lt;br /&gt;
&lt;br /&gt;
if (GfxBase)&lt;br /&gt;
    CloseLibrary( (struct Library *) GfxBase);&lt;br /&gt;
&lt;br /&gt;
/*---------------*/&lt;br /&gt;
/* Open the File */&lt;br /&gt;
/*---------------*/&lt;br /&gt;
&lt;br /&gt;
v8handle= (struct FileHandle *) Open(fname,MODE_OLDFILE);&lt;br /&gt;
if (v8handle==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;Can&#039;t open 8SVX file.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*-------------------------------------------*/&lt;br /&gt;
/* Read the 1st 8 Bytes of the File for Size */&lt;br /&gt;
/*-------------------------------------------*/&lt;br /&gt;
rd8count=Read((BPTR)v8handle,iobuffer,8L);&lt;br /&gt;
if (rd8count==-1)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx (&amp;amp;quot;Read error.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
if (rd8count&amp;amp;lt;8)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx (&amp;amp;quot;Not an IFF 8SVX file, too short\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*-----------------*/&lt;br /&gt;
/* Evaluate Header */&lt;br /&gt;
/*-----------------*/&lt;br /&gt;
p8Chunk=(Chunk *)iobuffer;&lt;br /&gt;
if (p8Chunk-&amp;amp;gt;ckID != FORM )&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;Not an IFF FORM.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*--------------------------------------------*/&lt;br /&gt;
/* Allocate Memory for File and Read it in.   */&lt;br /&gt;
/*--------------------------------------------*/&lt;br /&gt;
fbase= (UBYTE *)AllocMem(fsize=p8Chunk-&amp;amp;gt;ckSize , MEMF_PUBLIC|MEMF_CLEAR);&lt;br /&gt;
if (fbase==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No memory for read.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
p8data=fbase;&lt;br /&gt;
&lt;br /&gt;
rd8count=Read((BPTR)v8handle,p8data,p8Chunk-&amp;amp;gt;ckSize);&lt;br /&gt;
if (rd8count==-1)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx (&amp;amp;quot;Read error.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
if (rd8count&amp;amp;lt;p8Chunk-&amp;amp;gt;ckSize)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx (&amp;amp;quot;Malformed IFF, too short.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
/* Evaluate IFF Type */&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
if (MakeID( *p8data, *(p8data+1) , *(p8data+2) , *(p8data+3) ) != MY8S )&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;Not an IFF 8SVX file.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*----------------------*/&lt;br /&gt;
/* Evaluate 8SVX Chunks */&lt;br /&gt;
/*----------------------*/&lt;br /&gt;
p8data=p8data+4;&lt;br /&gt;
&lt;br /&gt;
while( p8data &amp;amp;lt; fbase+fsize )&lt;br /&gt;
  {&lt;br /&gt;
  p8Chunk=(Chunk *)p8data;&lt;br /&gt;
&lt;br /&gt;
  switch(p8Chunk-&amp;amp;gt;ckID)&lt;br /&gt;
    {&lt;br /&gt;
    case VHDR:&lt;br /&gt;
      /*------------------------------------------------*/&lt;br /&gt;
      /* Get a pointer to the 8SVX header for later use */&lt;br /&gt;
      /*------------------------------------------------*/&lt;br /&gt;
      pVoice8Header=(Voice8Header *)(p8data+8L);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case BODY:&lt;br /&gt;
      /*-------------------------------------------------*/&lt;br /&gt;
      /* Create pointers to 1-shot and continuous parts  */&lt;br /&gt;
      /* for the top octave and get length. Store them.  */&lt;br /&gt;
      /*-------------------------------------------------*/&lt;br /&gt;
        psample[0] = (BYTE *)(p8data + 8L);&lt;br /&gt;
        psample[1] = psample[0] + pVoice8Header-&amp;amp;gt;oneShotHiSamples;&lt;br /&gt;
        length[0] = (ULONG)pVoice8Header-&amp;amp;gt;oneShotHiSamples;&lt;br /&gt;
        length[1] = (ULONG)pVoice8Header-&amp;amp;gt;repeatHiSamples;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    /* end switch */&lt;br /&gt;
&lt;br /&gt;
  p8data = p8data + 8L + p8Chunk-&amp;amp;gt;ckSize;&lt;br /&gt;
&lt;br /&gt;
  if (p8Chunk-&amp;amp;gt;ckSize&amp;amp;amp;1L == 1)&lt;br /&gt;
      p8data++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
/* Play either the one-shot or continuous, not both */&lt;br /&gt;
if (length[0]==0)&lt;br /&gt;
    y=1;&lt;br /&gt;
else&lt;br /&gt;
    y=0;&lt;br /&gt;
&lt;br /&gt;
/*---------------------------------------*/&lt;br /&gt;
/* Allocate chip memory for samples and  */&lt;br /&gt;
/* copy from read buffer to chip memory. */&lt;br /&gt;
/*---------------------------------------*/&lt;br /&gt;
if (length[y]&amp;amp;lt;=102400)&lt;br /&gt;
    ssize=length[y];&lt;br /&gt;
else&lt;br /&gt;
    ssize=102400;&lt;br /&gt;
&lt;br /&gt;
sbase=(UBYTE *)AllocMem( ssize , MEMF_CHIP | MEMF_CLEAR);&lt;br /&gt;
if (sbase==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No chip memory.\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
CopyMem(psample[y],sbase,ssize);&lt;br /&gt;
psample[y]+=ssize;&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------*/&lt;br /&gt;
/* Calculate playback sampling rate */&lt;br /&gt;
/*----------------------------------*/&lt;br /&gt;
speed =  clock / pVoice8Header-&amp;amp;gt;samplesPerSec;&lt;br /&gt;
&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
/* Bump our priority */&lt;br /&gt;
/*-------------------*/&lt;br /&gt;
mt=FindTask(NULL);&lt;br /&gt;
oldpri=SetTaskPri(mt,21);&lt;br /&gt;
&lt;br /&gt;
/*--------------------------------*/&lt;br /&gt;
/* Allocate two audio I/O blocks  */&lt;br /&gt;
/*--------------------------------*/&lt;br /&gt;
AIOptr1=(struct IOAudio *)&lt;br /&gt;
      AllocMem( sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);&lt;br /&gt;
if (AIOptr1==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No IO memory\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
AIOptr2=(struct IOAudio *)&lt;br /&gt;
      AllocMem( sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);&lt;br /&gt;
if (AIOptr2==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No IO memory\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*----------------------*/&lt;br /&gt;
/* Make two reply ports */&lt;br /&gt;
/*----------------------*/&lt;br /&gt;
&lt;br /&gt;
port1=CreatePort(0,0);&lt;br /&gt;
if (port1==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No port\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
port2=CreatePort(0,0);&lt;br /&gt;
if (port2==0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No port\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
c=0;&lt;br /&gt;
while(device!=0 &amp;amp;amp;&amp;amp;amp; c&amp;amp;lt;4)&lt;br /&gt;
  {&lt;br /&gt;
  /*---------------------------------------*/&lt;br /&gt;
  /* Set up audio I/O block for channel    */&lt;br /&gt;
  /* allocation and Open the audio device  */&lt;br /&gt;
  /*---------------------------------------*/&lt;br /&gt;
  AIOptr1-&amp;amp;gt;ioa_Request.io_Message.mn_ReplyPort   = port1;&lt;br /&gt;
  AIOptr1-&amp;amp;gt;ioa_Request.io_Message.mn_Node.ln_Pri = 127;  /* No stealing! */&lt;br /&gt;
  AIOptr1-&amp;amp;gt;ioa_AllocKey                          = 0;&lt;br /&gt;
  AIOptr1-&amp;amp;gt;ioa_Data                              = chans[c];&lt;br /&gt;
  AIOptr1-&amp;amp;gt;ioa_Length                            = 1;&lt;br /&gt;
&lt;br /&gt;
  device=OpenDevice(AUDIONAME,0L,(struct IORequest *)AIOptr1,0L);&lt;br /&gt;
  c++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
if (device!=0)&lt;br /&gt;
    {&lt;br /&gt;
    kill8svx(&amp;amp;quot;No channel\n&amp;amp;quot;);&lt;br /&gt;
    exit(1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*-------------------------------------------*/&lt;br /&gt;
/* Set Up Audio IO Blocks for Sample Playing */&lt;br /&gt;
/*-------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Request.io_Command   =CMD_WRITE;&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Request.io_Flags     =ADIOF_PERVOL;&lt;br /&gt;
&lt;br /&gt;
/*--------*/&lt;br /&gt;
/* Volume */&lt;br /&gt;
/*--------*/&lt;br /&gt;
&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Volume=60;&lt;br /&gt;
&lt;br /&gt;
/*---------------*/&lt;br /&gt;
/* Period/Cycles */&lt;br /&gt;
/*---------------*/&lt;br /&gt;
&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Period =(UWORD)speed;&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Cycles =1;&lt;br /&gt;
&lt;br /&gt;
*AIOptr2 = *AIOptr1;   /* Make sure we have the same allocation keys, */&lt;br /&gt;
                       /* same channels selected and same flags       */&lt;br /&gt;
                       /* (but different ports...)                    */&lt;br /&gt;
&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Request.io_Message.mn_ReplyPort   = port1;&lt;br /&gt;
AIOptr2-&amp;amp;gt;ioa_Request.io_Message.mn_ReplyPort   = port2;&lt;br /&gt;
&lt;br /&gt;
/*--------*/&lt;br /&gt;
/*  Data  */&lt;br /&gt;
/*--------*/&lt;br /&gt;
&lt;br /&gt;
AIOptr1-&amp;amp;gt;ioa_Data            =(UBYTE *)sbase;&lt;br /&gt;
AIOptr2-&amp;amp;gt;ioa_Data            =(UBYTE *)sbase + 51200;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*-----------------*/&lt;br /&gt;
/*  Run the sample */&lt;br /&gt;
/*-----------------*/&lt;br /&gt;
&lt;br /&gt;
if (length[y]&amp;amp;lt;=102400)&lt;br /&gt;
    {&lt;br /&gt;
    AIOptr1-&amp;amp;gt;ioa_Length=length[y];         /* No double buffering needed */&lt;br /&gt;
    BeginIO((struct IORequest *)AIOptr1);  /* Begin the sample, wait for */&lt;br /&gt;
    wakebit=0L;                            /* it to finish, then quit.   */&lt;br /&gt;
    wakebit=Wait(1 &amp;amp;lt;&amp;amp;lt; port1-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
    while((msg=GetMsg(port1))==0){};&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    length[y]-=102400;                    /* It&#039;s a real long sample so  */&lt;br /&gt;
    AIOptr1-&amp;amp;gt;ioa_Length=51200L;           /* double buffering is needed  */&lt;br /&gt;
    AIOptr2-&amp;amp;gt;ioa_Length=51200L;&lt;br /&gt;
    BeginIO((struct IORequest *)AIOptr1); /* Start up the first 2 blocks... */&lt;br /&gt;
    BeginIO((struct IORequest *)AIOptr2);&lt;br /&gt;
    Aptr=AIOptr1;&lt;br /&gt;
    port=port1;                           /* Set the switch... */&lt;br /&gt;
&lt;br /&gt;
    while(length[y]&amp;amp;gt;0)&lt;br /&gt;
      {                                   /* We Wait() for one IO to finish, */&lt;br /&gt;
      wakebit=Wait(1 &amp;amp;lt;&amp;amp;lt; port-&amp;amp;gt;mp_SigBit); /* then reuse the IO block &amp;amp;amp; queue */&lt;br /&gt;
      while((msg=GetMsg(port))==0){};     /* it up again while the 2nd IO    */&lt;br /&gt;
                                          /* block plays. Switch and repeat. */&lt;br /&gt;
      /* Set length of next IO block */&lt;br /&gt;
      if (length[y]&amp;amp;lt;=51200)&lt;br /&gt;
          Aptr-&amp;amp;gt;ioa_Length=length[y];&lt;br /&gt;
      else&lt;br /&gt;
          Aptr-&amp;amp;gt;ioa_Length=51200L;&lt;br /&gt;
&lt;br /&gt;
      /* Copy sample fragment from read buffer to chip memory */&lt;br /&gt;
      CopyMem(psample[y],Aptr-&amp;amp;gt;ioa_Data,Aptr-&amp;amp;gt;ioa_Length);&lt;br /&gt;
&lt;br /&gt;
      /* Adjust size and pointer of read buffer*/&lt;br /&gt;
      length[y]-=Aptr-&amp;amp;gt;ioa_Length;&lt;br /&gt;
      psample[y]+=51200;&lt;br /&gt;
&lt;br /&gt;
      BeginIO((struct IORequest *)Aptr);&lt;br /&gt;
&lt;br /&gt;
      if (Aptr==AIOptr1)&lt;br /&gt;
          {&lt;br /&gt;
          Aptr=AIOptr2;                  /* This logic handles switching  */&lt;br /&gt;
          port=port2;                    /* between the 2 IO blocks and   */&lt;br /&gt;
          }                               /* the 2 ports we are using.     */&lt;br /&gt;
      else&lt;br /&gt;
          {&lt;br /&gt;
          Aptr=AIOptr1;&lt;br /&gt;
          port=port1;&lt;br /&gt;
          }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    /*-------------------------------------------------*/&lt;br /&gt;
    /* OK we are at the end of the sample so just wait */&lt;br /&gt;
    /* for the last two parts of the sample to finish  */&lt;br /&gt;
    /*-------------------------------------------------*/&lt;br /&gt;
    wakebit=Wait(1 &amp;amp;lt;&amp;amp;lt; port-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
    while((msg=GetMsg(port))==0){};&lt;br /&gt;
    if (Aptr==AIOptr1)&lt;br /&gt;
        {&lt;br /&gt;
        Aptr=AIOptr2;                  /* This logic handles switching  */&lt;br /&gt;
        port=port2;                    /* between the 2 IO blocks and   */&lt;br /&gt;
        }                              /* the 2 ports we are using.     */&lt;br /&gt;
    else&lt;br /&gt;
        {&lt;br /&gt;
        Aptr=AIOptr1;&lt;br /&gt;
        port=port1;&lt;br /&gt;
        }&lt;br /&gt;
    wakebit=Wait(1 &amp;amp;lt;&amp;amp;lt; port-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
    while((msg=GetMsg(port))==0){};&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
kill8();&lt;br /&gt;
exit(0L);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------*/&lt;br /&gt;
/* Abort the Read */&lt;br /&gt;
/*----------------*/&lt;br /&gt;
void&lt;br /&gt;
kill8svx(kill8svxstring)&lt;br /&gt;
char *kill8svxstring;&lt;br /&gt;
{&lt;br /&gt;
puts(kill8svxstring);&lt;br /&gt;
kill8();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-------------------------*/&lt;br /&gt;
/* Return system resources */&lt;br /&gt;
/*-------------------------*/&lt;br /&gt;
void&lt;br /&gt;
kill8()&lt;br /&gt;
{&lt;br /&gt;
if (device ==0)&lt;br /&gt;
    CloseDevice((struct IORequest *)AIOptr1);&lt;br /&gt;
if (port1  !=0)&lt;br /&gt;
    DeletePort(port1);&lt;br /&gt;
if (port2  !=0)&lt;br /&gt;
    DeletePort(port2);&lt;br /&gt;
if (AIOptr1!=0)&lt;br /&gt;
    FreeMem( AIOptr1,sizeof(struct IOAudio) );&lt;br /&gt;
if (AIOptr2!=0)&lt;br /&gt;
    FreeMem( AIOptr2,sizeof(struct IOAudio) );&lt;br /&gt;
&lt;br /&gt;
if (mt!=0)&lt;br /&gt;
    SetTaskPri(mt,oldpri);&lt;br /&gt;
&lt;br /&gt;
if (sbase !=0)&lt;br /&gt;
    FreeMem (sbase, ssize);&lt;br /&gt;
if (fbase !=0)&lt;br /&gt;
    FreeMem(fbase,fsize);&lt;br /&gt;
if (v8handle!=0)&lt;br /&gt;
    Close((BPTR)v8handle);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Additional Information on the Audio Device ==&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the audio device can be found in the include files and the Autodocs for the audio device. Both are contained in the [[Autodocs:Main|Autodocs]]. Information can also be found in the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Audio Device Information&lt;br /&gt;
|-&lt;br /&gt;
|Includes||devices/audio.h&amp;lt;br /&amp;gt;devices/audio.i&lt;br /&gt;
|-&lt;br /&gt;
|[[Autodocs:Main|Autodocs]]||[[audio.doc]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Menu_Class&amp;diff=12566</id>
		<title>Intuition Menu Class</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Menu_Class&amp;diff=12566"/>
		<updated>2025-01-26T19:36:51Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= An overview =&lt;br /&gt;
&lt;br /&gt;
Starting with version 54.6, Intuition features a built-in BOOPSI menu class (&amp;quot;menuclass&amp;quot;) which aims to fully replace the traditional way of setting up and handling menus in applications.&lt;br /&gt;
&lt;br /&gt;
Its object-oriented API allows to easily build complex menu trees, freeing a program from the need to use gadtools.library for that job. It also provides extensive control over menus, making it possible to define, read and change their properties thanks to simple yet flexible methods and attributes.&lt;br /&gt;
&lt;br /&gt;
Moreover, menuclass extends the capabilities of the original Intuition menu system. For instance, it can handle any number of sub-menu levels, as well as any amount of items in menus and sub-menus. For nearly all operations, it transparently synchronizes itself with Intuition and performs menu relayout if needed. Automatic placement of custom images in front of menu item labels is also supported.&lt;br /&gt;
&lt;br /&gt;
== BOOPSI menus vs. traditional menus ==&lt;br /&gt;
&lt;br /&gt;
BOOPSI menus may look and feel identical to traditional ones from the point of view of the user, but are handled quite differently in application code.&lt;br /&gt;
&lt;br /&gt;
The old-style, white-box Menu and MenuItem structures are replaced by abstract objects (instances of menuclass) accessed through an object-oriented interface, removing the need for manual poking or dedicated menu functions.&lt;br /&gt;
&lt;br /&gt;
Such BOOPSI menu objects are able to perform automatically most operations for which their traditional counterparts would require special assistance from the application.&lt;br /&gt;
&lt;br /&gt;
== Types of menuclass objects ==&lt;br /&gt;
&lt;br /&gt;
There exist three distinct types of menuclass objects -- menu root, menu and menu item. Any instance of the class belongs to one of these types, specified at OM_NEW time through the MA_Type attribute.&lt;br /&gt;
&lt;br /&gt;
The possible values for MA_Type are:&lt;br /&gt;
&lt;br /&gt;
; T_ROOT&lt;br /&gt;
: A menu root object, also known as a menu strip as it is the parent of all menu objects. This differs from the old-style menu system, where a &amp;quot;menu strip&amp;quot; was just a linked list of Menu structures. Besides being the actual root of a menu tree, a menu root object also holds essential state information on the tree as a whole. It may be used in any place an old-style menu strip can, except for the ItemAddress() function (replaced by the MM_FINDID method). A menu root object can have any number of menu objects as children.&lt;br /&gt;
&lt;br /&gt;
; T_MENU&lt;br /&gt;
: A menu object, equivalent to the old-style Menu structure. A menu object can have any number of menu item objects as children (and should always have at least one).&lt;br /&gt;
&lt;br /&gt;
; T_ITEM&lt;br /&gt;
: A menu item object, equivalent to the old-style MenuItem structure. A menu item object can have any number of menu item objects as children (sub-items), even in the case it is itself a sub-item of some other menu item. Only a menu (sub-)item having no children (i.e. a leaf item) can be actually selected by the user.&lt;br /&gt;
&lt;br /&gt;
== Compatibility ==&lt;br /&gt;
&lt;br /&gt;
Save where explicitly allowed, menu and menu item objects instantiated from menuclass cannot be transparently used in place of the equivalent old-style Intuition elements, as not all of the traditional menu-related functions and rules apply to them.&lt;br /&gt;
&lt;br /&gt;
A limited form of compatibility exists due to menuclass objects having Menu or MenuItem structures embedded in them, just like gadgetclass ones do with the (Ext)Gadget structure. However, such structures should never be directly accessed by user code unless otherwise documented.&lt;br /&gt;
&lt;br /&gt;
Under most circumstances, applications should treat menuclass instances as &amp;quot;black boxes&amp;quot;, the only exception to this rule currently being that it is permitted to read certain bits from the Flags field of their embedded legacy structure (see also next subsection).&lt;br /&gt;
&lt;br /&gt;
== Recognizing a BOOPSI menu object ==&lt;br /&gt;
&lt;br /&gt;
Although an application normally should know if it is using BOOPSI menus, it may happen that a library or a class needs to check the type of menus passed to it by a client. This is easily done with the following test:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  if ((((struct Menu *)menu_object_ptr)-&amp;gt;Flags &amp;amp; BOOPSIMENU) != 0)&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the flag BOOPSIMENU is set, the object is a menuclass instance, otherwise it is a traditional Menu or MenuItem structure.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In the above test it would have been exactly the same if we had chosen to cast the object to a struct MenuItem * rather than to a struct Menu * because the Flags field is located at the same offset (and has the same size) in both structures.}}&lt;br /&gt;
&lt;br /&gt;
= Adding BOOPSI menus to an application =&lt;br /&gt;
&lt;br /&gt;
In this section we&#039;ll cover the basics of using the new menuclass in a typical application, and the main differences between BOOPSI and traditional menus.&lt;br /&gt;
&lt;br /&gt;
The first step in adding BOOPSI menus to your application is to build a tree of menuclass objects, each with its appearance and behavior defined by attributes that can be set at NewObject() time.&lt;br /&gt;
&lt;br /&gt;
== Label and ID of menus and menu items ==&lt;br /&gt;
&lt;br /&gt;
For menu and menu item objects, the application should specify at least a label string and an ID number as a minimal set of attributes.&lt;br /&gt;
&lt;br /&gt;
The label is assigned via MA_Label and is the text Intuition will display to represent a menu or menu item on the screen. A special value for this attribute is the constant ML_SEPARATOR, which turns a menu item into a separator line.&lt;br /&gt;
&lt;br /&gt;
The ID, passed with the MA_ID tag, is a 32-bit unsigned integer that uniquely identifies a particular menu or menu item object. This number is passed back to the application through the IDCMP mechanism whenever the user picks a menu item or asks for help on a menu or menu item.&lt;br /&gt;
&lt;br /&gt;
Any number may be chosen as an ID for a menuclass object, except for 0 (zero): this constant has the alias NO_MENU_ID in &amp;lt;intuition/menuclass.h&amp;gt; and is always an invalid value for an object&#039;s MA_ID attribute. Also, when an IDCMP_MENUPICK or IDCMP_MENUHELP ExtIntuiMessage reports NO_MENU_ID in its eim_LongCode field, this is to be interpreted as &amp;quot;no menu selection&amp;quot; (i.e. it has the same meaning MENUNULL has for old-style menus).&lt;br /&gt;
&lt;br /&gt;
Menu root objects, as well as menu items acting as separator lines, don&#039;t need to have an ID number. Menu root objects don&#039;t need a label string either.&lt;br /&gt;
&lt;br /&gt;
== Building a menu tree with menuclass ==&lt;br /&gt;
&lt;br /&gt;
A menu tree is organized as a hierarchy of nested menuclass objects.&lt;br /&gt;
&lt;br /&gt;
At the very top of the tree there is the menu root, having one or more menus as children. These make up the menu strip that appears on the screen when the user presses the right mouse button.&lt;br /&gt;
&lt;br /&gt;
Each menu is the parent of one or more menu items. A menu item can have other menu items as children as well, thus forming a sub-menu. A menu item which is part of a sub-menu (also called a sub-item, i.e. the child of another menu item) can in turn have children (sub-items) of its own.&lt;br /&gt;
&lt;br /&gt;
The leaf nodes of the tree are menu items or sub-items with no children. These are the actually selectable menu options.&lt;br /&gt;
&lt;br /&gt;
There is no limit to the depth of the menu tree other than the available system memory.&lt;br /&gt;
&lt;br /&gt;
You can append children to a menuclass object at OM_NEW or OM_SET time with the MA_AddChild tag, or by using the OM_ADDMEMBER method (more on this later).&lt;br /&gt;
&lt;br /&gt;
An example of a very simple menu tree generation could be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  Object *menustripobj;&lt;br /&gt;
&lt;br /&gt;
  menustripobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ROOT,&lt;br /&gt;
                   MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_MENU,&lt;br /&gt;
                     MA_Label, &amp;quot;Project&amp;quot;,&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, &amp;quot;Open...&amp;quot;,&lt;br /&gt;
                       MA_ID, MID_PROJECT_OPEN,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, &amp;quot;Save&amp;quot;,&lt;br /&gt;
                       MA_ID, MID_PROJECT_SAVE,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, &amp;quot;Save as...&amp;quot;,&lt;br /&gt;
                       MA_ID, MID_PROJECT_SAVEAS,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, ML_SEPARATOR,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, &amp;quot;About...&amp;quot;,&lt;br /&gt;
                       MA_ID, MID_PROJECT_ABOUT,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, ML_SEPARATOR,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     MA_AddChild, IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ITEM,&lt;br /&gt;
                       MA_Label, &amp;quot;Quit&amp;quot;,&lt;br /&gt;
                       MA_ID, MID_PROJECT_QUIT,&lt;br /&gt;
                       TAG_END),&lt;br /&gt;
                     TAG_END),&lt;br /&gt;
                   TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you prefer a more compact style of coding, &amp;lt;intuition/menuclass.h&amp;gt; offers a number of macros allowing to simplify the tree description a little. Using said macros, the above code could be rewritten as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  Object *menustripobj;&lt;br /&gt;
&lt;br /&gt;
  menustripobj = MStrip,&lt;br /&gt;
                   MA_AddChild, MTitle(&amp;quot;Project&amp;quot;),&lt;br /&gt;
                     MA_AddChild, MItem(&amp;quot;Open...&amp;quot;),&lt;br /&gt;
                       MA_ID, MID_PROJECT_OPEN,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MItem(&amp;quot;Save&amp;quot;),&lt;br /&gt;
                       MA_ID, MID_PROJECT_SAVE,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MItem(&amp;quot;Save as...&amp;quot;),&lt;br /&gt;
                       MA_ID, MID_PROJECT_SAVEAS,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MSeparator,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MItem(&amp;quot;About...&amp;quot;),&lt;br /&gt;
                       MA_ID, MID_PROJECT_ABOUT,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MSeparator,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                     MA_AddChild, MItem(&amp;quot;Quit&amp;quot;),&lt;br /&gt;
                       MA_ID, MID_PROJECT_QUIT,&lt;br /&gt;
                     MEnd,&lt;br /&gt;
                   MEnd,&lt;br /&gt;
                 MEnd;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=When using the MTitle() and MItem() macros, the label is passed as an argument to the macro itself rather than via an explicit MA_Label tag.}}&lt;br /&gt;
&lt;br /&gt;
We&#039;ll use the compact style in the rest of this document, except in cases where the more verbose one provides greater clarity.&lt;br /&gt;
&lt;br /&gt;
== Menu item keyboard shortcuts ==&lt;br /&gt;
&lt;br /&gt;
As with traditional Intuition menus, menuclass item objects can have a keyboard shortcut which is displayed next to the item&#039;s label. Shortcuts can have one or more characters; a shortcut of more than one character is also called &amp;quot;command string&amp;quot; (for instance, &amp;quot;ctrl z&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
A single-character shortcut is processed directly by Intuition, and appears at the right side of the menu item with an &amp;quot;Amiga key&amp;quot; symbol prepended to it. When using a command string of more than one character, however, Intuition will display it at the right side of the item (without the &amp;quot;Amiga key&amp;quot; symbol) but won&#039;t process the corresponding key combination; the application has to handle that on its own.&lt;br /&gt;
&lt;br /&gt;
There are two ways to specify a keyboard shortcut for a menuclass item object.&lt;br /&gt;
&lt;br /&gt;
You can pass it with the MA_Key attribute, as in the following example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  item = MItem(&amp;quot;Save&amp;quot;),&lt;br /&gt;
           MA_ID, MID_SAVE,&lt;br /&gt;
           MA_Key, &amp;quot;S&amp;quot;,&lt;br /&gt;
         MEnd;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternatively, you can prepend it to the item&#039;s label string, separed by a &#039;|&#039; character, this way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  item = MItem(&amp;quot;S|Save&amp;quot;),&lt;br /&gt;
           MA_ID, MID_SAVE,&lt;br /&gt;
         MEnd;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you opt for the latter approach, you may also use a NUL (&#039;\0&#039;) character in place of the &#039;|&#039;. This can prove useful when converting to BOOPSI menu usage an existing application employing such a method to embed shortcuts in its catalog strings.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=The NUL byte solution only works for single-letter shortcuts and requires that no item in the menu tree has a label of just one letter, as any such label would be mistaken for a shortcut followed by random bytes. When you have single-letter labels, therefore, you cannot use this feature and must disable it for the whole tree. That&#039;s done by passing { MA_EmbeddedKey, FALSE } to the menu root object; this attribute will be inherited by all objects.}}&lt;br /&gt;
&lt;br /&gt;
== Inheritance of menu attributes ==&lt;br /&gt;
&lt;br /&gt;
There are some attributes of menuclass objects that are automatically inherited by all their descendants, except those for which they&#039;re explicitly set to some different value. For instance, if you want your menus to use a particular font, you only need to set the MA_TextAttr attribute for the menu root, and all items (and sub-items) of all menus will be displayed with that font.&lt;br /&gt;
&lt;br /&gt;
Any menuclass attribute which is automatically inherited from the parent object when not specified explicitly is clearly described as such, both in the autodoc and in the &amp;lt;intuition/menuclass.h&amp;gt; header file.&lt;br /&gt;
&lt;br /&gt;
== Attaching the menu tree to a window ==&lt;br /&gt;
&lt;br /&gt;
You can attach a menu tree built with menuclass to a window just like you would do with traditional non-BOOPSI menus, i.e. by calling SetMenuStrip(). Starting with Intuition V54, this function accepts a BOOPSI menu root object as the menu strip pointer.&lt;br /&gt;
Therefore, once you&#039;ve opened your window, you can simply do the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  IIntuition-&amp;gt;SetMenuStrip(window,menustripobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the menu tree will be ready to use, after undergoing an automatic layout to adapt itself to the window&#039;s screen.&lt;br /&gt;
&lt;br /&gt;
An alternative to the above is passing the menus to the window at opening time, via the new V54 tag WA_MenuStrip. This way your window will open with the menus already attached and laid out.&lt;br /&gt;
&lt;br /&gt;
When using window.class, you can achieve the same by passing the class-specific tag WINDOW_MenuStrip. (Starting with V54, window.class supports WA_MenuStrip as well, as an alias for WINDOW_MenuStrip.)&lt;br /&gt;
&lt;br /&gt;
== Detaching the menu tree from a window ==&lt;br /&gt;
&lt;br /&gt;
BOOPSI menus, like traditional ones, can be detached from a window at any time with ClearMenuStrip(). After having been detached, they can also be reattached by way of ResetMenuStrip(), as usual.&lt;br /&gt;
&lt;br /&gt;
In many cases, however, you don&#039;t need to remove BOOPSI menus as you would with old-style menus. Changing the selection state of &amp;quot;checkmarkable&amp;quot; items, as well as disabling or enabling single items or whole menus, may be performed by just setting the appropriate attribute of the menu object in question, regardless of whether or not the menus are in use. Any needed synchronization with Intuition is handled internally by menuclass.&lt;br /&gt;
&lt;br /&gt;
The above is generally true for all operations you can do on menuclass objects, such as changing their label, image, font or charset, or even their position in the menu tree. In fact, a BOOPSI menu tree even supports addition or removal of menus and items on-the-fly without any clearing and resetting of the menus.&lt;br /&gt;
&lt;br /&gt;
Whenever a change requires a relayout of some part of the menu tree, menuclass will automatically take care of that.&lt;br /&gt;
&lt;br /&gt;
Additionally, as of Intuition V54 it is no longer mandatory to remove the menus with ClearMenuStrip() before closing the window. CloseWindow() itself will take care of clearing the menus if needed (no matter if BOOPSI or old-style).&lt;br /&gt;
&lt;br /&gt;
{{Note|text=It is still required, however, to use ClearMenuStrip() to remove the menu strip of a window before setting a different one with SetMenuStrip().}}&lt;br /&gt;
&lt;br /&gt;
== Handling menu item selections by the user ==&lt;br /&gt;
&lt;br /&gt;
Upon receiving a menu pick event such as IDCMP_MENUPICK (or WMHI_MENUPICK if using window.class), your application can find out which menu items the user selected by invoking repeatedly the MM_NEXTSELECT method on the root object of the menu tree (i.e. the menu strip object). This will return all selected item IDs in sequence; in many cases there will be only one, but there may be more if the user performed multiple selection.&lt;br /&gt;
&lt;br /&gt;
Once all IDs have been retrieved, MM_NEXTSELECT will return NO_MENU_ID which means there are no more selections in the list.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=MM_NEXTSELECT might even return NO_MENU_ID immediately on the first invocation; this would mean the user initiated and terminated a menu session without picking any item. Be prepared to handle this case.}}&lt;br /&gt;
&lt;br /&gt;
The MM_NEXTSELECT method uses the following mpNextSelect message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpNextSelect&lt;br /&gt;
  {&lt;br /&gt;
     uint32 MethodID;&lt;br /&gt;
     uint32 mpns_Reserved;&lt;br /&gt;
     uint32 mpns_CurrentID;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_NEXTSELECT, mpns_Reserved should be always set to zero, and mpns_CurrentID is the menu item ID the method returned on the previous invocation. To obtain the first ID in the selection list, pass NO_MENU_ID in the mpns_CurrentID field.&lt;br /&gt;
&lt;br /&gt;
An example of this method&#039;s usage would be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  uint32 id = NO_MENU_ID;&lt;br /&gt;
&lt;br /&gt;
  while ((id = IIntuition-&amp;gt;IDoMethod(menustripobj,MM_NEXTSELECT,0,id)) != NO_MENU_ID)&lt;br /&gt;
  {&lt;br /&gt;
    switch (id)&lt;br /&gt;
    {&lt;br /&gt;
      /* Process menu selections */&lt;br /&gt;
&lt;br /&gt;
      case MID_PROJECT_OPEN:&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An alternative way to get the ID of the first (or only) selected menu item is to read it from the eim_LongCode field of the ExtIntuiMessage reporting the IDCMP_MENUPICK event (IntuiMessages generated by Intuition are always really ExtIntuiMessages, and can be cast as such). Since a menuclass object&#039;s ID is a 32-bit value, it can&#039;t fit in the 16-bit IntuiMessage.Code field, the value of which is currently undefined for IDCMP events coming from BOOPSI menus.&lt;br /&gt;
&lt;br /&gt;
The above example could thus be modified as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  uint32 id = ((struct ExtIntuiMessage *)intuimsg)-&amp;gt;eim_LongCode;&lt;br /&gt;
&lt;br /&gt;
  while (id != NO_MENU_ID)&lt;br /&gt;
  {&lt;br /&gt;
    switch (id)&lt;br /&gt;
    {&lt;br /&gt;
      /* Process menu selections */&lt;br /&gt;
&lt;br /&gt;
      case MID_PROJECT_OPEN:&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    id = IIntuition-&amp;gt;IDoMethod(menustripobj,MM_NEXTSELECT,0,id);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first approach is probably more convenient when using window.class since in that case you would need an additional IDCMP hook to access the ExtIntuiMessage in order to read its eim_LongCode field.&lt;br /&gt;
&lt;br /&gt;
== Handling menu help requests by the user ==&lt;br /&gt;
&lt;br /&gt;
If your window has the WA_MenuHelp attribute set to TRUE, and it&#039;s listening to IDCMP_MENUHELP events, the user can request help on a menu item or a menu title by pressing the [Help] key when that item or title is being highlighted by the mouse pointer. Upon reception of a menu help event, your application can easily find out the ID of the item or title the help request was about by querying the MA_MenuHelpID attribute of the menu root object.&lt;br /&gt;
&lt;br /&gt;
An example of this might be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  uint32 help_id;&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;GetAttr(MA_MenuHelpID,menustripobj,&amp;amp;help_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=The menu help ID value may be NO_MENU_ID, which means the [Help] key was pressed by the user while no menu item or title was highlighted.}}&lt;br /&gt;
&lt;br /&gt;
Menu help doesn&#039;t support multiple selection, so you have to deal with just one ID per event. As with menu pick, you can also retrieve the ID value by reading the ExtIntuiMessage.eim_LongCode field.&lt;br /&gt;
&lt;br /&gt;
== Menu pick hooks and menu help hooks ==&lt;br /&gt;
&lt;br /&gt;
Any menuclass object can have custom hooks associated to it with dedicated code to handle menu selections or help requests for that specific object. Such hooks can be invoked automatically by the main event handling loop every time a menu pick or menu help event occurs.&lt;br /&gt;
&lt;br /&gt;
The hook that handles menu item selections is specified through the MA_PickHook attribute; similarly, you can use the MA_HelpHook attribute to specify the hook handling menu help requests.&lt;br /&gt;
&lt;br /&gt;
These two attributes are inherited by all children of a menuclass object. This allows, if desired, to reuse the same hook for all the items of a menu (or all the sub-items of an item) simply by setting it for that menu (or item), rather than having to pass it to each child explicitly. Such a &amp;quot;common&amp;quot; hook could act as a dispatcher, calling the appropriate individual handling function according to the ID of the object it is invoked on.&lt;br /&gt;
&lt;br /&gt;
A menuclass object&#039;s pick or help hook will be invoked as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  HookFunction(struct Hook *hook, Object *obj, struct MenuEventMessage *msg)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &#039;obj&#039; is the object itself and &#039;msg&#039; is a pointer to a MenuEventMessage structure.&lt;br /&gt;
&lt;br /&gt;
The MenuEventMessage structure is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct MenuEventMessage&lt;br /&gt;
  {&lt;br /&gt;
      uint32 StructSize;      /* For future expansion */&lt;br /&gt;
      uint32 EventType;       /* ET_MENUPICK or ET_MENUHELP */&lt;br /&gt;
      struct Window *Window;  /* Event window pointer */&lt;br /&gt;
      APTR UserData;          /* Custom data pointer */&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The EventType field is useful to distinguish between the two types of event the hook could be invoked for, in case the same hook function is used for both. The Window field holds the address of the event&#039;s window (i.e. the window your menu tree is associated to) and the UserData field is used to pass to the hook any kind of context information it might require, as explained a little below.&lt;br /&gt;
&lt;br /&gt;
The pick or help hook can use the supplied information (menu object, window and custom data pointer) to process the event. The return value of the hook is not currently defined; it&#039;s recommended to set it to zero for future compatibility.&lt;br /&gt;
&lt;br /&gt;
To have menuclass invoke the pick hook of a menuclass object, your application needs to use the MM_HANDLEPICK method in its event loop.&lt;br /&gt;
&lt;br /&gt;
Like MM_NEXTSELECT, MM_HANDLEPICK can be invoked repeatedly to browse through all items in the menu selection chain. The difference is that this method only returns the ID of selected items that don&#039;t have a pick hook; whenever it finds a selected item whose pick hook is non-NULL, it invokes that hook and proceeds to the next item without returning anything. When there are no more items left in the menu selection chain, MM_HANDLEPICK returns NO_MENU_ID.&lt;br /&gt;
&lt;br /&gt;
The mechanism is exactly the same for help hooks, except in that case you have to use the MM_HANDLEHELP method, which only needs to invoked once as menu help events don&#039;t support multiple selection: if the menu object the user asked help about doesn&#039;t have a help hook, MM_HANDLEHELP will return its ID, otherwise it will invoke the hook and return NO_MENU_ID.&lt;br /&gt;
&lt;br /&gt;
Both the MM_HANDLEPICK and MM_HANDLEHELP methods should be invoked on the menu root object, and use the same message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpHandleEvent&lt;br /&gt;
  {&lt;br /&gt;
      uint32 MethodID;&lt;br /&gt;
      uint32 mphe_Reserved;&lt;br /&gt;
      uint32 mphe_CurrentID;&lt;br /&gt;
      struct Window *mphe_Window;&lt;br /&gt;
      APTR mphe_UserData;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_HANDLEPICK or MM_HANDLEHELP, mphe_Reserved should always be set to zero, and mphe_CurrentID is the ID returned by the method&#039;s previous invocation (ignored by MM_HANDLEHELP; pass NO_MENU_ID on the first invocation of MM_HANDLEPICK). Additionally, you should store the event&#039;s window address in mphe_Window (e.g. IntuiMessage-&amp;gt;IDCMPWindow) and any custom data to be passed to the pick or help hook in mphe_UserData; your hook will be able to read back this information from the MenuEventMessage.&lt;br /&gt;
&lt;br /&gt;
A simple example of how to take advantage of the custom pick hook feature might be the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  uint32 id = NO_MENU_ID;&lt;br /&gt;
&lt;br /&gt;
  while ((id = IIntuition-&amp;gt;IDoMethod(menustripobj,MM_HANDLEPICK,&lt;br /&gt;
                                                  0,&lt;br /&gt;
                                                  id,&lt;br /&gt;
                                                  imsg-&amp;gt;IDCMPWindow,&lt;br /&gt;
                                                  mycustomdata)) != NO_MENU_ID)&lt;br /&gt;
  {&lt;br /&gt;
    /* This selected menu object doesn&#039;t have&lt;br /&gt;
     * a pick hook, so let&#039;s handle it here.&lt;br /&gt;
     */&lt;br /&gt;
    switch (id)&lt;br /&gt;
    {&lt;br /&gt;
      /* Process menu selections */&lt;br /&gt;
&lt;br /&gt;
      case MID_PROJECT_OPEN:&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It goes without saying that if none of your application&#039;s menuclass object has a pick hook or help hook, you never need to invoke MM_HANDLEPICK/MM_HANDLEHELP in your event handling code; you can just use the simpler MM_NEXTSELECT method and MA_MenuHelpID attribute instead, as covered in previous subsections.&lt;br /&gt;
&lt;br /&gt;
== Retrieving a menuclass object&#039;s address from its ID number ==&lt;br /&gt;
&lt;br /&gt;
Once you have found out which menu item was picked by the user by examining its ID, you may want to do some operations on the item object itself, and thus need its actual address. One way to achieve this could be to keep a look-up table in your application, initialized as the menu tree gets built, and using the IDs as indices. For instance, something like the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  MA_AddChild, mtable[MID_PROJECT_OPEN] = MItem(&amp;quot;Open...&amp;quot;),&lt;br /&gt;
    MA_ID, MID_PROJECT_OPEN,&lt;br /&gt;
  MEnd,&lt;br /&gt;
  MA_AddChild, mtable[MID_PROJECT_SAVE] = MItem(&amp;quot;Save&amp;quot;),&lt;br /&gt;
    MA_ID, MID_PROJECT_SAVE,&lt;br /&gt;
  MEnd,&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The drawbacks of such an approach are increased memory consumption and reduced code readability. A simpler solution is offered by menuclass in the form of the MM_FINDID method. This uses the mpFindID structure, defined as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpFindID&lt;br /&gt;
  {&lt;br /&gt;
      uint32 MethodID;&lt;br /&gt;
      uint32 mpfi_Reserved;&lt;br /&gt;
      uint32 mpfi_ID;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_FINDID, mpfi_Reserved should always be set to zero, while mpfi_ID is the ID of the menu object whose address you&#039;re looking for. Here&#039;s a simple example of this method&#039;s invocation on the menu root object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  m_obj = (Object *)IIntuition-&amp;gt;IDoMethod(menustripobj,MM_FINDID,0,id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a menu object with the specified ID is found within the menu tree, MM_FINDID will return its address, otherwise it will return NULL.&lt;br /&gt;
&lt;br /&gt;
The MM_FINDID method doesn&#039;t actually need to be invoked on the menu root as in the above example. It just searches for an object having a given ID within the menu (sub-)tree hanging from the object it&#039;s invoked on. So if you already know the address of an object whose local sub-tree contains the one you want, it is possible to invoke MM_FINDID directly on that object and restrict the search to only a part of the menu tree.&lt;br /&gt;
&lt;br /&gt;
Invoking the MM_FINDID method is the BOOPSI equivalent of calling ItemAddress() with traditional menus.&lt;br /&gt;
&lt;br /&gt;
== Enabling and disabling menus and menu items ==&lt;br /&gt;
&lt;br /&gt;
By default, a BOOPSI menu or item is enabled, meaning it looks clearly readable and can be selected by the user. When disabled, it is instead rendered with low contrast or a recessed appearance and is not selectable.&lt;br /&gt;
&lt;br /&gt;
The MA_Disabled attribute is used to control the enable state of BOOPSI menu or item objects. It is possible to disable a menuclass object right from the start by passing { MA_Disabled, TRUE } to NewObject(), or change the object&#039;s enable state later at any time via SetAttrs(). Setting MA_Disabled to TRUE or FALSE is the BOOPSI equivalent of calling OnMenu() or OffMenu() on old-style menus.&lt;br /&gt;
&lt;br /&gt;
Disabling a menu or a menu item will make all of its children show up disabled as well, down to the deepest sub-menu levels. Additionally, you can disable all your menus at once (i.e. make the entire menu tree disabled and not selectable) simply by disabling the menu strip object.&lt;br /&gt;
&lt;br /&gt;
Another way to examine and modify the enable state of a menu or menu item is to invoke the MM_GETSTATE and MM_SETSTATE methods (see below for more on this).&lt;br /&gt;
&lt;br /&gt;
== Selection state of toggle select and mutual exclude items ==&lt;br /&gt;
&lt;br /&gt;
Whenever a toggle select or mutual exclude menu item is picked by the user, you can examine its selection state by reading the item&#039;s MA_Selected attribute. If its value is TRUE, the item is currently selected (checked) and is displayed with some small checkmark or radio button symbol drawn in front of it.&lt;br /&gt;
&lt;br /&gt;
You can also programmatically change the selection state of this kind of items at any time by setting the value of MA_Selected with SetAttrs(), without having to clear and then reset the menu strip as you would with non-BOOPSI menus.&lt;br /&gt;
&lt;br /&gt;
This attribute has no meaning for action items, i.e. items that don&#039;t represent the &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; state of some option.&lt;br /&gt;
&lt;br /&gt;
Another way to examine and modify the selection state of a menu or menu item is to invoke the MM_GETSTATE and MM_SETSTATE methods (see below for more on this).&lt;br /&gt;
&lt;br /&gt;
== Methods for controlling the enable and selection state of menu items ==&lt;br /&gt;
&lt;br /&gt;
It is possible to examine and modify the enable state of a menu or menu item by invoking the MM_GETSTATE and MM_SETSTATE methods. These may prove more handy in many cases since they take an ID number to specify the target object instead of the object&#039;s actual address. This means they can be directly fed the result of MM_NEXTSELECT without having to go through MM_FINDID first.&lt;br /&gt;
&lt;br /&gt;
Both these methods use the bitmask constants MS_CHECKED and MS_DISABLED to read or modify the corresponding MA_Selected and MA_Disabled attributes.&lt;br /&gt;
&lt;br /&gt;
The MM_GETSTATE method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpGetState&lt;br /&gt;
  {&lt;br /&gt;
      uint32 MethodID;&lt;br /&gt;
      uint32 mpgs_Reserved;&lt;br /&gt;
      uint32 mpgs_ID;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_GETSTATE, mpgs_Reserved should always be set to zero, and mpgs_ID is the ID number of the menuclass object whose state you want to read. The method should be invoked on the menu root object (or on the parent of the target object, if you have its address) and will return a bit mask whose value can be MS_CHECKED, MS_DISABLED, both constants ORed together, or zero. For instance, if MS_CHECKED is set in the method&#039;s return value it means that the object&#039;s MA_Selected attribute is currently TRUE.&lt;br /&gt;
&lt;br /&gt;
The MM_SETSTATE method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpSetState&lt;br /&gt;
  {&lt;br /&gt;
      uint32 MethodID;&lt;br /&gt;
      uint32 mpss_Reserved;&lt;br /&gt;
      uint32 mpss_ID;&lt;br /&gt;
      uint32 mpss_ApplyMask;&lt;br /&gt;
      uint32 mpss_StateMask;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the first three fields are analogous to those of the mpGetState structure while mpss_ApplyMask is used to specify which attribute(s) should be modified, and mpss_StateMask contains the new values. For instance, if MS_DISABLED is set in mpss_ApplyMask, but not in mpss_StateMask, the object will become enabled as its MA_Disabled attribute will be set to FALSE. If MS_CHECKED is cleared in the mpss_ApplyMask field, on the other hand, the object&#039;s selection state will stay unchanged no matter what the value of mpss_StateMask is.&lt;br /&gt;
&lt;br /&gt;
== Disposing of the menu tree ==&lt;br /&gt;
&lt;br /&gt;
Due to its dynamic nature, usually a BOOPSI menu tree doesn&#039;t need to be freed and rebuilt by your application on environment changes or each time it has to undergo some significant modification. You may for instance reuse the same menu tree on different screens (as long as it&#039;s used on one screen at a time).&lt;br /&gt;
&lt;br /&gt;
This means that all important state information stored in the menu tree such as which items are checked, disabled, etc. is never lost while the application is running, and that the menu tree can be disposed of just once on program&#039;s exit.&lt;br /&gt;
&lt;br /&gt;
To free an entire menu tree, you just have to call DisposeObject() on its root object. Whatever disposable object you attached to some menu item, such as e.g. a BOOPSI image, will be freed as well unless you asked not to do so.&lt;br /&gt;
&lt;br /&gt;
= Further menuclass features =&lt;br /&gt;
&lt;br /&gt;
This section will outline a number of attributes and methods giving access to some more specialized functionality of menuclass.&lt;br /&gt;
&lt;br /&gt;
== Specifying the menu font ==&lt;br /&gt;
&lt;br /&gt;
Each menu item object can be displayed in its own font and size, although this is strongly discouraged as such an arrangement would look quite unprofessional. Still, if you for any reason need to set the font used by a menu item, you can do so with the MA_TextAttr attribute.&lt;br /&gt;
&lt;br /&gt;
The font specified this way will be opened and closed by menuclass as needed.&lt;br /&gt;
&lt;br /&gt;
If you set MA_TextAttr for a menuclass object, it will be inherited by all of its children. To specify a font for the entire menu tree, therefore, just set MA_TextAttr for the menu root object.&lt;br /&gt;
&lt;br /&gt;
Whenever there isn&#039;t any specific reason to do otherwise, it&#039;s recommended not to use MA_TextAttr at all and just let the menus be displayed in the font that was chosen in the user&#039;s preferences for the screen they appear on.&lt;br /&gt;
&lt;br /&gt;
== Adding images to menu items ==&lt;br /&gt;
&lt;br /&gt;
A BOOPSI menu item can have an image displayed at the left side of (or in place of) the item&#039;s label. The image is specified through the MA_Image attribute; it may be either traditional or BOOPSI.&lt;br /&gt;
&lt;br /&gt;
The item&#039;s height is adjusted if needed to make enough room for the image.&lt;br /&gt;
&lt;br /&gt;
If the image is BOOPSI, it will be automatically disposed of when the menu item object is; to avoid that, you should set the item&#039;s MA_FreeImage attribute to FALSE. However, if you want to prevent automatic disposal for ALL images in the menu tree, it is more practical to set MA_FreeImage to FALSE for the menu root object since this way it will be inherited by all objects.&lt;br /&gt;
&lt;br /&gt;
If the menu item has no label (i.e. it&#039;s an image-only item), you can still use MA_TextAttr to specify the font used for its keyboard shortcut character.&lt;br /&gt;
&lt;br /&gt;
== Hiding menus and menu items ==&lt;br /&gt;
&lt;br /&gt;
Any member of a BOOPSI menu tree can be hidden or revealed again at any time by your application. A hidden menuclass object is still physically present in the menu tree, but does not appear on the screen when menus are displayed, as if it had been actually removed.&lt;br /&gt;
&lt;br /&gt;
The MA_Hidden attribute is used to control the hiding of a BOOPSI menu or item objects. It is possible to make a menu or item hidden right from the start by passing { MA_Hidden, TRUE } to NewObject(), or change the object&#039;s hiding state later via SetAttrs().&lt;br /&gt;
&lt;br /&gt;
Hiding a menu or a menu item will (obviously) hide all of its children as well. Unlike disabling, however, you cannot hide the menu strip object -- if you want to make the entire menu tree disappear, just call ClearMenuStrip().&lt;br /&gt;
&lt;br /&gt;
== Dynamic menu localization ==&lt;br /&gt;
&lt;br /&gt;
So far, we&#039;ve only examined the simple case where you pass an actual string as the value of a menuclass object&#039;s MA_Label attribute. There is however another possibility, and that is to specify a numeric index as MA_Label&#039;s value, which is later used to look up the appropriate string in the currently open catalog.&lt;br /&gt;
&lt;br /&gt;
To be able to pass an integer value for MA_Label instead of a string, you need to tell menuclass what is the numeric range of string IDs. Any value outside of that range will be considered a string, and used directly as such. Values lying in the range, on the other hand, will be treated as catalog indices. The string ID range is specified with the MA_MinStringID and MA_MaxStringID attributes. (By default, the string ID range is empty and MA_Label is always interpreted as a string.)&lt;br /&gt;
&lt;br /&gt;
Whenever an object&#039;s MA_Label attribute is actually a numeric index, its value is passed on each menu layout to a &amp;quot;localization hook&amp;quot; which will convert it to the correct string for the current language and charset.&lt;br /&gt;
&lt;br /&gt;
The localization hook (aka string hook) is supplied by your application via the MA_StringHook attribute. You can simply set it for the root object, and it will be inherited by all objects in the menu tree.&lt;br /&gt;
&lt;br /&gt;
Your hook will be invoked as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  HookFunction(struct Hook *hook, Object *obj, struct MenuStringMessage *msg)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &#039;obj&#039; is the menu or item the string ID to be converted belongs to, and &#039;msg&#039; is a pointer to a MenuStringMessage structure (see below for definition) which carries the string ID.&lt;br /&gt;
&lt;br /&gt;
You can also specify the address of an open catalog and/or a charset by setting the MA_Catalog and MA_CharSet attributes for the menu root object; if supplied, this information is also passed back to the hook in the MenuStringMessage.&lt;br /&gt;
&lt;br /&gt;
The MenuStringMessage structure is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct MenuStringMessage&lt;br /&gt;
{&lt;br /&gt;
    uint32 StructSize;        /* For future expansion */&lt;br /&gt;
    uint32 StringID;          /* The string ID number */&lt;br /&gt;
    struct Catalog *Catalog;  /* Catalog pointer, may be NULL */&lt;br /&gt;
    uint32 CharSet;           /* Charset number, may be zero */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your string hook should return the correct catalog string for the passed string ID, or a default string if no catalog is available.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=All of the above is equally valid for the MA_Key attribute, which can be a string ID instead of an actual string just like MA_Label. Its string ID range is the same one of MA_Label (as defined by MA_MinStringID and MA_MaxStringID).}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Also Note|text=If you don&#039;t use a string hook, MA_Label and MA_Key should always be real strings and not numeric IDs, otherwise they&#039;ll get converted by menuclass to &amp;quot;????&amp;quot;, which isn&#039;t very meaningful. The same will happen if the string hook returns a NULL string pointer.}}&lt;br /&gt;
&lt;br /&gt;
== Adding and removing menus and menu items ==&lt;br /&gt;
&lt;br /&gt;
A BOOPSI menu tree built with menuclass allows for dynamic addition and removal of menus and items at any time. As with other modifications, there&#039;s no need to detach the menu tree from the window in order to perform these operations; it&#039;s menuclass that takes care of synchronizing each change with Intuition and doing any necessary relayout afterwards.&lt;br /&gt;
&lt;br /&gt;
To add a child to a menuclass object, you can simply call SetAttrs() to pass it the MA_AddChild tag with the address of the new child as data, the same way you would at OM_NEW time.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of adding a sub-item to an item:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  /* Append a new entry to our &amp;quot;recent files&amp;quot; sub-menu */&lt;br /&gt;
&lt;br /&gt;
  new_item = MItem(filename), MA_ID, new_id++, MEnd;&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;SetAttrs(recentfiles_itemobj,MA_AddChild,new_item,TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A NULL pointer can safely be passed with MA_AddChild, and is simply ignored.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In this example we&#039;re using a simple counter variable to generate an unique ID for each new sub-item -- we don&#039;t care about the ID value itself, we simply want to take notice if the item gets selected, and be able to retrieve its address with MM_FINDID whenever needed. This requires the new item to have a valid and unique ID number.}}&lt;br /&gt;
&lt;br /&gt;
Just like they can be added, menus and menu items can also be removed from the menu tree as needed. This is achieved by passing the MA_RemoveChild tag to the parent menu object via SetAttrs(), with the address of the child as data.&lt;br /&gt;
&lt;br /&gt;
For instance, we could remove an item from a menu this way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  /* Close a window and remove its entry from our &amp;quot;open windows&amp;quot; menu */&lt;br /&gt;
&lt;br /&gt;
  id = (uint32)window-&amp;gt;UserData;  /* We stored the menu item&#039;s ID there */&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;CloseWindow(window);&lt;br /&gt;
&lt;br /&gt;
  item = (Object *)IIntuition-&amp;gt;IDoMethod(openwindows_menuobj,MM_FINDID,0,id);&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;SetAttrs(openwindows_menuobj,MA_RemoveChild,item,TAG_END);&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;DisposeObject(item);  /* Accepts a NULL argument */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with MA_AddChild, you can safely pass a NULL pointer with MA_RemoveChild.&lt;br /&gt;
&lt;br /&gt;
Another way to perform addition and removal of children with menuclass objects is to use rootclass&#039; OM_ADDMEMBER and OM_REMMEMBER methods. The above examples could thus be rewritten by replacing the SetAttrs() calls with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  IIntuition-&amp;gt;IDoMethod(recentfiles_itemobj,OM_ADDMEMBER,new_item);&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;IDoMethod(openwindows_menuobj,OM_REMMEMBER,item);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you would when dealing with Exec lists, you must be careful to avoid adding or removing the same child twice, or removing a child which was never added in the first place, since doing so will cause memory corruption.&lt;br /&gt;
&lt;br /&gt;
== Browsing through all children of a menu object ==&lt;br /&gt;
&lt;br /&gt;
You can obtain the addresses of all children of a menuclass object in sequence by invoking the MM_NEXTCHILD method in a loop. Once all the object&#039;s children have been been retrieved, MM_NEXTCHILD will return NULL to signal there are no more children.&lt;br /&gt;
&lt;br /&gt;
The MM_NEXTCHILD method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpNextChild&lt;br /&gt;
  {&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    uint32 mpnc_Reserved;&lt;br /&gt;
    Object *mpnc_Current;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_NEXTCHILD, mpnc_Reserved should always be set to zero, and mpnc_Current is the child object address the method did return on the previous invocation. Pass NULL in mpnc_Current to get the address of the first child.&lt;br /&gt;
&lt;br /&gt;
An example of a loop retrieving all items of a menu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  Object *obj = NULL;&lt;br /&gt;
&lt;br /&gt;
  while ((obj = (Object *)IIntuition-&amp;gt;IDoMethod(menuobj,MM_NEXTCHILD,0,obj)) != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    /* Do something with the item object */&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you use MM_NEXTCHILD in a loop to remove an object&#039;s children one by one, be careful not to pass the method the address of an already removed object.&lt;br /&gt;
&lt;br /&gt;
The correct way to do a sequential child removal is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  while ((obj = (Object *)IIntuition-&amp;gt;IDoMethod(menuobj,MM_NEXTCHILD,0,NULL)) != NULL)&lt;br /&gt;
  {&lt;br /&gt;
     IIntuition-&amp;gt;IDoMethod(menuobj,OM_REMMEMBER,obj);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is analogous to performing a RemHead() loop on an Exec list.&lt;br /&gt;
&lt;br /&gt;
== Scanning the menu tree ==&lt;br /&gt;
&lt;br /&gt;
You can perform a scan of the whole menu tree, or even just a subsection of it, and have a custom function called on each object that is part of it. This makes it possible to implement any kind of operation that is not already provided by the existing menuclass methods.&lt;br /&gt;
&lt;br /&gt;
To scan a menu (sub-)tree, you invoke the MM_SCAN method on its root object and specify a callback function and some parameters that will be passed back to it on each call. The MM_SCAN method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpScan&lt;br /&gt;
  {&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    uint32 mps_Reserved;&lt;br /&gt;
    struct Hook *mps_Hook;&lt;br /&gt;
    uint32 mps_Args[4];&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_SCAN, mps_Reserved should always be set to zero, mps_Hook is a pointer to your custom hook, and mps_Args[0] to mps_Args[3] are (optional) arguments for the hook&#039;s function. Your hook will be invoked as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  HookFunction(struct Hook *hook, Object *obj, struct MenuScanMessage *msg)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &#039;obj&#039; is a menuclass object (whose exact type you can inspect by reading its MA_Type attribute) and &#039;msg&#039; is a pointer to a MenuScanMessage structure.&lt;br /&gt;
&lt;br /&gt;
The MenuScanMessage structure is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct MenuScanMessage&lt;br /&gt;
  {&lt;br /&gt;
    uint32 StructSize;  /* For future expansion */&lt;br /&gt;
    int32 Level;        /* How deep we are in the menu tree */&lt;br /&gt;
    uint32 Args[4];     /* Custom arguments */&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Level field indicates the depth in the tree of the current object, starting from -1 for the root object. The four Args variables hold the same values which were passed upon invocation of the MM_SCAN method and can be used to feed your custom arguments back to the hook.&lt;br /&gt;
&lt;br /&gt;
If your hook returns a non-zero value, the scan stops with the method returning the address of the object on which it stopped. If the hook always returns zero, all objects are scanned and the MM_SCAN method returns NULL.&lt;br /&gt;
&lt;br /&gt;
An example of this method&#039;s usage could be the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  /* A simple function to count how many items a menu has, using MM_SCAN */&lt;br /&gt;
&lt;br /&gt;
  uint32 HookFunction(struct Hook *h, Object *o, struct MenuScanMessage *msg)&lt;br /&gt;
  {&lt;br /&gt;
     if (msg-&amp;gt;Level == 1)&lt;br /&gt;
        *((uint32 *)msg-&amp;gt;Args[0]) += 1;&lt;br /&gt;
     return (0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  uint32 CountMenuItems(Object *menuobj)&lt;br /&gt;
  {&lt;br /&gt;
     struct Hook hook;&lt;br /&gt;
     uint32 count = 0;&lt;br /&gt;
&lt;br /&gt;
     memset(&amp;amp;hook,0,sizeof(struct Hook));&lt;br /&gt;
&lt;br /&gt;
     hook.h_Entry = (HOOKFUNC)HookFunction;&lt;br /&gt;
&lt;br /&gt;
     IIntuition-&amp;gt;IDoMethod(menuobj,MM_SCAN,0,&amp;amp;hook,&amp;amp;count,0,0,0);&lt;br /&gt;
&lt;br /&gt;
     return (count);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example we pass as the only custom argument the address of an variable which, once the MM_SCAN method returns, will hold the amount of children of the specified menu object. In the hook function we check that the object&#039;s level is 1, to make sure we only take it into account if it is a direct child of a menu (menus have a level of zero), rather than being a sub-item of an item, or even the menu object itself on which the method is invoked.&lt;br /&gt;
&lt;br /&gt;
== Generating a whole menu (sub-)tree from a single tag list ==&lt;br /&gt;
&lt;br /&gt;
So far we have only seen menu trees built by way of a series of nested calls to NewObject(). This is certainly a working approach to putting together a tree of BOOPSI objects organized in a hierarchy. Still, it presents a few drawbacks:&lt;br /&gt;
&lt;br /&gt;
* It can consume a significant amount of stack with very deep trees;&lt;br /&gt;
* It may produce code which is hard to read unless special macros are used;&lt;br /&gt;
* It makes error checking not very easy.&lt;br /&gt;
&lt;br /&gt;
All in all, nesting NewObject() calls is quite acceptable with relatively small menu trees, while it can become less manageable as the tree&#039;s size grows.&lt;br /&gt;
&lt;br /&gt;
A different technique for building menu trees, which attempts to overcome these issues, is provided by menuclass&#039; MM_NEWMENU method. This allows to generate an entire tree, or a sub-tree, out of a flat description of its structure given in the form of a single tag list.&lt;br /&gt;
&lt;br /&gt;
The MM_NEWMENU method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpNewMenu&lt;br /&gt;
  {&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    uint32 mpnm_Reserved;&lt;br /&gt;
    struct TagItem *mpnm_AttrList;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_NEWMENU, mpnm_Reserved should always be set to zero, and mpnm_AttrList is the tag list describing the menu tree to be generated.&lt;br /&gt;
&lt;br /&gt;
Said tag list is made up for the most part of the usual menuclass tags that are used to specify attributes of the individual objects; however, the menu objects themselves are introduced by special tags prepended with NM_ which are actually directives for the method.&lt;br /&gt;
&lt;br /&gt;
MM_NEWMENU understands three directives: NM_Menu / NM_Item, indicating to add a new menu or item object at the current level (the tag data is the object&#039;s text label), and NM_SubItems, indicating to move down or up by one level before the next item object is added. If the tag data of NM_SubItems is SI_BEGIN, the next item will become a child of the most recent one added (i.e. we move down by one level); if it is SI_END, the next item will become a sibling of the most recent one&#039;s parent (i.e. we move up by one level).&lt;br /&gt;
&lt;br /&gt;
{{Note|text=NM_SubItems is not needed to add menus to a menu strip object, nor to add items to a menu object -- its only purpose is to act as a &amp;quot;delimiter&amp;quot; for a list of sub-items of an item object.}}&lt;br /&gt;
&lt;br /&gt;
Any menuclass tag that is encountered in the tag list is passed as an attribute to the most recently added object.&lt;br /&gt;
&lt;br /&gt;
The method should be invoked on an existing menuclass object, which in case of success will become the new tree&#039;s root. MM_NEWMENU returns FALSE to indicate failure, the exact reasons for which can be inspected through the MA_ErrorCode and MA_ErrorTagItem tags. See &amp;lt;intuition/menuclass.h&amp;gt; for a list of menuclass error codes and their explanation.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a simple example to illustrate the method&#039;s usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  /* Service function allowing to pass varargs tag list to MM_NEWMENU&lt;br /&gt;
   */&lt;br /&gt;
  static BOOL VARARGS68K DoNewMenu(Object *root, ...)&lt;br /&gt;
  {&lt;br /&gt;
    va_list ap;&lt;br /&gt;
    struct TagItem *tags;&lt;br /&gt;
    BOOL result;&lt;br /&gt;
&lt;br /&gt;
    va_startlinear(ap,root);&lt;br /&gt;
    tags = (struct TagItem *)va_getlinearva(ap,struct TagItem *);&lt;br /&gt;
    result = IIntuition-&amp;gt;IDoMethod(root,MM_NEWMENU,0,tags);&lt;br /&gt;
    va_end(ap);&lt;br /&gt;
&lt;br /&gt;
    return (result);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* Build a menu tree with the MM_NEWMENU method&lt;br /&gt;
   */&lt;br /&gt;
  Object *BuildMenuTree(void)&lt;br /&gt;
  {&lt;br /&gt;
    Object *menustripobj;&lt;br /&gt;
    uint32 success, error;&lt;br /&gt;
    struct TagItem *error_ti;&lt;br /&gt;
&lt;br /&gt;
    menustripobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;menuclass&amp;quot;,MA_Type,T_ROOT,TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (menustripobj)&lt;br /&gt;
    {&lt;br /&gt;
      success = DoNewMenu(menustripobj,&lt;br /&gt;
        MA_ErrorCode, &amp;amp;error,&lt;br /&gt;
        MA_ErrorTagItem, &amp;amp;error_ti,&lt;br /&gt;
        NM_Menu, &amp;quot;Project&amp;quot;,         MA_ID, MID_PROJECT,&lt;br /&gt;
          NM_Item, &amp;quot;O|Open&amp;quot;,        MA_ID, MID_OPEN,&lt;br /&gt;
          NM_Item, &amp;quot;S|Save&amp;quot;,        MA_ID, MID_SAVE,&lt;br /&gt;
          NM_Item, &amp;quot;A|Save as...&amp;quot;,  MA_ID, MID_SAVEAS,&lt;br /&gt;
          NM_Item, ML_SEPARATOR,&lt;br /&gt;
          NM_Item, &amp;quot;Q|Quit&amp;quot;,        MA_ID, MID_QUIT,&lt;br /&gt;
        NM_Menu, &amp;quot;Options&amp;quot;,         MA_ID, MID_OPTIONS,&lt;br /&gt;
          NM_Item, &amp;quot;F|Font...&amp;quot;,     MA_ID, MID_FONT,&lt;br /&gt;
          NM_Item, &amp;quot;Style&amp;quot;,         MA_ID, MID_STYLE,&lt;br /&gt;
            NM_SubItems, SI_BEGIN,&lt;br /&gt;
            NM_Item, &amp;quot;B|Bold&amp;quot;,      MA_ID, MID_BOLD,      MA_Toggle, TRUE,&lt;br /&gt;
            NM_Item, &amp;quot;I|Italic&amp;quot;,    MA_ID, MID_ITALIC,    MA_Toggle, TRUE,&lt;br /&gt;
            NM_Item, &amp;quot;U|Underline&amp;quot;, MA_ID, MID_UNDERLINE, MA_Toggle, TRUE,&lt;br /&gt;
            NM_SubItems, SI_END,&lt;br /&gt;
          NM_Item, &amp;quot;W|Word wrap&amp;quot;,   MA_ID, MID_WORDWRAP,  MA_Toggle, TRUE,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
      if (!success)&lt;br /&gt;
      {&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Failed with error %ld, at tag %08lX\n&amp;quot;,error,error_ti-&amp;gt;ti_Tag);&lt;br /&gt;
        IIntuition-&amp;gt;IDoMethod(menustripobj,MM_DELETEMENU,0);&lt;br /&gt;
        IIntuition-&amp;gt;DisposeObject(menustripobj);&lt;br /&gt;
        menustripobj = NULL;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (menustripobj);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example we make use of a small service function to pass the tag list on the stack as varargs; of course the tag list can also be allocated statically. Also, the above tree is rather small for the sake of keeping the example short, so it does not benefit much from using MM_NEWMENU instead of nested NewObject() calls, but obviously the same technique can be used to build trees of any size.&lt;br /&gt;
&lt;br /&gt;
As you can see, with MM_NEWMENU you can write compact and quite readable code to describe your menu trees, without having to resort to using macros on top of nested calls. The tree&#039;s description can even be made somewhat reminiscent of traditional NewMenu arrays as used with GadTools, which may look more familiar to long-time developers.&lt;br /&gt;
&lt;br /&gt;
To free a menu (sub-)tree generated with MM_NEWMENU, you invoke MM_DELETEMENU on the root object. This method uses the following message structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct mpDeleteMenu&lt;br /&gt;
  {&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    uint32 mpdm_Reserved;&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where MethodID is MM_DELETEMENU, and mpdm_Reserved should (as usual) be set to zero. Therefore, the correct way to invoke the method is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  IIntuition-&amp;gt;IDoMethod(menustripobj,MM_DELETEMENU,0);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=This call only disposes of all of the object&#039;s children, not of the root object itself. This way you can reuse the object in case you need to build another menu tree with MM_NEWMENU. To free the root object (and its children if you didn&#039;t already do that), simply use DisposeObject().}}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Preferences&amp;diff=12565</id>
		<title>Preferences</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Preferences&amp;diff=12565"/>
		<updated>2025-01-26T19:36:36Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP}}&lt;br /&gt;
== Preferences ==&lt;br /&gt;
&lt;br /&gt;
To make the Amiga operating system easily configurable by the user, the OS comes with a family of editors and associated data files known collectively as Preferences. Preferences allows the user to set system-wide configuration options such as the printer driver to use, serial port baud rate and other items. To make an application appealing to the user, the system-wide Preferences settings should be respected as much as possible by applications. This article describes how to use the Preferences system in your programs.&lt;br /&gt;
&lt;br /&gt;
== Preferences Editors and Storage ==&lt;br /&gt;
&lt;br /&gt;
In AmigaOS there can be any number of Preferences editors each with its own separate configuration file covering a specific area. All these Preferences editors have the same look and feel. Using separate Preferences editors and configuration files allows for adding new Preferences items (and editors) in future versions of the OS.&lt;br /&gt;
&lt;br /&gt;
Preferences are store in various &amp;quot;.prefs&amp;quot; files located in the &amp;quot;ENV:sys&amp;quot; and &amp;quot;ENVARC:sys&amp;quot; directories. Preferences options currently in use are located in &amp;quot;ENV:sys&amp;quot;. Permanent, saved copies of Preferences files are stored in &amp;quot;ENVARC:sys&amp;quot;. These are copied to &amp;quot;ENV:&amp;quot; on an as-needed basis. Applications may also store their own preference files in &amp;quot;ENV:&amp;quot; but should use a sub-directory for that purpose.&lt;br /&gt;
&lt;br /&gt;
Currently the following Preferences editors and files are available:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Preferences Editors&lt;br /&gt;
! Preferences Editor&lt;br /&gt;
! Preferences Configuration File&lt;br /&gt;
|-&lt;br /&gt;
| IControl&lt;br /&gt;
| icontrol.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Input&lt;br /&gt;
| input.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Palette&lt;br /&gt;
| palette.ilbm&lt;br /&gt;
|-&lt;br /&gt;
| Pointer&lt;br /&gt;
| pointer.ilbm&lt;br /&gt;
|-&lt;br /&gt;
| Printer&lt;br /&gt;
| printer.prefs&lt;br /&gt;
|-&lt;br /&gt;
| PrinterGfx&lt;br /&gt;
| printergfx.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Overscan&lt;br /&gt;
| overscan.prefs&lt;br /&gt;
|-&lt;br /&gt;
| ScreenMode&lt;br /&gt;
| screenmode.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Serial&lt;br /&gt;
| serial.prefs&lt;br /&gt;
|-&lt;br /&gt;
| -&lt;br /&gt;
| wbconfig.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Font&lt;br /&gt;
| wbfont.prefs, sysfont.prefs and screenfont.prefs&lt;br /&gt;
|-&lt;br /&gt;
| Time&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| WBPattern&lt;br /&gt;
| wb.pat and win.pat&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each .prefs file is managed by editor with the same name, except for wbconfig.prefs, which is written directly by Workbench and has no editor. One Preferences editor has no .prefs file, Time. That Preferences editor writes directly to the battery backed clock.&lt;br /&gt;
&lt;br /&gt;
When the user makes a change to a Preferences item with one of the editors, the changes will be saved in either &amp;quot;ENV:sys&amp;quot; or &#039;&#039;both&#039;&#039; &amp;quot;ENV:sys&amp;quot; and &amp;quot;ENVARC:sys&amp;quot; depending on whether the user saves the changes with the &amp;quot;Use&amp;quot; gadget or &amp;quot;Save&amp;quot; gadget of the Preferences editor.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Use&amp;quot; gadget is for making temporary changes and the new preferences will be stored only in &amp;quot;ENV:sys&amp;quot;. If the user reboots, the old preferences will be restored from the permanent copy in &amp;quot;ENVARC:sys&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Save&amp;quot; gadget is for making permanent changes and the new preferences will be stored in both &amp;quot;ENV:sys&amp;quot; and &amp;quot;ENVARC:sys&amp;quot;. That way, if the user reboots, the new preferences will still be in effect since the system looks in &amp;quot;ENVARC:sys&amp;quot; to find out what preferences should be set to at boot time.&lt;br /&gt;
&lt;br /&gt;
=== The ENV: Directory and Notification ===&lt;br /&gt;
&lt;br /&gt;
One advantage of the new Preferences system is file notification. File notification is a form of inter-process communication that allows an application to be automatically notified if a change is made to a specific file or directory. This makes it easy for the application to react to changes the user makes to Preferences files. See [[Notification]] for more complete information about the notification feature.&lt;br /&gt;
&lt;br /&gt;
File notification is also used by the system itself. The Preferences control program, IPrefs, sets up notification on most of the Preferences files in &amp;quot;ENV:sys&amp;quot;. If the user alters a Preferences item (normally this is done with a Preferences editor), the system will notify IPrefs about the change and IPrefs will attempt to alter the user&#039;s environment to reflect the change.&lt;br /&gt;
&lt;br /&gt;
For example, if the user opens the ScreenMode Preferences editor and changes the Workbench screen mode to high-resolution, the new settings are saved in &amp;quot;screenmode.prefs&amp;quot; in the &amp;quot;ENV:sys&amp;quot; directory. IPrefs sets up notification on this file at boot time, so the file system will notify IPrefs of the change. IPrefs will read in the Screenmode.prefs file and reset the Workbench screen to high resolution mode.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a short example showing how to set up notification on the &amp;quot;serial.prefs&amp;quot; file in &amp;quot;ENV:sys&amp;quot;. The program displays a message in a window whenever this file is changed (e.g., when the user selects the &amp;quot;Use&amp;quot; or &amp;quot;Save&amp;quot; gadget in the Serial Preferences editor).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** prefnotify.c - notified if serial prefs change&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/notify.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define PREFSFILENAME &amp;quot;ENV:sys/serial.prefs&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  /* Allocate a NotifyRequest structure */&lt;br /&gt;
  struct NotifyRequest *notifyrequest = IExec-&amp;gt;AllocMem(sizeof(struct NotifyRequest), MEMF_CLEAR);&lt;br /&gt;
&lt;br /&gt;
  if (notifyrequest != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    /* And allocate a signalsbit */&lt;br /&gt;
    int8 signum;&lt;br /&gt;
    if ((signum = IExec-&amp;gt;AllocSignal(-1L)) != -1)&lt;br /&gt;
    {&lt;br /&gt;
      /* Initialize notifcation request */&lt;br /&gt;
      CONST_STRPTR filename = PREFSFILENAME;&lt;br /&gt;
      notifyrequest-&amp;gt;nr_Name = filename;&lt;br /&gt;
      notifyrequest-&amp;gt;nr_Flags = NRF_SEND_SIGNAL;&lt;br /&gt;
      /* Signal this task */&lt;br /&gt;
      notifyrequest-&amp;gt;nr_stuff.nr_Signal.nr_Task = (struct Task *) IExec-&amp;gt;FindTask(NULL);&lt;br /&gt;
      /* with this signals bit */&lt;br /&gt;
      notifyrequest-&amp;gt;nr_stuff.nr_Signal.nr_SignalNum = signum;&lt;br /&gt;
&lt;br /&gt;
      if ((IDOS-&amp;gt;StartNotify(notifyrequest)) == DOSTRUE)&lt;br /&gt;
      {&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Select Serial Prefs SAVE or USE to notify this program\n&amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;CTRL-C to exit\n\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BOOL done = FALSE;&lt;br /&gt;
&lt;br /&gt;
        /* Loop until Ctrl-C to exit */&lt;br /&gt;
        while(!done)&lt;br /&gt;
        {&lt;br /&gt;
          uint32 signals = IExec-&amp;gt;Wait(  (1L &amp;lt;&amp;lt; signum) | SIGBREAKF_CTRL_C  );&lt;br /&gt;
          if (signals &amp;amp; (1L &amp;lt;&amp;lt; signum))&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Notification signal received.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          if (signals &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
          {&lt;br /&gt;
            IDOS-&amp;gt;EndNotify(notifyrequest);&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t start notification\n&amp;quot;);&lt;br /&gt;
      IExec-&amp;gt;FreeSignal(signum);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;No signals available\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeMem(notifyrequest, sizeof(struct NotifyRequest));&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Not enough memory for NotifyRequest.\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Preference File Format ===&lt;br /&gt;
&lt;br /&gt;
To understand the format of Preferences files, you must be familiar with IFF file standard (see [[IFF_Standard|IFF Standard]] for the complete specification).&lt;br /&gt;
&lt;br /&gt;
In general all Preferences files are stored in the IFF format with a type of PREF (see the exceptions noted below). Each file contains at least two Chunks, a header Chunk and a data Chunk.&lt;br /&gt;
&lt;br /&gt;
==== The Header Chunk ====&lt;br /&gt;
&lt;br /&gt;
The PRHD header chunk, contains a PrefHeader structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct PrefHeader&lt;br /&gt;
{&lt;br /&gt;
    UBYTE ph_Version;&lt;br /&gt;
    UBYTE ph_Type;&lt;br /&gt;
    ULONG ph_Flags;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Currently all the fields are set to NULL. In future revisions these fields may be used to indicate a particular version and contents of a PREF chunk.&lt;br /&gt;
&lt;br /&gt;
==== The Data Chunk ====&lt;br /&gt;
&lt;br /&gt;
The data Chunk that follows the header Chunk depends on the kind of Preferences data the file contains. The types of Preferences data Chunks that are currently part of the system are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ IFF Chunk Types in Preferences Data Files&lt;br /&gt;
! Chunk Name&lt;br /&gt;
! Used With&lt;br /&gt;
|-&lt;br /&gt;
| FONT&lt;br /&gt;
| Fonts, used for all font Preferences files. In future the PrefHeader may indicate what the font is used for.&lt;br /&gt;
|-&lt;br /&gt;
| ICTL&lt;br /&gt;
| IControl&lt;br /&gt;
|-&lt;br /&gt;
| INPT&lt;br /&gt;
| Input&lt;br /&gt;
|-&lt;br /&gt;
| OSCN&lt;br /&gt;
| Overscan&lt;br /&gt;
|-&lt;br /&gt;
| PGFX&lt;br /&gt;
| PrinterGfx&lt;br /&gt;
|-&lt;br /&gt;
| PTXT&lt;br /&gt;
| PrinterText&lt;br /&gt;
|-&lt;br /&gt;
| SCRM&lt;br /&gt;
| ScreenMode&lt;br /&gt;
|-&lt;br /&gt;
| SERL&lt;br /&gt;
| Serial&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each chunk contains a structure applicable to the type.&lt;br /&gt;
&lt;br /&gt;
=== FONT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct FontPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG            fp_Reserved[4];&lt;br /&gt;
    UBYTE           fp_FrontPen;                /* Textcolor */&lt;br /&gt;
    UBYTE           fp_BackPen;                 /* Character background color */&lt;br /&gt;
    UBYTE           fp_DrawMode;&lt;br /&gt;
    struct TextAttr fp_TextAttr;&lt;br /&gt;
    BYTE            fp_Name[FONTNAMESIZE];      /* Font name */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ICTL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IControlPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG  ic_Reserved[4];        /* System reserved              */&lt;br /&gt;
    UWORD ic_TimeOut;            /* Verify timeout               */&lt;br /&gt;
    WORD  ic_MetaDrag;           /* Meta drag mouse event        */&lt;br /&gt;
    ULONG ic_Flags;              /* IControl flags (see below)   */&lt;br /&gt;
    UBYTE ic_WBtoFront;          /* CKey: WB to front            */&lt;br /&gt;
    UBYTE ic_FrontToBack;        /* CKey: front screen to back   */&lt;br /&gt;
    UBYTE ic_ReqTrue;            /* CKey: Requester TRUE         */&lt;br /&gt;
    UBYTE ic_ReqFalse;           /* CKey: Requester FALSE        */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ic_Flags field can have the following values:&lt;br /&gt;
&lt;br /&gt;
; ICF_COERCE_COLORS&lt;br /&gt;
: This indicates that a displaymode with a matching number of colors has preference over a correct aspect ration when screen coercing takes place.&lt;br /&gt;
&lt;br /&gt;
; ICF_COERCE_LACE&lt;br /&gt;
: This indicates that choosing an interlaced display mode is allowed when coercing screens. Otherwise a non-interlaced display mode will be selected.&lt;br /&gt;
&lt;br /&gt;
; ICF_STRGAD_FILTER&lt;br /&gt;
: This indicates that control characters should be filtered out of string gadget user input.&lt;br /&gt;
&lt;br /&gt;
; ICF_MENUSNAP&lt;br /&gt;
: This indicates that an auto-scroll screen should be snapped back to origin when the mouse menu-button is selected.&lt;br /&gt;
&lt;br /&gt;
Note that the command key values in the last four fields of the IControlPrefs structure are ANSI codes, not RAWKEY codes.&lt;br /&gt;
&lt;br /&gt;
=== INPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct InputPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG           ip_Reserved[4];&lt;br /&gt;
    UWORD          ip_PointerTicks; /* Sensitivity of the pointer */&lt;br /&gt;
    struct timeval ip_DoubleClick;  /* Interval between clicks */&lt;br /&gt;
    struct timeval ip_KeyRptDelay;  /* keyboard repeat delay   */&lt;br /&gt;
    struct timeval ip_KeyRptSpeed;  /* Keyboard repeat speed   */&lt;br /&gt;
    WORD           ip_MouseAccel;   /* Mouse acceleration      */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== OSCN ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct OverscanPrefs&lt;br /&gt;
{&lt;br /&gt;
    ULONG            os_Reserved[4];&lt;br /&gt;
    ULONG            os_DisplayID;       /* Displaymode ID */&lt;br /&gt;
    Point            os_ViewPos;         /* View X/Y Offset */&lt;br /&gt;
    Point            os_Text;            /* TEXT overscan dimension */&lt;br /&gt;
    struct Rectangle os_Standard;        /* STANDARD overscan dimension */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PGFX ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct PrinterGfxPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG  pg_Reserved[4];&lt;br /&gt;
    UWORD pg_Aspect;                      /* Horizontal or vertical */&lt;br /&gt;
    UWORD pg_Shade;                       /* B&amp;amp;amp;W, Greyscale, Color */&lt;br /&gt;
    UWORD pg_Image;                       /* Positive or negative image */&lt;br /&gt;
    WORD  pg_Threshold;                   /* Black threshold */&lt;br /&gt;
    UBYTE pg_ColorCorrect;                /* RGB color correction */&lt;br /&gt;
    UBYTE pg_Dimensions;                  /* Dimension type */&lt;br /&gt;
    UBYTE pg_Dithering;                   /* Type of dithering */&lt;br /&gt;
    UWORD pg_GraphicFlags;                /* Rastport dump flags */&lt;br /&gt;
    UBYTE pg_PrintDensity;                /* Print density 1 - 7 */&lt;br /&gt;
    UWORD pg_PrintMaxWidth;               /* Maximum width */&lt;br /&gt;
    UWORD pg_PrintMaxHeight;              /* Maximum height */&lt;br /&gt;
    UBYTE pg_PrintXOffset;                /* X Offset */&lt;br /&gt;
    UBYTE pg_PrintYOffset;                /* Y Offset */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The possible values of each field are defined in &amp;amp;lt;prefs/printergfx.h&amp;amp;gt;. Note that your application is responsible for checking if the supplied values are valid.&lt;br /&gt;
&lt;br /&gt;
=== PTXT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct PrinterTxtPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG  pt_Reserved[4];                /* System reserved            */&lt;br /&gt;
    UBYTE pt_Driver[DRIVERNAMESIZE];     /* printer driver filename    */&lt;br /&gt;
    UBYTE pt_Port;                       /* printer port connection    */&lt;br /&gt;
&lt;br /&gt;
    UWORD pt_PaperType;                  /* Fanfold or single */&lt;br /&gt;
    UWORD pt_PaperSize;                  /* Standard, Legal, A4, A3 etc. */&lt;br /&gt;
    UWORD pt_PaperLength;                /* Paper length in # of lines */&lt;br /&gt;
&lt;br /&gt;
    UWORD pt_Pitch;                      /* Pica or Elite */&lt;br /&gt;
    UWORD pt_Spacing;                    /* 6 or 8 LPI */&lt;br /&gt;
    UWORD pt_LeftMargin;                 /* Left margin */&lt;br /&gt;
    UWORD pt_RightMargin;                /* Right margin */&lt;br /&gt;
    UWORD pt_Quality;                    /* Draft or Letter */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SCRM ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct ScreenModePrefs&lt;br /&gt;
{&lt;br /&gt;
    ULONG sm_Reserved[4];&lt;br /&gt;
    ULONG sm_DisplayID;                    /* Displaymode ID */&lt;br /&gt;
    UWORD sm_Width;                        /* Screen width */&lt;br /&gt;
    UWORD sm_Height;                       /* Screen height */&lt;br /&gt;
    UWORD sm_Depth;                        /* Screen depth */&lt;br /&gt;
    UWORD sm_Control;                      /* BIT 0, Autoscroll yes/no */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SERL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SerialPrefs&lt;br /&gt;
{&lt;br /&gt;
    LONG  sp_Reserved[4];                /* System reserved                  */&lt;br /&gt;
    ULONG sp_BaudRate;                   /* Baud rate                        */&lt;br /&gt;
&lt;br /&gt;
    ULONG sp_InputBuffer;                /* Input buffer: 0 - 64K          */&lt;br /&gt;
    ULONG sp_OutputBuffer;               /* Future: Output: 0 - 64K, def 0 */&lt;br /&gt;
&lt;br /&gt;
    UBYTE sp_InputHandshake;             /* Input handshaking                */&lt;br /&gt;
    UBYTE sp_OutputHandshake;            /* Future: Output handshaking       */&lt;br /&gt;
&lt;br /&gt;
    UBYTE sp_Parity;                     /* Parity                           */&lt;br /&gt;
    UBYTE sp_BitsPerChar;                /* I/O bits per character           */&lt;br /&gt;
    UBYTE sp_StopBits;                   /* Stop bits                        */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Preferences File Formats ===&lt;br /&gt;
&lt;br /&gt;
Not every Preferences file is stored as an IFF file of type PREF. The palette.ilbm and pointer.ilbm files contain a regular ILBM FORM to store their imagery. The win.pat and wb.pat files use a raw format with 16 bytes reserved, followed by a WORD giving the total size of the pattern, a WORD giving the bitplane count, and byte arrays (currently 32 bytes) for each bitplane. The format of the wbconfig.prefs file is private.&lt;br /&gt;
&lt;br /&gt;
=== Reading a Preferences File ===&lt;br /&gt;
&lt;br /&gt;
The following example shows a way to read a Preferences file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** showprefs.c - parse and show some info from an IFF Preferences file&lt;br /&gt;
**&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/prefhdr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/font.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/icontrol.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/input.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/overscan.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/printergfx.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/printertxt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/screenmode.h&amp;gt;&lt;br /&gt;
#include &amp;lt;prefs/serial.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFF;&lt;br /&gt;
&lt;br /&gt;
static uint8 *IFFErrTxt[] = {&lt;br /&gt;
  &amp;quot;EOF&amp;quot;,    /* (end of file, not an error) */&lt;br /&gt;
  &amp;quot;EOC&amp;quot;,     /* (end of context, not an error) */&lt;br /&gt;
  &amp;quot;no lexical scope&amp;quot;,&lt;br /&gt;
  &amp;quot;insufficient memory&amp;quot;,&lt;br /&gt;
  &amp;quot;stream read error&amp;quot;,&lt;br /&gt;
  &amp;quot;stream write error&amp;quot;,&lt;br /&gt;
  &amp;quot;stream seek error&amp;quot;,&lt;br /&gt;
  &amp;quot;file corrupt&amp;quot;,&lt;br /&gt;
  &amp;quot;IFF syntax error&amp;quot;,&lt;br /&gt;
  &amp;quot;not an IFF file&amp;quot;,&lt;br /&gt;
  &amp;quot;required call-back hook missing&amp;quot;,&lt;br /&gt;
  NULL,     /* (return to client, never shown) */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct RDArgs *readargs;&lt;br /&gt;
  int32 rargs[2];&lt;br /&gt;
  struct IFFHandle *iffhandle;&lt;br /&gt;
  struct ContextNode *cnode;&lt;br /&gt;
  struct StoredProperty *hdrsp;&lt;br /&gt;
  struct StoredProperty *sp;&lt;br /&gt;
  uint8 *filename;&lt;br /&gt;
  int32 ifferror, error = 0, rc = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
  struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary (&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
  IIFF = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  &lt;br /&gt;
  if (IIFF != NULL) {&lt;br /&gt;
      if (readargs = IDOS-&amp;gt;ReadArgs(&amp;quot;FILE/A&amp;quot;, rargs, NULL)) {&lt;br /&gt;
&lt;br /&gt;
        /* Test later if valid */&lt;br /&gt;
        filename = (uint8 *)rargs[0];&lt;br /&gt;
&lt;br /&gt;
        /* allocate an IFF handle */&lt;br /&gt;
        if (iffhandle = IIFF-&amp;gt;AllocIFF()) {&lt;br /&gt;
          /* Open the file for reading */&lt;br /&gt;
          if (iffhandle-&amp;gt;iff_Stream = (int32)IDOS-&amp;gt;Open(filename, MODE_OLDFILE)) {&lt;br /&gt;
            /* initialize the iff handle */&lt;br /&gt;
            IIFF-&amp;gt;InitIFFasDOS (iffhandle);&lt;br /&gt;
            if ((ifferror = IIFF-&amp;gt;OpenIFF (iffhandle, IFFF_READ)) == 0) {&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_PRHD);&lt;br /&gt;
&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_FONT);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_ICTL);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_INPT);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_OSCN);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_PGFX);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_PTXT);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_SCRM);&lt;br /&gt;
              IIFF-&amp;gt;PropChunk(iffhandle, ID_PREF, ID_SERL);&lt;br /&gt;
&lt;br /&gt;
              for (;;) {&lt;br /&gt;
                ifferror = IIFF-&amp;gt;ParseIFF(iffhandle, IFFPARSE_STEP);&lt;br /&gt;
&lt;br /&gt;
                 if (ifferror == IFFERR_EOC)&lt;br /&gt;
                   continue;&lt;br /&gt;
                else if (ifferror)&lt;br /&gt;
                  break;&lt;br /&gt;
&lt;br /&gt;
                /* Do nothing is this is a PrefHeader chunk,&lt;br /&gt;
                 * we&#039;ll pop it later when there is a pref&lt;br /&gt;
                 * chunk.&lt;br /&gt;
                 */&lt;br /&gt;
                if (cnode = IIFF-&amp;gt;CurrentChunk(iffhandle))&lt;br /&gt;
                  if (cnode-&amp;gt;cn_ID == ID_PRHD || cnode-&amp;gt;cn_ID == ID_FORM)&lt;br /&gt;
                    continue;&lt;br /&gt;
&lt;br /&gt;
                /* Get the preferences header, stored previously */&lt;br /&gt;
                hdrsp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_PRHD);&lt;br /&gt;
&lt;br /&gt;
                if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_FONT)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;FrontPen:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_FrontPen);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;BackPen:   %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_BackPen);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;DrawMode:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_DrawMode);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;Font:      %s\n&amp;quot;,&lt;br /&gt;
                        (LONG)((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_Name);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;ta_YSize:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_TextAttr.ta_YSize);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;ta_Style:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_TextAttr.ta_Style);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;ta_Flags:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct FontPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;fp_TextAttr.ta_Flags);&lt;br /&gt;
                } else   if (sp = FindProp(iffhandle, ID_PREF, ID_ICTL)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;TimeOut:   %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_TimeOut);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;MetaDrag:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_MetaDrag);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;WBtoFront: %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_WBtoFront);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;FrontToBack: %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_FrontToBack);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;ReqTrue:    %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_ReqTrue);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;ReqFalse:    %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct IControlPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ic_ReqFalse);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_INPT)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;PointerTicks:      %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct InputPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ip_PointerTicks);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;DoubleClick/Secs:  %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct InputPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ip_DoubleClick.tv_secs);&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;DoubleClick/Micro: %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct InputPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;ip_DoubleClick.tv_micro);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_OSCN)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;DisplayID:  0x%lx\n&amp;quot;,&lt;br /&gt;
                        ((struct OverscanPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;os_DisplayID);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_PGFX)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;Aspect:     %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct PrinterGfxPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;pg_Aspect);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_PTXT)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;Driver:     %s\n&amp;quot;,&lt;br /&gt;
                        (LONG)((struct PrinterTxtPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;pt_Driver);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_SCRM)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;DisplayID:  0x%lx\n&amp;quot;,&lt;br /&gt;
                        ((struct ScreenModePrefs *)sp-&amp;gt;sp_Data)-&amp;gt;sm_DisplayID);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                } else if (sp = IIFF-&amp;gt;FindProp(iffhandle, ID_PREF, ID_SERL)) {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;BaudRate:   %ld\n&amp;quot;,&lt;br /&gt;
                        ((struct SerialPrefs *)sp-&amp;gt;sp_Data)-&amp;gt;sp_BaudRate);&lt;br /&gt;
                  /* etc */&lt;br /&gt;
                }&lt;br /&gt;
              }&lt;br /&gt;
&lt;br /&gt;
              IIFF-&amp;gt;CloseIFF(iffhandle);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            if (ifferror != IFFERR_EOF) {&lt;br /&gt;
              rargs[1] = (LONG)IFFErrTxt[-ifferror - 1];&lt;br /&gt;
              IDOS-&amp;gt;VFPrintf(IDOS-&amp;gt;Output(), &amp;quot;%s: %s\n&amp;quot;, rargs);&lt;br /&gt;
              rc = RETURN_FAIL;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Close(iffhandle-&amp;gt;iff_Stream);&lt;br /&gt;
          } else&lt;br /&gt;
            error = IoErr();&lt;br /&gt;
&lt;br /&gt;
          IIFF-&amp;gt;FreeIFF(iffhandle);&lt;br /&gt;
        } else {&lt;br /&gt;
          IDOS-&amp;gt;VFPrintf(Output(), &amp;quot;Can&#039;t allocate IFF handle\n&amp;quot;, NULL);&lt;br /&gt;
          rc = RETURN_FAIL;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
      } else&lt;br /&gt;
        error = IoErr();&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;SetIoErr(error);&lt;br /&gt;
      if (error) {&lt;br /&gt;
        rc = RETURN_FAIL;&lt;br /&gt;
        IDOS-&amp;gt;PrintFault(error, filename);&lt;br /&gt;
      }&lt;br /&gt;
  } else {&lt;br /&gt;
    rc = RETURN_FAIL;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIFF);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
  return(rc);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preferences in 1.3 and Older Versions of the OS ==&lt;br /&gt;
&lt;br /&gt;
In 1.3, the Preferences program allows the user to see and change many system wide parameters, like the Workbench colors, pointer image, printer settings etc. When a Preferences item is changed, the new setting can be used temporarily (until a reset occurs) or stored permanently in the &amp;quot;DEVS:system-configuration&amp;quot; file. Under 1.3, all Preferences items are stored in this file which the system reads at boot time to find out how to set various system-wide options.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Preferences *GetPrefs(struct Preferences *preferences, LONG size);&lt;br /&gt;
struct Preferences *GetDefPrefs(struct Preferences *preferences, LONG size);&lt;br /&gt;
struct Preferences *SetPrefs( struct Preferences *preferences, LONG size, BOOL inform );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
None of these functions should be used in any new programs. They are only present for backwards compatibility.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the system functions that relate to the use of Preferences. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| GetPrefs()&lt;br /&gt;
| Old 1.3 (V34) function for making a copy of the Preferences structure&lt;br /&gt;
|-&lt;br /&gt;
| SetPrefs()&lt;br /&gt;
| Old 1.3 (V34) function for overwriting Preferences with new data&lt;br /&gt;
|-&lt;br /&gt;
| GetDefPrefs()&lt;br /&gt;
| Old 1.3 (V34) function for copying default Preferences from ROM&lt;br /&gt;
|-&lt;br /&gt;
| StartNotify()&lt;br /&gt;
| DOS library function for monitoring a .prefs file for changes&lt;br /&gt;
|-&lt;br /&gt;
| EndNotify()&lt;br /&gt;
| Ends notification started with StartNotify()&lt;br /&gt;
|-&lt;br /&gt;
| AllocIFF()&lt;br /&gt;
| IFFParse library function that creates an IFFHandle for parsing&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasDOS()&lt;br /&gt;
| Initialize the IFFHandle as a DOS stream&lt;br /&gt;
|-&lt;br /&gt;
| OpenIFF()&lt;br /&gt;
| Initialize an IFFHandle for reading or writing a new stream&lt;br /&gt;
|-&lt;br /&gt;
| PropChunk()&lt;br /&gt;
| Specify a property chunk to store&lt;br /&gt;
|-&lt;br /&gt;
| ParseIFF()&lt;br /&gt;
| Parse an IFF file from the IFFHandle stream&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Returns the top level context of an IFF stream&lt;br /&gt;
|-&lt;br /&gt;
| FindProp()&lt;br /&gt;
| Search for a property chunk previously declared with PropChunk()&lt;br /&gt;
|-&lt;br /&gt;
| CloseIFF()&lt;br /&gt;
| Closes an IFF context opened with OpenIFF()&lt;br /&gt;
|-&lt;br /&gt;
| FreeIFF()&lt;br /&gt;
| Frees the IFFHandle created with AllocIFF()&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Console_Device&amp;diff=12564</id>
		<title>Console Device</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Console_Device&amp;diff=12564"/>
		<updated>2025-01-26T19:36:02Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Devices|Console]]&lt;br /&gt;
== Console Device ==&lt;br /&gt;
&lt;br /&gt;
The console device provides the text-oriented interface for Intuition windows. It acts like an enhanced ASCII terminal obeying many of the standard ANSI sequences as well as special sequences unique to the Amiga. The console device also provides a copy-and-paste facility and an internal character map to redraw a window when it is resized.&lt;br /&gt;
&lt;br /&gt;
== Console Device Commands and Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command&lt;br /&gt;
! Command Operation&lt;br /&gt;
|-&lt;br /&gt;
| CD_ASKDEFAULTKEYMAP || Get the current default keymap.&lt;br /&gt;
|-&lt;br /&gt;
| CD_ASKKEYMAP || Get the current key map structure for this console.&lt;br /&gt;
|-&lt;br /&gt;
| CD_ASKKEYMAP_POINTERS || Get pointers to the current key map structures for this console. (V51.19)&lt;br /&gt;
|-&lt;br /&gt;
| CD_GETATTRS || Get attributes for this console. (V53)&lt;br /&gt;
|-&lt;br /&gt;
| CD_SETATTRS || Set attributes for this console. (V53)&lt;br /&gt;
|-&lt;br /&gt;
| CD_SETDEFAULTKEYMAP || Set the current default keymap.&lt;br /&gt;
|-&lt;br /&gt;
| CD_SETKEYMAP || Set the current key map structure for this console.&lt;br /&gt;
|-&lt;br /&gt;
| CD_SETKEYMAP_POINTERS || Set the current key map structures for the console. (V51.19)&lt;br /&gt;
|-&lt;br /&gt;
| CMD_CLEAR || Remove any reports waiting to satisfy read requests from the console input buffer.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_READ || Read the next input, generally from the keyboard. The form of this input is as an ANSI byte stream.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_WRITE || Write a text record to the display interpreting any ANSI control characters in the record.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Console Device Functions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CDInputHandler() || Handle an input event for the console device.&lt;br /&gt;
|-&lt;br /&gt;
| RawKeyConvert() || Decode raw input classes and convert input events of type IECLASS_RAWKEY to ANSI bytes based on the keymap in use.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device Interface ==&lt;br /&gt;
&lt;br /&gt;
The console device operates like the other Amiga devices. To use it, you must first open the console device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.&lt;br /&gt;
&lt;br /&gt;
The I/O request used by the console device is called IOStdReq.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IOStdReq&lt;br /&gt;
{&lt;br /&gt;
    struct  Message io_Message;&lt;br /&gt;
    struct  Device  *io_Device;     /* device node pointer  */&lt;br /&gt;
    struct  Unit    *io_Unit;       /* unit (driver private)*/&lt;br /&gt;
    UWORD   io_Command;             /* device command */&lt;br /&gt;
    UBYTE   io_Flags;&lt;br /&gt;
    BYTE    io_Error;               /* error or warning num */&lt;br /&gt;
    ULONG   io_Actual;              /* actual number of bytes transferred */&lt;br /&gt;
    ULONG   io_Length;              /* requested number bytes transferred*/&lt;br /&gt;
    APTR    io_Data;                /* points to data area */&lt;br /&gt;
    ULONG   io_Offset;              /* offset for block structured devices */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file exec/io.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
=== Console Device Units ===&lt;br /&gt;
&lt;br /&gt;
The console device provides four units, three that require a console window and one that does not. The unit type is specified when you open the device. See the [[#Opening the Console Device|Opening the Console Device]] section below for more details.&lt;br /&gt;
&lt;br /&gt;
The CONU_STANDARD unit (0) is generally used with a SMART_REFRESH window. This unit has the least amount of overhead (e.g., memory usage and rendering time), and is highly compatible with all versions of the operating system.&lt;br /&gt;
&lt;br /&gt;
There are two variations of character mapped console units. Both must be used with SIMPLE_REFRESH windows and both have the ability to automatically redraw a console window when resized or revealed.&lt;br /&gt;
&lt;br /&gt;
A character mapped console can be opened which allows the user to drag-select text with the mouse and &#039;&#039;&#039;Copy&#039;&#039;&#039; the highlighted area. The copied text can then be &#039;&#039;&#039;Paste&#039;&#039;&#039;d into other console windows or other windows which support reading data from the [[Clipboard_Device|clipboard device]].&lt;br /&gt;
&lt;br /&gt;
Character mapped console units have more overhead than standard consoles (e.g., rendering times and memory usage).&lt;br /&gt;
&lt;br /&gt;
The CONU_LIBRARY unit (-1) does not require a console window. It is designed to be primarily used with the console device functions and also with the console device commands that do not require a console window.&lt;br /&gt;
&lt;br /&gt;
The Amiga uses the [http://www.ecma-international.org/publications/standards/Ecma-094.htm ECMA-94 Latin 1 International 8-bit] character set.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Console Device ===&lt;br /&gt;
&lt;br /&gt;
Four primary steps are required to open the console device:&lt;br /&gt;
&lt;br /&gt;
# Create a message port using AllocSysObject(ASOT_PORT). Reply messages from the device must be directed to a message port.&lt;br /&gt;
# Create an I/O request structure of type IOStdReq. The IOStdReq structure is created by the AllocSysObject(ASOT_IOREQUEST) function. AllocSysObject() will initialize your I/O request to point to your reply port.&lt;br /&gt;
# Open an Intuition window and set a pointer to it in the io_Data field of the IOStdReq and the size of the window in the io_Length field. This is the window to which the console will be attached. The window must be SIMPLE_REFRESH for use with the CONU_CHARMAP and CONU_SNIPMAP units.&lt;br /&gt;
# Open the console device. Call OpenDevice() passing it the I/O request and the type of console unit set in the unit and flags fields. Console unit types and flag values are listed below.&lt;br /&gt;
&lt;br /&gt;
Console device units:&lt;br /&gt;
&lt;br /&gt;
; CONU_LIBRARY&lt;br /&gt;
: Return the device library vector pointer used for calling console device functions. No console is opened.&lt;br /&gt;
&lt;br /&gt;
; CONU_STANDARD&lt;br /&gt;
: Open a standard console.&lt;br /&gt;
&lt;br /&gt;
; CONU_CHARMAP&lt;br /&gt;
: Open a console with a character map.&lt;br /&gt;
&lt;br /&gt;
; CONU_SNIPMAP&lt;br /&gt;
: Open a console with a character map and copy-and-paste support.&lt;br /&gt;
&lt;br /&gt;
; CONU_HISTORY (V53)&lt;br /&gt;
: Similar to CONU_SNIPMAP with content history and tabbed windows.&lt;br /&gt;
&lt;br /&gt;
See the include file devices/conunit.h for the unit definitions and the SDK for an explanation of each unit.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=No Changes Required|text=CONU_STANDARD has a numeric value of zero to insure compatibility with pre-V36 applications. CONU_LIBRARY has a numeric value of negative one and is also compatible with pre-V36 applications.}}&lt;br /&gt;
&lt;br /&gt;
Console device flags:&lt;br /&gt;
&lt;br /&gt;
; CONFLAG_DEFAULT&lt;br /&gt;
: The console device will redraw the window when it is resized.&lt;br /&gt;
&lt;br /&gt;
; CONFLAG_NODRAW_ON_NEWSIZE&lt;br /&gt;
: The console device will not redraw the window when it is resized&lt;br /&gt;
&lt;br /&gt;
; CONFLAG_CREATE_WINDOW and CONFLAG_TABBED_WINDOW (V53)&lt;br /&gt;
: These bits define the combinations of window source, tabs and sharing. These are only applicable when using CONUNIT_HISTORY or greater.&lt;br /&gt;
&lt;br /&gt;
; CONFLAG_SYNC_WRITES (V53)&lt;br /&gt;
: Forces a task issuing a Write request to wait until the request is complete before returning to the caller.&lt;br /&gt;
&lt;br /&gt;
The character map units are the only units which use the flags parameter to set how the character map is used. CONU_STANDARD units ignore the flags parameter. See the console.doc autodoc for the most up to date information.&lt;br /&gt;
&lt;br /&gt;
See the include file devices/conunit.h for the flag definitions and the SDK for an explanation of the flags.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct NewWindow nw =&lt;br /&gt;
{&lt;br /&gt;
    10, 10,                    /* starting position (left,top) */&lt;br /&gt;
    620,180,                   /* width, height */&lt;br /&gt;
    -1,-1,                     /* detailpen, blockpen */&lt;br /&gt;
    CLOSEWINDOW,               /* flags for idcmp */&lt;br /&gt;
    WINDOWDEPTH|WINDOWSIZING|&lt;br /&gt;
    WINDOWDRAG|WINDOWCLOSE|&lt;br /&gt;
    SIMPLE_REFRESH|ACTIVATE,    /* window flags */&lt;br /&gt;
    NULL,                      /* no user gadgets */&lt;br /&gt;
    NULL,                      /* no user checkmark */&lt;br /&gt;
    &amp;quot;Console Test&amp;quot;,            /* title */&lt;br /&gt;
    NULL,                      /* pointer to window screen */&lt;br /&gt;
    NULL,                      /* pointer to super bitmap */&lt;br /&gt;
    100,45,                    /* min width, height */&lt;br /&gt;
    640,200,                   /* max width, height */&lt;br /&gt;
    WBENCHSCREEN               /* open on workbench screen */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
    /* Create reply port console */&lt;br /&gt;
struct MsgPort *ConsoleMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
     ASOPORT_Name, &amp;quot;RKM.Console&amp;quot;,&lt;br /&gt;
     TAG_END);&lt;br /&gt;
if (ConsoleMP == NULL)&lt;br /&gt;
    cleanexit(&amp;quot;Can&#039;t create write port\n&amp;quot;, RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Create message block for device I/O */&lt;br /&gt;
struct IOStdReq *ConsIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
    ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
    ASOIOR_ReplyPort, ConsoleMP,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
if (ConsIO == NULL)&lt;br /&gt;
    cleanexit(&amp;quot;Can&#039;t create IORequest\n&amp;quot;, RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Open a window --- we assume intuition.library is already open */&lt;br /&gt;
struct Window *win = IIntuition-&amp;gt;OpenWindow(&amp;amp;nw);&lt;br /&gt;
if (win == NULL)&lt;br /&gt;
    cleanexit(&amp;quot;Can&#039;t open window\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Set window pointer and size in I/O request */&lt;br /&gt;
ConsIO-&amp;gt;io_Data = (APTR) win;&lt;br /&gt;
ConsIO-&amp;gt;io_Length = sizeof(struct Window);&lt;br /&gt;
&lt;br /&gt;
    /* Open the console device */&lt;br /&gt;
if (error = IExec-&amp;gt;OpenDevice(&amp;quot;console.device&amp;quot;, CONU_CHARMAP, ConsIO, CONFLAG_DEFAULT))&lt;br /&gt;
    cleanexit(&amp;quot;Can&#039;t open console.device\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Closing the Console Device ===&lt;br /&gt;
&lt;br /&gt;
Each OpenDevice() must eventually be matched by a call to CloseDevice().&lt;br /&gt;
&lt;br /&gt;
All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (!(IExec-&amp;gt;CheckIO(ConsIO)))&lt;br /&gt;
    IExec-&amp;gt;AbortIO(ConsIO);      /* Ask device to abort any pending requests */&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;WaitIO(ConsIO);           /* Wait for abort, then clean up */&lt;br /&gt;
IExec-&amp;gt;CloseDevice(ConsIO);      /* Close console device */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== About Console I/O ==&lt;br /&gt;
&lt;br /&gt;
The console device may be thought of as a kind of terminal. You send character streams to the console device; you also receive them from the console device. These streams may be characters, control sequences or a combination of the two.&lt;br /&gt;
&lt;br /&gt;
Console I/O is closely associated with the Amiga Intuition interface; a console must be tied to a window that is already opened. From the Window data structure, the console device determines how many characters it can display on a line and how many lines of text it can display in a window without clipping at any edge.&lt;br /&gt;
&lt;br /&gt;
You can open the console device many times, if you wish. The result of each open call is a new console unit. AmigaDOS and Intuition see to it that only one window is currently active and its console, if any, is the only one (with a few exceptions) that receives notification of input events, such as keystrokes. Later in this article you will see that other Intuition events can be sensed by the console device as well.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Introducing...|text=For this entire article the characters &amp;lt;CSI&amp;gt; represent the &#039;&#039;control sequence introducer&#039;&#039;. For output you may use either the two-character sequence &amp;lt;Esc&amp;gt;[ (0x1B 0x5B) or the one-byte value 0x9B. For input you will receive 0x9B unless the sequence has been typed by the user.}}&lt;br /&gt;
&lt;br /&gt;
=== Exec Functions and the Console Device ===&lt;br /&gt;
&lt;br /&gt;
The various Exec functions such as DoIO(), SendIO(), AbortIO() and CheckIO() operate normally. The only caveats are that CMD_WRITE may cause your application to wait internally, even with SendIO(), and a task using CMD_READ to wait on a response from a console is at the user’s mercy. If the user never reselects that window, and the console response provides the only wake-up call, that task will sleep forever.&lt;br /&gt;
&lt;br /&gt;
=== General Console Screen Output ===&lt;br /&gt;
&lt;br /&gt;
Console character screen output (as compared to console command sequence transmission) outputs all standard printable characters (character values hex 20 through 7E and A0 through FF) normally.&lt;br /&gt;
&lt;br /&gt;
Many control characters such as &#039;&#039;&#039;Backspace&#039;&#039;&#039; (0x08) and &#039;&#039;&#039;Return&#039;&#039;&#039; (0x0D) are translated into their exact ANSI equivalent actions. The &#039;&#039;&#039;Line feed&#039;&#039;&#039; character (0x0A) is a bit different in that it can be translated into a &#039;&#039;&#039;Return/Line feed&#039;&#039;&#039; action. The net effect is that the cursor moves to the first column of the next line whenever an &amp;amp;lt;LF&amp;amp;gt; is displayed. This option is set via the mode control sequences discussed under “Control Sequences for Window Output.”&lt;br /&gt;
&lt;br /&gt;
=== Console Keyboard Input ===&lt;br /&gt;
&lt;br /&gt;
If you read from the console device, the keyboard inputs are preprocessed for you and you will get ASCII characters, such as “B.” Most normal text-gathering programs will read from the console device in this manner. Some programs may also ask to receive raw events in their console stream. Keypresses are converted to ASCII characters or CSI sequences via the keymap associated with the unit.&lt;br /&gt;
&lt;br /&gt;
== Writing to the Console Device ==&lt;br /&gt;
&lt;br /&gt;
You write to the console device by passing an I/O request to the device with a pointer to the write buffer set in io_Data, the number of bytes in the buffer set in io_Length and CMD_WRITE set in io_Command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
CONST_STRPTR outstring= &amp;quot;Make it so&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
ConsIO-&amp;gt;io_Data    = outstring;&lt;br /&gt;
ConsIO-&amp;gt;io_Length  = IUtility-&amp;gt;Strlen(outstring);&lt;br /&gt;
ConsIO-&amp;gt;io_Command = CMD_WRITE;&lt;br /&gt;
IExec-&amp;gt;DoIO(ConsIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may also send NULL-terminated strings to the console device in the same manner except that io_Length must be set to -1.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ConsIO-&amp;gt;io_Data    = &amp;quot;\033[3mOh boy.&amp;quot;;&lt;br /&gt;
ConsIO-&amp;gt;io_Length  = -1;&lt;br /&gt;
ConsIO-&amp;gt;io_Command = CMD_WRITE;&lt;br /&gt;
IExec-&amp;gt;DoIO(ConsIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fragment above will output the string “Oh boy.” in italics. Keep in mind that setting the text rendition to italics will remain in effect until you specifically instruct the console device to change it to another text style.&lt;br /&gt;
&lt;br /&gt;
=== Synchronized Writes ===&lt;br /&gt;
&lt;br /&gt;
Synchronized writes were the only option for Console Device before version 53.38. Returning from the Write call means that the data has been transferred from the caller&#039;s buffer and the IORequest structure contents are no longer required. The data may not yet have been written to the display and any effects caused by the data (e.g. change of cursor position) may not yet have started or been completed.&lt;br /&gt;
&lt;br /&gt;
If the Console was opened in legacy mode (no TABBED or HISTORY options), then the SYNCWRITES option is forced for greater compatibility with older applications. Otherwise, the Console is opened with synchronized writes disabled. This enables the application to continue to do work while the Console works in parallel.&lt;br /&gt;
&lt;br /&gt;
You can still force the Write operation to block until completed by opening the Console with the CONFLAG_SYNC_WRITES set or (for a Shell) including the option SYNCWRITES in the command line.&lt;br /&gt;
&lt;br /&gt;
If you do not want to wait for all Write requests, you can set the IOF_WAIT_WRITE bit in the io_Flags byte before calling CMD_WRITE. This flag bit has the same effect as opening the console with the CONFLAG_SYNC_WRITES set, but its scope is limited to the current IORequest.&lt;br /&gt;
&lt;br /&gt;
=== Control Sequences for Window Output ===&lt;br /&gt;
&lt;br /&gt;
The following table lists functions that the console device supports, along with the character stream that you must send to the console to produce the effect. For more information on the control sequences, consult the console.doc in the SDK. The table uses the second form of &amp;amp;lt;CSI&amp;amp;gt;, that is, the hex value 0x9B, to minimize the number of characters to be transmitted to produce a function.&lt;br /&gt;
&lt;br /&gt;
A couple of notes about the table. If an item is enclosed in square brackets, it is optional and may be omitted. For example, for &#039;&#039;&#039;Insert [N] Characters&#039;&#039;&#039; the value for &#039;&#039;N&#039;&#039; is shown as optional. The console device responds to such optional items by treating the value of &#039;&#039;N&#039;&#039; as 1 if it is not specified. The value of &#039;&#039;N&#039;&#039; or &#039;&#039;M&#039;&#039; is always a decimal number, having one or more ASCII digits to express its value.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ ANSI Console Control Sequences&lt;br /&gt;
! Console Command&lt;br /&gt;
! Comment&lt;br /&gt;
! Sequence of Characters (in Hexadecimal Form)&lt;br /&gt;
|-&lt;br /&gt;
| Bell || Flash the display—do an Intuition DisplayBeep() || 07&lt;br /&gt;
|-&lt;br /&gt;
| Backspace || Move left one column || 08&lt;br /&gt;
|-&lt;br /&gt;
| Horizontal Tab || Move right one tab stop || 09&lt;br /&gt;
|-&lt;br /&gt;
| Line Feed || Move down one text line as specified by the mode function || 0A&lt;br /&gt;
|-&lt;br /&gt;
| Vertical Tab || Move up one text line || 0B&lt;br /&gt;
|-&lt;br /&gt;
| Form Feed || Clear the console’s window || 0C&lt;br /&gt;
|-&lt;br /&gt;
| Carriage Return || Move to first column || 0D&lt;br /&gt;
|-&lt;br /&gt;
| Shift In || Undo Shift Out || OE&lt;br /&gt;
|-&lt;br /&gt;
| Shift Out || Set MSB of each character before displaying || 0F&lt;br /&gt;
|-&lt;br /&gt;
| Esc || Escape can be part of the control sequence introducer || 1B&lt;br /&gt;
|-&lt;br /&gt;
| Index || Move the active position down one line || 84&lt;br /&gt;
|-&lt;br /&gt;
| Next Line || Go to the beginning of the next line || 85&lt;br /&gt;
|-&lt;br /&gt;
| Horizontal Tabulation || Set a tab at the active cursor position || 88&lt;br /&gt;
|-&lt;br /&gt;
| Reverse Index || Move the active position up one line || 8D&lt;br /&gt;
|-&lt;br /&gt;
| CSI || Control sequence introducer || 9B&lt;br /&gt;
|-&lt;br /&gt;
| Reset to Initial State || || 1B 63&lt;br /&gt;
|-&lt;br /&gt;
| Insert [N] Characters || Insert one or more spaces, shifting the remainder of the line to the right || 9B [N] 40&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Up [N] Character Positions || (default = 1) || 9B [N] 41&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Down [N] Character Positions || (default = 1) || 9B [N] 42&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Forward [N] Character Positions || (default = 1) || 9B [N] 43&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Backward [N] Character Positions || (default = 1) || 9B [N] 44&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Position || Where &#039;&#039;N&#039;&#039; is row, &#039;&#039;M&#039;&#039; is column, and semicolon (hex 3B) must be present as a separator, or if row is left out, so the console device can tell that the number after the semicolon actually represents the column number || 9B [N] [3B M] 48&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Horizontal Tabulation || move cursor forward to &#039;&#039;N&#039;&#039;th tab position || 9B [N] 49&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Next Line [N] || (to column 1) || 9B [N] 45&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Preceding Line [N] || (to column 1) || 9B [N] 46&lt;br /&gt;
|-&lt;br /&gt;
| Erase in Display || (only to end of display) || 9B 4A&lt;br /&gt;
|-&lt;br /&gt;
| Erase in Line || (only to end of line) || 9B 4B&lt;br /&gt;
|-&lt;br /&gt;
| Insert Line || Above the line containing the cursor || 9B 4C&lt;br /&gt;
|-&lt;br /&gt;
| Delete Line || Remove current line, move all lines up one position to fill gap, blank bottom line || 9B 4D&lt;br /&gt;
|-&lt;br /&gt;
| Delete Character [N] || That cursor is sitting on and to the right if [N] is specified || 9B [N] 50&lt;br /&gt;
|-&lt;br /&gt;
| Scroll up [N] Lines || Remove line(s) from top of window, move all other lines up, blanks [N] bottom lines || 9B [N] 53&lt;br /&gt;
|-&lt;br /&gt;
| Scroll down [N] Lines || Remove line(s) from bottom of window, move all other lines down, blanks [N] top lines || 9B [N] 54&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Tabulation Control || where &#039;&#039;N&#039;&#039;=0 set tab, &#039;&#039;N&#039;&#039;=2 clear tab, &#039;&#039;N&#039;&#039;=5 clear all tabs || 9B [N] 57&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Backward Tabulation || move cursor backward to &#039;&#039;N&#039;&#039;th tab position || 9B [N] 5A&lt;br /&gt;
|-&lt;br /&gt;
| Set Line Feed Mode || Cause &#039;&#039;&#039;Line Feed&#039;&#039;&#039; to respond as Return-Line Feed || 9B 32 30 68&lt;br /&gt;
|-&lt;br /&gt;
| Reset Newline Mode || Cause &#039;&#039;&#039;Line Feed&#039;&#039;&#039; to respond only as Line Feed || 9B 32 30 6C&lt;br /&gt;
|-&lt;br /&gt;
| Device Status Report || Cause console device to insert a &#039;&#039;&#039;Cursor&#039;&#039;&#039; Position Report into your read stream; see “Reading from the Console Device” for more information || 9B 36 6E&lt;br /&gt;
|-&lt;br /&gt;
| Select Graphic Rendition || Select text style (K), character color (L), character cell color (M), background color (N). See note below. || 9B K 3B 3L 3B 4M 3B &amp;amp;gt;N 6D&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For &#039;&#039;&#039;Select Graphic Rendition&#039;&#039;&#039;, any number of parameters, in any order, are valid. They are separated by semicolons. The parameters follow:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| 0 || Plain text&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Boldface&lt;br /&gt;
|-&lt;br /&gt;
| 2 || faint (secondary color)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Italic&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Underscore&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Set slow character blink (V53.5)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Set fast character blink (V53.5)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reversed character/cell colors&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Concealed mode&lt;br /&gt;
|-&lt;br /&gt;
| 22 || Normal color, not bold (V36)&lt;br /&gt;
|-&lt;br /&gt;
| 23 || Italic off (V36)&lt;br /&gt;
|-&lt;br /&gt;
| 24 || Underscore off (V36)&lt;br /&gt;
|-&lt;br /&gt;
| 25 || Slow blink off (V53.5)&lt;br /&gt;
|-&lt;br /&gt;
| 26 || Fast blink off (V53.5)&lt;br /&gt;
|-&lt;br /&gt;
| 27 || Reversed off (V36)&lt;br /&gt;
|-&lt;br /&gt;
| 28 || Concealed off (V36)&lt;br /&gt;
|-&lt;br /&gt;
| 29 || Strike-through off (V53.5)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| 30–37 || System colors 0–7 for character color.&lt;br /&gt;
|-&lt;br /&gt;
| 39 || Reset to default character color&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Transmitted as two ASCII characters.&lt;br /&gt;
| 40–47 || System colors 0–7 for character cell color.&lt;br /&gt;
|-&lt;br /&gt;
| 39 || Reset to default character color&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Transmitted as two ASCII characters&lt;br /&gt;
| &amp;gt;0–7 || System colors 0–7 for background color. (V36)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You must specify the “&amp;gt;” in order for this to be recognized and it must be the last parameter.&lt;br /&gt;
&lt;br /&gt;
For example, to select bold face, with color 3 as the character color, and color 0 as the character cell color and the background color, send the hex sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;9B 31 3B 33 33 3B 34 30 3B 3E 30 6D&amp;lt;/pre&amp;gt;&lt;br /&gt;
representing the ASCII sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt;CSI&amp;amp;gt;1;33;40;&amp;amp;gt;0m&amp;lt;/pre&amp;gt;&lt;br /&gt;
where &amp;amp;lt;CSI&amp;amp;gt; is the control sequence introducer, here used as the single character value 0x9B.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Go Easy On The Eyes|text=In most cases, the character cell color and the background color should be the same.}}&lt;br /&gt;
&lt;br /&gt;
==== Set Graphic Rendition Implementation Notes ====&lt;br /&gt;
&lt;br /&gt;
The sequences in the next table are not ANSI standard sequences, they are private Amiga sequences. In these command descriptions, length, width, and offset are comprised of one or more ASCII digits, defining a decimal value.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Amiga Console Control Sequences&lt;br /&gt;
! Console Command&lt;br /&gt;
! Comment&lt;br /&gt;
! Sequence of Characters (in Hexadecimal Form)&lt;br /&gt;
|-&lt;br /&gt;
| Enable Scroll || (this is the default) || 9B 3E 31 68&lt;br /&gt;
|-&lt;br /&gt;
| Disable Scroll || || 9B 3E 31 6C&lt;br /&gt;
|-&lt;br /&gt;
| Autowrap On || (the default) || 9B 3F 37 68&lt;br /&gt;
|-&lt;br /&gt;
| Autowrap Off || || 9B 3F 37 6C&lt;br /&gt;
|-&lt;br /&gt;
| Set Page Length || In character raster lines, causes console to recalculate, using current font, how many text lines will fit on the page || 9B &amp;amp;lt;length&amp;amp;gt; 74&lt;br /&gt;
|-&lt;br /&gt;
| Set Line Length || In character positions, using current font, how many characters should be placed on each line&lt;br /&gt;
 || 9B &amp;amp;lt;width&amp;amp;gt; 75&lt;br /&gt;
|-&lt;br /&gt;
| Set Left Offset || In raster columns, how far from the left of the window should the text begin || 9B &amp;amp;lt;offset&amp;amp;gt; 78&lt;br /&gt;
|-&lt;br /&gt;
| Set Top Offset || In raster lines, how far from the top of the window’s RastPort should the topmost line of the character begin || 9B &amp;amp;lt;offset&amp;amp;gt; 79&lt;br /&gt;
|-&lt;br /&gt;
| Set Raw Events || Set the raw input events that will trigger an &#039;&#039;&#039;Input Event Report&#039;&#039;&#039;. see the “Selecting Raw Input Events” section below for more details. || 9B &amp;amp;lt;events&amp;amp;gt; 7B&lt;br /&gt;
|-&lt;br /&gt;
| Input Event Report || Returned by the console device in response to a raw event set by the &#039;&#039;&#039;Set Raw Event&#039;&#039;&#039; sequence. See the “Input Event Reports” section below for more details. || 9B &amp;amp;lt;parameters&amp;amp;gt; 7C&lt;br /&gt;
|-&lt;br /&gt;
| Reset Raw Events || Reset the raw events set by the &#039;&#039;&#039;Set Raw Event&#039;&#039;&#039; sequence. see the “Selecting Raw Input Events” section below. || 9B &amp;amp;lt;events&amp;amp;gt; 7D&lt;br /&gt;
|-&lt;br /&gt;
| Special Key Report || Returned by the console device whenever &#039;&#039;&#039;Help&#039;&#039;&#039;, or one of the function keys or arrow keys is pressed. Some sequences do not end with 7E || 9B &amp;amp;lt;keyvalue&amp;amp;gt; 7E&lt;br /&gt;
|-&lt;br /&gt;
| Set Cursor Rendition || Make the cursor visible or invisible; turning off the cursor increases text output speed || 9B N 20 70 &amp;lt;br/&amp;gt; Invisible = 9B 30 20 70 &amp;lt;br/&amp;gt; Visible = 9B 20 70&lt;br /&gt;
|-&lt;br /&gt;
| Window Status Request || Ask the console device to tell you the current bounds of the window, in upper and lower row and column character positions. User may have resized or repositioned it. See “Window Bounds Report” below. || 9B 30 20 71&lt;br /&gt;
|-&lt;br /&gt;
| Window Bounds Report || Returned by the console device in response to a &#039;&#039;&#039;Window Status Request&#039;&#039;&#039; sequence || 9B 31 3B 31 3B &amp;amp;lt;bottom margin&amp;amp;gt; 3B &amp;amp;lt;right margin&amp;amp;gt; 72&lt;br /&gt;
|-&lt;br /&gt;
| Right ‘Amiga V’ Press || Returned by the console device when the user presses &#039;&#039;&#039;Right-Amiga-V&#039;&#039;&#039;. See the “Copy and Paste Support” section below for more details. || 9B 30 20 76&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Give Back What You Take|text=The console device normally handles the &#039;&#039;&#039;Set Page Length&#039;&#039;&#039;, &#039;&#039;&#039;Set Line Length&#039;&#039;&#039;, &#039;&#039;&#039;Set Left Offset&#039;&#039;&#039;, and &#039;&#039;&#039;Set Top Offset&#039;&#039;&#039; functions automatically. To allow it to do so again after setting your own values, send the functions without a parameter.}}&lt;br /&gt;
&lt;br /&gt;
=== Example Console Control Sequences ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Move cursor right by 1 || &amp;amp;lt;CSI&amp;amp;gt;C or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;1C || 9B 43 &amp;lt;br/&amp;gt; 9B 31 43&lt;br /&gt;
|-&lt;br /&gt;
| Move cursor right by 20 || &amp;amp;lt;CSI&amp;amp;gt;20C || 9B 32 30 43&lt;br /&gt;
|-&lt;br /&gt;
| Move cursor to upper-left corner (Home Position) || &amp;amp;lt;CSI&amp;amp;gt;H or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;1;1H or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;;1H or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;1;H || 9B 48 &amp;lt;br/&amp;gt; 9B 31 3B 31 48 &amp;lt;br/&amp;gt; 9B 3B 31 48 &amp;lt;br/&amp;gt; 9B 31 3B 48&lt;br /&gt;
|-&lt;br /&gt;
| Move cursor to the fourth column of the first line of the window || &amp;amp;lt;CSI&amp;amp;gt;1;4H or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;;4H || 9B 31 3B 34 48 &amp;lt;br/&amp;gt; 9B 3B 34 48&lt;br /&gt;
|-&lt;br /&gt;
| Clear the window || &amp;amp;lt;FF&amp;amp;gt; or Ctrl-L (clear window) or &amp;lt;br/&amp;gt; &amp;amp;lt;CSI&amp;amp;gt;H&amp;amp;lt;CSI&amp;amp;gt;J (home and clear to end of window) || 0C &amp;lt;br/&amp;gt; 9B 48 9B 4A&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Reading from the Console Device ==&lt;br /&gt;
&lt;br /&gt;
Reading input from the console device returns an ANSI 3.64 standard byte stream. This stream may contain normal characters and/or raw input event information. You may also request other raw input events using the &#039;&#039;&#039;Set Raw Events&#039;&#039;&#039; and &#039;&#039;&#039;Reset Raw Events&#039;&#039;&#039; control sequences discussed below. See “Selection of Raw Input Events.”&lt;br /&gt;
&lt;br /&gt;
Generally, console reads are performed asynchronously so that your program can respond to other events and other user input (such as menu selections) when the user is not typing on the keyboard. To perform asynchronous I/O, an I/O request is sent to the console using the SendIO() function (rather than a synchronous DoIO() which would wait until the read request returned with a character).&lt;br /&gt;
&lt;br /&gt;
You read from the console device by passing an I/O request to the device with a pointer to the read buffer set in io_Data, the number of bytes in the buffer set in io_Length and CMD_READ set in io_Command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define READ_BUFFER_SIZE 25&lt;br /&gt;
char ConsoleReadBuffer[READ_BUFFER_SIZE];&lt;br /&gt;
&lt;br /&gt;
ConsIO-&amp;gt;io_Data    = (APTR)ConsoleReadBuffer;&lt;br /&gt;
ConsIO-&amp;gt;io_Length  = READ_BUFFER_SIZE;&lt;br /&gt;
ConsIO-&amp;gt;io_Command = CMD_READ;&lt;br /&gt;
IExec-&amp;gt;SendIO(ConsIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You May Get Less Than You Bargained For|text=A request for more than one character may be satisfied by the receipt of only one character. If you request more than one character, you will have to examine the io_Actual field of the request when it returns to determine how many characters you have actually received.}}&lt;br /&gt;
&lt;br /&gt;
After sending the read request, your program can wait on a combination of signal bits including that of the reply port you created. The following fragment demonstrates waiting on both a queued console read request, and Window IDCMP messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 conreadsig = 1 &amp;lt;&amp;lt; ConsoleMP-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 windowsig = 1 &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
/* A character, or an IDCMP msg, or both will wake us up */&lt;br /&gt;
ULONG signals = IExec-&amp;gt;Wait(conreadsig | windowsig);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; conreadsig)&lt;br /&gt;
    {&lt;br /&gt;
    /* Then check for a character */&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; windowsig)&lt;br /&gt;
    {&lt;br /&gt;
    /* Then check window messages */&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Information About the Input Stream ===&lt;br /&gt;
&lt;br /&gt;
For the most part, keys whose keycaps are labeled with ANSI-standard characters will ordinarily be translated into their ASCII-equivalent character by the console device through the use of its keymap. Keymap information can be found in [[Keymap_Library|Keymap Library]].&lt;br /&gt;
&lt;br /&gt;
For keys other than those with normal ASCII equivalents, an escape sequence is generated and inserted into your input stream. For example, in the default state (no raw input events selected) the function, arrow and special keys (reserved for 101 key keyboards) will cause the sequences shown in the next table to be inserted in the input stream.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Special Key Report Sequences&lt;br /&gt;
! Key&lt;br /&gt;
! Unshifted Sends &lt;br /&gt;
! Shifted Sends&lt;br /&gt;
! Comment&lt;br /&gt;
|-&lt;br /&gt;
| F1 || &amp;amp;lt;CSI&amp;amp;gt;0 || &amp;amp;lt;CSI&amp;amp;gt;10 ||&lt;br /&gt;
|-&lt;br /&gt;
| F2 || &amp;amp;lt;CSI&amp;amp;gt;1 || &amp;amp;lt;CSI&amp;amp;gt;11 ||&lt;br /&gt;
|-&lt;br /&gt;
| F3 || &amp;amp;lt;CSI&amp;amp;gt;2 || &amp;amp;lt;CSI&amp;amp;gt;12 ||&lt;br /&gt;
|-&lt;br /&gt;
| F4 || &amp;amp;lt;CSI&amp;amp;gt;3 || &amp;amp;lt;CSI&amp;amp;gt;13 ||&lt;br /&gt;
|-&lt;br /&gt;
| F5 || &amp;amp;lt;CSI&amp;amp;gt;4 || &amp;amp;lt;CSI&amp;amp;gt;14 ||&lt;br /&gt;
|-&lt;br /&gt;
| F6 || &amp;amp;lt;CSI&amp;amp;gt;5 || &amp;amp;lt;CSI&amp;amp;gt;15 ||&lt;br /&gt;
|-&lt;br /&gt;
| F7 || &amp;amp;lt;CSI&amp;amp;gt;6 || &amp;amp;lt;CSI&amp;amp;gt;16 ||&lt;br /&gt;
|-&lt;br /&gt;
| F8 || &amp;amp;lt;CSI&amp;amp;gt;7 || &amp;amp;lt;CSI&amp;amp;gt;17 ||&lt;br /&gt;
|-&lt;br /&gt;
| F9 || &amp;amp;lt;CSI&amp;amp;gt;8 || &amp;amp;lt;CSI&amp;amp;gt;18 ||&lt;br /&gt;
|-&lt;br /&gt;
| F10 || &amp;amp;lt;CSI&amp;amp;gt;9 || &amp;amp;lt;CSI&amp;amp;gt;19 ||&lt;br /&gt;
|-&lt;br /&gt;
| F11 || &amp;amp;lt;CSI&amp;amp;gt;20 || &amp;amp;lt;CSI&amp;amp;gt;30 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| F12 || &amp;amp;lt;CSI&amp;amp;gt;21 || &amp;amp;lt;CSI&amp;amp;gt;31 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Help || &amp;amp;lt;CSI&amp;amp;gt;? || &amp;amp;lt;CSI&amp;amp;gt;? || (same sequence for both)&lt;br /&gt;
|-&lt;br /&gt;
| Insert || &amp;amp;lt;CSI&amp;amp;gt;40 || &amp;amp;lt;CSI&amp;amp;gt;50 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Page Up || &amp;amp;lt;CSI&amp;amp;gt;41 || &amp;amp;lt;CSI&amp;amp;gt;51 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Page Down || &amp;amp;lt;CSI&amp;amp;gt;42 || &amp;amp;lt;CSI&amp;amp;gt;52 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Pause/Break || &amp;amp;lt;CSI&amp;amp;gt;43 || &amp;amp;lt;CSI&amp;amp;gt;53 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Home || &amp;amp;lt;CSI&amp;amp;gt;44 || &amp;amp;lt;CSI&amp;amp;gt;54 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| End || &amp;amp;lt;CSI&amp;amp;gt;45 || &amp;amp;lt;CSI&amp;amp;gt;55 || (101 key keyboard)&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Up || &amp;amp;lt;CSI&amp;amp;gt;A || &amp;amp;lt;CSI&amp;amp;gt;T ||&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Down || &amp;amp;lt;CSI&amp;amp;gt;B || &amp;amp;lt;CSI&amp;amp;gt;S ||&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Left || &amp;amp;lt;CSI&amp;amp;gt;D || &amp;amp;lt;CSI&amp;amp;gt; A || (notice the space after &amp;amp;lt;CSI&amp;amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| Cursor Right || &amp;amp;lt;CSI&amp;amp;gt;C || &amp;amp;lt;CSI&amp;amp;gt; @ || (notice the space after &amp;amp;lt;CSI&amp;amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Cursor Position Report ===&lt;br /&gt;
&lt;br /&gt;
If you have sent the &#039;&#039;&#039;Device Status Report&#039;&#039;&#039; command sequence, the console device returns a cursor position report into your input stream. It takes the form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;&amp;amp;lt;row&amp;amp;gt;;&amp;amp;lt;column&amp;amp;gt;R&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, if the cursor is at column 40 and row 12, here are the ASCII values (in hex) you receive in a stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
9B 34 30 3B 31 32 52&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Window Bounds Report ===&lt;br /&gt;
&lt;br /&gt;
A user may have either moved or resized the window to which your console is bound. By issuing a &#039;&#039;&#039;Window Status Report&#039;&#039;&#039; to the console, you can read the current position and size in the input stream. This window bounds report takes the following form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;1;1;&amp;amp;lt;bottom margin&amp;amp;gt;;&amp;amp;lt;right margin&amp;amp;gt; r&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The bottom and right margins give you the window row and column dimensions as well. For a window that holds 20 lines with 60 characters per line, you will receive the following in the input stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
9B 31 3B 31 3B 32 30 3B 36 30 20 72&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copy and Paste Support ==&lt;br /&gt;
&lt;br /&gt;
As noted above, opening the console device with a unit of CONU_SNIPMAP allows the user to drag-select text with the mouse and copy the selection with &#039;&#039;&#039;Right-Amiga-C&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Internally, the snip is copied to a private buffer managed by the console device where it can be copied to other console device windows by pressing &#039;&#039;&#039;Right-Amiga-V&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
However, your application should assume that the user is running the “Conclip” utility which is part of the standard Workbench 2.0 environment. Conclip copies snips from the console device to the clipboard device where they can be used by other applications which support reading from the clipboard.&lt;br /&gt;
&lt;br /&gt;
When Conclip is running and the user presses &#039;&#039;&#039;Right-Amiga-V&#039;&#039;&#039;, the console device puts an escape sequence in your read stream—&amp;amp;lt;CSI&amp;amp;gt;0 v (Hex 9B 30 20 76)—which tells you that the user wants to paste text from the clipboard.&lt;br /&gt;
&lt;br /&gt;
Upon receipt of this sequence, your application should read the contents of the clipboard device, make a copy of any text found there and then release the clipboard so that it can be used by other applications. See [[Clipboard_Device|Clipboard Device]] for more information on reading data from it.&lt;br /&gt;
&lt;br /&gt;
You paste what you read from the clipboard by using successive writes to the console. In order to avoid problems with excessively long data in the clipboard, you should limit the size of writes to something reasonable. (We define reasonable as no more than 1K per write with the ideal amount being 256 bytes.) You should also continue to monitor the console read stream for additional use input, paster requests and, possibly, &#039;&#039;&#039;Raw Input Events&#039;&#039;&#039; while you are doing this.&lt;br /&gt;
&lt;br /&gt;
You should &#039;&#039;not&#039;&#039; open a character mapped console unit with COPY capability if you are unable to support &#039;&#039;&#039;Paste&#039;&#039;&#039; from the clipboard device. The user will reasonably expect to be able to &#039;&#039;&#039;Paste&#039;&#039;&#039; into windows from which a &#039;&#039;&#039;Copy&#039;&#039;&#039; can be done.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that users do make mistakes, so an &#039;&#039;&#039;Undo&#039;&#039;&#039; mechanism for aborting a &#039;&#039;&#039;Paste&#039;&#039;&#039; is highly desirable—particularly if the user has just accidentally pasted text into an application like a terminal program which is sending data at a slow rate.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Use “CON:”, You’ll Be Glad You Did|text=It is highly recommended that you consider using the console-handler (CON:) if you want a console window with &#039;&#039;&#039;Copy&#039;&#039;&#039; and &#039;&#039;&#039;Paste&#039;&#039;&#039; capabilities. CON: provides you with free &#039;&#039;&#039;Paste&#039;&#039;&#039; support and is considerably easier to open and use than using the console device directly.}}&lt;br /&gt;
&lt;br /&gt;
== Selecting Raw Input Events ==&lt;br /&gt;
&lt;br /&gt;
If the keyboard information–including “cooked” keystrokes–does not give you enough information about input events, you can request additional information from the console driver.&lt;br /&gt;
&lt;br /&gt;
The command to &#039;&#039;&#039;Set Raw Events&#039;&#039;&#039; is formatted as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;[event-types-separated-by-semicolons]{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If, for example, you need to know when each key is pressed and released, you would request “Raw keyboard input.” This is done by writing &amp;amp;lt;CSI&amp;amp;gt;1&amp;lt;math&amp;gt;\{&amp;lt;/math&amp;gt; to the console. In a single &#039;&#039;&#039;Set Raw Events&#039;&#039;&#039; request, you can ask the console to set up for multiple event types at one time. You must send multiple numeric parameters, separating them by semicolons (;). For example, to ask for gadget pressed, gadget released, and close gadget events, write:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;7;8;11{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can reset, that is, delete from reporting, one or more of the raw input event types by using the &#039;&#039;&#039;Reset Raw Events&#039;&#039;&#039; command, in the same manner as the &#039;&#039;&#039;Set Raw Events&#039;&#039;&#039; was used to establish them in the first place. This command stream is formatted as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;[event-types-separated-by-semicolons]}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, for example, you could reset all of the events set in the above example by transmitting the command sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;7;8;11}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The Read Stream May Not Be Dry|text=There could still be pending &#039;&#039;&#039;Raw Input Events&#039;&#039;&#039; in your read stream after turning off one or more &#039;&#039;&#039;Raw Input Events&#039;&#039;&#039;.}}&lt;br /&gt;
&lt;br /&gt;
The following table lists the valid raw input event types.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Raw Input Event Types&lt;br /&gt;
! Request Number&lt;br /&gt;
! Request Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || No-op (used internally)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || RAW keyboard input (Intuition swallows all except the select button)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || RAW mouse input&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Private Console Event&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Pointer position&lt;br /&gt;
|-&lt;br /&gt;
| 5 || (unused)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Timer&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Gadget pressed&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Gadget released&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Requester activity&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Menu numbers&lt;br /&gt;
|-&lt;br /&gt;
| 11 || Close Gadget&lt;br /&gt;
|-&lt;br /&gt;
| 12 || Window resized&lt;br /&gt;
|-&lt;br /&gt;
| 13 || Window refreshed&lt;br /&gt;
|-&lt;br /&gt;
| 14 || Preferences changed&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Disk removed&lt;br /&gt;
|-&lt;br /&gt;
| 16 || Disk inserted&lt;br /&gt;
|-&lt;br /&gt;
| 17 || Active window&lt;br /&gt;
|-&lt;br /&gt;
| 18 || Inactive window&lt;br /&gt;
|-&lt;br /&gt;
| 19 || New pointer position&lt;br /&gt;
|-&lt;br /&gt;
| 20 || Menu help&lt;br /&gt;
|-&lt;br /&gt;
| 21 || Window changed (zoom, move)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The event types—requester, window refreshed, active window, inactive window, window resized and window changed—are dispatched to the console unit which owns the window from which the events are generated, even if it is not the active (selected ) window at the time the event is generated. This ensures that the proper console unit is notified of those events. All other events are dispatched to the active console unit (if it has requested those events).&lt;br /&gt;
&lt;br /&gt;
== Input Event Reports ==&lt;br /&gt;
&lt;br /&gt;
If you select any of these events you will start to get information about the events in the following form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;&amp;amp;lt;class&amp;amp;gt;;&amp;amp;lt;subclass&amp;amp;gt;;&amp;amp;lt;keycode&amp;amp;gt;;&amp;amp;lt;qualifiers&amp;amp;gt;;&amp;amp;lt;x&amp;amp;gt;;&amp;amp;lt;y&amp;amp;gt;;&amp;amp;lt;seconds&amp;amp;gt;;&amp;amp;lt;microseconds&amp;amp;gt;|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;CSI&amp;gt;&lt;br /&gt;
: is a one-byte field. It is the “control sequence introducer,” 0x9B in hex.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;class&amp;gt;&lt;br /&gt;
: is the raw input event type, from the above table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;subclass&amp;gt;&lt;br /&gt;
: is usually 0. If the mouse is moved to the right controller, this would be 1.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;keycode&amp;gt;&lt;br /&gt;
: indicates which raw key number was pressed. This field can also be used for mouse information.&lt;br /&gt;
&lt;br /&gt;
: {{Note|title=The Raw Key Might Be The Wrong Key|text=National keyboards often have different keyboard arrangements. This means that a particular raw key number may represent different characters on different national keyboards. The normal console read stream (as opposed to raw events) will contain the proper ASCII character for the keypress as translated according to the user’s keymap.}}&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;qualifiers&amp;gt;&lt;br /&gt;
: indicates the state of the keyboard and system. The qualifiers are defined as follows:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Input Event Qualifiers&lt;br /&gt;
! Bit&lt;br /&gt;
! Mask&lt;br /&gt;
! Key&lt;br /&gt;
! Comment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0001 || Left shift&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0002 || Right shift&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0004 || Caps Lock || Associated keycode is special; see below&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 0008 || Ctrl&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 0010 || Left Alt&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 0020 || Right Alt&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 0040 || Left Amiga key pressed&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 0080 || Right Amiga key pressed&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 0100 || Numeric pad&lt;br /&gt;
|-&lt;br /&gt;
| 9 || 0200 || Repeat&lt;br /&gt;
|-&lt;br /&gt;
| 10 || 0400 || Interrupt || Not currently used.&lt;br /&gt;
|-&lt;br /&gt;
| 11 || 0800 || Multibroadcast || This window (active one) or all windows.&lt;br /&gt;
|-&lt;br /&gt;
| 12 || 1000 || Middle mouse button || (Not available on standard mouse)&lt;br /&gt;
|-&lt;br /&gt;
| 13 || 2000 || Right mouse button&lt;br /&gt;
|-&lt;br /&gt;
| 14 || 4000 || Left mouse button&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 8000 || Relative mouse || Mouse coordinates are relative, not absolute.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
: The &#039;&#039;&#039;Caps Lock&#039;&#039;&#039; key is handled in a special manner. It generates a keycode only when it is pressed, not when it is released. However, the up/down bit (80 hex) is still used and reported. If pressing the &#039;&#039;&#039;Caps Lock&#039;&#039;&#039; key causes the LED to light, keycode 62 (&#039;&#039;&#039;Caps Lock&#039;&#039;&#039; pressed) is sent. If pressing the &#039;&#039;&#039;Caps Lock&#039;&#039;&#039; key extinguishes the LED, keycode 190 (&#039;&#039;&#039;Caps Lock&#039;&#039;&#039; released) is sent. In effect, the keyboard reports this key as held down until it is struck again.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;x&amp;gt; and &amp;lt;y&amp;gt;&lt;br /&gt;
: filled by some classes with an Intuition address: x&amp;amp;lt;&amp;amp;lt;16+y.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;seconds&amp;gt; and &amp;lt;microseconds&amp;gt;&lt;br /&gt;
: contain the system time stamp taken at the time the event occurred. These values are stored as longwords by the system.&lt;br /&gt;
&lt;br /&gt;
With RAW keyboard input selected, keys will no longer return a simple one-character “A” to “Z” but will instead return raw keycode reports of the form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;1;0;&amp;amp;lt;keycode&amp;amp;gt;;&amp;amp;lt;qualifiers&amp;amp;gt;;&amp;amp;lt;prev1&amp;amp;gt;;&amp;amp;lt;prev2&amp;amp;gt;;&amp;amp;lt;seconds&amp;amp;gt;;&amp;amp;lt;microseconds&amp;amp;gt;|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, if the user pressed and released the &#039;&#039;&#039;A&#039;&#039;&#039; key with the &#039;&#039;&#039;Left Shift&#039;&#039;&#039; and &#039;&#039;&#039;Right Amiga&#039;&#039;&#039; keys also pressed, you might receive the following data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;1;0;32;32769;14593;5889;421939940;316673|&lt;br /&gt;
&amp;amp;lt;CSI&amp;amp;gt;1;0;160;32769;0;0;421939991;816683|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;lt;keycode&amp;amp;gt; field is an ASCII decimal value representing the key pressed or released. Adding 128 to the pressed key code will result in the released keycode.&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;lt;prev1&amp;amp;gt; and &amp;amp;lt;prev2&amp;amp;gt; fields are relevant for the interpretation of keys which are modifiable by dead-keys (see “Dead-Class Keys” section). The &amp;amp;lt;prev1&amp;amp;gt; field shows the previous key pressed. The lower byte shows the qualifier, the upper byte shows the key code. The &amp;amp;lt;prev2&amp;amp;gt; field shows the key pressed before the previous key. The lower byte shows the qualifier, the upper byte shows the key code.&lt;br /&gt;
&lt;br /&gt;
== Using the Console Device Without a Window ==&lt;br /&gt;
&lt;br /&gt;
Most console device processing involves a window, but there are functions and special commands that may be used without a window. To use the console device without a window, you call OpenDevice() with the console unit CONU_LIBRARY.&lt;br /&gt;
&lt;br /&gt;
The console device functions are CDInputHandler() and RawKeyConvert(); they may only be used with the CONU_LIBRARY console unit. The console device commands which do not require a window are CD_ASKDEFAULTKEYMAP and CD_SETDEFAULTKEYMAP; they be used with any console unit. The advantage of using the commands with the CONU_LIBRARY unit is the lack of overhead required for CONU_LIBRARY because it doesn’t require a window.&lt;br /&gt;
&lt;br /&gt;
To use the functions requires the following steps:&lt;br /&gt;
&lt;br /&gt;
* Declare the console device base address variable ConsoleDevice in the global data area.&lt;br /&gt;
* Declare storage for an I/O request of type IOStdReq.&lt;br /&gt;
* Open the console device with CONU_LIBRARY set as the console unit.&lt;br /&gt;
* Set the console device base address variable to point to the device library vector which is returned in io_Device.&lt;br /&gt;
* Call the console device function(s).&lt;br /&gt;
* Close the console device when you are finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/conunit.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct IOStdReq ConsIO = {0};&lt;br /&gt;
&lt;br /&gt;
  /* Open the device with CONU_LIBRARY for function use */&lt;br /&gt;
  if (0 == IExec-&amp;gt;OpenDevice(&amp;quot;console.device&amp;quot;, CONU_LIBRARY, (struct IORequest *)&amp;amp;ConsIO, 0))&lt;br /&gt;
  {&lt;br /&gt;
    /* Set the base address variable to the device library vector */&lt;br /&gt;
    struct ConsoleDevice *ConsoleDevice = (struct ConsoleDevice *)ConsIO.io_Device;&lt;br /&gt;
    struct ConsoleIFace *IConsole = (struct ConsoleIFace*)&lt;br /&gt;
      IExec-&amp;gt;GetInterface((struct Library*)ConsoleDevice, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
                  .&lt;br /&gt;
                  .    (console device functions would be called here)&lt;br /&gt;
                  .&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *)IConsole);&lt;br /&gt;
    IExec-&amp;gt;CloseDevice(ConsIO);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code fragment shows only the steps outlined above, it is not complete in any sense of the word. For a complete example of using a console device function, see the &#039;&#039;rawkey.c&#039;&#039; code example in the [[Intuition_Keyboard|Intuition Keyboard]]. The example uses the RawKeyConvert() function.&lt;br /&gt;
&lt;br /&gt;
To use the commands with the CONU_LIBRARY console unit, you follow the same steps that were outlined in [[#Opening the Console Device|Opening the Console Device]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct KeyMap  *keymap;          /* pointer to keymap */&lt;br /&gt;
&lt;br /&gt;
    /* Create the message port */&lt;br /&gt;
struct MsgPort *ConsoleMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
if (ConsoleMP != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* Create the I/O request */&lt;br /&gt;
    struct IOStdReq *ConsoleIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
        ASOIOR_ReplyPort, ConsoleMP,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
        &lt;br /&gt;
    if (ConsoleIO != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            /* Open the Console device */&lt;br /&gt;
        if (IExec-&amp;gt;OpenDevice(&amp;quot;console.device&amp;quot;, CONU_LIBRARY, (struct IORequest *)ConsoleIO, 0))&lt;br /&gt;
&lt;br /&gt;
            /* Inform user that it could not be opened */&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Error: console.device did not open\n&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
            {&lt;br /&gt;
               /* Allocate memory for the keymap */&lt;br /&gt;
            if (keymap = (struct KeyMap *)&lt;br /&gt;
                    AllocVecTags(sizeof(struct KeyMap), AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
                {&lt;br /&gt;
                /* device opened, send CD_ASKKEYMAP command to it */&lt;br /&gt;
                ConsoleIO-&amp;gt;io_Length  = sizeof(struct KeyMap);&lt;br /&gt;
                ConsoleIO-&amp;gt;io_Data    = (APTR)keymap;      /* where to put it */&lt;br /&gt;
                ConsoleIO-&amp;gt;io_Command = CD_ASKKEYMAP;&lt;br /&gt;
                IExec-&amp;gt;DoIO((struct IORequest *)ConsoleIO))&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;CloseDevice(ConsIO);&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again, as in the previous code fragment, this is not complete and you should only use it as a guide.&lt;br /&gt;
&lt;br /&gt;
== Console Device Caveats ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Only one console unit can be attached per window. Sharing a console window must be done at a level higher than the device.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Do not mix graphics.library calls with console rendering in the same areas of a window. It is permissible to send console sequences to adjust the area in which console renders, and use graphics.library calls to render outside of the area console is using.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;For example, do not render text with console sequences and scroll using the graphics.library ScrollRaster() function.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The character map feature is private and cannot be accessed by the programmer. Implementation details and behaviors of the character map my change in the future.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Do not use an IDCMP with character mapped consoles. All Intuition messages should be obtained via &#039;&#039;&#039;Raw Input Events&#039;&#039;&#039; from the console device.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Console Device Example Code ==&lt;br /&gt;
&lt;br /&gt;
The following is a console device demonstration program with supporting routines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Console.c&lt;br /&gt;
 *&lt;br /&gt;
 * Example of opening a window and using the console device&lt;br /&gt;
 * to send text and control sequences to it.  The example can be&lt;br /&gt;
 * easily modified to do additional control sequences.&lt;br /&gt;
 *&lt;br /&gt;
 * Run from CLI only.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/console.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Note - using two character &amp;lt;CSI&amp;gt; ESC[.  Hex 9B could be used instead */&lt;br /&gt;
#define RESETCON  &amp;quot;\033c&amp;quot;&lt;br /&gt;
#define CURSOFF   &amp;quot;\033[0 p&amp;quot;&lt;br /&gt;
#define CURSON    &amp;quot;\033[ p&amp;quot;&lt;br /&gt;
#define DELCHAR   &amp;quot;\033[P&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* SGR (set graphic rendition) */&lt;br /&gt;
#define COLOR02   &amp;quot;\033[32m&amp;quot;&lt;br /&gt;
#define COLOR03   &amp;quot;\033[33m&amp;quot;&lt;br /&gt;
#define ITALICS   &amp;quot;\033[3m&amp;quot;&lt;br /&gt;
#define BOLD      &amp;quot;\033[1m&amp;quot;&lt;br /&gt;
#define UNDERLINE &amp;quot;\033[4m&amp;quot;&lt;br /&gt;
#define NORMAL    &amp;quot;\033[0m&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* our functions */&lt;br /&gt;
void cleanexit(uint8 *,LONG);&lt;br /&gt;
void cleanup(void);&lt;br /&gt;
int8 OpenConsole(struct IOStdReq *,struct IOStdReq *, struct Window *);&lt;br /&gt;
void CloseConsole(struct IOStdReq *);&lt;br /&gt;
void QueueRead(struct IOStdReq *, uint8 *);&lt;br /&gt;
uint8 ConGetChar(struct MsgPort *, uint8 *);&lt;br /&gt;
LONG ConMayGetChar(struct MsgPort *, uint8 *);&lt;br /&gt;
void ConPuts(struct IOStdReq *, uint8 *);&lt;br /&gt;
void ConWrite(struct IOStdReq *, uint8 *, LONG);&lt;br /&gt;
void ConPutChar(struct IOStdReq *, uint8);&lt;br /&gt;
&lt;br /&gt;
struct NewWindow nw =&lt;br /&gt;
    {&lt;br /&gt;
    10, 10,                           /* starting position (left,top) */&lt;br /&gt;
    620,180,                          /* width, height */&lt;br /&gt;
    -1,-1,                            /* detailpen, blockpen */&lt;br /&gt;
    CLOSEWINDOW,                      /* flags for idcmp */&lt;br /&gt;
    WINDOWDEPTH|WINDOWSIZING|&lt;br /&gt;
    WINDOWDRAG|WINDOWCLOSE|&lt;br /&gt;
    SMART_REFRESH|ACTIVATE,           /* window flags */&lt;br /&gt;
    NULL,                             /* no user gadgets */&lt;br /&gt;
    NULL,                             /* no user checkmark */&lt;br /&gt;
    &amp;quot;Console Test&amp;quot;,                   /* title */&lt;br /&gt;
    NULL,                             /* pointer to window screen */&lt;br /&gt;
    NULL,                             /* pointer to super bitmap */&lt;br /&gt;
    100,45,                           /* min width, height */&lt;br /&gt;
    640,200,                          /* max width, height */&lt;br /&gt;
    WBENCHSCREEN                      /* open on workbench screen */&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Opens/allocations we&#039;ll need to clean up */&lt;br /&gt;
struct Library *IntuitionBase = NULL;&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
struct Window   *win = NULL;&lt;br /&gt;
struct IOStdReq *writeReq = NULL;    /* IORequest block pointer */&lt;br /&gt;
struct MsgPort  *writePort = NULL;   /* replyport for writes      */&lt;br /&gt;
struct IOStdReq *readReq = NULL;     /* IORequest block pointer */&lt;br /&gt;
struct MsgPort  *readPort = NULL;    /* replyport for reads       */&lt;br /&gt;
BOOL OpenedConsole = FALSE;&lt;br /&gt;
&lt;br /&gt;
BOOL FromWb;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
    {&lt;br /&gt;
    struct IntuiMessage *winmsg;&lt;br /&gt;
    ULONG signals, conreadsig, windowsig;&lt;br /&gt;
    LONG lch;&lt;br /&gt;
    SHORT InControl = 0;&lt;br /&gt;
    BOOL Done = FALSE;&lt;br /&gt;
    uint8 ch, ibuf;&lt;br /&gt;
    uint8 obuf[200];&lt;br /&gt;
    int8 error;&lt;br /&gt;
&lt;br /&gt;
    FromWb = (argc==0) ? TRUE : FALSE;&lt;br /&gt;
&lt;br /&gt;
    IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if(IIntuition == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t open intuition\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Create reply port and io block for writing to console */&lt;br /&gt;
    writePort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
        ASOPORT_Name, &amp;quot;RKM.console.write&amp;quot;,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    if(writePort == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t create write port\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    writeReq = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
        ASOIOR_ReplyPort, writePort,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    if(writeReq == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t create write request\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Create reply port and io block for reading from console */&lt;br /&gt;
    readPort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
        ASOPORT_Name, &amp;quot;RKM.console.read&amp;quot;,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    if(readPort == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t create read port\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    readReq = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
        ASOIOR_ReplyPort, readPort,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    if(readReq == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t create read request\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Open a window */&lt;br /&gt;
    if(!(win = IIntuition-&amp;gt;OpenWindow(&amp;amp;nw)))&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t open window\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* Now, attach a console to the window */&lt;br /&gt;
    if(error = OpenConsole(writeReq, readReq, win))&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t open console.device\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
    else OpenedConsole = TRUE;&lt;br /&gt;
&lt;br /&gt;
    /* Demonstrate some console escape sequences */&lt;br /&gt;
    ConPuts(writeReq,&amp;quot;Here&#039;s some normal text\n&amp;quot;);&lt;br /&gt;
    IUtility-&amp;gt;SNPrintf(obuf, sizeof(obuf), &amp;quot;%s%sHere&#039;s text in color 3 and italics\n&amp;quot;, COLOR03, ITALICS);&lt;br /&gt;
    ConPuts(writeReq,obuf);&lt;br /&gt;
    ConPuts(writeReq,NORMAL);&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);      /* Delay for dramatic demo effect */&lt;br /&gt;
    ConPuts(writeReq,&amp;quot;We will now delete this asterisk =*=&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
    ConPuts(writeReq,&amp;quot;\b\b&amp;quot;);  /* backspace twice */&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
    ConPuts(writeReq,DELCHAR); /* delete the character */&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
&lt;br /&gt;
    QueueRead(readReq,&amp;amp;ibuf); /* send the first console read request */&lt;br /&gt;
&lt;br /&gt;
    ConPuts(writeReq,&amp;quot;\n\nNow reading console\n&amp;quot;);&lt;br /&gt;
    ConPuts(writeReq,&amp;quot;Type some keys.  Close window when done.\n\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    conreadsig = 1 &amp;lt;&amp;lt; readPort-&amp;gt;mp_SigBit;&lt;br /&gt;
    windowsig = 1 &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
    while(!Done)&lt;br /&gt;
        {&lt;br /&gt;
        /* A character, or an IDCMP msg, or both could wake us up */&lt;br /&gt;
        signals = IExec-&amp;gt;Wait(conreadsig|windowsig);&lt;br /&gt;
&lt;br /&gt;
        /* If a console signal was received, get the character */&lt;br /&gt;
        if (signals &amp;amp; conreadsig)&lt;br /&gt;
            {&lt;br /&gt;
            if((lch = ConMayGetChar(readPort,&amp;amp;ibuf)) != -1)&lt;br /&gt;
                {&lt;br /&gt;
                ch = lch;&lt;br /&gt;
                /* Show hex and ascii (if printable) for char we got.&lt;br /&gt;
                 * If you want to parse received control sequences, such as&lt;br /&gt;
                 * function or Help keys, you would buffer control sequences&lt;br /&gt;
                 * as you receive them, starting to buffer whenever you&lt;br /&gt;
                 * receive 0x9B (or 0x1B[ for user-typed sequences) and&lt;br /&gt;
                 * ending when you receive a valid terminating character&lt;br /&gt;
                 * for the type of control sequence you are receiving.&lt;br /&gt;
                 * For CSI sequences, valid terminating characters&lt;br /&gt;
                 * are generally 0x40 through 0x7E.&lt;br /&gt;
                 * In our example, InControl has the following values:&lt;br /&gt;
                 * 0 = no, 1 = have 0x1B, 2 = have 0x9B OR 0x1B and [,&lt;br /&gt;
                 * 3 = now inside control sequence, -1 = normal end esc,&lt;br /&gt;
                 * -2 = non-CSI(no [) 0x1B end esc&lt;br /&gt;
                 * NOTE - a more complex parser is required to recognize&lt;br /&gt;
                 *  other types of control sequences.&lt;br /&gt;
                 */&lt;br /&gt;
&lt;br /&gt;
                /* 0x1B ESC not followed by &#039;[&#039;, is not CSI seq */&lt;br /&gt;
                if (InControl==1)&lt;br /&gt;
                    {&lt;br /&gt;
                    if(ch==&#039;[&#039;) InControl = 2;&lt;br /&gt;
                    else InControl = -2;&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                if ((ch==0x9B)||(ch==0x1B))  /* Control seq starting */&lt;br /&gt;
                    {&lt;br /&gt;
                    InControl = (ch==0x1B) ? 1 : 2;&lt;br /&gt;
                    ConPuts(writeReq,&amp;quot;=== Control Seq ===\n&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                /* We&#039;ll show value of this char we received */&lt;br /&gt;
                if (((ch &amp;gt;= 0x1F)&amp;amp;&amp;amp;(ch &amp;lt;= 0x7E))||(ch &amp;gt;= 0xA0))&lt;br /&gt;
                   IUtility-&amp;gt;SNPrintf(obuf, sizeof(obuf), &amp;quot;Received: hex %02x = %c\n&amp;quot;,ch,ch);&lt;br /&gt;
                else IUtility-&amp;gt;SNPrintf(obuf, sizeof(obuf), &amp;quot;Received: hex %02x\n&amp;quot;,ch);&lt;br /&gt;
                ConPuts(writeReq,obuf);&lt;br /&gt;
&lt;br /&gt;
                /* Valid ESC sequence terminator ends an ESC seq */&lt;br /&gt;
                if ((InControl==3)&amp;amp;&amp;amp;((ch &amp;gt;= 0x40) &amp;amp;&amp;amp; (ch &amp;lt;= 0x7E)))&lt;br /&gt;
                    {&lt;br /&gt;
                    InControl = -1;&lt;br /&gt;
                    }&lt;br /&gt;
                if (InControl==2) InControl = 3;&lt;br /&gt;
                /* ESC sequence finished (-1 if OK, -2 if bogus) */&lt;br /&gt;
                if (InControl &amp;lt; 0)&lt;br /&gt;
                    {&lt;br /&gt;
                    InControl = 0;&lt;br /&gt;
                    ConPuts(writeReq,&amp;quot;=== End Control ===\n&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        /* If IDCMP messages received, handle them */&lt;br /&gt;
        if (signals &amp;amp; windowsig)&lt;br /&gt;
            {&lt;br /&gt;
            /* We have to ReplyMsg these when done with them */&lt;br /&gt;
            while (winmsg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort))&lt;br /&gt;
                {&lt;br /&gt;
                switch(winmsg-&amp;gt;Class)&lt;br /&gt;
                    {&lt;br /&gt;
                    case CLOSEWINDOW:&lt;br /&gt;
                      Done = TRUE;&lt;br /&gt;
                      break;&lt;br /&gt;
                    default:&lt;br /&gt;
                      break;&lt;br /&gt;
                     }&lt;br /&gt;
                IExec-&amp;gt;ReplyMsg((struct Message *)winmsg);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    /* We always have an outstanding queued read request&lt;br /&gt;
     * so we must abort it if it hasn&#039;t completed,&lt;br /&gt;
     * and we must remove it.&lt;br /&gt;
     */&lt;br /&gt;
    if(!(IExec-&amp;gt;CheckIO(readReq)))  IExec-&amp;gt;AbortIO(readReq);&lt;br /&gt;
    IExec-&amp;gt;WaitIO(readReq);     /* clear it from our replyport */&lt;br /&gt;
&lt;br /&gt;
    cleanup();&lt;br /&gt;
    return RETURN_OK;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
void cleanexit(uint8 *s,LONG n)&lt;br /&gt;
    {&lt;br /&gt;
    if(*s &amp;amp; (!FromWb)) IDOS-&amp;gt;Printf(s);&lt;br /&gt;
    cleanup();&lt;br /&gt;
    exit(n);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
void cleanup()&lt;br /&gt;
    {&lt;br /&gt;
    if(OpenedConsole) CloseConsole(writeReq);&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, readReq);&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, readPort);&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, writeReq);&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, writePort);&lt;br /&gt;
    if(IIntuition != NULL &amp;amp;&amp;amp; win != NULL) IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Attach console device to an open Intuition window.&lt;br /&gt;
 * This function returns a value of 0 if the console&lt;br /&gt;
 * device opened correctly and a nonzero value (the error&lt;br /&gt;
 * returned from OpenDevice) if there was an error.&lt;br /&gt;
 */&lt;br /&gt;
int8 OpenConsole(struct IOStdReq *writereq, struct IOStdReq *readreq, struct Window *window)&lt;br /&gt;
    {&lt;br /&gt;
    int8 error;&lt;br /&gt;
&lt;br /&gt;
    writereq-&amp;gt;io_Data = (APTR) window;&lt;br /&gt;
    writereq-&amp;gt;io_Length = sizeof(struct Window);&lt;br /&gt;
    error = IExec-&amp;gt;OpenDevice(&amp;quot;console.device&amp;quot;, 0, writereq, 0);&lt;br /&gt;
    readreq-&amp;gt;io_Device = writereq-&amp;gt;io_Device; /* clone required parts */&lt;br /&gt;
    readreq-&amp;gt;io_Unit   = writereq-&amp;gt;io_Unit;&lt;br /&gt;
    return(error);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
void CloseConsole(struct IOStdReq *writereq)&lt;br /&gt;
    {&lt;br /&gt;
    IExec-&amp;gt;CloseDevice(writereq);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Output a single character to a specified console&lt;br /&gt;
 */&lt;br /&gt;
void ConPutChar(struct IOStdReq *writereq, uint8 character)&lt;br /&gt;
    {&lt;br /&gt;
    writereq-&amp;gt;io_Command = CMD_WRITE;&lt;br /&gt;
    writereq-&amp;gt;io_Data = (APTR)&amp;amp;character;&lt;br /&gt;
    writereq-&amp;gt;io_Length = 1;&lt;br /&gt;
    IExec-&amp;gt;DoIO(writereq);&lt;br /&gt;
    /* command works because DoIO blocks until command is done&lt;br /&gt;
     * (otherwise ptr to the character could become invalid)&lt;br /&gt;
     */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Output a stream of known length to a console&lt;br /&gt;
 */&lt;br /&gt;
void ConWrite(struct IOStdReq *writereq, uint8 *string, LONG length)&lt;br /&gt;
    {&lt;br /&gt;
    writereq-&amp;gt;io_Command = CMD_WRITE;&lt;br /&gt;
    writereq-&amp;gt;io_Data = (APTR)string;&lt;br /&gt;
    writereq-&amp;gt;io_Length = length;&lt;br /&gt;
    IExec-&amp;gt;DoIO(writereq);&lt;br /&gt;
    /* command works because DoIO blocks until command is done&lt;br /&gt;
     * (otherwise ptr to string could become invalid in the meantime)&lt;br /&gt;
     */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Output a NULL-terminated string of characters to a console&lt;br /&gt;
 */&lt;br /&gt;
void ConPuts(struct IOStdReq *writereq,uint8 *string)&lt;br /&gt;
    {&lt;br /&gt;
    writereq-&amp;gt;io_Command = CMD_WRITE;&lt;br /&gt;
    writereq-&amp;gt;io_Data = (APTR)string;&lt;br /&gt;
    writereq-&amp;gt;io_Length = -1;  /* means print till terminating null */&lt;br /&gt;
    IExec-&amp;gt;DoIO(writereq);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Queue up a read request to console, passing it pointer&lt;br /&gt;
 * to a buffer into which it can read the character&lt;br /&gt;
 */&lt;br /&gt;
void QueueRead(struct IOStdReq *readreq, uint8 *whereto)&lt;br /&gt;
   {&lt;br /&gt;
   readreq-&amp;gt;io_Command = CMD_READ;&lt;br /&gt;
   readreq-&amp;gt;io_Data = (APTR)whereto;&lt;br /&gt;
   readreq-&amp;gt;io_Length = 1;&lt;br /&gt;
   IExec-&amp;gt;SendIO(readreq);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Check if a character has been received.&lt;br /&gt;
 * If none, return -1&lt;br /&gt;
 */&lt;br /&gt;
LONG ConMayGetChar(struct MsgPort *msgport, uint8 *whereto)&lt;br /&gt;
    {&lt;br /&gt;
    struct IOStdReq *readreq;&lt;br /&gt;
&lt;br /&gt;
    if (!(readreq = (struct IOStdReq *)IExec-&amp;gt;GetMsg(msgport))) return(-1);&lt;br /&gt;
    int temp = *whereto;            /* get the character */&lt;br /&gt;
    QueueRead(readreq,whereto);     /* then re-use the request block */&lt;br /&gt;
    return(temp);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Wait for a character&lt;br /&gt;
 */&lt;br /&gt;
uint8 ConGetChar(struct MsgPort *msgport, uint8 *whereto)&lt;br /&gt;
    {&lt;br /&gt;
    struct IOStdReq *readreq;&lt;br /&gt;
&lt;br /&gt;
    WaitPort(msgport);&lt;br /&gt;
    readreq = (struct IOStdReq *)GetMsg(msgport);&lt;br /&gt;
    int temp = *whereto;           /* get the character */&lt;br /&gt;
    QueueRead(readreq,whereto);    /* then re-use the request block*/&lt;br /&gt;
    return((uint8)temp);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional Information on the Console Device ==&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the console device can be found in the include files and the Autodocs for the console device. Both are contained in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Includes&lt;br /&gt;
|-&lt;br /&gt;
| devices/console.h&lt;br /&gt;
|-&lt;br /&gt;
| devices/conunit.h&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! AutoDocs&lt;br /&gt;
|-&lt;br /&gt;
| console.doc&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Utility_-_Little_Helpers&amp;diff=12563</id>
		<title>Programming AmigaOS 4: Utility - Little Helpers</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Utility_-_Little_Helpers&amp;diff=12563"/>
		<updated>2025-01-26T19:35:24Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;This article was adapted from Amiga Future magazine&#039;s series on developing for AmigaOS....&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The inconspicuous AmigaOS 4 utility.library has been extended with some useful functions. So it&#039;s about time we took a closer look at it.&lt;br /&gt;
&lt;br /&gt;
We have sub-divided the overall description into different sections. So, let&#039;s get started with part one, the very flexible taglists. &lt;br /&gt;
&lt;br /&gt;
= Tag list improvements =&lt;br /&gt;
&lt;br /&gt;
Extending function-calls and their parameters can be a complicated issue, without updating all the old sources. By using a tag list, you can include new tags very flexibly, so unknown tags will be ignored. So if you are running the program on an older system, these new options are not available. That means, tag lists will become more and more important. Many of these new functions are about creating, searching and filtering the tagslists. A complete overview about all the new functions can be found in the final section of this article. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct TagItem *taglist;&lt;br /&gt;
&lt;br /&gt;
  /* allocates memory for four pairs of tag-entries */&lt;br /&gt;
  /* the fifth entry only includes the identification for the list-ending */&lt;br /&gt;
  if((taglist = IUtility-&amp;gt;AllocateTagItems(5)))&lt;br /&gt;
  {&lt;br /&gt;
    /* Fill the taglist */&lt;br /&gt;
    taglist[0].ti_Tag  = TAG_IGNORE;&lt;br /&gt;
    taglist[0].ti_Data = 1;&lt;br /&gt;
    ...&lt;br /&gt;
    taglist[4].ti_Tag  = TAG_END;&lt;br /&gt;
    taglist[4].ti_Data = 0;&lt;br /&gt;
&lt;br /&gt;
    /* do something wiht the taglist */&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /*at the end the taglist memory can be deallocated*/&lt;br /&gt;
    IUtility-&amp;gt;FreeTagItems(taglist);&lt;br /&gt;
  }  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Check-digits =&lt;br /&gt;
 &lt;br /&gt;
The utility.library provides a list of interesting features for check-digits. There are a great number of different check-digits and algorithms (i.e. CR32, MD5). The Amiga allows you to calculate a 160 bit (= 20 byte) check-digit via the SHA-1 algorithm. This comes in handy, if you want to check over huge data sections, without having an identical number. In comparison to check-digits with only 2 respectively 4 bytes, this can become a real problem. In detail, there are 3 steps to be made.&lt;br /&gt;
&lt;br /&gt;
First of all, you have to initialise the data structure by using the IUtility-&amp;gt;MessageDigest_SHA_Unit() command. Second comes the actual calculation via the IUtility-&amp;gt;MessageDigest_SHA_Update() command, whereas the data and the length must be given to create the check-digit.&lt;br /&gt;
&lt;br /&gt;
Lastly we type in the IUtility-&amp;gt;MessageDigest_SHA_Final() command. After completing all three steps, it&#039;s possible to get the calculated check-digit through mdsha_Code. The example below shows you how:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct MessageDigest_SHA mdsha = { 0 };&lt;br /&gt;
  STRPTR testdata = &amp;quot;This is just a test&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  IUtility-&amp;gt;MessageDigest_SHA_Init(&amp;amp;mdsha);&lt;br /&gt;
  IUtility-&amp;gt;MessageDigest_SHA_Update(&amp;amp;mdsha, testdata, IUtility-&amp;gt;Strlen(somedata));&lt;br /&gt;
  IUtility-&amp;gt;MessageDigest_SHA_Final(&amp;amp;mdsha);&lt;br /&gt;
&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot; Checksum is: %.20s\n&amp;quot;,mdsha.mdsha_Code);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The test value will be output bit by bit, so it&#039;s not very clever to use the IDOS-&amp;gt;Printf() command, because control-characters can be included there as well. The value can be compared with the reference value thru the IUtility-&amp;gt;StriCmp() command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * CalcChecksum.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc CalcChecksum.c -o CalcChecksum -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************* DEFINES **************************************/&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 65536  /* 64 kByte Block */&lt;br /&gt;
&lt;br /&gt;
/************************ VARIABLEN DEKLARATIONEN *****************************/&lt;br /&gt;
&lt;br /&gt;
const UBYTE *gb_ArgTemplate = &amp;quot;FILENAME/A&amp;quot;;&lt;br /&gt;
enum { ARG_filename, ARG_MAX };&lt;br /&gt;
ULONG gb_Args[ARG_MAX] = { 0 };&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
BOOL CalculateChecksum(CONST_STRPTR filename, UBYTE *checksum)&lt;br /&gt;
{&lt;br /&gt;
  BPTR fh;&lt;br /&gt;
  struct MessageDigest_SHA mdsha = { 0 };&lt;br /&gt;
&lt;br /&gt;
  /* Preparing checksum calculation */&lt;br /&gt;
  IUtility-&amp;gt;MessageDigest_SHA_Init(&amp;amp;mdsha);&lt;br /&gt;
&lt;br /&gt;
  if((fh = IDOS-&amp;gt;Open((STRPTR)filename, MODE_OLDFILE)))&lt;br /&gt;
  {&lt;br /&gt;
    LONG character;&lt;br /&gt;
    UBYTE *buffer;&lt;br /&gt;
&lt;br /&gt;
    if((buffer = (UBYTE *) IExec-&amp;gt;AllocVecTags(BUFFER_SIZE, TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      while((character = IDOS-&amp;gt;Read(fh, buffer, BUFFER_SIZE)) &amp;gt; 0)&lt;br /&gt;
      {&lt;br /&gt;
        IUtility-&amp;gt;MessageDigest_SHA_Update(&amp;amp;mdsha, buffer, character);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeVec(buffer);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Close(fh);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IUtility-&amp;gt;MessageDigest_SHA_Final(&amp;amp;mdsha);&lt;br /&gt;
  IUtility-&amp;gt;Strlcpy(checksum, mdsha.mdsha_Code, 20);&lt;br /&gt;
  checksum[20] = &#039;\0&#039;;&lt;br /&gt;
&lt;br /&gt;
  return fh ? TRUE : FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct RDArgs *rda;&lt;br /&gt;
&lt;br /&gt;
  if((rda = IDOS-&amp;gt;ReadArgs((STRPTR)gb_ArgTemplate,(LONG*)gb_Args,NULL)))&lt;br /&gt;
  {&lt;br /&gt;
    UBYTE keybuffer[21]; /* 20 Bytes Checksum + \0 */&lt;br /&gt;
&lt;br /&gt;
    if(BerechneChecksumme((STRPTR)gb_Args[ARG_filename], keybuffer))&lt;br /&gt;
    {&lt;br /&gt;
      /*&lt;br /&gt;
      ** The checksum is a 160-bit data stream which should&lt;br /&gt;
      ** not strictly be output like a string. We do so here&lt;br /&gt;
      ** for convenience only.&lt;br /&gt;
      */&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Checksum for file &#039;%s&#039; is %s\n&amp;quot;,&lt;br /&gt;
        gb_Args[ARG_filename], keybuffer);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;FreeArgs(rda);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),&amp;quot;CalcChecksum&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF111_CalcChecksum.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Standard-C-Functions =&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t like to include the additonal C-libraries (newlib or clib2), it&#039;s possible to get similar functions from the utility.libary. In the case of string-functions, there are IUtility-&amp;gt;Strlcpy() and IUtility-&amp;gt;Strlcat() functions, which seem to have a slightly strange name, but work in a similar way as strncpy and strncat from the C-libaries. Since version 53.4 there is also a function called IUtility-&amp;gt;Strlen() which determines the length of a string. With AmigaOS 4.1 Final Edition new functions were added for handling UTF-8 coded strings.&lt;br /&gt;
&lt;br /&gt;
Like the memset function, there is a function called IUtility-&amp;gt;SetMem(), which is able to set the memory-area. The required arguments are the starting-address in the memory, the value to be set (usually 0 deletes the memory-area) and the number of bytes that have to be amended. In general the order of the arguments are the same as in the memset function. A shortcut can be taken, if you just use the IUtility-&amp;gt;ClearMem() function. This one only needs the starting-address and the number of bytes that should be cleared to zero. One other function, that works with memory-areas, is called IUtility-&amp;gt;MoveMem(). As the name implies, the memory-area will be moved. Well the source material stays unchanged, so it&#039;s more like copying the areas. You can compare this function to the IExec-&amp;gt;CopyMem() function from the exec.libary, but with the difference of allowing overlapping memory-areas. Make sure that your destination has enough free space for the data and, of course, it has to be reserved in your own program as well, because without reserving it, the OS will shutdown your program, if &amp;quot;aggravated&amp;quot; memory protection is enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * MemoryFunctions.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc MemoryFunctions.c -o MemoryFunctions -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  UBYTE *mem;&lt;br /&gt;
&lt;br /&gt;
  /* Request memory */&lt;br /&gt;
  if((mem = (UBYTE *) IExec-&amp;gt;AllocVecTags(512, TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;memory allocated\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    /* Clear memory area */&lt;br /&gt;
    IUtility-&amp;gt;ClearMem(mem,512);&lt;br /&gt;
&lt;br /&gt;
    /* Set memory area */    &lt;br /&gt;
    IUtility-&amp;gt;SetMem(mem,&#039;@&#039;,128);&lt;br /&gt;
&lt;br /&gt;
    /* Move memory area */&lt;br /&gt;
    IUtility-&amp;gt;MoveMem(mem,mem + 128,256);&lt;br /&gt;
&lt;br /&gt;
    /* Free memory area */&lt;br /&gt;
    IExec-&amp;gt;FreeVec(mem);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Not enough memory\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return( 0 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Randomizing =&lt;br /&gt;
&lt;br /&gt;
If you ever liked to create your own lottery numbers, the new IUtility-&amp;gt;Random() function will allow you to do just that. All you need is a number, so you can just pick the actual time or a random value from the stack. The function will return a number ranging from 1 up to 2147483647, which can be cut down via the modulo operation. Next up we see an example of how to create your own lottery numbers. &lt;br /&gt;
The same number can be chosen again, so there is room for improvement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct RandomState state;&lt;br /&gt;
  ULONG i, number;&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;6 out of 49:\n&amp;quot;);&lt;br /&gt;
  for(i=1; i&amp;lt;7; i++)&lt;br /&gt;
  {&lt;br /&gt;
    /* determines a number between 1 and 49 */&lt;br /&gt;
    number = (IUtility-&amp;gt;Random(&amp;amp;state) % 48) + 1;&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;%ld. Number %2ld\n&amp;quot;,i,number);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  number = (IUtility-&amp;gt;Random(&amp;amp;state) % 48) + 1;&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Additional number %2ld\n&amp;quot;,number); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* LottoGenerator.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc LottoGenerator.c -o LottoGenerator -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct RandomState state;&lt;br /&gt;
  uint32 i, number;&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
  ** Initialize the start values so that a different&lt;br /&gt;
  ** series of random numbers are generated each time.&lt;br /&gt;
  */&lt;br /&gt;
  state.rs_High = time(NULL);&lt;br /&gt;
  state.rs_Low  = time(NULL);&lt;br /&gt;
&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Lotto 6 out of 49:\n&amp;quot;);&lt;br /&gt;
  for(i=1; i&amp;lt;7; i++)&lt;br /&gt;
  {&lt;br /&gt;
    /*&lt;br /&gt;
    ** Determine a number from 1 to 49.&lt;br /&gt;
    */&lt;br /&gt;
    number = (IUtility-&amp;gt;Random(&amp;amp;state) % 48) + 1;&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;%ld. Number is %2ld\n&amp;quot;,i,number);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  number = (IUtility-&amp;gt;Random(&amp;amp;state) % 48) + 1;&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;The bonus number is %2ld\n&amp;quot;,number);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF111_LottoGenerator.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Miscellaneous functions =&lt;br /&gt;
&lt;br /&gt;
Another helpful function is called IUtility-&amp;gt;GetUniqueID(), which determines a unique number. This comes in handy by creating temporary files using multiple processes. As long as the system isn&#039;t shutdown, this unique number will be available. By restarting the system, the numbers will be created from scratch. Here is an easy example of how to create files with unique numbers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  ULONG id = IUtility-&amp;gt;GetUniqueID();&lt;br /&gt;
  BPTR fh;&lt;br /&gt;
  TEXT filename[30];&lt;br /&gt;
  IUtility-&amp;gt;SNPrintf(filename,sizeof(filename),&amp;quot;t:tmp.%ld&amp;quot;,id);&lt;br /&gt;
  if(IDOS-&amp;gt;Open(filename,MODE_NEWFILE))&lt;br /&gt;
  {&lt;br /&gt;
    ...&lt;br /&gt;
    IDOS-&amp;gt;CLose(fh);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= New utility.library functions =&lt;br /&gt;
&lt;br /&gt;
== Taglist functions ==&lt;br /&gt;
&lt;br /&gt;
Allocates the memory for the number of given tags.&lt;br /&gt;
 struct TagItem *AllocateTagItems(ULONG numtags);&lt;br /&gt;
&lt;br /&gt;
Changes the tag-data-values in the changelist based on the values from the list. Unknown tags in the list will be ignored.&lt;br /&gt;
 VOID ApplyTagChanges(struct TagItem *list, struct TagItem *changelist);&lt;br /&gt;
&lt;br /&gt;
Creating a taglist copy; has to be deallocated via FreeTagItems().&lt;br /&gt;
 struct TagItem *CloneTagItems(struct TagItem *orglist);&lt;br /&gt;
&lt;br /&gt;
Deallocates the memory for the taglist.&lt;br /&gt;
 VOID FreeTagItems(struct TagItem *taglist);&lt;br /&gt;
&lt;br /&gt;
Creates a skip-tag-list; has to be deallocated via DeleteSkipList().&lt;br /&gt;
 struct SkipList * CreateSkipList(struct Hook *hook, LONG max_levels);&lt;br /&gt;
 VOID DeleteSkipList(struct SkipList *list);&lt;br /&gt;
&lt;br /&gt;
Searches the changelist and deletes all entries, which are identical to the entries in the orglist. In the case of different values, the keyword &amp;quot;apply&amp;quot; defines if the value should be assumed from the orglist.&lt;br /&gt;
 VOID FilterTagChanges(struct TagItem *changelist, struct TagItem *orglist, ULONG apply);&lt;br /&gt;
&lt;br /&gt;
Deletes the tags from the changelist, if they occur in the filter array.&lt;br /&gt;
 ULONG FilterTagItems(struct TagItem *changelist, Tag *filterarray ,ULONG logic);&lt;br /&gt;
&lt;br /&gt;
Searches the taglist for the right entry and returns the matching items.&lt;br /&gt;
 struct TagItem *FindTagItem(Tag tag,struct TagItem *taglist);&lt;br /&gt;
&lt;br /&gt;
Determines the data value of the declared tag from the list.&lt;br /&gt;
 ULONG GetTagData(Tag tag, ULONG default, struct TagItem *taglist);&lt;br /&gt;
&lt;br /&gt;
Transfers the maplist values into the corresponding fields of the taglist. Type pin-points what happens to all entries, which only occur in the taglist.&lt;br /&gt;
 VOID MapTags(struct TagItem *taglist, struct TagItem *maplist, ULONG type);&lt;br /&gt;
&lt;br /&gt;
== Splay-Tree-Functions ==&lt;br /&gt;
&lt;br /&gt;
Creating a Splay-Tree-Datastructure; has to be deallocated via DeleteSplayTree()&lt;br /&gt;
 struct SplayTree * CreateSplayTree(struct Hook *hook);&lt;br /&gt;
 VOID DeleteSplayTree(struct SplayTree *tree);&lt;br /&gt;
&lt;br /&gt;
Searches the tree for a specific key and returns the matching node&lt;br /&gt;
 struct SplayNode * FindSplayNode(struct SplayTree *tree, APTR key);&lt;br /&gt;
&lt;br /&gt;
Creates a new SplayNode and adds it to the tree&lt;br /&gt;
 struct SplayNode * InsertSplayNode(struct SplayTree *tree, APTR key, ULONG data_size);&lt;br /&gt;
&lt;br /&gt;
Removes the SplayNode from the tree, if a matching entry for the key was found.&lt;br /&gt;
 BOOL RemoveSplayNode(struct SplayTree *tree, APTR key);&lt;br /&gt;
&lt;br /&gt;
== Skip-List-Functions ==&lt;br /&gt;
&lt;br /&gt;
Searches the list for a specific key and returns the matching node.&lt;br /&gt;
 struct SkipNode * FindSkipNode(struct SkipList *list, APTR key);&lt;br /&gt;
&lt;br /&gt;
Determines the first node in the list&lt;br /&gt;
 struct SkipNode * GetFirstSkipNode(struct SkipList *list);&lt;br /&gt;
&lt;br /&gt;
Determines the next node after the previous node in the list.&lt;br /&gt;
 struct SkipNode * GetNextSkipNode(struct SkipList *list, struct SkipNode *previousnode);&lt;br /&gt;
&lt;br /&gt;
Creates a new skipnode and adds it to the list.&lt;br /&gt;
 struct SkipNode * InsertSkipNode(struct SkipList *list, APTR key, ULONG total_size);&lt;br /&gt;
&lt;br /&gt;
Removes the SkipNode from the list, if a matching entry for the key was found.&lt;br /&gt;
 BOOL RemoveSkipNode(struct SkipList *list, APTR key);&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous functions ==&lt;br /&gt;
&lt;br /&gt;
Calculates a 160 bit checksum&lt;br /&gt;
 VOID MessageDigest_SHA_Init(struct MessageDigest_SHA *mdsha);&lt;br /&gt;
 VOID MessageDigest_SHA_Update(struct MessageDigest_SHA *mdsha, APTR data, LONG num_bytes);&lt;br /&gt;
 VOID MessageDigest_SHA_Final(struct MessageDigest_SHA *mdsha);&lt;br /&gt;
&lt;br /&gt;
A hook-function for an object to send a message&lt;br /&gt;
 ULONG CallHookPkt(struct Hook *hook, APTR object, APTR message);&lt;br /&gt;
&lt;br /&gt;
Provides a unique number (as long as the computer is running)&lt;br /&gt;
 ULONG GetUniqueID(void);&lt;br /&gt;
&lt;br /&gt;
A random number between 1 ... 2147483647 will be returned&lt;br /&gt;
 LONG Random(struct RandomState *state);&lt;br /&gt;
&lt;br /&gt;
Adds a value to the memory-area&lt;br /&gt;
 APTR SetMem(APTR destination, UBYTE value, LONG length);&lt;br /&gt;
&lt;br /&gt;
Deletes a memory-area (it will be filled with 0 bytes):&lt;br /&gt;
 VOID ClearMem(APTR destination, uint32 size);&lt;br /&gt;
&lt;br /&gt;
Moves parts of the memory-area (even if they are overlapping):&lt;br /&gt;
 VOID MoveMem(APTR source, APTR destination, uint32 size);&lt;br /&gt;
&lt;br /&gt;
Adds the source string to the destination, where the max length will be considered.&lt;br /&gt;
 LONG Strlcat(STRPTR destination, CONST_STRPTR source, LONG destination_size);&lt;br /&gt;
&lt;br /&gt;
Copies the string from its source to its destination, where the max length will be considered.&lt;br /&gt;
 LONG Strlcpy(STRPTR destination, CONST_STRPTR source, LONG destination_size);&lt;br /&gt;
&lt;br /&gt;
Determines the length of a string&lt;br /&gt;
uint32 Strlen(CONST_STRPTR string);&lt;br /&gt;
&lt;br /&gt;
Searches a node based on its name. Lower and Upper case will be ignored.&lt;br /&gt;
 struct Node *node = FindNameNC(struct Node *start, STRPTR name);&lt;br /&gt;
&lt;br /&gt;
Creates a string based on a specific format and the required args, (the returned string has to be deallocated via FreeVec)&lt;br /&gt;
 STRPTR VASPrintf(CONST_STRPTR format, APTR args);&lt;br /&gt;
 STRPTR ASPrintf(CONST_STRPTR format, ...);&lt;br /&gt;
&lt;br /&gt;
Creates a Format-String, where the result is stored in the buffer.&lt;br /&gt;
 LONG VSNPrintf(STRPTR buffer, LONG buffer_size, CONST_STRPTR format, APTR args);&lt;br /&gt;
 LONG SNPrintf(STRPTR buffer, LONG buffer_size, CONST_STRPTR format, ...);&lt;br /&gt;
&lt;br /&gt;
= Authors =&lt;br /&gt;
&lt;br /&gt;
Written by Michael Christoph&amp;lt;br/&amp;gt;&lt;br /&gt;
Translation by Florian Hanel&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright (c) 2013 Michael Christoph&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=SANA-II_Revision_7&amp;diff=12562</id>
		<title>SANA-II Revision 7</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=SANA-II_Revision_7&amp;diff=12562"/>
		<updated>2025-01-26T19:34:56Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP}}&lt;br /&gt;
= SANA-II Network Device Driver Specification =&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;SANA-II Network Device Driver Specification&amp;quot; is a standard for an Amiga software interface between networking hardware and network protocol stacks (or for software tools such as network monitors). A network protocol stack is a layer of software that network applications use to address particular processes on remote machines and to send data reliably in spite of hardware errors. There are several common network protocol stacks including &#039;&#039;TCP/IP&#039;&#039;, &#039;&#039;OSI&#039;&#039;, &#039;&#039;AppleTalk&#039;&#039;, &#039;&#039;DECNet&#039;&#039; and &#039;&#039;Novell&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
SANA-II device drivers are intended to allow multiple network protocol stacks running on the same machine to share one network device. For example, the TCP/IP and AppleTalk protocol stacks could both run on the same machine over one ethernet board. The device drivers are also intended to allow network protocol stacks to be written in a hardware-independent fashion so that a different version of each protocol stack doesn&#039;t have to be written for each networking hardware device.&lt;br /&gt;
&lt;br /&gt;
The standard does not address the writing of network applications. Application writers must not use SANA-II Device Drivers directly. Network applications must use the API provided by the network protocol software the application supports. The Amiga standard network API for TCP/IP is provided via the bsdsocket.library which is a part of Roadshow.&lt;br /&gt;
&lt;br /&gt;
To write a SANA-II device driver, you will need to be familiar with the specification documents for the hardware you are writing to and with the &amp;quot;SANA-II Network Device Driver Specification&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To write a network protocol stack which will use SANA-II device drivers, you should have general familiarity with common network hardware and must be very familiar with the &amp;quot;SANA-II Network Device Driver Specification&amp;quot; as well as the specification for the protocol you are developing. If you are creating a new protocol, you must obtain a protocol type number for any hardware on which your protocol will be used.&lt;br /&gt;
&lt;br /&gt;
This version of the specification is final. Any new version of the standard (i.e., to add new features) is planned to be backward compatible. No SANA-II device driver or software utilizing those drivers should be written to any earlier version of the specification.&lt;br /&gt;
&lt;br /&gt;
Distribution of this version of the standard is unlimited. Anyone may write Amiga software which implements a SANA-II network device driver or which calls a SANA-II network device driver without restriction and may freely distribute such software that they have written.&lt;br /&gt;
&lt;br /&gt;
It is important to try to test each SANA-II device driver against all software which uses SANA-II devices. Available example programs are valuable in initial testing. The Amiga Networking Group is interested in receiving evaluation and/or beta test copies of all Amiga networking hardware, SANA-II device drivers and software which uses SANA-II devices. However, we make no assurances regarding any testing which we may or may not perform with such evaluation copies.&lt;br /&gt;
&lt;br /&gt;
The SANA-II standard caters to both Motorola 68000 platforms and PowerPC platforms.&lt;br /&gt;
&lt;br /&gt;
Please feel free to comment. You can contact the AmigaOS development team through the [http://www.amigaos.net/contact AmigaOS contact form].&lt;br /&gt;
&lt;br /&gt;
= Driver Form =&lt;br /&gt;
&lt;br /&gt;
SANA-II device drivers are Amiga Exec device drivers. They use an extended IORequest structure and a number of extended commands for tallying network statistics, sending broadcasts and multicasts, network addressing and the handling of unexpected packets. The SDK includes information on how to construct an Exec device.&lt;br /&gt;
&lt;br /&gt;
= Opening a SANA-II Device =&lt;br /&gt;
&lt;br /&gt;
As when opening any other Exec device, on the call to OpenDevice() a SANA-II device receives an IORequest structure which the device initializes for the opener&#039;s use. The opener must copy this structure if it desires to use multiple asynchronous requests. The SANA-II IORequest is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IOSana2Req&lt;br /&gt;
{&lt;br /&gt;
  struct IORequest ios2_Req;&lt;br /&gt;
  ULONG ios2_WireError;&lt;br /&gt;
  ULONG ios2_PacketType;&lt;br /&gt;
  UBYTE ios2_SrcAddr[SANA2_MAX_ADDR_BYTES];&lt;br /&gt;
  UBYTE ios2_DstAddr[SANA2_MAX_ADDR_BYTES];&lt;br /&gt;
  ULONG ios2_DataLength;&lt;br /&gt;
  APTR ios2_Data;&lt;br /&gt;
  APTR ios2_StatData;&lt;br /&gt;
  APTR ios2_BufferManagement;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; ios2_Req&lt;br /&gt;
: A standard Exec device IORequest.&lt;br /&gt;
; ios2_WireError&lt;br /&gt;
: A more specific device code which may be set when there is an io_Error. See &amp;amp;lt;devices/sana2.h&amp;amp;gt; for the defined WireErrors.&lt;br /&gt;
; ios2_PacketType&lt;br /&gt;
: The type of packet requested. See the section on &amp;quot;Packet Types&amp;quot;.&lt;br /&gt;
; ios2_SrcAddr&lt;br /&gt;
: The device fills in this field with the interface (network hardware) address of the source of the packet that satisfied a read command. The bytes used to hold the address will be left justified but the bit layout is dependent on the particular type of network.&lt;br /&gt;
; ios2_DstAddr&lt;br /&gt;
: Before the device user sends a packet, it fills this with the interface destination address of the packet. On receives, the device fills this with the interface destination address. Other commands may use this field differently (see the &amp;quot;SANA-II network device driver Autodocs&amp;quot; in the SDK). The bytes used to hold the address will be left justified but the bit layout is dependent on the particular type of network.&lt;br /&gt;
; ios2_DataLength&lt;br /&gt;
: The device user initializes this field with the amount of data available in the Data buffer before passing the IOSana2Req to the device. The device fills in this field with the size of the packet data as it was sent on the wire. This does not include the header and trailer information. Depending on the network type and protocol type, the driver may have to calculate this value. This is generally used only for reads and writes (including broadcast and multicast).&lt;br /&gt;
; ios2_Data&lt;br /&gt;
: A pointer to some abstract data structure containing packet data. &#039;&#039;Drivers may not directly manipulate or examine anything pointed to by Data!&#039;&#039; This is generally used only for reads and writes (including broadcast and multicast).&lt;br /&gt;
&lt;br /&gt;
; ios2_StatData&lt;br /&gt;
: Pointer to a structure in which to place a snapshot of device statistics. The data area must be long word aligned. This is only used on calls to the statistics commands.&lt;br /&gt;
&lt;br /&gt;
; ios2_BufferManagement&lt;br /&gt;
: The opener places a pointer to a tag list in this field before calling OpenDevice(). Functions pointed to in the tag list are called by the device when processing IORequests from the opener. When returned from OpenDevice(), this field contains a pointer to driver-private information used to access these functions. See &amp;quot;Buffer Management&amp;quot; below for more details.&lt;br /&gt;
&lt;br /&gt;
: Note that the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field provided by the driver on &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time in conjunction with &amp;lt;tt&amp;gt;io_Device&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;io_Unit&amp;lt;/tt&amp;gt; is the unique identifier for all requests coming from this protocol stack until &amp;lt;tt&amp;gt;CloseDevice()&amp;lt;/tt&amp;gt;. The driver must not ever change the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field for a protocol stack at run time, even if &amp;lt;tt&amp;gt;S2_SANAHOOK&amp;lt;/tt&amp;gt; is called to request extended features.&lt;br /&gt;
&lt;br /&gt;
The flags used with the device on OpenDevice() are (SANA2OPB_xxx):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| SANA2OPB_MINE&lt;br /&gt;
| Exclusive access to the unit requested.&lt;br /&gt;
|-&lt;br /&gt;
| SANA2OPB_PROM&lt;br /&gt;
| Promiscuous mode requested. Hardware which supports promiscuous mode allows all packets sent over the wire to be captured whether or not they are addressed to this node.&amp;lt;/br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Note:&#039;&#039;&#039; Promiscuous mode requires exclusive opening of the device.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The flags used during I/O requests are (SANA2IOB_xxx):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| SANA2IOB_RAW&lt;br /&gt;
| Raw packet read/write requested. Raw packets should include the entire data-link layer packet. Devices with the same hardware device number should have the same raw packet format.&lt;br /&gt;
|-&lt;br /&gt;
| SANA2IOB_BCAST&lt;br /&gt;
| Broadcast packet (received).&lt;br /&gt;
|-&lt;br /&gt;
| SANA2IOB_MCAST&lt;br /&gt;
| Multicast packet (received).&lt;br /&gt;
|-&lt;br /&gt;
| SANA2IOB_QUICK&lt;br /&gt;
| Quick IO requested.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Buffer Management =&lt;br /&gt;
&lt;br /&gt;
Unlike most other Exec Device drivers, SANA-II drivers have no internal buffers. Instead, they read/write to/from an abstract data structure allocated by the driver user. The driver accesses these buffers only via functions that the driver user provides to the driver. The driver user must provide two functions: one copies data to the abstract data structure and one copies data from the abstract data structure. The driver user can therefore choose the data structure used for buffer management by both the driver and driver user in order to have efficient memory and CPU usage overall.&lt;br /&gt;
&lt;br /&gt;
The IOSana2Req contains a pointer to data and the length of said data. A driver is not allowed to make assumptions about how the data is stored. The driver cannot directly manipulate or examine the buffer in any manner. The driver can only access the buffer by calling the functions provided by the driver user.&lt;br /&gt;
&lt;br /&gt;
Before calling OpenDevice(), the driver user points ios2_BufferManagement to a list of tags (defined in &amp;amp;lt;devices/sana2.h&amp;amp;gt;) which include pointers to the buffer management functions required by the driver (defined below). The driver will fail to open if the driver user does not supply all of the required functions. If the device opens successfully, the driver sets ios2_BufferManagement to a value which this opener must use in all future calls to the driver. This &amp;quot;magic cookie&amp;quot; is used from then on to access these functions (a &amp;quot;magic cookie&amp;quot; is a value which one software entity passes to another but which is only meaningful to one of the software entities). The driver user may not use the &amp;quot;magic cookie&amp;quot; in any way--it is for the driver to do with as it wishes. The driver could in theory choose to just copy the tag list to driver-owned memory and then parse the list for every IORequest, but it is much more efficient for the driver to create some sort of table of functions and to point ios2_BufferManagement to that table.&lt;br /&gt;
&lt;br /&gt;
Another recommendation for the ``magic cookie`` is to use it to maintain a separate packet read queue for each device opener. This would allow multiple protocol stacks that all wish to receive the same packet type to work together without having to &amp;quot;know&amp;quot; about each other as &#039;&#039;Envoy&#039;&#039; and &#039;&#039;AS225&#039;&#039; do right now. What does multiple protocol stack support mean? Basically this means that each opener gets all the packets necessary. If a packet comes in that fills a request for more than one opener of the device, all of them will get a copy of the packet. This feature should never be left out of a device design. If it is missing, the usefulness of the device is severely limited.&lt;br /&gt;
&lt;br /&gt;
In order to help system load, a packet filter callback allows protocol stacks to reject packets that are known to not be useful. &#039;&#039;Envoy&#039;&#039;&#039;s nipc.library (for example) could be modified to reject TCP packets (as it never uses them).&lt;br /&gt;
&lt;br /&gt;
The specification currently defines the following tags for the OpenDevice() ios2_BufferManagement tag list:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Copy data from network interface to protocol stack buffer&lt;br /&gt;
! Tag&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyToBuff (mandatory)&lt;br /&gt;
| This is a pointer to a function which conforms to the CopyToBuff Autodoc.&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyToBuff16 (optional)&lt;br /&gt;
| Copy to a 16 bit aligned buffer using 16 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyToBuff32 (optional)&lt;br /&gt;
| Copy to a 32 bit aligned buffer using 32 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyToBuff32 (optional)&lt;br /&gt;
| Perform a DMA copy to a 32 bit aligned contiguous buffer using 32 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyToBuff64 (optional)&lt;br /&gt;
| Perform a DMA copy to a 64 bit aligned contiguous buffer using 64 bit data words.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Copy data from protocol stack buffer to network interface&lt;br /&gt;
! Tag&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyFromBuff (mandatory)&lt;br /&gt;
| This is a pointer to a function which conforms to the CopyFromBuff Autodoc.&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyFromBuff16 (optional)&lt;br /&gt;
| Copy from a 16 bit aligned buffer using 16 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyFromBuff32 (optional)&lt;br /&gt;
| Copy from a 32 bit aligned buffer using 32 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyFromBuff32 (optional)&lt;br /&gt;
| Perform a DMA copy from a 32 bit aligned contiguous buffer using 32 bit data words.&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyFromBuff64 (optional)&lt;br /&gt;
| Perform a DMA copy from a 64 bit aligned contiguous buffer using 64 bit data words.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Filter data before protocol stack buffer&lt;br /&gt;
! Tag&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2_PacketFilter (optional)&lt;br /&gt;
| This is a pointer to a standard Hook to be called before S2_CopyToBuff is done. See the PacketFilter Autodoc for more information.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Logging messages from the driver&lt;br /&gt;
! Tag&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2_Log (optional)&lt;br /&gt;
| This is a pointer to a standard Hook to be called when the driver logs events.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Better buffer management ==&lt;br /&gt;
&lt;br /&gt;
The mandatory buffer management callbacks may not be very efficient for certain types of hardware. They also do not allow driver DMA access.&lt;br /&gt;
&lt;br /&gt;
All the new features are completely optional and do not collide with existing features. They may be used only when the protocol stack asks for them on opening a driver.&lt;br /&gt;
&lt;br /&gt;
The enhancements consist of several new tags that may be specified by a protocol stack on OpenDevice() to offer certain data transfer options. It is up to the device driver to chose which callbacks to use at what time. These tags are advisory only and may be ignored by the driver for any data buffer at any time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| S2_CopyToBuff16&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyFromBuff16&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyToBuff32&lt;br /&gt;
|-&lt;br /&gt;
| S2_CopyFromBuff32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These are optional callbacks presented to the device with the same calling interface as for S2_CopyToBuff or S2_CopyFromBuff, respectively. The difference to the original callbacks is the required and guaranteed transfer size and alignment for accessing the device&#039;s buffer for a single piece of a data of either 16 or 32 bits, a data word. The copy function called may only use 16/32 bit aligned read/write commands of 16/32 bits at once to transfer the data words, respectively. If the buffer data length is not a multiple of the required data word transfer size, the last data word transfer may contain garbage padding in either transfer direction.&lt;br /&gt;
&lt;br /&gt;
The following tags have been added to support direct writes into hardware buffers that do not allow arbitrarily sized or aligned accesses:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| S2_DMACopyToBuff32&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyFromBuff32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the protocol stack wants to optionally enhance data transfer efficiency with DMA supporting devices, it may pass any of these optional tags to the device on OpenDevice().&lt;br /&gt;
&lt;br /&gt;
If the device driver supports DMA, it may call the respective callback with the abstract magic cookie ios2_Data in register A0. The callback may return NULL in D0. In this case, the driver may not use DMA for this buffer. Alternatively, the callback may return the address of the actual data buffer in D0, if it has these characteristics:&lt;br /&gt;
&lt;br /&gt;
* The buffer is in contiguous memory. Depending on the intended data direction, it shall be readable or writable.&lt;br /&gt;
* The buffer is aligned on a 32 bit boundary.&lt;br /&gt;
* The buffer size shall be a multiple of 32 bit and it is at least = ios2_DataLength.&lt;br /&gt;
* It is up to the driver to decide if it can use DMA for this buffer and it shall fall back to the standard CPU callbacks if necessary. The data transfer method actually used by the driver will not be known in advance by the protocol stack.&lt;br /&gt;
&lt;br /&gt;
The following tags have been added to allow for 64 bit aligned PCI DMA accesses to take place:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| S2_DMACopyToBuff64&lt;br /&gt;
|-&lt;br /&gt;
| S2_DMACopyFromBuff64&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These two callbacks are identical in operation to the &amp;lt;tt&amp;gt;S2_DMACopyToBuff32&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DMACopyFromBuff32&amp;lt;/tt&amp;gt; callbacks. The difference is in that the memory region DMA is to take place in must be aligned to a 64 bit boundary and must be large enough to hold data that is a multiple of 64 bits in size.&lt;br /&gt;
&lt;br /&gt;
== Best buffer management ==&lt;br /&gt;
&lt;br /&gt;
The SANA-II driver interface is intended to transform data between the hardware layer and the link layer, to be used by networking software such as TCP/IP stacks. This transformation is performed by callback functions which are supplied by the networking software at the time the device driver is opened. The device driver then invokes these functions later in order to transfer data received and data to be sent.&lt;br /&gt;
&lt;br /&gt;
The function parameters used by these callbacks are passed in 68000 registers for the lowest overhead. The problem with 68000 register parameters is that on the PowerPC platform, this form of parameter passing may require the use of emulation code. This is costly and may incur a severe performance penalty. It is an even greater problem if PowerPC native networking software is calling PowerPC native networking driver software and the other way round. In both cases the runtime environment will have to enter emulation mode, return to to PowerPC native execution, dip into emulation mode and return to native PowerPC execution. It would be much better if the chain of execution would stay in PowerPC mode all the time.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if a hook function is used, the operating system may be able to decide whether the function to be invoked needs emulating or called directly. The &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; effectively works as an abstraction which makes the function invocation platform independent.&lt;br /&gt;
&lt;br /&gt;
The following standard wraps the copying and logging functions into the standardized &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; interface.&lt;br /&gt;
&lt;br /&gt;
==== The hook function ====&lt;br /&gt;
&lt;br /&gt;
The hook function itself is invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   result = hook_function(hook, sana2req, sana2hookmsg)&lt;br /&gt;
&lt;br /&gt;
   ULONG hook_function(struct Hook *hook,&lt;br /&gt;
                       struct IOSana2Req *sana2req,&lt;br /&gt;
                       struct SANA2HookMsg *sana2hookmsg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the result is not necessarily of type &amp;lt;tt&amp;gt;ULONG&amp;lt;/tt&amp;gt;. It is a 32 bit value, which can be a boolean result code (for &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; and their like) or a pointer to a memory address (for &amp;lt;tt&amp;gt;DMACopyToBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;DMACopyFromBuff&amp;lt;/tt&amp;gt; and their like).&lt;br /&gt;
&lt;br /&gt;
=== Data structures ===&lt;br /&gt;
&lt;br /&gt;
The following hook messages are to be used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2HookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG shm_Method;&lt;br /&gt;
  ULONG shm_MsgSize;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this data structure the &amp;lt;tt&amp;gt;shm_Method&amp;lt;/tt&amp;gt; field would indicate the task to be performed. This can be a request to copy data or to store a log message. The &amp;lt;tt&amp;gt;shm_MsgSize&amp;lt;/tt&amp;gt; field tells you how large the data structure is for future enhancements which may cause the data structure to grow.&lt;br /&gt;
&lt;br /&gt;
==== Copying operations ====&lt;br /&gt;
&lt;br /&gt;
For copying operations the message is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2CopyHookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG schm_Method;&lt;br /&gt;
  ULONG schm_MsgSize;&lt;br /&gt;
&lt;br /&gt;
  APTR  schm_To;&lt;br /&gt;
  APTR  schm_From;&lt;br /&gt;
  ULONG schm_Size;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure members are as follows:&lt;br /&gt;
&lt;br /&gt;
; schm_Method&lt;br /&gt;
: This must be one &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyToBuff16&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff16&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyToBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyToBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyFromBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyToBuff64&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;S2_DMACopyFromBuff64&amp;lt;/tt&amp;gt; to identify the function to be performed.&lt;br /&gt;
&lt;br /&gt;
; schm_MsgSize&lt;br /&gt;
: Size of this message data structure in bytes. This must be &amp;amp;gt;= 20 for this message type.&lt;br /&gt;
: The driver shall set &amp;lt;tt&amp;gt;schm_MsgSize&amp;lt;/tt&amp;gt; always correctly to be compliant. The protocol stack shall use this field to validate the message and to reject/ignore bad messages via a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; hook function return value. For DMA related hooks, a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; return value is equivalent to a &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; pointer.&lt;br /&gt;
&lt;br /&gt;
; schm_To&lt;br /&gt;
: Equivalent to the &amp;lt;tt&amp;gt;to&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DMACopyToBuff&amp;lt;/tt&amp;gt; functions.&lt;br /&gt;
&lt;br /&gt;
; schm_From&lt;br /&gt;
: Equivalent to the &amp;lt;tt&amp;gt;from&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; functions.&lt;br /&gt;
&lt;br /&gt;
; schm_Size&lt;br /&gt;
: Equivalent to the &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt;, functions.&lt;br /&gt;
&lt;br /&gt;
==== Logging operations ====&lt;br /&gt;
&lt;br /&gt;
For logging operations the message is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2LogHookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG  slhm_Method;&lt;br /&gt;
  ULONG  slhm_MsgSize;&lt;br /&gt;
&lt;br /&gt;
  ULONG  slhm_Priority;&lt;br /&gt;
  STRPTR slhm_Name;&lt;br /&gt;
  STRPTR slhm_Message;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure members would be used as follows:&lt;br /&gt;
&lt;br /&gt;
; slhm_Method&lt;br /&gt;
: This must be &amp;lt;tt&amp;gt;S2_Log&amp;lt;/tt&amp;gt;, as defined in the SANA-IIR4 specification.&lt;br /&gt;
&lt;br /&gt;
; slhm_MsgSize&lt;br /&gt;
: Size of this message data structure in bytes. This must be &amp;amp;gt;= 20 for this message type.&lt;br /&gt;
: The driver shall set &amp;lt;tt&amp;gt;slhm_MsgSize&amp;lt;/tt&amp;gt; always correctly to be compliant. The protocol stack shall use this field to validate the message and to reject/ignore bad messages via a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; hook function return value. For DMA related hooks, a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; return value is equivalent to a &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; pointer.&lt;br /&gt;
&lt;br /&gt;
; slhm_Priority&lt;br /&gt;
: The smaller this value, the more important the message to be logged or displayed. The following priority levels are defined (similar to the Unix syslog() mechanism):&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| S2LOG_Emergency || A panic condition.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Alert || A condition that should be corrected immediately.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Critical || Critical conditions.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Error || A plain error.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Warning || A warning message.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Notice || Conditions that are not error conditions, but should possibly be handled specially.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Information || An informational message.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Debug || Messages that contain information normally of use only when debugging.&lt;br /&gt;
|}&lt;br /&gt;
: Only these priority values may be used by a driver. It is suggested that a driver is configurable to generate different types of messages or not, e.g., a driver may be configured to only emit &amp;lt;tt&amp;gt;S2LOG_Emergency&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2LOG_Debug&amp;lt;/tt&amp;gt; messages&lt;br /&gt;
&lt;br /&gt;
; slhm_Name&lt;br /&gt;
: Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which identifies the source of this message. This can be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; in which case the OS device name of the driver shall be used by the protocol stack.&lt;br /&gt;
&lt;br /&gt;
; slhm_Message&lt;br /&gt;
: Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which contains the log message. The text should not contain any formatting characters such as line feeds or carriage returns. The &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; member must never be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&lt;br /&gt;
: All message texts shall preferably be formatted in the current user&#039;s locale. If that is not possible, the english language shall be used. &amp;lt;tt&amp;gt;slhm_Name&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; shall only contain printable characters.&lt;br /&gt;
&lt;br /&gt;
=== Application and driver software use of the new functions ===&lt;br /&gt;
&lt;br /&gt;
Since plenty of software exists which uses the &#039;traditional&#039; &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list provided at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time, drivers must always examine these parameters and not expect a &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; command to be sent later.&lt;br /&gt;
&lt;br /&gt;
If the new &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;-based callback functions are used then the driver must invoke the Hooks via &amp;lt;tt&amp;gt;utility.library/CallHookPkt&amp;lt;/tt&amp;gt;. It must never invoke the hook functions through local assembly language stubs or the &amp;lt;tt&amp;gt;amiga.lib/CallHook&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;amiga.lib/CallHookA&amp;lt;/tt&amp;gt; functions.&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
&lt;br /&gt;
The functionality above suggests that one could do entirely without the &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list passed in at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time. However, at this time it is hard to tell how existing driver software will react to empty &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; lists or even a NULL pointer in the &amp;lt;tt&amp;gt;IOSana2Req-&amp;amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field. It is therefore important to always provide for a &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list which includes proper (i.e. they must point to working functions and may not be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;) function pointers for the &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt; tags. Once the device has been opened successfully, the next step is to try and install the copy &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; through the proposed &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; command. If the command fails, the application can still expect that the &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt; tags supplied at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time will work.&lt;br /&gt;
&lt;br /&gt;
== Packet filtering ==&lt;br /&gt;
&lt;br /&gt;
With the original &amp;quot;SANA-II Network Device Driver Specification&amp;quot;, a protocol stack could open a device and ask for certain packet types. It got all the packets that matched this type. As it turned out, this could be mighty inefficient if there were packets that the protocol stack did not use at all. These would go into read processing of the protocol stack and waste CPU time even though they could have been easily identified on arrival.&lt;br /&gt;
&lt;br /&gt;
== Driver event logging ==&lt;br /&gt;
&lt;br /&gt;
A driver may want to report an important event for the user to see. Adding a log message to a file or opening a window to display a message in may not be the optimum approach as the user may be unaware of the context into which the message belongs. It may be advisable for the driver to use the message reporting and logging facilities used by the client software that uses its services, such as a TCP/IP stack. The &amp;lt;tt&amp;gt;S2_Log&amp;lt;/tt&amp;gt; callback hook is intended to provide for such a link.&lt;br /&gt;
&lt;br /&gt;
If present, the driver must use this callback hook rather than whatever logging methods it implements itself. Note that unlike the other SANA-II callbacks, this is a regular hook, as to be invoked using &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CallHookPkt()&amp;lt;/tt&amp;gt;. The hook function is invoked using the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
log_hook_function(hook, reserved, message)&lt;br /&gt;
&lt;br /&gt;
void log_hook_function(struct hook * hook,APTR reserved,&lt;br /&gt;
                       struct S2LogMessage * message);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be set to &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;S2LogMessage&amp;lt;/tt&amp;gt; structure passed as the third parameter looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct S2LogMessage&lt;br /&gt;
{&lt;br /&gt;
   LONG   s2lm_Size;&lt;br /&gt;
   ULONG  s2lm_Priority;&lt;br /&gt;
   STRPTR s2lm_Name;&lt;br /&gt;
   STRPTR s2lm_Message;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The individual structure members serve the following functions:&lt;br /&gt;
&lt;br /&gt;
; s2lm_Size&lt;br /&gt;
: Size of the &amp;lt;tt&amp;gt;S2LogMessage&amp;lt;/tt&amp;gt; structure, in bytes. The idea is to extend this data structure in the future, and the size stored in here tells you how long the structure is. The size &#039;&#039;&#039;must&#039;&#039;&#039; always be &amp;amp;gt;= 16.&lt;br /&gt;
&lt;br /&gt;
;s2lm_Priority&lt;br /&gt;
: The smaller this value, the more important the message to be logged or displayed. The following priority levels are defined (similar to the Unix &amp;lt;tt&amp;gt;syslog()&amp;lt;/tt&amp;gt; mechanism):&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| S2LOG_Emergency || A panic condition.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Alert || A condition that should be corrected immediately.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Critical || Critical conditions.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Error || A plain error.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Warning || A warning message.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Notice || Conditions that are not error conditions, but should possibly be handled specially.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Information || An informational message.&lt;br /&gt;
|-&lt;br /&gt;
| S2LOG_Debug || Messages that contain information normally of use only when debugging.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; s2lm_Name&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which identifies the source of this message. This can be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; in which case the name is treated as being unknown.&lt;br /&gt;
&lt;br /&gt;
; s2lm_Message&lt;br /&gt;
: Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which contains the log message. The text should not contain any formatting characters such as line feeds or carriage returns. The &amp;lt;tt&amp;gt;s2lm_Message&amp;lt;/tt&amp;gt; member &#039;&#039;&#039;must never&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All error messages issued by the device driver should use the current system locale wherever this is possible. The purpose of an error message is, after all, to assist the user in recovering from the error. Which may be difficult if the user does not even know the language in which the message is written.&lt;br /&gt;
&lt;br /&gt;
The log message string is valid until the log hook function returns. If the driver needs to retain the message any longer, it must make a copy of it.&lt;br /&gt;
&lt;br /&gt;
Since the client software into which the log hook calls may have to allocate memory to hold and display the log message, the log hook &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code. The log hook shall not &amp;lt;tt&amp;gt;Wait()&amp;lt;/tt&amp;gt; and it shall assume only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority. &amp;lt;tt&amp;gt;dos.library&amp;lt;/tt&amp;gt; functions may not be called. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited and the callback should be designed to be fast and short.&lt;br /&gt;
&lt;br /&gt;
This hook is installed at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time, which means that the hook is used for the unit that was opened, and not just for the I/O request it was opened with. The hook must remain installed until the I/O request that installed it is eventually used to close the device. When this happens, the device should fall back to use no log hook at all. No nesting is permitted or required.&lt;br /&gt;
&lt;br /&gt;
= Packet Type =&lt;br /&gt;
&lt;br /&gt;
Network frames always have a type field associated with them. These type fields vary in length, position and meaning by frame type (frame types generally correspond one-to-one with hardware types, but see &amp;quot;Ethernet Packet Types&amp;quot; below). The meanings of the type numbers are always carefully defined and every type number is registered with some official body. Do not use a type number which is not registered for any standard hardware you use or in a manner inconsistent with that registration.&lt;br /&gt;
&lt;br /&gt;
The type field allows the SANA-II device driver to fulfill CMD_READs based on the type of packet the driver user wants. Multiple protocols can therefore run over the same wire using the same driver without stepping on each other&#039;s toes.&lt;br /&gt;
&lt;br /&gt;
Packet types are specified as a long word. Unfortunately, the type field means different things on different wires. Driver users must allow their software to be configured with a SANA-II device name, unit number and the type number(s) used by the protocol stack with each device. This way, if new hardware becomes available, a hardware manufacturer can supply a listing of type assignments to configure pre-existing software.&lt;br /&gt;
&lt;br /&gt;
== Ethernet Packet Types ==&lt;br /&gt;
&lt;br /&gt;
Ethernet has a special problem with packet types. Two types of ethernet frames can be sent over the same wire: ethernet and 802.3. These frames differ in that the Type field of an ethernet frame is the Length field of an 802.3 frame. This creates a problem in that demultiplexing incoming packets can be cumbersome and inefficient, as well as requiring driver users to be aware of the frame type used.&lt;br /&gt;
&lt;br /&gt;
All 802.3 frames have numbers less than 1500 in the Type field. The only frames with numbers less than 1500 in the type field are 802.3 frames. SANA-II ethernet drivers abnormally return packets contained in ethernet frames when the requested Type falls within the 802.3 range-if the Type requested is within the 802.3 range, the driver returns the next packet contained within an 802.3 frame, regardless of the type specified for the packet within the 802.3 frame. This requires that there be no more than one driver user requesting 802.3 packets and that it do its own interpretation of the frames.&lt;br /&gt;
&lt;br /&gt;
== ARCNET Frames ==&lt;br /&gt;
&lt;br /&gt;
ARCNET also has a special problem with framing. ARCNET frames consist of a hardware header and a software header. The software header is in the data area of the hardware packet, and includes at least the protocol ID.&lt;br /&gt;
&lt;br /&gt;
There are two types of software header. Old-style ARCNET software headers consist entirely of a one or two byte protocol ID. New ARCNET software headers (defined in RFC 1201 and in the paper &amp;quot;ARCNET Packet Header Definition Standard&amp;quot;, Novell, Inc., 1989) include more information. They allow more efficient use of ARCNET through data link layer fragmentation and reassembly (ARCNET has a small Maximum Transmission Unit) and allow sending any size packet up to the MTU (rather than requiring that packets of size 253, 254 and 255 be padded to at least 256 bytes).&lt;br /&gt;
&lt;br /&gt;
SANA-II device drivers for ARCNET should implement the old ARCNET packet headers. Driver users which wish to interoperate with platforms using the new software headers must add the new fields to the data to be sent and must process it for incoming data. A SANA-II driver which implemented the data link layer fragmentation internally (and advertised a large MTU) could be more efficient than requiring the driver user to do it. This would make driver writing more difficult and reduce interoperability, but if there is ever a demand for that extra performance, a new hardware type may be assigned by Amiga for SANA-II ARCNET device drivers which implement the new framing.&lt;br /&gt;
&lt;br /&gt;
= Addressing =&lt;br /&gt;
&lt;br /&gt;
In the SANA-II standard, network hardware addresses are stored in an array of &#039;&#039;n&#039;&#039; bytes. No meaning is ascribed by the standard to the contents of the array.&lt;br /&gt;
&lt;br /&gt;
In case there exists a network which does not have an address field consisting of a number of bits not divisible by eight, add pad bits at the end of the bit stream. For example, if an address is ten bits long it will be stored like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
98765432 10PPPPPP&lt;br /&gt;
BYTE 0   BYTE 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where the numerals are bit numbers and &amp;quot;P&amp;quot; is a pad (ignored) bit.&lt;br /&gt;
&lt;br /&gt;
Driver users which do not implement the bit shifting necessary to use a network with such addressing (if one exists) should at least check the number of significant bits in the address field (returned from the device&#039;s S2_DEVICEQUERY function) to make sure that it is evenly divisible by eight.&lt;br /&gt;
&lt;br /&gt;
Driver users will map hardware addresses to protocol addresses in a protocol and hardware dependent manner, as described by the relevant standards (i.e., RFC 826 for TCP/IP over Ethernet, RFC 1201 or RFC 1051 for TCP/IP over ARCNET). Some protocols will always use the same mapping on all hardware, but other protocols will have particular address mapping schemes for some particular hardware and a reasonable default for other (unknown) hardware.&lt;br /&gt;
&lt;br /&gt;
Some SANA-II devices will have &amp;quot;hardware addresses&amp;quot; which aren&#039;t really hardware addresses. As an example, consider &#039;&#039;PPP&#039;&#039; (Point-to-Point Protocol). PPP is a standard for transmitting IP packets over a serial line. It uses IP addresses negotiated during the establishment of a connection. In a SANA-II driver implementation of PPP, the driver would negotiate the address at S2_CONFIGINTERFACE. Thus, the address in SrcAddr returned by the device on an S2_CONFIGINTERFACE (or in a subsequent S2_GETSTATIONADDRESS) will be a protocol address, not a true hardware address.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Some hardware always uses a ROM hardware address. Other hardware which has a ROM address or is configurable with DIP switches may be overridden by software. Some hardware always dynamically allocates a new hardware address at initialization. See &amp;quot;Configuration&amp;quot; for details on how this is handled by driver writers and by driver users.&lt;br /&gt;
&lt;br /&gt;
= Hardware Type =&lt;br /&gt;
&lt;br /&gt;
The HardwareType returned by the device&#039;s S2_DEVICEQUERY function is necessary for those protocols whose standards require different behavior on different hardware. It is also useful for determining appropriate packet type numbers to use with the device. The HardwareType values already issued for standard network hardware are the same as those in RFC 1060 (assigned numbers). Hardware developers implementing networks without a SANA-II hardware number must contact the AmigaOS development team to have a new hardware type number assigned. Driver users should all have reasonable defaults which can be used for hardware with which they are not familiar.&lt;br /&gt;
&lt;br /&gt;
= Errors =&lt;br /&gt;
&lt;br /&gt;
The SANA-II extended IORequest structure (struct IOSana2Req) includes both the ios2_Error and ios2_WireError fields. Driver users must always check IOSana2Reqs on return for an error in ios2_Error. ios2_Error will be zero if no error occurred, otherwise it will contain a value from &amp;amp;lt;exec/errors.h&amp;amp;gt; or &amp;amp;lt;devices/sana2.h&amp;amp;gt;. If there was an error, there may be more specific information in ios2_WireError. Drivers are required to fill in the WireError if there is an applicable error code.&lt;br /&gt;
&lt;br /&gt;
Error codes are #define&#039;d in the &amp;quot;defined errors&amp;quot; sections of the file &amp;amp;lt;devices/sana2.h&amp;amp;gt;:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IOSana2Req S2io_Error field&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_NO_RESOURCES&lt;br /&gt;
| Insufficient resources available.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_BAD_ARGUMENT&lt;br /&gt;
| Noticeably bad argument.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_BAD_STATE&lt;br /&gt;
| Command inappropriate for current state.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_BAD_ADDRESS&lt;br /&gt;
| Noticeably bad address.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_MTU_EXCEEDED&lt;br /&gt;
| Write data too large.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_NOT_SUPPORTED&lt;br /&gt;
| Command is not supported by this driver. This is similar to IOERR_NOCMD as defined in &amp;amp;lt;exec/errors.h&amp;amp;gt; but S2ERR_NOT_SUPPORTED indicates that the requested command is a valid SANA-II command and that the driver does not support it because the hardware is incapable of supporting it (e.g., S2_MULTICAST). Note that IOERR_NOCMD is still valid for reasons other than a lack of hardware support (i.e., commands which are no-ops in a SANA-II driver).&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_SOFTWARE&lt;br /&gt;
| Software error of some kind.&lt;br /&gt;
|-&lt;br /&gt;
| S2ERR_OUTOFSERVICE&lt;br /&gt;
| When a hardware device is taken off-line, any pending requests are returned with this error.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
See also the standard errors in &amp;amp;lt;exec/errors.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IOSana2Req S2io_WireError field&lt;br /&gt;
! Description&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_NOT_CONFIGURED&lt;br /&gt;
| Command requires unit to be configured.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_UNIT_ONLINE&lt;br /&gt;
| Command requires that the unit be off-line.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_UNIT_OFFLINE&lt;br /&gt;
| Command requires that the unit be on-line.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_ALREADY_TRACKED&lt;br /&gt;
| Protocol is already being tracked.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_NOT_TRACKED&lt;br /&gt;
| Protocol is not being tracked.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_BUFF_ERROR&lt;br /&gt;
| Buffer management function returned an error.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_SRC_ADDRESS&lt;br /&gt;
| Problem with the source address field.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_DST_ADDRESS&lt;br /&gt;
| Problem with destination address field.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_BAD_BROADCAST&lt;br /&gt;
| Problem with an attempt to broadcast.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_BAD_MULTICAST&lt;br /&gt;
| Problem with an attempt to multicast.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_MULTICAST_FULL&lt;br /&gt;
| Multicast address list full.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_BAD_EVENT&lt;br /&gt;
| Event specified is unknown.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_BAD_STATDATA&lt;br /&gt;
| The ios2_StatData pointer or the data it points to failed a sanity check.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_IS_CONFIGURED&lt;br /&gt;
| Attempt to reconfigure the unit.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_NULL_POINTER&lt;br /&gt;
| A NULL pointer was detected in one of the arguments. S2ERR_BAD_ARGUMENT should always be the S2ERR.&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_UNIT_DISCONNECTED&lt;br /&gt;
| This error code is a counterpart to &amp;lt;tt&amp;gt;S2WERR_UNIT_OFFLINE&amp;lt;/tt&amp;gt;. It indicates that the associated command could not be executed because the link layer is not connected.&lt;br /&gt;
| 19&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_UNIT_CONNECTED&lt;br /&gt;
| This error code is a counterpart to &amp;lt;tt&amp;gt;S2WERR_UNIT_ONLINE&amp;lt;/tt&amp;gt;. It indicates that the associated command could not be executed because the link layer is already connected.&lt;br /&gt;
| 20&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_INVALID_OPTION&lt;br /&gt;
| This error code indicates that an option, such as passed by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command, is not acceptable. The option&#039;s value may be out of range or may not match the syntax specifications. To indicate which option that may be, a different mechanism &#039;&#039;&#039;must&#039;&#039;&#039; be used; a simple indication that something was wrong is &#039;&#039;&#039;not sufficient&#039;&#039;&#039;.&lt;br /&gt;
| 21&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_MISSING_OPTION&lt;br /&gt;
| This error code indicates that a mandatory option, such as passed by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command, is not present. To indicate which option that may be, a different mechanism &#039;&#039;&#039;must&#039;&#039;&#039; be used; a simple indication that something was wrong is &#039;&#039;&#039;not sufficient&#039;&#039;&#039;.&lt;br /&gt;
| 22&lt;br /&gt;
|-&lt;br /&gt;
| S2WERR_AUTHENTICATION_FAILED&lt;br /&gt;
| Some drivers run protocols that require them to authenticate to a server. That process may fail. This wire error code is to indicate this fact.&lt;br /&gt;
| 23&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Standard Commands =&lt;br /&gt;
&lt;br /&gt;
See the &amp;quot;SANA-II network device driver Autodocs&amp;quot; for full details on each of the SANA-II device commands. Extended commands are explained in the sections below.&lt;br /&gt;
&lt;br /&gt;
Many of the Exec device standard commands are no-ops in SANA-II devices, but this may not always be the case. For example, CMD_RESET might someday be used for dynamically reconfiguring hardware. This should present no compatibility problems for properly written drivers.&lt;br /&gt;
&lt;br /&gt;
== Broadcast and Multicast ==&lt;br /&gt;
&lt;br /&gt;
Some hardware supports broadcast and/or multicast. A broadcast is a packet sent to all other machines. A multicast is a packet sent to a set of machines. Drivers for hardware which does not allow broadcast or multicast will return ios2_Error S2ERR_NOT_SUPPORTED as appropriate.&lt;br /&gt;
&lt;br /&gt;
To send a broadcast, use S2_BROADCAST instead of CMD_WRITE. Broadcasts are received just like any other packets (using a CMD_READ for the appropriate packet type).&lt;br /&gt;
&lt;br /&gt;
To send a multicast, use S2_MULTICAST instead of CMD_WRITE. The device keeps a list of addresses that want to receive multicasts. You add a receiver&#039;s address to this list by using S2_ADDMULTICASTADDRESS. The receiver then posts a CMD_READ for the type of packet to be received. Some SANA-II devices which support multicast may have a limit on the number of addresses that can simultaneously wait for packets. Always check for an S2WERR_MULTICAST_FULL error return when adding a multicast address.&lt;br /&gt;
&lt;br /&gt;
Note that when the device adds a multicast address, it is usually added for all users of the device, not just the driver user which called S2_ADDMULTICASTADDRESS. In other words, received multicast packets will fill a read request of the appropriate type regardless of whether the requesting driver user is the same one which added the multicast address.&lt;br /&gt;
&lt;br /&gt;
In general, driver users should not care how received packets were sent (normally or broadcast/multicast), only that it was received. If a driver user really must know, however, it can check for SANA2IOB_BCAST and/or SANA2IOB_MCAST in the ios2_Flags field.&lt;br /&gt;
&lt;br /&gt;
Drivers should keep a count for the number of opens on a multicast address so that they don&#039;t actually remove it until it has been S2_DELMULTICASTADDRESS&#039;d as many times as it has been S2_ADDMULTICASTADDRESS&#039;d.&lt;br /&gt;
&lt;br /&gt;
== Stats ==&lt;br /&gt;
&lt;br /&gt;
There are many statistics which may be very important to someone trying to debug, tune or optimize a protocol stack, as well as to the end user who may need to tune parameters or investigate a problem. Some of these statistics can only be kept by the SANA-II driver, thus there are several required and optional statistics and commands for this purpose.&lt;br /&gt;
&lt;br /&gt;
S2_TRACKTYPE tells the device driver to gather statistics for a particular packet type. S2_UNTRACKTYPE tells it to stop (keeping statistics by type causes the driver to use additional resources). S2_GETTYPESTATS returns any statistics accumulated by the driver for a type being tracked (stats are lost when a type is S2_UNTRACKTYPE&#039;d). Drivers are required to implement the functionality of type tracking. The stats are returned in a struct Sana2PacketTypeStats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2PacketTypeStats&lt;br /&gt;
{&lt;br /&gt;
  ULONG PacketsSent;&lt;br /&gt;
  ULONG PacketsReceived;&lt;br /&gt;
  ULONG BytesSent;&lt;br /&gt;
  ULONG BytesReceived;&lt;br /&gt;
  ULONG PacketsDropped;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; PacketsSent&lt;br /&gt;
: Number of packets of a particular type sent.&lt;br /&gt;
; PacketsReceived&lt;br /&gt;
: Number of packets of a particular type that satisfied a read command.&lt;br /&gt;
; BytesSent&lt;br /&gt;
: Number of bytes of data sent in packets of a particular type.&lt;br /&gt;
; BytesReceived&lt;br /&gt;
: Number of bytes of data of a particular packet type that satisfied a read command.&lt;br /&gt;
; PacketsDropped&lt;br /&gt;
: Number of packets of a particular type that were received while there were no pending reads of that packet type.&lt;br /&gt;
&lt;br /&gt;
returns global statistics kept by the driver. Drivers are required to keep all applicable statistics. Since all are applicable to most hardware, most drivers will maintain all statistics. The stats are returned in a struct Sana2DeviceStats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2DeviceStats&lt;br /&gt;
{&lt;br /&gt;
  ULONG PacketsReceived;&lt;br /&gt;
  ULONG PacketsSent;&lt;br /&gt;
  ULONG BadData;&lt;br /&gt;
  ULONG Overruns;&lt;br /&gt;
  ULONG UnknownTypesReceived;&lt;br /&gt;
  ULONG Reconfigurations;&lt;br /&gt;
  struct timeval LastStart;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; PacketsReceived&lt;br /&gt;
: Number of packets that this unit has received.&lt;br /&gt;
; PacketsSent&lt;br /&gt;
: Number of packets that this unit has sent.&lt;br /&gt;
; BadData&lt;br /&gt;
: Number of bad packets received (i.e., hardware CRC failed).&lt;br /&gt;
; Overruns&lt;br /&gt;
: Number of packets dropped due to insufficient resources available in the network interface.&lt;br /&gt;
; UnknownTypeReceived&lt;br /&gt;
: Number of packets received that had no pending read command with the appropriate packet type.&lt;br /&gt;
; Reconfigurations&lt;br /&gt;
: Number of network reconfigurations since this unit was last configured.&lt;br /&gt;
; LastStart&lt;br /&gt;
: The time when this unit last went on-line.&lt;br /&gt;
&lt;br /&gt;
returns any special statistics kept by a particular driver. Each new wire type will have a set of documented, required statistics for that wire type and a standard set of optional statistics for that wire type (optional because they might not be available from all hardware). The data returned by S2_GETSPECIALSTATS will require wire-specific interpretation. See &amp;amp;lt;devices/sana2specialstats.h&amp;amp;gt; on page devices&amp;lt;sub&amp;gt;s&amp;lt;/sub&amp;gt;ana2specialstats&amp;lt;sub&amp;gt;p&amp;lt;/sub&amp;gt;age for currently defined special statistics. The statistics are returned in the following structures:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2SpecialStatRecord&lt;br /&gt;
{&lt;br /&gt;
  ULONG Type;&lt;br /&gt;
  ULONG Count;&lt;br /&gt;
  char * String;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Type&lt;br /&gt;
: Statistic identifier.&lt;br /&gt;
; Count&lt;br /&gt;
: Statistic itself.&lt;br /&gt;
; String&lt;br /&gt;
: An identifying, null-terminated string for the statistic. Should be plain ASCII with no formatting characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2SpecialStatHeader&lt;br /&gt;
{&lt;br /&gt;
  ULONG RecordCountMax;&lt;br /&gt;
  ULONG RecordCountSupplied;&lt;br /&gt;
  struct Sana2SpecialStatRecord[RecordCountMax];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; RecordCountMax&lt;br /&gt;
: There is space for this many records into which statistics may be placed.&lt;br /&gt;
; RecordCountSupplied&lt;br /&gt;
: Number of statistic records supplied.&lt;br /&gt;
&lt;br /&gt;
is not, strictly speaking, a statistical function. It is a request to read any packet of a type for which there is no outstanding CMD_READ. S2_READORPHAN might be used in the same manner as many statistics, though, such as to determine what packet types are causing overruns, etc.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
The device driver needs to configure the hardware before using it. The driver user must know some network hardware parameters (hardware address and MTU, for example) when using it. These commands address those needs.&lt;br /&gt;
&lt;br /&gt;
When a driver user is initialized, it should try to S2_CONFIGINTERFACE even though an interface can only be configured once and someone else may have done it. Before you call S2_CONFIGINTERFACE, first call S2_GETSTATIONADDRESS to determine the factory address (if any). Also provide for user-override of the factory address (that address may be optional and the user may need to override it). When S2_CONFIGINTERFACE returns, check the ios2_SrcAddr for the actual address the hardware has been configured with. This is because some hardware (or serial line standards such as PPP) always dynamically allocates an address at initialization.&lt;br /&gt;
&lt;br /&gt;
Driver users will want to use S2_DEVICEQUERY to determine the MTU and other characteristics of the network. The structure returned from S2_DEVICEQUERY is defined as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2DeviceQuery&lt;br /&gt;
{&lt;br /&gt;
    /*&lt;br /&gt;
    ** Standard information&lt;br /&gt;
    */&lt;br /&gt;
    ULONG SizeAvailable;    /* bytes available */&lt;br /&gt;
    ULONG SizeSupplied;     /* bytes supplied */&lt;br /&gt;
    LONG  DevQueryFormat;   /* this is type 0 */&lt;br /&gt;
    LONG  DeviceLevel;      /* this document is level 0 */&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
    ** Common information&lt;br /&gt;
    */&lt;br /&gt;
    UWORD AddrFieldSize;    /* address size in bits */&lt;br /&gt;
    ULONG MTU;              /* maximum packet data size */&lt;br /&gt;
    LONG  BPS;              /* line rate (bits/sec) */&lt;br /&gt;
    LONG  HardwareType;     /* what the wire is */&lt;br /&gt;
    ULONG RawMTU;           /* maximum raw packet data size */&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
    ** Format specific information&lt;br /&gt;
    */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; SizeAvailable&lt;br /&gt;
: Size, in bytes, of the space available in which to place device information. This includes both size fields.&lt;br /&gt;
; SizeSupplied&lt;br /&gt;
: Size, in bytes, of the data supplied.&lt;br /&gt;
; DevQueryFormat&lt;br /&gt;
: The format defined here is format 0.&lt;br /&gt;
; DeviceLevel&lt;br /&gt;
: This spec defines level 0.&lt;br /&gt;
; AddrFieldSize&lt;br /&gt;
: The number of bits in an interface address.&lt;br /&gt;
; MTU&lt;br /&gt;
: Maximum Transmission Unit, the size, in bytes, of the maximum packet size, not including header and trailer information.&lt;br /&gt;
; BPS&lt;br /&gt;
: Best guess at the raw line rate for this network in bits per second.&lt;br /&gt;
&lt;br /&gt;
; HardwareType&lt;br /&gt;
: Specifies the type of network hardware the driver controls.&lt;br /&gt;
&lt;br /&gt;
; RawMTU&lt;br /&gt;
: The &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is new as of SANA-II Revision 4. Devices which do not know and support this structure member may fill in the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure only up to and including the &amp;lt;tt&amp;gt;HardwareType&amp;lt;/tt&amp;gt; member.&lt;br /&gt;
&lt;br /&gt;
: In this context &#039;raw&#039; means the number of bytes that are available for reading and writing when using the &amp;lt;tt&amp;gt;SANA2IOB_RAW&amp;lt;/tt&amp;gt; flag with a &amp;lt;tt&amp;gt;CMD_READ&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CMD_WRITE&amp;lt;/tt&amp;gt; request on a device that supports these access methods. Currently, software developers can only make assumptions on how many bytes might comprise the &#039;raw&#039; MTU by checking the &amp;lt;tt&amp;gt;Sana2DeviceQuery.HardwareType&amp;lt;/tt&amp;gt; member and hoping that the driver supports raw &amp;lt;tt&amp;gt;CMD_READ&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CMD_WRITE&amp;lt;/tt&amp;gt; access.&lt;br /&gt;
&lt;br /&gt;
: Devices which know and support the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member must fill it with a well-defined value. For Amiga Ethernet drivers, that value would be 1514, which is the standard MTU value of 1500 bytes plus the size of the Ethernet frame header, as per RFC894 (six bytes for the destination address, six bytes for the source address and two bytes for the frame type; the eight byte preamble and the terminating four byte CRC value are typically not under the control of the driver). Drivers which do not support raw read or write access must set the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member to zero.&lt;br /&gt;
&lt;br /&gt;
: If the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is not provided, all bets are off and the application software must fall back to making estimates based upon the hardware type and the raw frame types it wishes to read and write. Ultimatively, the driver itself must decide whether it can accept raw read and write commands (or has to reject them with &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt;) and whether the raw packet size is still covered by the underlying hardware MTU (or must be rejected with &amp;lt;tt&amp;gt;S2ERR_MTU_EXCEEDED&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
: A word of warning: a little testing with various Ethernet hardware drivers has revealed that the A2065 driver &amp;lt;tt&amp;gt;a2065.device&amp;lt;/tt&amp;gt; does not handle the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt; command properly if the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure provided is larger than 30 bytes. In other words, the command will fail if the proposed &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is present in the query data structure to be filled in.&lt;br /&gt;
&lt;br /&gt;
== On-line ==&lt;br /&gt;
&lt;br /&gt;
In order to run hardware tests on an otherwise live system, the S2_OFFLINE command allows the SANA-II device driver to be &amp;quot;turned off&amp;quot; until the tests are complete and an S2_ONLINE is sent to the driver. S2_ONLINE causes the interface to re-configure and re-initialize. Any packets destined for the hardware while the device is off-line will be lost. All pending and new requests to the driver shall be returned with S2ERR_OUTOFSERVICE when a device is off-line.&lt;br /&gt;
&lt;br /&gt;
All driver users must understand that any IO request may return with S2ERR_OUTOFSERVICE because the driver is off-line (any other program may call S2_OFFLINE to make it so). In such an event, the driver will usually want to wait until the unit comes back on-line (for the program which called S2_OFFLINE to call S2_ONLINE). It may do this by calling S2_ONEVENT to wait for S2EVENT_ONLINE. S2_ONEVENT allows the driver user to wait on various events.&lt;br /&gt;
&lt;br /&gt;
A driver must track events, but may not distinguish between some types of events. Drivers return S2_ONEVENT with S2ERR_NOT_SUPPORTED and S2WERR_BAD_EVENT for unsupported events. One error may cause more than one event (see below). Errors which seem to have been caused by a malformed or unusual request should not generally trigger an event.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Event type&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_ERROR&lt;br /&gt;
| Return when any error occurs.&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_TX&lt;br /&gt;
| Return on any transmit error (always an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_RX&lt;br /&gt;
| Return on any receive error (always an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_ONLINE&lt;br /&gt;
| Return when unit goes on-line or return immediately if unit is already on-line (not an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_OFFLINE&lt;br /&gt;
| Return when unit goes off-line or return immediately if unit is already off-line (not an error.)&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_BUFF&lt;br /&gt;
| Return on any buffer management function error (always an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_HARDWARE&lt;br /&gt;
| Return when any hardware error occurs (always an error, may be a S2EVENT_TX or S2EVENT_RX, too).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_SOFTWARE&lt;br /&gt;
| Return when any software error occurs (always an error, may be a S2EVENT_TX or S2EVENT_RX, too).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_CONFIGCHANGED&lt;br /&gt;
| Return when client-visible configuration information changes (not an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_CONNECT&lt;br /&gt;
| Return when the driver has successfully established a link layer connection (not an error).&lt;br /&gt;
|-&lt;br /&gt;
| S2EVENT_DISCONNECT &lt;br /&gt;
| Return when the driver has closed the link layer connection previously established by the S2_CONNECT command (not an error).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_CONFIGCHANGED ===&lt;br /&gt;
&lt;br /&gt;
For drivers such as those which implement the &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt; commands it is vital that such changes can take place and be noticed by the client software. For this purpose a new event type is introduced, to be used with the SANA-II &amp;lt;tt&amp;gt;S2_ONEVENT&amp;lt;/tt&amp;gt; command, using the following definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_CONFIGCHANGED (1L&amp;amp;lt;&amp;amp;lt;8)&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This event should be triggered whenever client-visible configuration information changes, as can be queried via the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSTATIONADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt; commands. Here is a short list of what could change:&lt;br /&gt;
&lt;br /&gt;
# S2_DEVICEQUERY: &amp;lt;tt&amp;gt;AddrFieldSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;MTU&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;BPS&amp;lt;/tt&amp;gt;&lt;br /&gt;
# S2_GETSTATIONADDRESS: &amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
# S2_GETGLOBALSTATS: &amp;lt;tt&amp;gt;Reconfigurations&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;LastStart&amp;lt;/tt&amp;gt;&lt;br /&gt;
# S2_GETPEERADDRESS: &amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ios2_DstAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
# S2_GETDNSADDRESS: &amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ios2_DstAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The purpose of this event &#039;&#039;&#039;is not&#039;&#039;&#039; to post a notification whenever another byte or event counter has changed so that a monitoring program may update its display. The purpose &#039;&#039;&#039;is&#039;&#039;&#039; to convey to the client software that an important device configuration option has changed and that it is supposed to react and adapt to it. For example, a TCP/IP stack may, upon learning that a device&#039;s IP address has changed, rebuild its routing table.&lt;br /&gt;
&lt;br /&gt;
Since the &amp;lt;tt&amp;gt;S2EVENT_CONFIGCHANGED&amp;lt;/tt&amp;gt; event may arrive at any time and does not indicate what exactly has changed, application software should query the information it expects to change during its life time, and keep a copy of it around for later reference. When the &amp;lt;tt&amp;gt;S2EVENT_CONFIGCHANGED&amp;lt;/tt&amp;gt; event arrives, it can compare the contents of the copy against the current state of affairs and act according to the differences it finds.&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_CONNECT ===&lt;br /&gt;
&lt;br /&gt;
This event is a counterpart to &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt;, and is associated with the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command. It has the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_CONNECT (1L&amp;amp;lt;&amp;amp;lt;9) /* Driver has opened session */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The event is to be sent when the driver has successfully established a link layer connection.&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_DISCONNECT ===&lt;br /&gt;
&lt;br /&gt;
This event is a counterpart to &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt;, and is associated with the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command. It has the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_DISCONNECT (1L&amp;amp;lt;&amp;amp;lt;10) /* Driver has closed session */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The event is to be sent when the driver has closed the link layer connection previously established by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command.&lt;br /&gt;
&lt;br /&gt;
== S2_ONLINE and S2_OFFLINE ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; commands are somewhat related to the &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; commands. How this relation works out shall be explained below. Note that the following text assumes that both the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; command pairs are implemented.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; implies &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and, if successful, may report &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; events. If the unit is currently disconnected, but still online, only the &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; event shall be sent. Invoking the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command on a driver which is already connected must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_CONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; implies &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; and, if successful, may report &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; events. If the unit is currently connected and offline, then only the &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; event shall be sent. Invoking the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command on a driver which is already disconnected must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_DISCONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; may be used after the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; has successfully connected the unit. In this case the driver will release control over the link layer and report the &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt; event. The connection established using the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; will, however, persist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; may be used after &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; has successfully connected the unit and the &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; was used. In this case the driver will again try to obtain control over the link layer and report the &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; event if successful.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command was never successfully executed, then the commands &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_DISCONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Wireless (IEEE 802.11) ==&lt;br /&gt;
&lt;br /&gt;
Several new commands have been added to the SANA-II API to facilitate use of IEEE 802.11 wireless network devices in client and ad-hoc modes. The new commands are split into two overlapping sets. One set needs to be implemented by drivers for Soft-MAC wireless devices, while the other is implemented for Hard-MAC devices.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Soft-MAC commands&lt;br /&gt;
|-&lt;br /&gt;
| S2_SETOPTIONS&lt;br /&gt;
|-&lt;br /&gt;
| S2_SETKEY&lt;br /&gt;
|-&lt;br /&gt;
| S2_WRITEMGMT&lt;br /&gt;
|-&lt;br /&gt;
| S2_READMGMT&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Hard-MAC commands&lt;br /&gt;
|-&lt;br /&gt;
| S2_SETOPTIONS&lt;br /&gt;
|-&lt;br /&gt;
| S2_SETKEY&lt;br /&gt;
|-&lt;br /&gt;
| S2_GETNETWORKS&lt;br /&gt;
|-&lt;br /&gt;
| S2_GETNETWORKINFO&lt;br /&gt;
|-&lt;br /&gt;
| S2_GETSIGNALQUALITY&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== sana2wireless.h ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#ifndef DEVICES_SANA2WIRELESS_H&lt;br /&gt;
#define DEVICES_SANA2WIRELESS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Constants */&lt;br /&gt;
/* ========= */&lt;br /&gt;
&lt;br /&gt;
/* Tags to get and set information */&lt;br /&gt;
&lt;br /&gt;
#define S2INFO_SSID           (TAG_USER + 0)&lt;br /&gt;
#define S2INFO_BSSID          (TAG_USER + 1)&lt;br /&gt;
#define S2INFO_AuthTypes      (TAG_USER + 2)&lt;br /&gt;
#define S2INFO_AssocID        (TAG_USER + 3)&lt;br /&gt;
#define S2INFO_Encryption     (TAG_USER + 4)&lt;br /&gt;
#define S2INFO_PortType       (TAG_USER + 5)&lt;br /&gt;
#define S2INFO_BeaconInterval (TAG_USER + 6)&lt;br /&gt;
#define S2INFO_Channel        (TAG_USER + 7)&lt;br /&gt;
#define S2INFO_Signal         (TAG_USER + 8)&lt;br /&gt;
#define S2INFO_Noise          (TAG_USER + 9)&lt;br /&gt;
#define S2INFO_Capabilities   (TAG_USER + 10)&lt;br /&gt;
#define S2INFO_InfoElements   (TAG_USER + 11)&lt;br /&gt;
#define S2INFO_WPAInfo        (TAG_USER + 12)&lt;br /&gt;
#define S2INFO_Band           (TAG_USER + 13)&lt;br /&gt;
#define S2INFO_DefaultKeyNo   (TAG_USER + 14)&lt;br /&gt;
&lt;br /&gt;
/* Wireless Commands */&lt;br /&gt;
&lt;br /&gt;
#define S2_GETSIGNALQUALITY 0xc010&lt;br /&gt;
#define S2_GETNETWORKS      0xc011&lt;br /&gt;
#define S2_SETOPTIONS       0xc012&lt;br /&gt;
#define S2_SETKEY           0xc013&lt;br /&gt;
#define S2_GETNETWORKINFO   0xc014&lt;br /&gt;
#define S2_READMGMT         0xc015&lt;br /&gt;
#define S2_WRITEMGMT        0xc016&lt;br /&gt;
#define S2_GETRADIOBANDS    0xc017&lt;br /&gt;
&lt;br /&gt;
/* Encryption types */&lt;br /&gt;
&lt;br /&gt;
#define S2ENC_NONE 0&lt;br /&gt;
#define S2ENC_WEP  1&lt;br /&gt;
#define S2ENC_TKIP 2&lt;br /&gt;
#define S2ENC_CCMP 3&lt;br /&gt;
&lt;br /&gt;
/* Radio modes */&lt;br /&gt;
&lt;br /&gt;
#define S2BAND_A 0&lt;br /&gt;
#define S2BAND_B 1&lt;br /&gt;
#define S2BAND_G 2&lt;br /&gt;
#define S2BAND_N 3&lt;br /&gt;
&lt;br /&gt;
/* Network topologies */&lt;br /&gt;
&lt;br /&gt;
#define S2PORT_MANAGED 7&lt;br /&gt;
#define S2PORT_ADHOC   8&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Structures */&lt;br /&gt;
/* ========== */&lt;br /&gt;
&lt;br /&gt;
/* Structure for returning signal quality */&lt;br /&gt;
&lt;br /&gt;
struct Sana2SignalQuality&lt;br /&gt;
{&lt;br /&gt;
   LONG SignalLevel;   /* signal level in dBm */&lt;br /&gt;
   LONG NoiseLevel;   /* noise level in dBm */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Driver requirements =&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to clarify parts of the specification and to lay down a few rules that every SANA-II driver must follow.&lt;br /&gt;
&lt;br /&gt;
* A driver that does not use a broadcast medium, such as Ethernet, must not implement the &amp;lt;tt&amp;gt;S2_BROADCAST&amp;lt;/tt&amp;gt; command. Likewise, if no multicast mechanism is supported, the &amp;lt;tt&amp;gt;S2_MULTICAST&amp;lt;/tt&amp;gt; must not be implemented either.&lt;br /&gt;
&lt;br /&gt;
* SANA-II standard commands which the driver does not implement must be rejected with the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; error code. Commands that are implemented, but which cannot perform the requested services, must be rejected with the &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; error code. The difference between the two cases is in when the decision is made whether a command can be handled or not. Which commands should return &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; is decided upon at the time the driver is designed and implemented. At this stage the implementer knows for sure which capabilities the driver will have and which it will not have. Commands which the driver will never be able to execute will be made to return the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; error code. If the decision whether a command can be executed is made only at run time, by evaluating the conditions under which a command can be executed, then the error code &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; should be returned in case of failure.&lt;br /&gt;
&lt;br /&gt;
* It must be possible to open the driver with an ordinary &amp;lt;tt&amp;gt;struct IOStdReq&amp;lt;/tt&amp;gt;. This is necessary for the NewStyleDevices query command to work. In the command dispatcher, the driver must verify that all SANA-II commands are invoked with a proper size &amp;lt;tt&amp;gt;struct IOSana2Req&amp;lt;/tt&amp;gt; I/O request. If the I/O request is shorter (as can be verified by looking at the embedded Message&#039;s &amp;lt;tt&amp;gt;mn_Length&amp;lt;/tt&amp;gt; member), the command must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* A driver must implement the NewStyleDevices &amp;lt;tt&amp;gt;NSCMD_DEVICEQUERY&amp;lt;/tt&amp;gt; command, in conformance with the NSD specification 1.6 or newer. The motivation for this is to have a mechanism available for probing the capabilities of the driver, and the supported command set can provide for vital clues. In this context, the absence of the &amp;lt;tt&amp;gt;S2_BROADCAST&amp;lt;/tt&amp;gt; command would suggest that the driver cannot send or receive broadcast messages.&lt;br /&gt;
&lt;br /&gt;
* A driver that does not allow its station address to be set with the &amp;lt;tt&amp;gt;S2_CONFIGINTERFACE&amp;lt;/tt&amp;gt; command may silently ignore the command (returning it without setting an error condition) and even pretend that it can be configured more than once.&lt;br /&gt;
&lt;br /&gt;
* In response to the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt; commands a driver may return information that is not entirely correct if it has to go online before it can provide for the correct data. For example, the maximum transmission unit for PPP is a number in the range of [1..1500] which is negotiated during the protocol configuration process. It is unlikely that numbers greater than 1500 will be used, yet it is still not impossible. Since the actual number will be known only after the driver has configured the protocol, the MTU value returned before the session was opened can differ from the MTU value valid after it has been opened. A driver should therefore attempt to return &#039;safe&#039; defaults in place of information that is unavailable at the time it is queried. The &#039;safe&#039; values shall be set up to allow the driver to work even if the protocol stack is not aware of later changes to those values. Beware of zero-length buffer sizes or time intervals that may cause client software to perform zero-length memory allocations or divisions by zero.&lt;br /&gt;
&lt;br /&gt;
* SANA-II assigns packet type numbers according to the underlying transport media. For example, Ethernet uses packet type 2048 for IP frames. No such packet type definition exists for PPP yet, which is why this standard proposes to assign packet type 31 for IP packets transmitted via PPP. To simplify client software configuration, drivers may treat packet type 2048 as equivalent to the packet number associated with IP frames. This association is permitted only if it does not introduce ambiguity. For example, this association would not be permitted if the driver would receive and transmit IP packets in two different frame types or if the driver already associates packet type 2048 with non-IP packets.&lt;br /&gt;
&lt;br /&gt;
= Driver Installation =&lt;br /&gt;
&lt;br /&gt;
The standard system location for SANA-II network device driver is in a directory called &amp;amp;quot;Networks&amp;amp;quot; which exists in the &amp;amp;quot;DEVS:&amp;amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
DEVS:Networks/eth3com.device&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the official location for the drivers. It may be necessary for your install program/script to create this directory if it doesn&#039;t exist in a user&#039;s system.&lt;br /&gt;
&lt;br /&gt;
= Unresolved Issues =&lt;br /&gt;
&lt;br /&gt;
* Unfortunately, it isn&#039;t possible to completely isolate network protocols from the hardware they run on. Hardware types and addressing both remain somewhat hardware-dependent in spite of our efforts. See the &amp;quot;Packet Type&amp;quot; section for an explanation of how packet types are handled and why protocols cannot be isolated from them. See the &amp;quot;Addressing&amp;quot; section for an explanation of how addressing is handled any why protocols cannot be isolated from it.&lt;br /&gt;
&lt;br /&gt;
* Additionally, there are at least two cases where a hardware type has multiple framing methods in use (ethernet/802.3 and arcnet/(Novell) &amp;quot;ARCNET Packet Header Definition Standard&amp;quot;). In both cases, software which must interoperate with other platforms on this hardware may need to be aware of the distinctions and may have to do extra processing in order to use the appropriate frame type. See the sections on &amp;quot;Ethernet Packet Types&amp;quot; and on &amp;quot;ARCNET frames&amp;quot; for more details.&lt;br /&gt;
&lt;br /&gt;
* Protocols like PPP can provide both for IPv4 and IPv6 addresses that should be used by the local client, the peer or any of the domain name servers. While the two addressing families are interoperable, there is a problem in how the driver should report them. Can you assume 128 bit addresses and encapsulate 32 bit addresses in them? If so, how do you make sure that the address format is unambiguous?&lt;br /&gt;
&lt;br /&gt;
* Currently, only the device&#039;s hardware type provides a clue as to what packet type responds to which protocol transported via the link. For Ethernet, IP packets are encapsulated in type 2048 frames, PPP encapsulates IP packets in type 31 frames, Arcnet can use type 240 or 212. Matching a protocol with a frame type is not an easy process which could be handled more elegantly.&lt;br /&gt;
&lt;br /&gt;
* How to extend the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure in the future? The current layout separates standard, common and format specific information, but there is no hint as to where which each section starts and where the next begins. Now that there is a proposal to add a new field to the common section, how would you add fields to the format specific section?&lt;br /&gt;
&lt;br /&gt;
= SANA-II network device driver Autodocs =&lt;br /&gt;
&lt;br /&gt;
== AbortIO ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        AbortIO -- Remove an existing device request.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        error = AbortIO(Sana2Req)&lt;br /&gt;
        D0              A1&lt;br /&gt;
&lt;br /&gt;
        LONG AbortIO(struct IOSana2Req *);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This is an exec.library call.&lt;br /&gt;
&lt;br /&gt;
        This function aborts an ioRequest. If the request is active, it may or&lt;br /&gt;
        may not be aborted. If the request is queued it is removed. The&lt;br /&gt;
        request will be returned in the same way as if it had normally&lt;br /&gt;
        completed.  You must WaitIO() after AbortIO() for the request to&lt;br /&gt;
        return.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        Sana2Req        - Sana2Req to be aborted.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        error           - Zero if the request was aborted, non-zero otherwise.&lt;br /&gt;
                          io_Error in Sana2Req will be set to IOERR_ABORTED&lt;br /&gt;
                          if it was aborted.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        exec.library/AbortIO(), exec.library/WaitIO()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CloseDevice ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CloseDevice -- Close the device.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        CloseDevice(Sana2Req)&lt;br /&gt;
                    A1&lt;br /&gt;
&lt;br /&gt;
        void CloseDevice(struct IOSana2Req *);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function is called by exec.library CloseDevice().&lt;br /&gt;
&lt;br /&gt;
        This function performs whatever cleanup is required at device closes.&lt;br /&gt;
&lt;br /&gt;
        Note that all IORequests MUST be complete before closing. If any are&lt;br /&gt;
        pending, your program must AbortIO() then WaitIO() each outstanding&lt;br /&gt;
        IORequest to complete them.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        Sana2Req        - Pointer to IOSana2Req initialized by OpenDevice().&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        exec.library/CloseDevice(), exec.library/OpenDevice()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_CLEAR ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_CLEAR -- Clear internal network interface read buffers.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        There are no device internal buffers, so CMD_CLEAR does not apply to&lt;br /&gt;
        this class of device.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_CLEAR.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_FLUSH ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_FLUSH -- Clear all queued I/O requests for the SANA-II device.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command aborts all I/O requests in both the read and write&lt;br /&gt;
        request queues of the device.  All pending I/O requests are&lt;br /&gt;
        returned with an error message (IOERR_ABORTED).  CMD_FLUSH does not&lt;br /&gt;
        affect active requests.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_FLUSH.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_INVALID ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_INVALID -- Return with error IOERR_NOCMD.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes device driver to reply with an error IOERR_NOCMD&lt;br /&gt;
        as defined in &amp;amp;lt;exec/errors.h&amp;amp;gt; indicating the command is not supported.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_INVALID.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&lt;br /&gt;
   BUGS&lt;br /&gt;
        Not known to be useful.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_READ ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_READ -- Get a packet from the network.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Get the next packet available of the requested packet type. The data&lt;br /&gt;
        copied (via a call to the requestor-provided CopyToBuffer function)&lt;br /&gt;
        into ios2_Data is normally the Data Link Layer packet data only. If&lt;br /&gt;
        bit SANA2IOB_RAW is set in ios2_Flags, then the entire physical frame&lt;br /&gt;
        will be returned.&lt;br /&gt;
&lt;br /&gt;
        Unlike most Exec devices, SANA-II device drivers do not have internal&lt;br /&gt;
        buffers.  If you wish to read data from a SANA-II device you should&lt;br /&gt;
        have multiple CMD_READ requests pending at any given time.  The&lt;br /&gt;
        functions provided by you the requestor will be used for any incoming&lt;br /&gt;
        packets of the type you&#039;ve requested.  If no read requests are&lt;br /&gt;
        outstanding for a type which comes in and no read_orphan requests are&lt;br /&gt;
        outstanding, the packet will be lost.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_READ&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                            SANA2IOB_RAW&lt;br /&gt;
                            SANA2IOB_QUICK&lt;br /&gt;
        ios2_PacketType - Packet type desired.&lt;br /&gt;
        ios2_Data       - Abstract data structure to hold packet data.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
        ios2_Flags      - The following flags may be returned:&lt;br /&gt;
                            SANA2IOB_RAW&lt;br /&gt;
                            SANA2IOB_BCAST&lt;br /&gt;
                            SANA2IOB_MCAST&lt;br /&gt;
        ios2_SrcAddr    - Source interface address of packet.&lt;br /&gt;
        ios2_DstAddr    - Destination interface address of packet.&lt;br /&gt;
        ios2_DataLength - Length of packet data.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The driver may not directly examine or modify anything pointed to by&lt;br /&gt;
        ios2_Data.  It *must* use the requester-provided functions to access&lt;br /&gt;
        this data.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_READORPHAN, CMD_WRITE, any_protocol/CopyToBuffer&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_RESET ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_RESET -- Reset the network interface to initialized state.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Currently, SANA-II devices can only be configured once (with&lt;br /&gt;
        CMD_CONFIGINTERFACE) and cannot be re-configured, hence,&lt;br /&gt;
        CMD_RESET does not apply to this class of device.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_RESET.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_START ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_START -- Restart device operation.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        There is no way for the driver to keep queuing requests without&lt;br /&gt;
        servicing them, so CMD_STOP does not apply to this class of device.&lt;br /&gt;
        S2_OFFLINE and S2_ONLINE do perform a similar function to CMD_STOP&lt;br /&gt;
        and CMD_START&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_START.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_ONLINE, S2_OFFLINE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_STOP ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_STOP -- Pause device operation.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        There is no way for the driver to keep queuing requests without&lt;br /&gt;
        servicing them, so CMD_STOP does not apply to this class of device.&lt;br /&gt;
        S2_OFFLINE and S2_ONLINE do perform a similar function to CMD_STOP&lt;br /&gt;
        and CMD_START&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_STOP.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_ONLINE, S2_OFFLINE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_UPDATE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_UPDATE -- Force packets out to device.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Since there are no device internal buffers, CMD_UPDATE does not&lt;br /&gt;
        apply to this class of device.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_UPDATE.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CMD_WRITE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CMD_WRITE -- Send packet to the network.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the packet to be sent to the specified network&lt;br /&gt;
        interface. Normally, appropriate packet header and trailer information&lt;br /&gt;
        will be added to the packet data when it is sent.  If bit SANA2IOB_RAW&lt;br /&gt;
        is set in io_Flags, then the ios2_Data is assumed to contain an entire&lt;br /&gt;
        physical frame and will be sent (copied to the wire via&lt;br /&gt;
        CopyFromBuffer() unmodified.&lt;br /&gt;
&lt;br /&gt;
        Note that the device should not check to see if the destination&lt;br /&gt;
        address is on the local hardware.  Network protocols should realize&lt;br /&gt;
        that the packet has a local destination long before it gets to a&lt;br /&gt;
        SANA-II driver.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_WRITE.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                            SANA2IOB_RAW&lt;br /&gt;
                            SANA2IOB_QUICK&lt;br /&gt;
        ios2_PacketType - Packet type to send.&lt;br /&gt;
        ios2_DstAddr    - Destination interface address for this packet.&lt;br /&gt;
        ios2_DataLength - Length of the Data to be sent.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The driver may not directly examine or modify anything pointed to by&lt;br /&gt;
        ios2_Data.  It *must* use the requester-provided functions to access&lt;br /&gt;
        this data.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        CMD_READ, S2_BROADCAST, S2_MULTICAST, any_protocol/CopyFromBuffer&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== OpenDevice ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        Open -- Request an opening of the network device.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        error = OpenDevice(unit, IOSana2Req, flags)&lt;br /&gt;
        D0                 D0    A1          D1&lt;br /&gt;
&lt;br /&gt;
        BYTE OpenDevice(ULONG, struct IOSana2Req *, ULONG);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function is called by exec.library OpenDevice().&lt;br /&gt;
&lt;br /&gt;
        This function performs whatever initialization is required per&lt;br /&gt;
        device open and initializes the Sana2Req for use by the&lt;br /&gt;
        device.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        unit            - Device unit to open.&lt;br /&gt;
        Sana2Req        - Pointer to IOSana2Req structure to be initialized by&lt;br /&gt;
                          the sana2.device.&lt;br /&gt;
        flags           - Supported flags are:&lt;br /&gt;
                                SANA2OPB_MINE&lt;br /&gt;
                                SANA2OPB_PROM&lt;br /&gt;
        ios2_BufferManagement   - A pointer to a tag list containing&lt;br /&gt;
                                  pointers to buffer management functions.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        error           - same as io_Error&lt;br /&gt;
        io_Error        - Zero if successful; non-zero otherwise.&lt;br /&gt;
        io_Device       - A pointer to whatever device will handle the calls&lt;br /&gt;
                          for this unit.  This pointer may be different&lt;br /&gt;
                          depending on what unit is requested.&lt;br /&gt;
        ios2_BufferManagement   - A pointer to device internal information&lt;br /&gt;
                                  used to call buffer management functions.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        A SANA-II device must reject all open requests with a request&lt;br /&gt;
        structure that is too short, e.g. an IOStdReq. A simple check of the&lt;br /&gt;
        mn_Length field in the Message part of the request is needed to make&lt;br /&gt;
        sure that a device does not dereference invalid data due to a wrong&lt;br /&gt;
        device configuration.&lt;br /&gt;
&lt;br /&gt;
        A SANA-II device may open if no buffer management tags are provided to&lt;br /&gt;
        make the configuration process and obtaining statistics easier. Buffer&lt;br /&gt;
        management tags with a NULL value must be treated as not specified.&lt;br /&gt;
        The device shall fail requests gracefully depending on the missing&lt;br /&gt;
        tags in this case. Any malfunction is not acceptable.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        exec.library/OpenDevice(), exec.library/CloseDevice()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_ADDMULTICASTADDRESS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_ADDMULTICASTADDRESS -- Enable an interface multicast address.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to enable multicast packet&lt;br /&gt;
        reception for the requested address.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_ADDMULTICASTADDRESS.&lt;br /&gt;
        ios2_SrcAddr    - Multicast address to enable.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Multicast addresses are added globally -- anyone using the device&lt;br /&gt;
        may receive packets as a result of any multicast address which has&lt;br /&gt;
        been added for the device.&lt;br /&gt;
&lt;br /&gt;
        Since multicast addresses are not &amp;amp;quot;bound&amp;amp;quot; to a particular packet type,&lt;br /&gt;
        each enabled multicast address has an &amp;amp;quot;enabled&amp;amp;quot; count associated with&lt;br /&gt;
        it so that if two protocols add the same multicast address and later&lt;br /&gt;
        one removes it, it is still enabled until the second removes it.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_MULTICAST, S2_DELMULTICASTADDRESS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_BROADCAST ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_BROADCAST -- Broadcast a packet on network.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command works the same as CMD_WRITE except that it also performs&lt;br /&gt;
        whatever special processing of the packet is required to do a&lt;br /&gt;
        broadcast send. The actual broadcast mechanism is neccessarily&lt;br /&gt;
        network/interface/device specific.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_BROADCAST.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                                SANA2IOB_RAW&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_PacketType - Packet type to send.&lt;br /&gt;
        ios2_DataLength - Length of the Data to be sent.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_DstAddr    - The contents of this field are to be&lt;br /&gt;
                          considered trash upon return of the IOReq.&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
                          This command can fail for many reasons and&lt;br /&gt;
                          is not supported by all networks and/or&lt;br /&gt;
                          network interfaces.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The DstAddr field may be trashed by the driver because this function&lt;br /&gt;
        may be implemented by filling DstAddr with a broadcast address and&lt;br /&gt;
        internally calling CMD_WRITE.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        CMD_WRITE, S2_MULTICAST&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_CONFIGINTERFACE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_CONFIGINTERFACE -- Configure the network interface.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to initialize the interface&lt;br /&gt;
        hardware and to set the network interface address to the address in&lt;br /&gt;
        ios2_SrcAddr. This command can only be executed once and, if&lt;br /&gt;
        successful, will leave the driver and network interface fully&lt;br /&gt;
        operational and the network interface in ios2_SrcAddr.&lt;br /&gt;
&lt;br /&gt;
        To set the interface address to the factory address, the network&lt;br /&gt;
        management software must use GetStationAddress first and then call&lt;br /&gt;
        ConfigInterface with the result. If there is no factory address then&lt;br /&gt;
        the network software must pick an address to use.&lt;br /&gt;
&lt;br /&gt;
        Until this command is executed the device will not listen for any&lt;br /&gt;
        packets on the hardware.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_CONFIGINTERFACE.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_SrcAddr    - Address for this interface.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
        ios2_SrcAddr    - Address of this interface as configured.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Some networks have the interfaces choose a currently unused interface&lt;br /&gt;
        address each time the interface is initialized. The caller must check&lt;br /&gt;
        ios2_SrcAddr for the actual interface address after configuring the&lt;br /&gt;
        interface.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_GETSTATIONADDRESS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_CONNECT ==&lt;br /&gt;
&lt;br /&gt;
The driver model specified by the SANA-II standard really only covers networking hardware well. Software-only drivers, such as for dial-up networking, are, well, somehow mentioned in the standard, but they don&#039;t receive much attention. In particular, this means that a networking driver is assumed to be practically always attached to its link layer and no provisions exist to specify what kind of link layer that might be and how it might be accessed. This small oversight can probably be explained by the fact that at the time the SANA-II standard was adopted, dial-up networking had not yet gained the prominence it has today.&lt;br /&gt;
&lt;br /&gt;
To bridge this gap, I propose a new command which will make a driver connect to its link layer and go online, which uses the following data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2Connection&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2c_Size;&lt;br /&gt;
   struct MinList s2c_Options;&lt;br /&gt;
   struct Hook    s2c_ErrorHook;&lt;br /&gt;
   struct Hook    s2c_ConnectHook;&lt;br /&gt;
   struct Hook    s2c_DisconnectHook;&lt;br /&gt;
   STRPTR         s2c_Login;&lt;br /&gt;
   STRPTR         s2c_Password;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The individual structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the entire data structure is stored here. This value &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;amp;gt;= 84. Smaller values &#039;&#039;&#039;must&#039;&#039;&#039; be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;. The purpose of &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt; is to allow for future expansion during which the structure may grow in size.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This list contains options, to be used during the connection process. Each node has the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ConnectionOption&lt;br /&gt;
{&lt;br /&gt;
   struct MinNode s2co_MinNode;&lt;br /&gt;
   STRPTR         s2co_Name;&lt;br /&gt;
   STRPTR         s2co_Value;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;s2co_Name&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2co_Value&amp;lt;/tt&amp;gt; entries point to &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated strings, which contain the name and the value of a parameter. &#039;&#039;Note that for numeric values, the respective number will be encoded in a text string.&#039;&#039; A number of parameters are reserved, which are [[#s2c_options|listed later]] in this text.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called whenever an error message is to be reported during the connection/disconnection process. The hook function is invoked using the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;error_hook_func(hook,reserved,message);&lt;br /&gt;
&lt;br /&gt;
VOID error_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                     STRPTR message);&amp;lt;/pre&amp;gt;&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;message&amp;lt;/tt&amp;gt; parameter points to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string. It &#039;&#039;&#039;must not&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Because the hook function may have to allocate memory, it &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called when the link level device has been set up, but further initializations are necessary, such as telling a modem to dial out. The hook function is invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
success = connect_hook_func(hook,reserved,s2cm);&lt;br /&gt;
&lt;br /&gt;
BOOL connect_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                       struct Sana2ConnectionMessage *s2cm);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;s2cm&amp;lt;/tt&amp;gt; parameter points to a data structure, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ConnectionMessage&lt;br /&gt;
{&lt;br /&gt;
   ULONG                    s2cm_Size;&lt;br /&gt;
   struct Sana2Connection * s2cm_Connection;&lt;br /&gt;
   struct IORequest *       s2cm_Request[2];&lt;br /&gt;
   LONG                     s2cm_RequestType;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this structure, the members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Size&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of this data structure; it &#039;&#039;&#039;must&#039;&#039;&#039; be at least 20 bytes in size. The purpose of &amp;lt;tt&amp;gt;s2cm_Size&amp;lt;/tt&amp;gt; is to allow for future expansion, which may cause the size of this structure to grow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Connection&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This points back to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure which the hook that was invoked with the &amp;lt;tt&amp;gt;Sana2ConnectionMessage&amp;lt;/tt&amp;gt; is embedded in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Request&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here you will find two I/O requests which can be used for reading and writing data to the link layer. These pointers &#039;&#039;&#039;must not&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; and they &#039;&#039;&#039;must&#039;&#039;&#039; refer to different I/O requests, it is not permitted to pass the same request twice.&lt;br /&gt;
&lt;br /&gt;
The dialer can use these requests for communicating with the modem, but it is also permitted to clone these requests by creating new I/O requests of the same size, copying the original contents and filling in different reply ports.&lt;br /&gt;
&lt;br /&gt;
There is a danger in that the hook code may not receive the right kind of I/O request, which is why the &amp;lt;tt&amp;gt;s2cm_RequestType&amp;lt;/tt&amp;gt; field identifies the kind of device the requests were created for.&lt;br /&gt;
&lt;br /&gt;
When the hook function returns, it &#039;&#039;&#039;must&#039;&#039;&#039; make sure that none of the I/O requests are still pending, i.e. asynchronous I/O &#039;&#039;&#039;must&#039;&#039;&#039; have been stopped.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_RequestType&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This identifies the type of device the I/O requests passed in &amp;lt;tt&amp;gt;s2cm_Request&amp;lt;/tt&amp;gt; were created for. Possible values for this entry come from the New Style Device specification, e.g. &amp;lt;tt&amp;gt;NSDEVTYPE_SERIAL&amp;lt;/tt&amp;gt; for a &amp;lt;tt&amp;gt;serial.device&amp;lt;/tt&amp;gt;-like device or &amp;lt;tt&amp;gt;NSDEVTYPE_SANA2&amp;lt;/tt&amp;gt; for a networking driver.&lt;br /&gt;
&lt;br /&gt;
The hook function &#039;&#039;&#039;must&#039;&#039;&#039; return &amp;lt;tt&amp;gt;TRUE&amp;lt;/tt&amp;gt; if the connection could be established, and &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; otherwise. Note that it is &#039;&#039;not&#039;&#039; sufficient to just return &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; in case of failure. Your code &#039;&#039;&#039;must&#039;&#039;&#039; have called the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an explanation why things went wrong first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called by &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; when the connection could not be established (the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; returned &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt;), or by &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt;, shortly before the link level device is to be closed. The hook function is invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disconnect_hook_func(hook,reserved,s2cm);&lt;br /&gt;
&lt;br /&gt;
VOID disconnect_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                          struct Sana2ConnectionMessage *s2cm);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;s2cm&amp;lt;/tt&amp;gt; parameter points to a data structure, as was described for the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The purpose of these entries is to transport the authentication information the protocol may require. These entries are either &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; or contain pointers to &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated strings. If &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;, then both login and password &#039;&#039;&#039;must&#039;&#039;&#039; be assumed to be empty. If &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; is not &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;, then the password &#039;&#039;&#039;must&#039;&#039;&#039; be assumed to be empty.&lt;br /&gt;
&lt;br /&gt;
The list of options in &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; supplies the necessary information on how the driver is to connect to the link layer. Each node contains an option, which bears a name and contains a value. This pair is what I call a &#039;&#039;parameter&#039;&#039;. A number of parameter names are reserved, as will be listed below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.device&lt;br /&gt;
&lt;br /&gt;
Name of device driver to use for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;serial.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.unit&lt;br /&gt;
&lt;br /&gt;
Device unit number to use for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.speed&lt;br /&gt;
&lt;br /&gt;
Transmission speed to use for asynchronous PPP in bits per second.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;115200&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.buffersize&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Receive buffer size for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;50000&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.checkcarrier&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether the carrier signal of the link layer should be tested or not. This can be either 0 (do not test) or 1 (test the carrier signal).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.rtscts&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not hardware handshaking should be used by the link layer. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.shared&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the link layer device should be opened in shared mode. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.nullmodem&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the link layer is a direct connection, such as a nullmodem. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.eof&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the underlying serial device driver&#039;s &#039;EOF mode&#039; should be enabled. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.readrequests&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of read requests to be used for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.writerequests&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of write requests to be used for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.accm&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Asynchronous control character map, expressed as a hexadecimal value.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;$000A0000&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.pfc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not protocol field compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.aacfc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not address and control field compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.vjhc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not Van Jacobson header compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.ignorefcs&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not frame check sequences should be ignored upon reception. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.initialize&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem initialization command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;AT\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.dial&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem dial command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;ATD12345\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.dialtimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The dial timeout, i.e. the number of seconds to wait after the dial command has been sent for the modem to establish a connection.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;60&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.hangup&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem hangup command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;ATH0\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.idletimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of seconds the local host may remain idle, i.e. send no data to the peer, before a watchdog timeout elapses and proceeds to verify that the line is still operational.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;30&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.localaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to assign to the local host, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.remoteaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to assume for the peer, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dns1address&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to be used by the primary domain name server, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dns2address&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to be used by the secondary domain name server, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxfail&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of negative configuration acknowledgements to be sent before the PPP negotiation process switches to reject those options.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;5&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxterm&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of termination requests to be sent before the respective PPP network or link protocol gives up.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxconfig&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of configuration requests to be sent before the respective PPP network or link protocol gives up.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;10&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.timeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of seconds that have to pass before the respective PPP network or link protocol will retry to do whatever didn&#039;t work during the last attempt.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.mtu&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum transmission unit to use.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1500&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.peeridletimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of seconds the peer may remain idle, i.e. send no data to the local host, before a watchdog timeout elapses and proceeds to verify that the line is still operational.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;30&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.rejectpap&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the Password Authentication Protocol should be accepted, if offered by the peer. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (accept) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (reject).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.sendid&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A flag which controls whether the local host should send LCP identification packets or not. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.pap.timeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Password Authentication Protocol requires that the server answers to the client&#039;s request to log in. The server may be unable to respond immediately, which means that the client will have to repeat its request. A short delay should separate each request sent, such as three seconds.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.pap.retry&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the client does not manage to authenticate with the server immediately, it may resend the authentication request several times. But the attempts have to stop eventually, such as after having resent the message ten times.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;10&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dummyremoteaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the PPP driver should make up an IP address if the peer refuses to state its own IP address.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;logfile&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of a log file to create. If the file already exists, then new data should be appended to it.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;t:logfile&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;logoptions&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A list of options which control what exactly should be logged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name of device driver to use for PPPoE (PPP over Ethernet).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;a2065.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.unit&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Device unit number to use for PPPoE.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.raw&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not raw link layer frames should be constructed for transmission or not. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.bypass&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the IP packet transmission and reception should bypass several copying steps. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.readpackets&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of read requests to queue for the link layer.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.writepackets&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of write requests to queue for the link layer.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.service&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of the PPPoE service to request.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.ac&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of the PPPoE access concentrator to request.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.connecttimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of seconds to wait for the PPPoE server to allow a session to be opened.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this list of parameters may suggest that the command described above can be used solely with the PPP protocol, do not let that put you off. This list is merely the starting point, but it is not set in stone that it cannot be extended.&lt;br /&gt;
&lt;br /&gt;
The names of the parameters are not case sensitive. As the names suggest, the name space itself is hierarchic in construction, i.e. everything related to the PPP protocol bears a name starting with the letters &#039;ppp&#039; with the dot &#039;.&#039; separating the individual items. By this rule, ppp.async refers to options that concern asynchronous PPP and ppp.ethernet to options that concern PPP over Ethernet wire.&lt;br /&gt;
&lt;br /&gt;
To add your own parameter, register it with the maintainer of the SANA-II standard or prefix its name with the letters &#039;x-&#039;. For example, to use your own kind of &#039;ppp.ethernet.connecttimeout&#039; parameter, change the name of the last component like this: ppp.ethernet.x-connecttimeout. No officially-registered parameter will ever begin with the prefix &#039;x-&#039;.&lt;br /&gt;
&lt;br /&gt;
The command should work as follows:&lt;br /&gt;
&lt;br /&gt;
# The client must set up the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure, initialize the &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields.&lt;br /&gt;
# The connection options must be filled in, which means that nodes containing the respective information must be stored in the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list. The client must make sure that the syntax of the parameters conforms to the specifications described above.&lt;br /&gt;
# A pointer to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure is placed in the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of an &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;, the command is set to &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and the request is sent via &amp;lt;tt&amp;gt;DoIO()&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SendIO()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver receives the request and begins to examine the contents of the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list, as provided in the data structure pointed to by the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of the request. Unknown options are ignored, options whose values do not conform to the syntax specification are rejected; this is done by calling the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an error message referring to the option in question and by returning the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an error code of &amp;lt;tt&amp;gt;S2ERR_BAD_ARGUMENT&amp;lt;/tt&amp;gt; and wire error code of &amp;lt;tt&amp;gt;S2WERR_INVALID_OPTION&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# If the options are all in good order, the driver proceeds to verify that all mandatory options are provided. If this is not the case, the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; is called with an error message referring to the option in question and the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an error code of &amp;lt;tt&amp;gt;S2ERR_BAD_ARGUMENT&amp;lt;/tt&amp;gt; and wire error code of &amp;lt;tt&amp;gt;S2WERR_MISSING_OPTION&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver proceeds to do its local initialization, which involves opening the link layer device, etc. If this initialization fails, the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; is called with an error message referring to the problem and the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an appropriate error code.&lt;br /&gt;
# When the initialization has finished, the driver may invoke the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; callback. Some drivers may require this, such as asynchronous PPP, some may not, such as PPPoE. The purpose of the hook function is to give the client a chance to perform modem initializations and connect to the peer. If the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; cannot perform its duties, it has to invoke the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an error message and eventually return &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt;. If &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; is returned, the driver must invoke the &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, reverse any initializations it had made and eventually returned the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an appropriate error code. If the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; returned &amp;lt;tt&amp;gt;TRUE&amp;lt;/tt&amp;gt;, then the driver must proceed with the actions that require that the link layer is operational. A protocol negotiation may follow, which, if successful, will make the driver return the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an error code of zero, indicating success. If successful, the SANA-II events &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; must be sent.&lt;br /&gt;
# The command will eventually return, but the client &#039;&#039;&#039;must not&#039;&#039;&#039; release the memory allocated for the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure and the option nodes in the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list. This is because the driver may have to invoke the &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt; hook due to the connection shutting down on its own accord.&lt;br /&gt;
&lt;br /&gt;
The connect and disconnect hook functions &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code. For each hook only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority must be assumed. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_CONNECT -- Establish a link layer connection and go&lt;br /&gt;
            online.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command is for use by networking devices which require&lt;br /&gt;
        a special link layer device to transmit their data, such as&lt;br /&gt;
        an asynchronous serial line and need to know about the&lt;br /&gt;
        configuration parameters necessary to open the connection.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_CONNECT&lt;br /&gt;
        ios2_Data             - Pointer to Sana2Connection structure&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If successful, this command implies S2_ONLINE, i.e. the&lt;br /&gt;
        link layer is allocated and used by the driver.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Connection structure must be valid&lt;br /&gt;
        until the connection is eventually shut down. The driver will&lt;br /&gt;
        need to cache it, so it must not be deallocated or otherwise&lt;br /&gt;
        modified.&lt;br /&gt;
&lt;br /&gt;
        Note that S2_ONLINE does not necessarily imply S2_CONNECT, if&lt;br /&gt;
        the S2_CONNECT command is listed as supported by the driver via&lt;br /&gt;
        NSCMD_DEVICEQUERY. If S2_CONNECT is not listed as supported,&lt;br /&gt;
        S2_ONLINE obviously implies connect functionality.&lt;br /&gt;
&lt;br /&gt;
        S2_CONNECT/S2_DISCONNECT do not nest.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_DISCONNECT&amp;lt;/pre&amp;gt;&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_CONNECT 0xC005&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_DELMULTICASTADDRESS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_DELMULTICASTADDRESS -- Disable an interface multicast address.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes device driver to disable multicast packet&lt;br /&gt;
        reception for the requested address.&lt;br /&gt;
&lt;br /&gt;
        It is an error to disable a multicast address that is not enabled.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_DELMULTICASTADDRESS&lt;br /&gt;
        ios2_SrcAddr    - Multicast address to disable.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Multicast addresses are added globally -- anyone using the device&lt;br /&gt;
        may receive packets as a result of any multicast address which has&lt;br /&gt;
        been added for the device.&lt;br /&gt;
&lt;br /&gt;
        Since multicast addresses are not &amp;amp;quot;bound&amp;amp;quot; to a particular packet type,&lt;br /&gt;
        each enabled multicast address has an &amp;amp;quot;enabled&amp;amp;quot; count associated with&lt;br /&gt;
        it so that if two protocols add the same multicast address and later&lt;br /&gt;
        one removes it, it is still enabled until the second removes it.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_ADDMULTICASTADDRESS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_DEVICEQUERY ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_DEVICEQUERY -- Return parameters for this network interface.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to report information about the&lt;br /&gt;
        device. Up to SizeAvailable bytes of the information is copied&lt;br /&gt;
        into a buffer pointed to by ios2_StatData. The format of the data is&lt;br /&gt;
        as follows:&lt;br /&gt;
&lt;br /&gt;
            struct Sana2DeviceQuery&lt;br /&gt;
            {&lt;br /&gt;
            /*&lt;br /&gt;
            ** Standard information&lt;br /&gt;
            */&lt;br /&gt;
                ULONG SizeAvailble; /* bytes available */&lt;br /&gt;
                ULONG SizeSupplied; /* bytes supplied */&lt;br /&gt;
                LONG  DevQueryFormat;   /* this is type 0 */&lt;br /&gt;
                LONG  DeviceLevel;      /* this document is level 0 */&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            ** Common information&lt;br /&gt;
            */&lt;br /&gt;
                UWORD AddrFieldSize;    /* address size in bits */&lt;br /&gt;
                ULONG MTU;              /* maximum packet data size */&lt;br /&gt;
                LONG  bps;              /* line rate (bits/sec) */&lt;br /&gt;
                LONG  HardwareType;     /* what the wire is */&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            ** Format specific information&lt;br /&gt;
            */&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
        The SizeAvailable specifies the number of bytes that the caller&lt;br /&gt;
        is prepared to accomodate, including the standard information fields.&lt;br /&gt;
&lt;br /&gt;
        SizeSupplied is the number of bytes actually supplied,&lt;br /&gt;
        including the standard information fields, which will not exceed&lt;br /&gt;
        SizeAvailable.&lt;br /&gt;
&lt;br /&gt;
        &amp;amp;lt;devices/sana2.h&amp;amp;gt; includes constants for these values.  If your&lt;br /&gt;
        hardware does not have a number assigned to it, you must contact&lt;br /&gt;
        the AmigaOS development team to get a hardware number.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_DEVICEQUERY.&lt;br /&gt;
        ios2_StatData   - Pointer to Sana2DeviceQuery structure to fill in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_DISCONNECT ==&lt;br /&gt;
&lt;br /&gt;
This command complements &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; in that it tears down a connection. It uses the same &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure and hooks, but most of these members are ignored.&lt;br /&gt;
&lt;br /&gt;
The command should work as follows:&lt;br /&gt;
&lt;br /&gt;
# The client must set up the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure, initialize the &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields. The &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields will be ignored, but the client should play things safe.&lt;br /&gt;
# A pointer to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure is placed in the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of an &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;, the command is set to &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and the request is sent via &amp;lt;tt&amp;gt;DoIO()&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SendIO()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver receives the request and proceeds to reverse the steps that previously allowed it to establish a connection. This includes telling the peer to shut down the link, but it does not include cleaning up the link layer device access, i.e. no I/O requests used for accessing a modem may be shut down yet.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt; may be invoked with the proper parameters. Some drivers, such as for asynchronous PPP, will need the hook to tell the modem to hang up the line. Some drivers, such as for PPPoE, may not need this hook and thus ignore it.&lt;br /&gt;
# The initialization is reversed completely, all resources allocated when the connection was previously opened are released. The &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an error code of zero, indicating success. The SANA-II event &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; must be sent, and, if necessary, &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The connect and disconnect hook functions must not be called from interrupt code. For each hook only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority must be assumed. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_DISCONNECT -- Go offline and close a link layer connection&lt;br /&gt;
            previously established with S2_CONNECT.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command complements the S2_CONNECT command in that it&lt;br /&gt;
        reverses the steps taken to establish a link layer connection.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_DISCONNECT&lt;br /&gt;
        ios2_Data             - Pointer to Sana2Connection structure&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If successful, this command implies S2_OFFLINE, i.e. the&lt;br /&gt;
        link layer is deallocated.&lt;br /&gt;
&lt;br /&gt;
        The driver must ignore the S2_DISCONNECT command and&lt;br /&gt;
        recover gracefully if the S2_CONNECT was never sent or&lt;br /&gt;
        returned with an error.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Connection structure are valid only&lt;br /&gt;
        until the device driver has processed the command and returned&lt;br /&gt;
        the IOSana2Req. Any data the driver may need to retain beyond&lt;br /&gt;
        that point of time must be copied.&lt;br /&gt;
&lt;br /&gt;
        Once the S2_DISCONNECT command has returned, it is safe to dispose&lt;br /&gt;
        of the Sana2Connection structure provided at S2_CONNECT time.&lt;br /&gt;
&lt;br /&gt;
        Note that S2_OFFLINE does not necessarily imply S2_DISCONNECT, if&lt;br /&gt;
        the S2_DISCONNECT command is listed as supported by the driver via&lt;br /&gt;
        NSCMD_DEVICEQUERY. If S2_DISCONNECT is not listed as supported,&lt;br /&gt;
        S2_OFFLINE obviously implies disconnect functionality.&lt;br /&gt;
&lt;br /&gt;
        S2_CONNECT/S2_DISCONNECT do not nest.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_CONNECT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_DISCONNECT 0xC006&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETDNSADDRESS ==&lt;br /&gt;
&lt;br /&gt;
The PPP negotiation process may produce information on which domain name and NetBIOS name servers are available to the client. I think that it is doubtful that the availability of NetBIOS name servers will be useful for Amiga software (let alone whether NetBIOS name resolution has a future), which is why I suggest that a SANA-II command for returning only the domain name servers is introduced.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETDNSADDRESS -- Obtain the addresses of the primary&lt;br /&gt;
           and secondary domain name servers.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Obtain the addresses of the domain name servers available to the&lt;br /&gt;
        client using this driver&#039;s address.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETDNSADDRESS&lt;br /&gt;
        ios2_Flags            - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
        ios2_SrcAddr      - Address of primary domain name server&lt;br /&gt;
        ios2_DstAddr      - Address of secondary domain name server&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The size of the address returned by S2_GETPEERADDRESS must not be&lt;br /&gt;
        different from the size as returned by the S2_DEVICEQUERY command. For&lt;br /&gt;
        example, if a 32 bit IPv4 address was advertized, the driver must not&lt;br /&gt;
        return a 128 bit IPv6 address instead.&lt;br /&gt;
&lt;br /&gt;
        If the driver is unable to return the primary domain name server&lt;br /&gt;
        address (ios2_SrcAddr) or the secondary domain name server address&lt;br /&gt;
        (ios2_DstAddr) it must fill the respective address fields with zeroes.&lt;br /&gt;
        It is legal for a driver to respond to the S2_GETDNSADDRESS command&lt;br /&gt;
        with two zero addresses (both ios2_SrcAddr and ios2_DstAddr filled&lt;br /&gt;
        with zeroes).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETDNSADDRESS 0xC003&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This command may be useful beyond the typical application (PPP) described above.&lt;br /&gt;
&lt;br /&gt;
== S2_GETEXTENDEDGLOBALSTATS ==&lt;br /&gt;
&lt;br /&gt;
There already exists a SANA-II command for querying global device statistics (&amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt;) which should be common to all kinds of networking devices. Statistics that are particular to a certain device type are intended to be returned through the &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt; command. I feel that these mechanisms both fail to cater well enough for dial-up or session-oriented networking applications such as PPP or PPPoE. Since the data structure used by &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt; is of a fixed size and not intended to accomodate for new fields, I propose to introduce a new command which uses a different data structure, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ExtDeviceStats&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2xds_Length;&lt;br /&gt;
   ULONG          s2xds_Actual;&lt;br /&gt;
&lt;br /&gt;
   S2QUAD         s2xds_PacketsReceived;&lt;br /&gt;
   S2QUAD         s2xds_PacketsSent;&lt;br /&gt;
   S2QUAD         s2xds_BadData;&lt;br /&gt;
   S2QUAD         s2xds_Overruns;&lt;br /&gt;
   S2QUAD         s2xds_UnknownTypesReceived;&lt;br /&gt;
   S2QUAD         s2xds_Reconfigurations;&lt;br /&gt;
   struct timeval s2xds_LastStart;&lt;br /&gt;
&lt;br /&gt;
   struct timeval s2xds_LastConnected;&lt;br /&gt;
   struct timeval s2xds_LastDisconnected;&lt;br /&gt;
   struct timeval s2xds_TimeConnected;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before I proceed to explain what purposes the individual members serve, a few words on the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type, which is defined as follows:&lt;br /&gt;
&amp;lt;tt&amp;gt;typedef struct { ULONG s2q_High; ULONG s2q_Low; } S2QUAD;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type stands for an unsigned 64 bit big endian integer, as expressed in ISO &#039;C&#039; terms.&lt;br /&gt;
&lt;br /&gt;
The structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the size of the data structure to be filled in and should be initialized by the caller to &amp;lt;tt&amp;gt;sizeof(struct Sana2ExtDeviceStats)&amp;lt;/tt&amp;gt;. Smaller values are permitted, but these &#039;&#039;&#039;must not&#039;&#039;&#039; be smaller than 8 (which covers the &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_Actual&amp;lt;/tt&amp;gt; members). A driver which finds an &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt; &amp;amp;lt; 8 &#039;&#039;&#039;must&#039;&#039;&#039; treat this as an error and reject the command with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Actual&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the data structure filled with information. This member is initialized by the driver and &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;amp;lt;= &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_PacketsReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets that this unit has received. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_PacketsSent&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets that this unit has sent. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_BadData&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of bad packets received (i.e., hardware CRC failed). This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Overruns&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets dropped due to insufficient resources available in the network interface. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_UnknownTypesReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets received that had no pending read command with the appropriate packet type. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Reconfigurations&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of network reconfigurations since this unit was last configured. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastStart&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last went on-line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last established a connection. For dial-up connections, this should be the time when the underlying serial line started to accumulate costs. &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero if the unit never managed to make a connection.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last shut down a connection. For dial-up connections, this should be the time when the underlying serial line stopped accumulating costs, e.g. when the modem&#039;s carrier signal was lost. &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero if the unit never disconnected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time this unit has been connected. For dial-up connections this should be the time between now and when the underlying serial line started accumulating costs.&lt;br /&gt;
&lt;br /&gt;
If this unit is not currently connected, then &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero. This means in particular that when the connection is lost, &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be immediately set to zero and &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be filled in so that client software can query how long the unit was connected by subtracting &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; from &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If this unit is currently connected, &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must never&#039;&#039;&#039; be zero; if necessary, set &amp;lt;tt&amp;gt;s2xds_TimeConnected.tv_secs=0&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_TimeConnected.tv_micros=1&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; is zero, check &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;; if the latter two are not zero, you can calculate the previous connection time by subtracting &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; from &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The layout and semantics used by the &amp;lt;tt&amp;gt;Sana2ExtDeviceStats&amp;lt;/tt&amp;gt; data structure suggest that there is a difference between the underlying networking media (the link layer) and the state of the protocol that is running on top of it. With drivers for networking hardware such as Ethernet there was no difference between these two, but for protocols like SLIP, PPP or PPPoE there is a difference. The difference is in that a session or connection may exist for a certain time whereas the protocol running inside that session may be switched &#039;online&#039; later. The primary purpose of the &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; fields is to allow for cost accounting and traffic monitoring (so that, for example, a driver may be disconnected after it has been idle for a while) to be written.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETEXTENDEDGLOBALSTATS -- Get interface accumulated statistics;&lt;br /&gt;
           updated version.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to retrieve various global&lt;br /&gt;
        runtime statistics for this network interface. The format of the data&lt;br /&gt;
        returned is as follows:&lt;br /&gt;
&lt;br /&gt;
           struct Sana2ExtDeviceStats&lt;br /&gt;
           {&lt;br /&gt;
              ULONG s2xds_Length;&lt;br /&gt;
              ULONG s2xds_Actual;&lt;br /&gt;
&lt;br /&gt;
              S2QUAD s2xds_PacketsReceived;&lt;br /&gt;
              S2QUAD s2xds_PacketsSent;&lt;br /&gt;
              S2QUAD s2xds_BadData;&lt;br /&gt;
              S2QUAD s2xds_Overruns;&lt;br /&gt;
              S2QUAD s2xds_UnknownTypesReceived;&lt;br /&gt;
              S2QUAD s2xds_Reconfigurations;&lt;br /&gt;
              struct timeval s2xds_LastStart;&lt;br /&gt;
&lt;br /&gt;
              struct timeval s2xds_LastConnected;&lt;br /&gt;
              struct timeval s2xds_LastDisconnected;&lt;br /&gt;
              struct timeval s2xds_TimeConnected;&lt;br /&gt;
           };&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETEXTENDEDGLOBALSTATS&lt;br /&gt;
        ios2_StatData         - Pointer to Sana2ExtDeviceStats structure&lt;br /&gt;
                                to fill in&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETEXTENDEDGLOBALSTATS 0xC004&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETGLOBALSTATS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        GetGlobalStats -- Get interface accumulated statistics.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to retrieve various global&lt;br /&gt;
        runtime statistics for this network interface. The format of the&lt;br /&gt;
        data returned is as follows:&lt;br /&gt;
&lt;br /&gt;
            struct Sana2DeviceStats&lt;br /&gt;
            {&lt;br /&gt;
                ULONG PacketsReceived;&lt;br /&gt;
                ULONG PacketsSent;&lt;br /&gt;
                ULONG BadData;&lt;br /&gt;
                ULONG Overruns;&lt;br /&gt;
                ULONG UnknownTypesReceived;&lt;br /&gt;
                ULONG Reconfigurations;&lt;br /&gt;
                timeval LastStart;&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_GETGLOBALSTATS.&lt;br /&gt;
        ios2_StatData   - Pointer to Sana2DeviceStats structure to fill.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_GETSPECIALSTATS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETNETWORKINFO ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_GETNETWORKINFO -- Get information on current network.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	This command provides information on the status of the currently&lt;br /&gt;
	used network. If this command completes successfully, ios2_StatData&lt;br /&gt;
	will contain a pointer to a tag list that contains information on&lt;br /&gt;
	the network. The S2INFO_#? tags used are defined in the&lt;br /&gt;
	devices/sana2wireless.h include file.&lt;br /&gt;
&lt;br /&gt;
	The returned taglist is allocated from the supplied memory pool.&lt;br /&gt;
	To discard the results of this command, the entire memory pool&lt;br /&gt;
	should be destroyed.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_Data - Pointer to an Exec memory pool.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	ios2_Data - Remains unchanged.&lt;br /&gt;
	ios2_StatData - Pointer to a tag list.&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETNETWORKS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_GETNETWORKS -- Scan for available networks.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	This command supplies details of available networks. If the scan&lt;br /&gt;
	should be limited to one specific network, the S2INFO_SSID tag&lt;br /&gt;
	should specify its name.&lt;br /&gt;
&lt;br /&gt;
	If this command completes successfully, ios2_StatData will contain&lt;br /&gt;
	an array of pointers to tag lists, each of which contains&lt;br /&gt;
	information on a single network. The device will set ios2_DataLength&lt;br /&gt;
	to the number of elements in this array.&lt;br /&gt;
&lt;br /&gt;
	The returned taglists are allocated from the supplied memory pool.&lt;br /&gt;
	To discard the results of this command, the entire memory pool&lt;br /&gt;
	should be destroyed.&lt;br /&gt;
&lt;br /&gt;
	The S2INFO_#? tags used with this command are defined in the&lt;br /&gt;
	devices/sana2wireless.h include file.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_Data - Pointer to an Exec memory pool.&lt;br /&gt;
	ios2_StatData - Pointer to taglist that specifies parameters to use.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	ios2_DataLength - Number of tag lists returned.&lt;br /&gt;
	ios2_Data - Remains unchanged.&lt;br /&gt;
	ios2_StatData - Pointer to an array of tag lists.&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETPEERADDRESS ==&lt;br /&gt;
&lt;br /&gt;
As part of the negotiation process, the PPP protocol can return the addresses used by the peer (the other side of the point-to-point connection PPP establishes; typically a dial-in server) and assigned to the client establishing the connection. The SANA-II standard does not provide for a mechanism to return such information. Existing drivers therefore had to resort to other means, such as by setting global environment variables containing this information.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETPEERADDRESS -- Obtain the addresses used by the peer&lt;br /&gt;
           (server) and the client of a point-to-point connection.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Obtain the address used by the peer of a point-to-point connection and&lt;br /&gt;
        the address assigned to the local driver.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETPEERADDRESS&lt;br /&gt;
        ios2_Flags            - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
        ios2_SrcAddr      - Address assigned to the local driver&lt;br /&gt;
        ios2_DstAddr      - Address used by the peer&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The size of the address returned by S2_GETPEERADDRESS must not be&lt;br /&gt;
        different from the size returned by the S2_DEVICEQUERY command. For&lt;br /&gt;
        example, if a 32 bit IPv4 address was advertized, the driver must not&lt;br /&gt;
        return a 128 bit IPv6 address instead.&lt;br /&gt;
&lt;br /&gt;
        If the driver is unable to return the local driver address&lt;br /&gt;
        (ios2_SrcAddr) or the peer&#039;s address (ios2_DstAddr) it must fill the&lt;br /&gt;
        respective address fields with zeroes. It is legal for a driver to&lt;br /&gt;
        respond to the S2_GETPEERADDRESS command with two zero addresses (both&lt;br /&gt;
        ios2_SrcAddr and ios2_DstAddr filled with zeroes).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETPEERADDRESS 0xC002&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This command may be useful beyond the typical application (PPP) described above.&lt;br /&gt;
&lt;br /&gt;
== S2_GETSIGNALQUALITY ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_GETSIGNALQUALITY -- Get signal quality statistics.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	This command fills in the supplied Sana2SignalQuality structure with&lt;br /&gt;
	current signal and noise levels. The unit for these figures is dBm.&lt;br /&gt;
	Typically, they are negative values.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_StatData - Pointer to Sana2SignalQuality structure.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
	ios2_StatData - Pointer to filled Sana2SignalQuality structure.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETSPECIALSTATS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETSPECIALSTATS -- Get network type specific statistics.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function returns statistics which are specific to the type of&lt;br /&gt;
        network medium this driver controls. For example, this command could&lt;br /&gt;
        return statistics common to all Ethernets which are not common to all&lt;br /&gt;
        network mediums in general.&lt;br /&gt;
&lt;br /&gt;
        The supplied Sana2SpecialStatData structure is given below:&lt;br /&gt;
&lt;br /&gt;
            struct Sana2SpecialStatData&lt;br /&gt;
            {&lt;br /&gt;
                ULONG RecordCountMax;&lt;br /&gt;
                ULONG RecordCountSupplied;&lt;br /&gt;
                struct Sana2StatRecord[RecordCountMax];&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
        The format of the data returned is:&lt;br /&gt;
&lt;br /&gt;
            struct Sana2StatRecord&lt;br /&gt;
            {&lt;br /&gt;
                ULONG Type;     /* Amiga registered */&lt;br /&gt;
                LONG Count;     /* the stat itself */&lt;br /&gt;
                char *String;   /* null terminated */&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
        The RecordCountMax field specifies the number of records that the&lt;br /&gt;
        caller is prepared to accomodate.&lt;br /&gt;
&lt;br /&gt;
        RecordCountSupplied is the number of record actually supplied which&lt;br /&gt;
        will not exceed RecordCountMax.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_GETSPECIALSTATS.&lt;br /&gt;
        ios2_StatData   - Pointer to a Sana2SpecialStatData structure to fill.&lt;br /&gt;
                          RecordCountMax must be initialized.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Amiga will maintain registered statistic Types.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_GETGLOBALSTATS, &amp;amp;lt;devices/sana2specialstats.h&amp;amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETSTATIONADDRESS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETSTATIONADDRESS -- Get default and interface address.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to copy the current interface&lt;br /&gt;
        address into ios2_SrcAddr, and to copy the factory default station&lt;br /&gt;
        address (if any) into ios2_DstAddr.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_GETSTATIONADDRESS.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
        ios2_SrcAddr    - Current interface address.&lt;br /&gt;
        ios2_DstAddr    - Default interface address (if any).&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_CONFIGINTERFACE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_GETTYPESTATS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETTYPESTATS -- Get accumulated type specific statistics.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to retrieve various packet type&lt;br /&gt;
        specific runtime statistics for this network interface. The format of&lt;br /&gt;
        the data returned is as follows:&lt;br /&gt;
&lt;br /&gt;
            struct Sana2TypeStatData&lt;br /&gt;
            {&lt;br /&gt;
                LONG PacketsSent;&lt;br /&gt;
                LONG PacketsReceived;&lt;br /&gt;
                LONG BytesSent;&lt;br /&gt;
                LONG BytesReceived;&lt;br /&gt;
                LONG PacketsDropped;&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_GETTYPESTATS.&lt;br /&gt;
        ios2_PacketType - Packet type of interest.&lt;br /&gt;
        ios2_StatData   - Pointer to TypeStatData structure to fill in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Statistics for a particular packet type are only available while that&lt;br /&gt;
        packet type is being ``tracked&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_TRACKTYPE, S2_UNTRACKTYPE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_MULTICAST ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_MULTICAST -- Multicast a packet on network.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command works the same as CMD_WRITE except that it also performs&lt;br /&gt;
        whatever special processing of the packet is required to do a&lt;br /&gt;
        multicast send. The actual multicast mechanism is neccessarily&lt;br /&gt;
        network/interface/device specific.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_MULTICAST.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                                SANA2IOB_RAW&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_PacketType - Packet type to send.&lt;br /&gt;
        ios2_DstAddr    - Destination interface address for this packet.&lt;br /&gt;
        ios2_DataLength - Length of the Data to be sent.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
                          This command can fail for many reasons and&lt;br /&gt;
                          is not supported by all networks and/or&lt;br /&gt;
                          network interfaces.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The address supplied in ios2_DstAddr will be sanity checked (if&lt;br /&gt;
        possible) by the driver. If the supplied address fails this sanity&lt;br /&gt;
        check, the multicast request will fail immediately with ios2_Error&lt;br /&gt;
        set to S2WERR_BAD_MULTICAST.&lt;br /&gt;
&lt;br /&gt;
        Another Amiga will not receive a multicast packet unless it has had&lt;br /&gt;
        the particular multicast address being used S2_ADDMULTICASTADRESS&#039;d.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        CMD_WRITE, S2_BROADCAST, S2_ADDMULTICASTADDRESS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_OFFLINE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_OFFLINE -- Remove interface from service.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command removes a network interface from service.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_OFFLINE.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        Aborts all pending reads and writes with ios2_Error set to&lt;br /&gt;
        S2ERR_OUTOFSERVICE.&lt;br /&gt;
&lt;br /&gt;
        While the interface is offline, all read, writes and any other&lt;br /&gt;
        command that touches interface hardware will be rejected with&lt;br /&gt;
        ios2_Error set to S2ERR_OUTOFSERVICE.&lt;br /&gt;
&lt;br /&gt;
        This command is intended to permit a network interface to be&lt;br /&gt;
        tested on an otherwise live system.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_ONLINE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_ONEVENT ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_ONEVENT -- Return when specified event occures.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command returns when a particular event condition has occured&lt;br /&gt;
        on the network or this network interface.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_ONEVENT.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_WireError  - Mask of event(s) to wait for&lt;br /&gt;
                          (from &amp;amp;lt;devices/sana2.h&amp;amp;gt;).&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - Mask of events that occured.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If this device driver does not understand the specified event&lt;br /&gt;
        condition(s) then the command returns immediately with&lt;br /&gt;
        ios2_Req.io_Error set to S2_ERR_NOT_SUPPORTED and ios2_WireError&lt;br /&gt;
        S2WERR_BAD_EVENT.  A successful return will have ios2_Error set to&lt;br /&gt;
        zero ios2_WireError set to the event number.&lt;br /&gt;
&lt;br /&gt;
        All pending requests for a particular event will be returned when&lt;br /&gt;
        that event occurs.&lt;br /&gt;
&lt;br /&gt;
        All event types that cover a particular condition are returned when&lt;br /&gt;
        that condition occures. For instance, if an error is returned by&lt;br /&gt;
        a buffer management function during receive processing, events of&lt;br /&gt;
        types S2EVENT_ERROR, S2EVENT_RX and S2EVENT_BUFF would be returned if&lt;br /&gt;
        pending.&lt;br /&gt;
&lt;br /&gt;
        Types ONLINE and OFFLINE return immediately if the device is&lt;br /&gt;
        already in the state to be waited for.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_ONLINE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_ONLINE -- Put a network interface back in service.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command places an offline network interface back into service.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_ONLINE.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This command is responsible for putting the network interface&lt;br /&gt;
        hardware back into a known state (as close as possible to the&lt;br /&gt;
        state before S2_OFFLINE) and resets the unit global and special&lt;br /&gt;
        statistics.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_OFFLINE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_READMGMT ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_READMGMT -- Read a management frame.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	Reads a raw IEEE 802.11 management frame from the device. The buffer&lt;br /&gt;
	management mechanism used is simpler than that used for data&lt;br /&gt;
	packets (e.g. with CMD_READ): a buffer pointer and a length value&lt;br /&gt;
	are passed to the device.&lt;br /&gt;
&lt;br /&gt;
	As with CMDREAD/S2_READORPHAN, multiple S2_READMGMT requests can&lt;br /&gt;
	(and should) be queued concurrently to ensure no incoming frames are&lt;br /&gt;
	missed.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_DataLength - size of frame buffer.&lt;br /&gt;
	ios2_Data - pointer to a frame buffer.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
	ios2_DataLength - actual size of received frame.&lt;br /&gt;
	ios2_Data - pointer to the filled frame buffer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_READORPHAN ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_READORPHAN -- Get a packet for which there is no reader.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Get the next packet available that does not satisfy any then-pending&lt;br /&gt;
        CMD_READ requests. The data returned in the ios2_Data structure is&lt;br /&gt;
        normally the Data Link Layer packet type field and the packet data. If&lt;br /&gt;
        bit SANA2IOB_RAW is set in ios2_Flags, then the entire Data Link Layer&lt;br /&gt;
        packet, including both header and trailer information, will be&lt;br /&gt;
        returned.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - CMD_READORPHAN.&lt;br /&gt;
        ios2_Flags      - Supported flags are:&lt;br /&gt;
                                SANA2IOB_RAW&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_DataLength - Length of the Data to be sent.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
        ios2_Flags      - The following flags may be returned:&lt;br /&gt;
                                SANA2IOB_RAW&lt;br /&gt;
                                SANA2IOB_BCAST&lt;br /&gt;
                                SANA2IOB_MCAST&lt;br /&gt;
        ios2_SrcAddr    - Source interface address of packet.&lt;br /&gt;
        ios2_DstAddr    - Destination interface address of packet.&lt;br /&gt;
        ios2_DataLength - Length of the Data to be sent.&lt;br /&gt;
        ios2_Data       - Abstract data structure which packet data is&lt;br /&gt;
                          contained in.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This is intended for debugging and management tools.  Protocols should&lt;br /&gt;
        not use this.&lt;br /&gt;
&lt;br /&gt;
        As with 802.3 packets on an ethernet, to determine which protocol&lt;br /&gt;
        family the returned packet belongs to you may have to specify&lt;br /&gt;
        SANA2IOB_RAW to get the entire data link layer wrapper (which is where&lt;br /&gt;
        the protocol type may be kept). Notice this necessarily means that&lt;br /&gt;
        this cannot be done in a network interface independent fashion.  The&lt;br /&gt;
        driver will, however, fill in the PacketType field to the best of its&lt;br /&gt;
        ability.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        CMD_READ, CMD_WRITE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_SAMPLE_THROUGHPUT ==&lt;br /&gt;
&lt;br /&gt;
The SANA-II standard already allows for statistics to be returned on the amount of data that has passed through a driver. Unfortunately, that information is not very accurate in that no information is provided on the time span in which the data was accumulated. Such information would be helpful in trying to determine as accurately as possible how large the data throughput actually is. I therefore propose a new command which can be used to obtain that information, which uses the following data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ThroughputStats&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2ts_Length;&lt;br /&gt;
   ULONG          s2ts_Actual;&lt;br /&gt;
&lt;br /&gt;
   struct Task *  s2ts_NotifyTask;&lt;br /&gt;
   ULONG          s2ts_NotifyMask;&lt;br /&gt;
&lt;br /&gt;
   struct timeval s2ts_StartTime;&lt;br /&gt;
   struct timeval s2ts_EndTime;&lt;br /&gt;
   S2QUAD         s2ts_BytesSent;&lt;br /&gt;
   S2QUAD         s2ts_BytesReceived;&lt;br /&gt;
   S2QUAD         s2ts_Updates;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before I proceed to explain what purposes the individual members serve, a few words on the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type, which is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;typedef struct { ULONG s2q_High; ULONG s2q_Low; } S2QUAD;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type stands for an unsigned 64 bit big endian integer, as expressed in ISO &#039;C&#039; terms.&lt;br /&gt;
&lt;br /&gt;
The structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the size of the data structure to be filled in and should be initialized by the caller to &amp;lt;tt&amp;gt;sizeof(struct Sana2ThroughputStats)&amp;lt;/tt&amp;gt;. Smaller values are permitted, but these must not be smaller than 8 (which covers the &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2ts_Actual&amp;lt;/tt&amp;gt; members). A driver which finds an &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt; &amp;amp;lt; 8 must treat this as an error and reject the command with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Actual&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the data structure filled with information. This member is initialized by the driver and must be &amp;amp;lt;= &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_NotifyTask&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; to notify whenever the contents of this data structure change. This must be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; if no notification is desired.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; This feature should be used carefully, as so much data may arrive that the driver will almost be constantly signalling this &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; that a change has taken place.&lt;br /&gt;
&lt;br /&gt;
It is recommend that periodic polling be used, such as to update displays of a link monitoring program.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_NotifyMask&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The signal mask to use for notifying the &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; whose address is found in &amp;lt;tt&amp;gt;s2ts_NotifyTask&amp;lt;/tt&amp;gt; (via &amp;lt;tt&amp;gt;Signal(s2ts-&amp;amp;gt;s2ts_NotifyTask,s2ts-&amp;amp;gt;s2ts_NotifyMask);&amp;lt;/tt&amp;gt;). This must be zero if no notification is desired.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_StartTime&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when the driver started to fill in this data structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_EndTime&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when the driver last updated the contents of this data structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_BytesSent&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total number of bytes sent since the driver started to fill in this data structure. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_BytesReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total number of bytes received since the driver started to fill in this data structure. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Updates&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of times the driver has updated this data structure. This value will increase with every change. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
A driver implementing this command should take care to update the members &amp;lt;tt&amp;gt;s2ts_EndTime&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2ts_BytesSent&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2ts_BytesReceived&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2ts_Updates&amp;lt;/tt&amp;gt; atomically each time changes are made.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_SAMPLE_THROUGHPUT -- Obtain accurate information on&lt;br /&gt;
           driver data throughput.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command installs a data structure which is updated every time&lt;br /&gt;
        data is sent or received by the driver.&lt;br /&gt;
&lt;br /&gt;
        This command must be sent via SendIO() or BeginIO(); until&lt;br /&gt;
        the associated I/O request is recalled using AbortIO(), the&lt;br /&gt;
        device unit will continue to update the Sana2ThroughputStats&lt;br /&gt;
        structure in real time.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_SAMPLE_THROUGHPUT&lt;br /&gt;
        ios2_StatData         - Pointer to Sana2ThroughputStats structure&lt;br /&gt;
                                to fill in&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If this device driver does not understand this command,&lt;br /&gt;
        it will immediately return the IOSana2Req with&lt;br /&gt;
        ios2_Req.io_Error set to IOERR_NOCMD. Otherwise, the request will&lt;br /&gt;
        remain queued until it is removed with AbortIO() later.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_SAMPLE_THROUGHPUT 0xC007&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_SANA2HOOK ==&lt;br /&gt;
&lt;br /&gt;
The &#039;traditional&#039; copy call-back functions are installed at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time, and they are found in a &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list which is passed along with the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;. For the new &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;-based call-back functionality, I propose to introduce a new command. This command would take care of installing one single &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; which will be invoked with the parameters described in section 3.1. The hook function can key off the &amp;lt;tt&amp;gt;SANA2HookMsg-&amp;amp;gt;schm_Method&amp;lt;/tt&amp;gt; field to figure out which function should be performed. Once the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; is installed via the command as follows, the driver shall ignore any and all tags passed to it during &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_SANA2HOOK -- Install a Hook to perform operations such as copying,&lt;br /&gt;
                        overriding the call-back functions installed at&lt;br /&gt;
                        OpenDevice() time.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        The S2_SANA2HOOK command is to replace the &#039;traditional&#039; call-back&lt;br /&gt;
        functions installed through the TagItem list found in the&lt;br /&gt;
        IOSana2Req-&amp;amp;gt;ios2_BufferManagement field. Instead of assigning a&lt;br /&gt;
        function pointer for each copying function, all operations are&lt;br /&gt;
        to be performed through a Hook. This is intended to make the&lt;br /&gt;
        interface more portable and less dependant on a certain hardware&lt;br /&gt;
        architecture.&lt;br /&gt;
&lt;br /&gt;
        The hook message and the hook data structures allow for more than&lt;br /&gt;
        copying to be done.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_SANA2HOOK&lt;br /&gt;
        ios2_Data       - Points to a struct Sana2Hook, which looks&lt;br /&gt;
                          like this:&lt;br /&gt;
&lt;br /&gt;
                             struct Sana2Hook&lt;br /&gt;
                             {&lt;br /&gt;
                                struct Hook s2h_Hook;&lt;br /&gt;
                                Tag *       s2h_Methods;&lt;br /&gt;
                             };&lt;br /&gt;
&lt;br /&gt;
                          The structure fields have the following purposes:&lt;br /&gt;
&lt;br /&gt;
                             s2h_Hook&lt;br /&gt;
                                A standard Hook structure, ready to be&lt;br /&gt;
                                called. Once installed, the complete Hook&lt;br /&gt;
                                structure including its Node structure is&lt;br /&gt;
                                off limits! The s2h_Hook remains installed&lt;br /&gt;
                                until CloseDevice().&lt;br /&gt;
&lt;br /&gt;
                             s2h_Methods&lt;br /&gt;
                                Points to a table of Tag values, each&lt;br /&gt;
                                identifying a copy method supported&lt;br /&gt;
                                (S2_CopyToBuff, S2_CopyFromBuff,&lt;br /&gt;
                                S2_CopyToBuff16, S2_CopyFromBuff16,&lt;br /&gt;
                                S2_CopyToBuff32, S2_CopyFromBuff32,&lt;br /&gt;
                                S2_DMACopyToBuff32, S2_DMACopyFromBuff32,&lt;br /&gt;
                                S2_DMACopyToBuff64 or S2_DMACopyFromBuff64)&lt;br /&gt;
                                or the logging facility (S2_Log).&lt;br /&gt;
                                The table must be terminated by TAG_END.&lt;br /&gt;
&lt;br /&gt;
                                The driver will check the table and&lt;br /&gt;
                                verify that the mandatory S2_CopyToBuff&lt;br /&gt;
                                and S2_CopyFromBuff commands are present.&lt;br /&gt;
                                Additional functionality is used as&lt;br /&gt;
                                available if the driver supports it.&lt;br /&gt;
&lt;br /&gt;
        ios2_DataLength - Must be &amp;amp;gt;= 20, which is the default length of&lt;br /&gt;
                          the Sana2Hook structure. This may grow in&lt;br /&gt;
                          the future, and larger values for ios2_DataLength&lt;br /&gt;
                          may indicate additional functionality associated&lt;br /&gt;
                          with the Sana2Hook.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD if this command is not supported&lt;br /&gt;
                          by the driver.&lt;br /&gt;
&lt;br /&gt;
                          IOERR_BADLENGTH if IOSana2Req-&amp;amp;gt;ios2_DataLength&lt;br /&gt;
                          is &amp;amp;lt; 20.&lt;br /&gt;
&lt;br /&gt;
                          IOERR_UNITBUSY if the Hook was already&lt;br /&gt;
                          installed or if any of the CMD_READ, CMD_WRITE,&lt;br /&gt;
                          S2_MULTICAST or S2_BROADCAST have already been&lt;br /&gt;
                          invoked.&lt;br /&gt;
&lt;br /&gt;
                          S2WERR_FUNCTIONS_MISSING if the table pointed&lt;br /&gt;
                          to by Sana2Hook-&amp;amp;gt;s2h_Methods does not&lt;br /&gt;
                          include the S2_CopyToBuff and S2_CopyFromBuff&lt;br /&gt;
                          tags.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The S2_SANA2HOOK command shall be invoked right after OpenDevice()&lt;br /&gt;
        as very first command.&lt;br /&gt;
&lt;br /&gt;
        When the command has been executed, the driver must use the&lt;br /&gt;
        newly installed Hook for all its copying or logging and cease to&lt;br /&gt;
        use the call-back functions provided at OpenDevice() time.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Hook structure, as passed to the&lt;br /&gt;
        driver, must not be modified. This includes the MinNode&lt;br /&gt;
        at the beginning of the Hook structure which the driver may&lt;br /&gt;
        need to use for its own purposes.&lt;br /&gt;
&lt;br /&gt;
        The table pointed to by Sana2Hook-&amp;amp;gt;s2h_Methods must&lt;br /&gt;
        include at least the S2_CopyToBuff and S2_CopyFromBuff tags.&lt;br /&gt;
        It must be valid until CloseDevice() is called.&lt;br /&gt;
&lt;br /&gt;
        This field is to be treated as private by a protocol stack.&lt;br /&gt;
        IOSana2Req structures may be duplicated by copying&lt;br /&gt;
        ios2_BufferManagement, io_Device, and io_Unit.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new command value is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   #define S2_SANA2HOOK 0xC008&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new error code is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   #define S2WERR_FUNCTIONS_MISSING 24 /* mandatory copy functions are missing */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_SETKEY ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_SETKEY -- Set an encryption key.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	Sets one of the encryption keys for the device. Note that&lt;br /&gt;
	ios2_StatData, if used, points to a byte array representing the RX&lt;br /&gt;
	RX counter value.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_WireError - Key index.&lt;br /&gt;
	ios2_PacketType - Encryption type (e.g. S2ENC_WEP).&lt;br /&gt;
	ios2_DataLength - Key length.&lt;br /&gt;
	ios2_Data - Key.&lt;br /&gt;
	ios2_StatData - RX counter number (NULL if unused).&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_SETOPTIONS ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_SETOPTIONS -- Set network options.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	Set various parameters for the network interface. This command&lt;br /&gt;
	should be called before going online to set any essential parameters&lt;br /&gt;
	not covered elsewhere. The S2INFO_#? tags used are defined in the&lt;br /&gt;
	devices/sana2wireless.h include file.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_Data - Pointer to a taglist that specifies parameters to use.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_TRACKTYPE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_TRACKTYPE -- Accumulate statistics about a packet type.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to accumulate statistics about&lt;br /&gt;
        a particular packet type. Packet type statistics, for the particular&lt;br /&gt;
        packet type, are zeroed by this command.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_TRACKTYPE.&lt;br /&gt;
        ios2_PacketType - Packet type of interest.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_UNTRACKTYPE, S2_GETTYPESTATS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_UNTRACKTYPE ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_UNTRACKTYPE -- End statistics about a packet type.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to stop accumulating&lt;br /&gt;
        statistics about a particular packet type.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_UNTRACKTYPE.&lt;br /&gt;
        ios2_PacketType - Packet type of interest.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - Zero if successful; non-zero otherwise.&lt;br /&gt;
        ios2_WireError  - More specific error number.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_TRACKTYPE, S2_GETTYPESTATS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== S2_WRITEMGMT ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
	S2_WRITEMGMT -- Write a management frame.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
	Writes a raw IEEE 802.11 management frame to the device. The buffer&lt;br /&gt;
	management mechanism used is simpler than that used for data&lt;br /&gt;
	packets (e.g. with CMD_WRITE): a buffer pointer and a length value&lt;br /&gt;
	are passed to the device.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
	ios2_DataLength - full frame length.&lt;br /&gt;
	ios2_Data - pointer to a complete IEEE 802.11 management frame.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
	io_Error - Zero if successful; non-zero otherwise.&lt;br /&gt;
	ios2_WireError - More specific error code.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Callback mechanisms =&lt;br /&gt;
&lt;br /&gt;
== CopyFromBuff ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CopyFromBuff -- Copy n bytes from an abstract data structure.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        success = CopyFromBuff(to, from, n)&lt;br /&gt;
        d0                     a0  a1    d0&lt;br /&gt;
&lt;br /&gt;
        BOOL CopyToBuff(VOID *, VOID *, ULONG);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function copies &#039;n&#039; bytes of data in the abstract data structure&lt;br /&gt;
        pointed to by &#039;from&#039; into the contigous memory pointed to by &#039;to&#039;.&lt;br /&gt;
        &#039;to&#039; must contain at least &#039;n&#039; bytes of usable memory or innocent&lt;br /&gt;
        memory will be overwritten.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        to              - pointer to contiguous memory to copy to.&lt;br /&gt;
        from            - pointer to abstract structure to copy from.&lt;br /&gt;
        n               - number of bytes to copy.&lt;br /&gt;
&lt;br /&gt;
   RESULT&lt;br /&gt;
        success         - TRUE if operation was successful, else FALSE.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This function must be callable from interrupts.  In particular, this&lt;br /&gt;
        means that this function may not directly or indirectly call any&lt;br /&gt;
        system memory functions (since those functions rely on Forbid() to&lt;br /&gt;
        protect themselves) and that  you must not compile this function&lt;br /&gt;
        with stack checking enabled.  See the Exec Interrupts&lt;br /&gt;
        chapter for more details on what is legal in a routine called from&lt;br /&gt;
        an interrupt handler.&lt;br /&gt;
&lt;br /&gt;
        &#039;C&#039; programmers should not compile with stack checking (option &#039;-v&#039;&lt;br /&gt;
        in SAS) and should geta4() or __saveds.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CopyToBuff ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        CopyToBuff -- Copy n bytes to an abstract data structure.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        success = CopyToBuff(to, from, n)&lt;br /&gt;
        d0                   a0  a1    d0&lt;br /&gt;
&lt;br /&gt;
        BOOL CopyToBuff(VOID *, VOID *, ULONG);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function first does any initialization and/or allocation&lt;br /&gt;
        required to prepare the abstract data structure pointed at by &#039;to&#039;&lt;br /&gt;
        to be filled with &#039;n&#039; bytes of data from &#039;from&#039;.  It then executes&lt;br /&gt;
        the copy operation.&lt;br /&gt;
&lt;br /&gt;
        If, for example, there is not enough memory available to prepare&lt;br /&gt;
        the abstract data structure, the call is failed and FALSE is returned.&lt;br /&gt;
&lt;br /&gt;
        The buffer management scheme should be such that any memory needed&lt;br /&gt;
        to fulfill CopyToBuff() calls is already allocated from the system&lt;br /&gt;
        before the call to CopyToBuff() is made.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        to              - pointer to abstract structure to copy to.&lt;br /&gt;
        from            - pointer to contiguous memory to copy from.&lt;br /&gt;
        n               - number of bytes to copy.&lt;br /&gt;
&lt;br /&gt;
   RESULT&lt;br /&gt;
        success         - TRUE if operation was successful, else FALSE.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This function must be callable from interrupts.  In particular, this&lt;br /&gt;
        means that this function may not directly or indirectly call any&lt;br /&gt;
        system memory functions (since those functions rely on Forbid() to&lt;br /&gt;
        protect themselves) and that you must not compile this function&lt;br /&gt;
        with stack checking enabled.  See the Exec Interrupts&lt;br /&gt;
        chapter for more details on what is legal in a routine called from&lt;br /&gt;
        an interrupt handler.&lt;br /&gt;
&lt;br /&gt;
        &#039;C&#039; programmers should not compile with stack checking (option &#039;-v&#039;&lt;br /&gt;
        in SAS) and should geta4() or __saveds.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== PacketFilter ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        PacketFilter -- Perform filtering operation on CMD_READ&#039;s.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        keep = PacketFilter(hook, ios2, data)&lt;br /&gt;
        d0                   a0    a2    a1&lt;br /&gt;
&lt;br /&gt;
        BOOL PacketFilter(struct Hook *, struct IOSana2Req *, APTR);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function (if supplied by a protocol stack) may be used to&lt;br /&gt;
        reject packets before they are copied into a protocol stack&#039;s&lt;br /&gt;
        internal buffers.&lt;br /&gt;
&lt;br /&gt;
        The IOSana2Req structure should be set up to look (almost) exactly&lt;br /&gt;
        as it would if it was successfully returned for the current packet.&lt;br /&gt;
        Specifically, the fields that should be set up correctly are:&lt;br /&gt;
&lt;br /&gt;
        ios2-&amp;amp;gt;ios2_DataLength&lt;br /&gt;
        ios2-&amp;amp;gt;ios2_SrcAddr&lt;br /&gt;
        ios2-&amp;amp;gt;ios2_DstAddr&lt;br /&gt;
&lt;br /&gt;
       The &amp;amp;quot;data&amp;amp;quot; pointer must point to the beginning of the packet data&lt;br /&gt;
        that is stored in contiguous memory.  The data should NOT include&lt;br /&gt;
        any hardware specific headers (unless of course the CMD_READ&lt;br /&gt;
        request wanted RAW packets).&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        hook            - pointer to the Hook originally supplied during&lt;br /&gt;
                          OpenDevice().&lt;br /&gt;
        ios2            - The IOSana2Req CMD_READ request that will be used&lt;br /&gt;
                          (the &amp;amp;quot;object&amp;amp;quot; of the Hook call).&lt;br /&gt;
        data            - The packet data (the &amp;amp;quot;message&amp;amp;quot; of the Hook call).&lt;br /&gt;
&lt;br /&gt;
   RESULT&lt;br /&gt;
        success         - TRUE if the driver should provide the packet to&lt;br /&gt;
                          the protocol stack, FALSE if the packet should be&lt;br /&gt;
                          ignored.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This function must be callable from interrupts.  In particular, this  &lt;br /&gt;
        means that this function may not directly or indirectly call any&lt;br /&gt;
        system memory functions (since those functions rely on Forbid() to&lt;br /&gt;
        protect themselves) and that  you must not compile this function&lt;br /&gt;
        with stack checking enabled.  See the Exec Interrupts&lt;br /&gt;
        chapter for more details on what is legal in a routine called from&lt;br /&gt;
        an interrupt handler.&lt;br /&gt;
&lt;br /&gt;
        &#039;C&#039; programmers should not compile with stack checking (option &#039;-v&#039;&lt;br /&gt;
        in SAS) and should geta4() or __saveds.&lt;br /&gt;
&lt;br /&gt;
        What does packet filtering do? With the original ``SANA-II Network&lt;br /&gt;
        Device Driver Specification&#039;&#039;, a protocol stack could open a device&lt;br /&gt;
        and ask for certain packet types. It got all the packets that matched&lt;br /&gt;
        this type. As it turned out, this could be mighty inefficient if there&lt;br /&gt;
        were packets that the protocol stack did not use at all. These would&lt;br /&gt;
        go into read processing of the protocol stack and waste CPU time even&lt;br /&gt;
        though they could have been easily identified on arrival.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Ethernet description =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#define S2WireType_Ethernet             1&lt;br /&gt;
&lt;br /&gt;
ios2_DataLength:&lt;br /&gt;
        valid ethernet packets have 64 to 1500 bytes of data.&lt;br /&gt;
&lt;br /&gt;
Address format:&lt;br /&gt;
        Ethernet addresses consist of 47 bits of address information and&lt;br /&gt;
        a 1 bit multicast flag. The standard for expressing ethernet&lt;br /&gt;
        addresses is as 6 bytes (octets) in the order in which the bytes&lt;br /&gt;
        are transmitted with the low-order bits in a byte transmitted&lt;br /&gt;
        first. The multicast flag bit is the least-significant bit of the&lt;br /&gt;
        first byte.&lt;br /&gt;
&lt;br /&gt;
        Ethernet addresses in a Sana2IOReq occupy the first 6 bytes of&lt;br /&gt;
        an address field in transmission order with the low-order bits in&lt;br /&gt;
        a byte transmitted first.&lt;br /&gt;
&lt;br /&gt;
Station Address:&lt;br /&gt;
        Each ethernet board must have a unique ethernet hardware address.&lt;br /&gt;
        Drivers will override any attempt to set the address to anything&lt;br /&gt;
        other than the ROM address.&lt;br /&gt;
&lt;br /&gt;
Raw reads and writes:&lt;br /&gt;
        6 bytes of destination address,&lt;br /&gt;
        6 bytes of source address,&lt;br /&gt;
        2 bytes of type,&lt;br /&gt;
        64 to 1500 bytes of data&lt;br /&gt;
        (followed by 4 byte CRC value covering all of the above&lt;br /&gt;
         which is hardware generated and checked, hence not included&lt;br /&gt;
         in even raw packets)&lt;br /&gt;
&lt;br /&gt;
Multicast:      Supported&lt;br /&gt;
&lt;br /&gt;
Broadcast:      Supported&lt;br /&gt;
&lt;br /&gt;
Promiscuous:    Supported&lt;br /&gt;
&lt;br /&gt;
Packet Type Numbers for Ethernet are assigned by:&lt;br /&gt;
&lt;br /&gt;
        Xerox Corporation&lt;br /&gt;
        Xerox Systems Institute&lt;br /&gt;
        475 Oakmead Parkway, Sunnyvale, CA 94086&lt;br /&gt;
        Attn: Ms. Fonda Pallone&lt;br /&gt;
        (408) 737-4652&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Some Common Packet Type Numbers:&lt;br /&gt;
&lt;br /&gt;
      decimal  Hex        Description&lt;br /&gt;
      -------  ---        -----------&lt;br /&gt;
         000   0000-05DC  IEEE 802.3 Length Field&lt;br /&gt;
        2048   0800       TCP/IP -- IP&lt;br /&gt;
        2054   0806       TCP/IP -- ARP&lt;br /&gt;
       32821   8035       TCP/IP -- RARP&lt;br /&gt;
       32923   809B       Appletalk&lt;br /&gt;
       33011   80F3       AppleTalk AARP (Kinetics)&lt;br /&gt;
       33100   814C       SNMP&lt;br /&gt;
       33079   8137-8138  Novell, Inc.&amp;lt;/pre&amp;gt;&lt;br /&gt;
= ARCNET description =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;S2WireType_Arcnet       7&lt;br /&gt;
&lt;br /&gt;
ios2_DataLength:&lt;br /&gt;
        506 byte MTU (because of the possibility of two byte Types).&lt;br /&gt;
        Packets of size 254, 255, or 256 bytes are padded to 257 bytes before&lt;br /&gt;
        transmition.&lt;br /&gt;
&lt;br /&gt;
Station Address:&lt;br /&gt;
        ARCNET hardware may have addresses set with jumpers, DIP switches&lt;br /&gt;
        or software.  Different drivers may therefore behave differently&lt;br /&gt;
        with S2_CONFIGINTERFACE.&lt;br /&gt;
&lt;br /&gt;
        Hardware addresses should be assigned by users from highest to lowest&lt;br /&gt;
        because there is some efficiency gained in the token passing scheme&lt;br /&gt;
        this way.  For example, on a three node network, hardware numbers 254,&lt;br /&gt;
        253 and 252 should be used rather than 1, 2 and 3.&lt;br /&gt;
&lt;br /&gt;
Raw reads and writes:&lt;br /&gt;
        Short Packets (1-253 bytes)&lt;br /&gt;
                Destination Address             (1 byte)&lt;br /&gt;
                Source Address                  (1 byte)&lt;br /&gt;
                Count (256-N-Type length)       (1 byte)&lt;br /&gt;
                Padding                         (to byte number Count)&lt;br /&gt;
                Type                            (1 or 2 bytes)&lt;br /&gt;
                Data                            (N bytes)&lt;br /&gt;
&lt;br /&gt;
        Long Packets (257-506 bytes)&lt;br /&gt;
                Destination Address             (1 byte)&lt;br /&gt;
                Source Address                  (1 byte)&lt;br /&gt;
                zero                            (1 byte)&lt;br /&gt;
                Count (512-N-Type length)       (1 byte)&lt;br /&gt;
                Padding                         (to byte number Count)&lt;br /&gt;
                Type                            (1 or 2 bytes)&lt;br /&gt;
                Data                            (N bytes)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Multicast:      Not Supported&lt;br /&gt;
&lt;br /&gt;
Broadcast:      Supported&lt;br /&gt;
&lt;br /&gt;
Promiscuous:    Generally Not Supported&lt;br /&gt;
&lt;br /&gt;
Packet Type Numbers for ARCNET are assigned by:&lt;br /&gt;
        Datapoint Corporation&lt;br /&gt;
&lt;br /&gt;
Some Common Packet Type Numbers&lt;br /&gt;
&lt;br /&gt;
        decimal  hex    description&lt;br /&gt;
        -------  ---    -----------&lt;br /&gt;
        221      DD     AppleTalk&lt;br /&gt;
        240      F0     TCP/IP -- IP   (RFC 1051)&lt;br /&gt;
        241      F1     TCP/IP -- ARP  (RFC 1051)&lt;br /&gt;
        212      F0     TCP/IP    IP   (RFC 1201, proposed)&lt;br /&gt;
        213      F1     TCP/IP -- ARP  (RFC 1201, proposed)&lt;br /&gt;
        214      D6     TCP/IP -- RARP (RFC 1201, proposed)&lt;br /&gt;
        247      F7     Banyan Vines&lt;br /&gt;
        250      FA     Novell IPX&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Authors =&lt;br /&gt;
&lt;br /&gt;
== Revision 2 &amp;amp; 3 ==&lt;br /&gt;
&lt;br /&gt;
Copyright © 1992-2000 Amiga, Inc. All Rights Reserved&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Amiga is a registered trademark of Amiga, Inc. Ethernet is a trademark of Xerox Corporation. ARCNET is a trademark of Datapoint Corporation. DECNet is a trademark of Digital Equipment Corporation. AppleTalk is a trademark of Apple Computer, Inc.&lt;br /&gt;
&lt;br /&gt;
== Revision 4 ==&lt;br /&gt;
&lt;br /&gt;
Extending the SANA-II network driver specification&amp;lt;br /&amp;gt;&lt;br /&gt;
by Olaf Barthel&lt;br /&gt;
&lt;br /&gt;
(Last updated 02-Mar-2003)&lt;br /&gt;
&lt;br /&gt;
== Revision 5 ==&lt;br /&gt;
&lt;br /&gt;
Hook-based callback function extensions for SANA-II (SANA-IIR5) &amp;lt;br /&amp;gt;&lt;br /&gt;
by Olaf Barthel and Heinz Wrobel&lt;br /&gt;
&lt;br /&gt;
== Revision 6 ==&lt;br /&gt;
&lt;br /&gt;
SANA-II IEEE 802.11 wireless API &amp;lt;br/&amp;gt;&lt;br /&gt;
by Neil Cafferkey&lt;br /&gt;
&lt;br /&gt;
= Acknowledgments =&lt;br /&gt;
&lt;br /&gt;
== Revision 2 &amp;amp; 3 ==&lt;br /&gt;
&lt;br /&gt;
Many people and companies have contributed to the &amp;quot;SANA-II Network Device Driver Specification&amp;quot;. The original SANA-II Autodocs and includes were put together by Ray Brand, Perry Kivolowitz (ASDG) and Martin Hunt. Those original documents evolved to their current state and grew to include this document at the hands of Dale Larson and Greg Miller. Brian Jackson and John Orr provided valuable editing. Randell Jesup has provided sage advice on several occasions. The buffer management callback mechanism was his idea. Dale Luck (GfxBase) and Rick Spanbauer (Ameristar Technologies) have provided valuable comments throughout the process. Nicolas Benezan (ADONIS) provided many detailed and useful comments on weaknesses in late drafts of the specification. The enhancements for better buffer management, clarifications and notes for device implementers were added to the specification by Heinz Wrobel whilst consulting for Amiga Technologies GmbH, yielding revision 3.0 of the specification.&lt;br /&gt;
&lt;br /&gt;
Thanks to all the above and the numerous others who have contributed with their comments, questions and discussions.&lt;br /&gt;
&lt;br /&gt;
= Changes =&lt;br /&gt;
&lt;br /&gt;
== Revision 4 ==&lt;br /&gt;
&lt;br /&gt;
Changes since 24-Dec-2002:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure used by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command now must remain valid until the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command is sent (see [[#2.4|section 2.4]]).&lt;br /&gt;
&lt;br /&gt;
Changes since 01-May-2002:&lt;br /&gt;
&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;ppp.async.readrequests&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ppp.async.eof&amp;lt;/tt&amp;gt; configuration keywords.&lt;br /&gt;
&lt;br /&gt;
Changes since 04-Jan-2002:&lt;br /&gt;
&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;ppp.dummyremoteaddress&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ppp.ethernet.ac&amp;lt;/tt&amp;gt; configuration keywords.&lt;br /&gt;
&lt;br /&gt;
Changes since 10-Dec-2001:&lt;br /&gt;
&lt;br /&gt;
* Converted to HTML format.&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;&amp;amp;lt;devices/sana2.h&amp;amp;gt;&amp;lt;/tt&amp;gt; header file to the appendix.&lt;br /&gt;
* The memory alignment for the &amp;lt;tt&amp;gt;S2_DMACopyToBuff64&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DMACopyFromBuff64&amp;lt;/tt&amp;gt; hooks refers to bits and not to bytes.&lt;br /&gt;
&lt;br /&gt;
Changes since 19-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Added to the list of reserved configuration keywords (&amp;lt;tt&amp;gt;ppp.idletimeout&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ppp.peeridletimeout&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ppp.sendid&amp;lt;/tt&amp;gt;).&lt;br /&gt;
* Renamed the fields of the &amp;lt;tt&amp;gt;Sana2ExtDeviceStats&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
* Clarified that the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type is a big endian integer.&lt;br /&gt;
* More clarifications for the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; commands.&lt;br /&gt;
* Updated the discussion of the &amp;lt;tt&amp;gt;Sana2DeviceQuery.RawMTU&amp;lt;/tt&amp;gt; field, clarifying what is included in the the Ethernet &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Updated the &amp;lt;tt&amp;gt;&amp;amp;lt;devices/sana2.h&amp;amp;gt;&amp;lt;/tt&amp;gt; header file. Note that there is no equivalent &amp;lt;tt&amp;gt;&amp;amp;quot;devices/sana2.i&amp;amp;quot;&amp;lt;/tt&amp;gt; header file yet.&lt;br /&gt;
&lt;br /&gt;
Changes since 12-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Changed the command numbers of &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT&amp;lt;/tt&amp;gt; to be NSD-compliant. Also assigned a new number to the &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT &amp;lt;/tt&amp;gt; command.&lt;br /&gt;
* Added section 4 (&amp;amp;quot;Extensions for existing commands&amp;amp;quot;).&lt;br /&gt;
* Added the last paragraph to section 9, relating to the future extension of the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
&lt;br /&gt;
Changes since 03-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Renamed &amp;lt;tt&amp;gt;S2_GETNEWGLOBALSTATS&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt; (see [[#2.3|section 2.3]]).&lt;br /&gt;
* The &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt; now uses 64 bit quantities for the &amp;lt;tt&amp;gt;s2xds_PacketsReceived&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_PacketsSent&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_BadData&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_Overruns&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_UnknownTypesReceived&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_Reconfigurations&amp;lt;/tt&amp;gt; counters.&lt;br /&gt;
* In section 2.6 the &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT&amp;lt;/tt&amp;gt; command was modified to use 64 bit integers for all members of the &amp;lt;tt&amp;gt;Sana2ThroughputStats&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
* All proposed commands are now listed with their numeric IDs.&lt;br /&gt;
* The command autodocs specifically mention the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field.&lt;br /&gt;
* All references to &amp;lt;tt&amp;gt;ios2_Error&amp;lt;/tt&amp;gt; have been replaced with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* In section 7 the use of the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; error codes is clarified.&lt;br /&gt;
* Section 7 takes a more detailed look at safe default values returned by the query commands.&lt;br /&gt;
* Inserted section 3 (&amp;amp;quot;Annotations for existing commands&amp;amp;quot;).&lt;br /&gt;
* The &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; commands now specifically mention the life time of the data they have to deal with.&lt;br /&gt;
&lt;br /&gt;
Changes since 14-Oct-2001:&lt;br /&gt;
&lt;br /&gt;
* Added &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; commands.&lt;br /&gt;
* Added &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; events.&lt;br /&gt;
* Added section 4 (&amp;amp;quot;New wire error codes&amp;amp;quot;).&lt;br /&gt;
* In section 5.2 the originally proposed log callback function has been wrapped into a standard Hook structure.&lt;br /&gt;
* Removed item on &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; from section 7 (&amp;amp;quot;Unsolved problems&amp;amp;quot;).&lt;br /&gt;
* Added section 8 (&amp;amp;quot;Changes&amp;amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Revision 5 ==&lt;br /&gt;
&lt;br /&gt;
22-Mar-2004:&lt;br /&gt;
&lt;br /&gt;
* Conversion to HTML&lt;br /&gt;
&lt;br /&gt;
21-Jan-2004:&lt;br /&gt;
&lt;br /&gt;
* Added clarifications for &amp;lt;tt&amp;gt;schm_MsgSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_MsgSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Priority&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Name&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2h_Hook&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Updated the &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; documentation.&lt;br /&gt;
* Shortened section 4.&lt;br /&gt;
* Updated section 5.&lt;br /&gt;
&lt;br /&gt;
30-Nov-2003:&lt;br /&gt;
&lt;br /&gt;
* Extended the applicability of the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; to logging.&lt;br /&gt;
* Renamed &amp;lt;tt&amp;gt;S2_COPYHOOK&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt;, which matches the extended scope it should cover.&lt;br /&gt;
* The message passed to the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; now includes a size field which holds the size of the message, expressed in bytes.&lt;br /&gt;
&lt;br /&gt;
06-Oct-2003:&lt;br /&gt;
&lt;br /&gt;
* Replaced the list of new tag items with a single &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;, which is installed through a new command.&lt;br /&gt;
* Added &amp;amp;quot;Caveats&amp;amp;quot; and &amp;amp;quot;Changes&amp;amp;quot; sections&lt;br /&gt;
* Removed the documentation for the originally proposed &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; (sections 3.2.1 through 3.2.10)&lt;br /&gt;
&lt;br /&gt;
= History =&lt;br /&gt;
&lt;br /&gt;
== Revision 2 and 3 ==&lt;br /&gt;
&lt;br /&gt;
This standard has undergone several drafts with long periods for comment from developers and the Amiga community at large. These drafts include a UseNet release which was also distributed on the Fish Disks in June, 1991 (as well as published in the &#039;91 DevCon notes), and the November 7 Draft for Final Comment and Approval distributed via Bix, ADSP and UseNet. There were also several intermediate drafts with more limited distribution.&lt;br /&gt;
&lt;br /&gt;
== Revision 4 ==&lt;br /&gt;
&lt;br /&gt;
(Olaf Barthel)&lt;br /&gt;
&lt;br /&gt;
I&#039;ve been working on a TCP/IP stack and PPP drivers to go with them for a while and found that there were some things that SANA-II did specifically not address, and which ought to be covered by it. In other areas clarification was needed. Also, discussions I had with Harald Frank suggested that the extensions made by Heinz Wrobel and Stefan Sticht in the SANA-IIR3 specification could need extending.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Layers_Library&amp;diff=12561</id>
		<title>Layers Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Layers_Library&amp;diff=12561"/>
		<updated>2025-01-26T19:34:52Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP}}&lt;br /&gt;
== Layers Library ==&lt;br /&gt;
&lt;br /&gt;
This article describes the layers library which provides routines that are used to manage overlapping rectangular drawing areas that share a common display. Intuition uses the layers library to manage its system of windows.&lt;br /&gt;
&lt;br /&gt;
The Graphics Library describes the use of &#039;&#039;regions&#039;&#039;, special structures used to mask off areas where drawing can take place. Regions are installed through the layers library function InstallClipRegion() but the routines for the creation, disposal and manipulation of regions are part of the graphics library.&lt;br /&gt;
&lt;br /&gt;
== Layers ==&lt;br /&gt;
&lt;br /&gt;
The concept of a &#039;&#039;layer&#039;&#039; is closely tied to Intuition windows. A layer is a rectangular drawing area. A layer can overlap other layers and has a display priority that determines whether it will appear in front or behind other layers. Every Intuition window has an associated Layer structure. Layers allow Intuition and application programs to :&lt;br /&gt;
&lt;br /&gt;
* Share a display&#039;s BitMap among various tasks in an orderly way by creating layers, separate drawing rectangles, within the BitMap.&lt;br /&gt;
* Move, size or depth-arrange a layer while automatically keeping track of which portions of other layers are hidden or revealed by the operation.&lt;br /&gt;
* Manage the remapping of coordinates, so the application need not track the layer&#039;s offset into the BitMap.&lt;br /&gt;
* Maintain each layer as a separate entity, which may optionally have its own BitMap.&lt;br /&gt;
* Automatically update same newly visible portions.&lt;br /&gt;
&lt;br /&gt;
The layers library takes care of housekeeping: the low level, repetitive tasks which are required to keep track of where to place bits. The layers library also provides a locking mechanism which coordinates display updating when multiple tasks are drawing graphics to layers. The windowing environment provided by the Intuition library is largely based on layers.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=WARNING|text=Layers may not be created or used directly with Intuition screens. Intuition windows are the only supported method of adding layers to Intuition screens. &#039;&#039;Only&#039;&#039; the layer locking and unlocking functions are safe to use with Intuition. An application must create and manage its own View if it will be creating layers directly on the display.}}&lt;br /&gt;
&lt;br /&gt;
=== The Layer Structure ===&lt;br /&gt;
&lt;br /&gt;
The internal representation of layers is essentially a set of clipping rectangles. Each layer is represented by an instance of the Layer structure. All the layers in a display are linked together through the Layer_Info structure. Any display shared by multiple layers (such as an Intuition screen) requires one Layer_Info data structure to handle interactions between the various layers.&lt;br /&gt;
&lt;br /&gt;
Here is a partial listing of the Layer structure from &amp;amp;lt;graphics/clip.h&amp;amp;gt;. (For a complete listing refer to the SDK.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer&lt;br /&gt;
{&lt;br /&gt;
    struct  Layer *front,*back;&lt;br /&gt;
    struct  ClipRect    *ClipRect;  /* read by ROMs to find first cliprect */&lt;br /&gt;
    struct  RastPort    *rp;&lt;br /&gt;
    struct  Rectangle   bounds;&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
    UWORD   Flags;                  /* obscured ?, Virtual BitMap? */&lt;br /&gt;
    struct  BitMap *SuperBitMap;&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
    struct  Region  *DamageList;    /* list of rectangles to refresh through */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The Layer Structure is Read-Only|text=Applications should never directly modify any of the elements of the Layer structure. In addition, applications should only read the front, back, rp, bounds, Flags, SuperBitMap and DamageList elements of the Layer structure. (Some of these elements are subject to dynamic change by the system so proper layer locking procedures must be followed when relying on what the application has read.)}}&lt;br /&gt;
&lt;br /&gt;
=== The Layer&#039;s RastPort ===&lt;br /&gt;
&lt;br /&gt;
When a layer is created, a RastPort is automatically to go along with it. The pointer to the RastPort is contained in the layer data structure. Using this RastPort, the application may draw anywhere into the layer&#039;s bounds rectangle. If the application tries to draw outside of this rectangle, the graphics routines will clip the graphics.&lt;br /&gt;
&lt;br /&gt;
Here is sample code showing how to access the layer&#039;s RastPort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RastPort *myRPort;    /* allocate a RastPort pointer for each layer */&lt;br /&gt;
&lt;br /&gt;
myRPort = layer-&amp;gt;rp;&lt;br /&gt;
&lt;br /&gt;
/* The layer&#039;s RastPort may be used with any of the graphics library calls&lt;br /&gt;
** that require this structure.  For instance, to fill layer with color:&lt;br /&gt;
*/&lt;br /&gt;
IGraphics-&amp;gt;SetRast(layer-&amp;gt;rp, color);&lt;br /&gt;
&lt;br /&gt;
/* set up for writing text into layer */&lt;br /&gt;
IGraphics-&amp;gt;SetDrMd(layer-&amp;gt;rp, JAM1);&lt;br /&gt;
IGraphics-&amp;gt;SetAPen(layer-&amp;gt;rp, 0);&lt;br /&gt;
IGraphics-&amp;gt;Move(layer-&amp;gt;rp, 5, 7);&lt;br /&gt;
&lt;br /&gt;
/* write into layer */&lt;br /&gt;
IGraphics-&amp;gt;Text(layer-&amp;gt;rp, string, strlen(string));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Types of Layers ===&lt;br /&gt;
&lt;br /&gt;
The layers library supports three types of layers: simple refresh, smart refresh and super bitmap. The type of the layer, specified by the Flags field in the Layers structure, determines what facilities the layer provides.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Use Only One Layer Type Flag|text=The three layer-type Flags are mutually exclusive. That is, only one layer-type flag (LAYERSIMPLE, LAYERSMART and LAYERSUPER) should be specified.}}&lt;br /&gt;
&lt;br /&gt;
==== Simple Refresh Layer ====&lt;br /&gt;
&lt;br /&gt;
When an application draws into the layer, any portion of the layer that is visible (not obscured) will be rendered into the common BitMap of the viewing area. All graphics rendering routines are &amp;quot;clipped&amp;quot; so that only exposed sections of the layer are drawn into. No back-up of obscured areas is provided.&lt;br /&gt;
&lt;br /&gt;
If another layer operation is performed that causes an obscured part of a simple refresh layer to be exposed, the application must determine if the section need be refreshed, re-drawing the newly exposed part of the layer as required.&lt;br /&gt;
&lt;br /&gt;
The basic advantage of simple refresh is that it does not require back-up area to save drawing sections that cannot be seen, saving memory. However, the application needs to monitor the layer to see if it needs refreshing. This is typically performed with statements like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (layer-&amp;gt;Flags &amp;amp; LAYERREFRESH)&lt;br /&gt;
    refresh(layer);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When an application restores the layer by performing a full-layer redraw, only the damaged areas are redrawn, making the operation fairly time efficient.&lt;br /&gt;
&lt;br /&gt;
==== Smart Refresh Layer ====&lt;br /&gt;
&lt;br /&gt;
Under smart refresh, the system provides dynamic backup of obscured sections of the layer. The graphics routines will automatically draw into these backup areas when they encounter an obscured part of the layer. The backup memory will be used to automatically update the display when obscured sections later become exposed.&lt;br /&gt;
&lt;br /&gt;
With smart refresh layers, the system handles all of the refresh requirements of the layer, except when the layer is made larger. When parts of the layer are exposed by a sizing operation, the application must refresh the newly exposed areas.&lt;br /&gt;
&lt;br /&gt;
The advantage of smart refresh is the speed of updating exposed regions of the layer and the ability of the system to manage part of the updating process for the application.. Its main disadvantage is the additional memory required to handle this automatic refresh.&lt;br /&gt;
&lt;br /&gt;
==== Super Bitmap Layer ====&lt;br /&gt;
&lt;br /&gt;
A super bitmap layer is similar to a smart refresh layer. It too has a back-up area for rendering graphics for currently obscured parts of the display. Whenever an obscured area is made visible, the corresponding part of the backup area is copied to the display automatically.&lt;br /&gt;
&lt;br /&gt;
However, it differs from smart refresh in that:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The back-up BitMap is user-supplied, rather than being allocated dynamically by the system.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The back-up BitMap may be as large or larger than the the current size of the layer. It may also be larger than the maximum size of the layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see a larger portion of a super bitmap on-display, use SizeLayer(). To see a different portion of the super bitmap in the layer, use ScrollLayer().&lt;br /&gt;
&lt;br /&gt;
When the graphics routines perform drawing commands, part of the drawing appears in the common BitMap (the on-display portion). Any drawing outside the displayed portion itself is rendered into the super bitmap. When the layer is scrolled or sized, the layer contents are copied into the super bitmap, the scroll or size positioning is modified, and the appropriate portions are then copied back into the layer. (Refer to the graphics library functions SyncSBitMap() and CopySBitMap().&lt;br /&gt;
&lt;br /&gt;
==== Backdrop Layer ====&lt;br /&gt;
&lt;br /&gt;
A layer of any type may be designated a backdrop layer which will always appear behind all other layers. They may not be moved, sized, or depth-arranged. Non-backdrop layers will always remain in front of backdrop layers regardless of how the non-backdrop layer is moved, sized or depth-arranged.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Layers Library ===&lt;br /&gt;
&lt;br /&gt;
Like all libraries, the layers library must be opened before it may be used. Check the Layers Autodocs to determine what version of the library is required for any particular Layers function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
&lt;br /&gt;
struct LayersIFace *ILayers = (struct LayersIFace*)IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
if (ILayers != NULL)&lt;br /&gt;
    {&lt;br /&gt;
    /* use Layers library */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface*)ILayers);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Working With Existing Layers ===&lt;br /&gt;
&lt;br /&gt;
A common operation performed by applications is to render text or graphics into an existing layer such as an Intuition window. To prevent Intuition from changing the layer (for instance when the user resizes or moves the window) during a series of graphic operations, the layers library provides locking functions for obtaining exclusive access to a layer.&lt;br /&gt;
&lt;br /&gt;
These locking functions are also useful for applications that create their own layers if the application has more than one task operating on the layers asynchronously. These calls coordinate multiple access to layers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Functions for Intertask Control of Layers (Layers Library)&lt;br /&gt;
| LockLayer()&lt;br /&gt;
| Lock out rendering in a single layer.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayer()&lt;br /&gt;
| Release LockLayer() lock.&lt;br /&gt;
|-&lt;br /&gt;
| LockLayers()&lt;br /&gt;
| Lock out rendering in all layers of a display.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayers()&lt;br /&gt;
| Release LockLayers() lock.&lt;br /&gt;
|-&lt;br /&gt;
| LockLayerInfo()&lt;br /&gt;
| Gain exclusive access to the display&#039;s layers.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerInfo()&lt;br /&gt;
| Release LockLayerInfo() lock.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The following routines from the graphics library also allow multitasking access to layer structures:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Functions for Intertask Control of Layers (Graphics Library)&lt;br /&gt;
| LockLayerRom()&lt;br /&gt;
| Same as LockLayer(), from layers library.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerRom()&lt;br /&gt;
| Release LockLayerRom() lock.&lt;br /&gt;
|-&lt;br /&gt;
| AttemptLockLayerRom()&lt;br /&gt;
| Lock layer only if it is immediately available.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These functions are similar to the layers LockLayer() and UnlockLayer() functions, but do not require the layers library to be open. See the SDK for details.&lt;br /&gt;
&lt;br /&gt;
==== Intertask Operations ====&lt;br /&gt;
&lt;br /&gt;
If multiple tasks are manipulating layers on the same display they will be sharing a Layer_Info structure and their use of it and its related data structures need to be coordinated. To ensure that a structure remains cohesive, it should be operated on by only one task at a time. The Layer_Info encompasses all the layers existing on a single display.&lt;br /&gt;
&lt;br /&gt;
LockLayerInfo() must be called whenever the visible portions of layers may be affected, or when the Layer_Info structure is changed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID LockLayerInfo( struct Layer_Info *li );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The lock should be obtained whenever a layer is created, deleted sized or moved, as the list of layers that is being managed by the Layer_Info data structure must be updated.&lt;br /&gt;
&lt;br /&gt;
It is not necessary to lock the Layer_Info data structure while rendering, or when calling routines like ScrollLayer(), because layer sizes and on-display positions are not being affected.&lt;br /&gt;
&lt;br /&gt;
Use UnlockLayerInfo() when you have finished the layer operation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID UnlockLayerInfo( struct Layer_Info *li );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t unlock the Layer_Info then any other task calling LockLayerInfo() on the same Layer_Info structure will be blocked creating a potential deadlock situation.&lt;br /&gt;
&lt;br /&gt;
In addition to locking the Layer_Info structure, the layer itself should be locked if it is shared between tasks so that only one task at a time renders graphics to it. LockLayer() is used to get exclusive graphics output to a layer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID LockLayer( long dummy, struct Layer *layer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a graphics function is in process, the lock will return when the function is completed. Other tasks are blocked only if they attempt to draw graphics into this layer, or try to obtain a lock on this layer. The MoveLayer(), SizeLayer() and ScrollLayer() functions automatically lock and unlock the layer they operate on.&lt;br /&gt;
&lt;br /&gt;
UnlockLayer() should be used after the graphics operation to make the layer available to other tasks again.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID UnlockLayer( struct Layer *layer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If more than one layer must be locked, then the LockLayer() calls should be surrounded by LockLayerInfo() and UnlockLayerInfo() calls, to prevent deadlock situations.&lt;br /&gt;
&lt;br /&gt;
The layers library provides two additional functions, LockLayers() and UnlockLayers(), for locking multiple layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID LockLayers( struct Layer_Info *li );&lt;br /&gt;
VOID UnlockLayers( struct Layer_Info *li );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
LockLayers() is used to lock all layers in a single command. UnlockLayers() releases the layers lock. The system calls these routines during the BehindLayer(), UpfrontLayer() and MoveLayerInFrontOf() operations (described below).&lt;br /&gt;
&lt;br /&gt;
==== Determining Layer Position ====&lt;br /&gt;
&lt;br /&gt;
If the viewing area has been separated into several layers, the application may need to find out which layer is topmost at a particular &#039;&#039;x,y&#039;&#039; coordinate. Use the WhichLayer() function for this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer *WhichLayer( struct Layer_Info *li, int32 x, int32 y );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To be sure that no task adds, deletes, or changes the sequence of layers before this information is used, call LockLayerInfo() before calling WhichLayer(), and call UnlockLayerInfo() when the operation is complete. In this way, the program may ensure that it is acting on valid information. Always check for a NULL return value (coordinate not in a layer) from WhichLayer().&lt;br /&gt;
&lt;br /&gt;
=== Creating and Using New Layers ===&lt;br /&gt;
&lt;br /&gt;
The functions described in this section are generally not safe to use with Intuition. To create new layers for Intuition you use Intuition window calls (see [[Intuition_Windows|Intuition Windows]]).&lt;br /&gt;
&lt;br /&gt;
Only applications that create and mange their own View will be able to call the layer creation and updating functions discussed here.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Functions for Creating and Updating Layers&lt;br /&gt;
| NewLayerInfo() || Allocating a Layer_Info structure.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeLayerInfo() || Deallocating a Layer_Info structure.&lt;br /&gt;
|-&lt;br /&gt;
| CreateUpfrontLayer() || Make a new layer in front of others.&lt;br /&gt;
|-&lt;br /&gt;
| CreateBehindLayer() || Make a new layer behind others.&lt;br /&gt;
|-&lt;br /&gt;
| DeleteLayer() || Remove and delete an existing layer.&lt;br /&gt;
|-&lt;br /&gt;
| MoveLayer() || Change the position (not depth) of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| SizeLayer() || Change the size of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| ScrollLayer() || Change the internal coordinates of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| BehindLayer() || Depth arrange a layer behind others.&lt;br /&gt;
|-&lt;br /&gt;
| UpfrontLayer() || Depth arrange a layer in front of others.&lt;br /&gt;
|-&lt;br /&gt;
| MoveLayerInFrontOf() || Depth arrange a layer to a specific position.&lt;br /&gt;
|-&lt;br /&gt;
| SwapBitsRastPortClipRect() || Fast, non-layered and non-damaging display operation.&lt;br /&gt;
|-&lt;br /&gt;
| BeginUpdate() || Synchronize optimized refreshing for layer.&lt;br /&gt;
|-&lt;br /&gt;
| EndUpdate() || End optimized layer refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Creating a Viewing Workspace ====&lt;br /&gt;
&lt;br /&gt;
A viewing workspace may be created by using the primitives InitVPort(), InitView(), MakeVPort(), MrgCop(), and LoadView(). Please reference [[Graphics_Primitives|Graphics Primitives]] for details on creating a low-level graphics display. Do not create Layers directly on Intuition screens. Windows are the only supported method of creating a layer on a screen.&lt;br /&gt;
&lt;br /&gt;
==== Creating the Layers ====&lt;br /&gt;
&lt;br /&gt;
The application must first allocate and initialize a Layer_Info data structure which the system uses to keep track of layers that are created, use statements like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer_Info *theLayerInfo;&lt;br /&gt;
&lt;br /&gt;
if (NULL != (theLayerInfo = ILayers-&amp;gt;NewLayerInfo()))&lt;br /&gt;
    {&lt;br /&gt;
    /* use Layer_Info */&lt;br /&gt;
&lt;br /&gt;
    ILayers-&amp;gt;DisposeLayerInfo(theLayerInfo);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Layers may be created in the common bit map by calling CreateUpfrontLayer() or CreateBehindLayer(), with a sequence such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer      *layer;&lt;br /&gt;
struct Layer_Info *theLayerInfo;&lt;br /&gt;
struct BitMap     *theBitMap;&lt;br /&gt;
&lt;br /&gt;
/* requests construction of a smart refresh layer. */&lt;br /&gt;
if (NULL == (layer = ILayers-&amp;gt;CreateUpfrontLayer(theLayerInfo, theBitMap, 20, 20, 100, 80, LAYERSMART, NULL)))&lt;br /&gt;
    error(&amp;quot;CreateUpfrontLayer() failed.&amp;quot;);&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    ; /* layer successfully created here. */&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Allocating and Deallocating Layer_Info ====&lt;br /&gt;
&lt;br /&gt;
Use NewLayerInfo() to allocate and initialize a Layer_Info structure and associated sub-structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer_Info *NewLayerInfo( VOID );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You &#039;&#039;must&#039;&#039; call this function before attempting to use any of the other layers functions described below. When you have finished with a Layer_Info structure, use DisposeLayerInfo() to deallocate it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID DisposeLayerInfo( struct Layer_Info *li );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function deallocates a Layer_Info and associated structures previously allocated with NewLayerInfo().&lt;br /&gt;
&lt;br /&gt;
==== Allocating and Deallocating Layers ====&lt;br /&gt;
&lt;br /&gt;
Layers are created using the routines CreateUpfrontLayer() and CreateBehindLayer(). CreateUpfrontLayer() creates a layer that will appear in front of any existing layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer *CreateUpfrontLayer( struct Layer_Info *li, struct BitMap *bm, int32 x0, int32 y0, int32 x1, int32 y1, int32 flags, struct BitMap *bm2 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CreateBehindLayer() creates a layer that appears behind existing layers, but in front of backdrop layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Layer *CreateBehindLayer( struct Layer_Info *li, struct BitMap *bm, int32 x0, int32 y0, int32 x1, int32 y1, int32 flags, struct BitMap *bm2 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both of these routines return a pointer to a Layer data structure (as defined in the include file &amp;amp;lt;graphics/layers.h&amp;amp;gt;), or NULL if the operation was unsuccessful.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A New Layer Also Gets a RastPort|text=When a layer is created, the routine automatically creates a RastPort to go along with it. If the layer&#039;s RastPort is passed to the drawing routines, drawing will be restricted to the layer. See &amp;quot;The Layer&#039;s RastPort&amp;quot; section above.}}&lt;br /&gt;
&lt;br /&gt;
Use the DeleteLayer() call to remove a layer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 DeleteLayer( int32 dummy, struct Layer *layer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DeleteLayer() removes a layer from the layer list and frees the memory allocated by the layer creation calls listed above.&lt;br /&gt;
&lt;br /&gt;
==== Moving and Sizing Layers ====&lt;br /&gt;
&lt;br /&gt;
The layers library includes three functions for moving and sizing layers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 MoveLayer( int32 dummy, struct Layer *layer, int32 dx, int32 dy );&lt;br /&gt;
int32 SizeLayer( int32 dummy, struct Layer *layer, int32 dx, int32 dy );&lt;br /&gt;
int32 MoveSizeLayer( struct Layer *layer, int32 dx, int32 dy, int32 dw, int32 dh);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
MoveLayer() moves a layer to a new position relative to its current position. SizeLayer() changes the size of a layer by modifying the coordinates of the lower right corner of the layer. MoveSizeLayer() changes both the size and position of a layer in a single call.&lt;br /&gt;
&lt;br /&gt;
==== Changing a Viewpoint ====&lt;br /&gt;
&lt;br /&gt;
The ScrollLayer() function changes the portion of a super bitmap that is shown by a layer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID ScrollLayer( int32 dummy, struct Layer *layer, int32 dx, int32 dy );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function is most useful with super bitmap layers but can also simulate the effect on other layer types by adding the scroll offset to all future rendering.&lt;br /&gt;
&lt;br /&gt;
==== Reordering Layers ====&lt;br /&gt;
&lt;br /&gt;
The layers library provides three function calls for reordering layers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 BehindLayer ( int32 dummy, struct Layer *layer );&lt;br /&gt;
int32 UpfrontLayer( int32 dummy, struct Layer *layer );&lt;br /&gt;
int32 MoveLayerInFrontOf( struct Layer *layer_to_move, struct Layer *other_layer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BehindLayer() moves a layer behind all other layers. This function considers any backdrop layers, moving a current layer behind all others except backdrop layers. UpfrontLayer() moves a layer in front of all other layers. MoveLayerInFrontOf() is used to place a layer at a specific depth, just in front of a given layer.&lt;br /&gt;
&lt;br /&gt;
As areas of simple refresh layers become exposed, due to layer movement or sizing for example, the newly exposed areas have not been drawn into, and need refreshing. The system keeps track of these areas by using a DamageList. To update only those areas that need it, the BeginUpdate() EndUpdate() functions are called.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 BeginUpdate( struct Layer *l );&lt;br /&gt;
VOID EndUpdate  ( struct Layer *layer, uint32 flag );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BeginUpdate() saves the pointer to the current clipping rectangles and installs a pointer to a set of ClipRects generated from the DamageList in the layer structure. To repair the layer, use the graphics rendering routines as if to redraw the entire layer, and the routines will automatically use the new clipping rectangle list. So, only the damaged areas are actually rendered into, saving time.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Never Modify the DamageList|text=The system generates and maintains the DamageList region. All application clipping should be done through the InstallClipRegion() function.}}&lt;br /&gt;
&lt;br /&gt;
To complete the update process call EndUpdate() which will restore the original ClipRect list.&lt;br /&gt;
&lt;br /&gt;
==== Sub-Layer Rectangle Operations ====&lt;br /&gt;
&lt;br /&gt;
The SwapBitsRastPortClipRect() routine is for applications that do not want to worry about clipping rectangles.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID SwapBitsRastPortClipRect( struct RastPort *rp, struct ClipRect *cr );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For instance, you may use The SwapBitsRastPortClipRect() to produce a menu without using Intuition. There are two ways to produce such a menu:&lt;br /&gt;
&lt;br /&gt;
# Create an up-front layer with CreateUpfrontLayer(), then render the menu in it. This could use lots of memory and require a lot of (very temporary) &amp;quot;slice-and-dice&amp;quot; operations to create all of the clipping rectangles for the existing windows and so on.&lt;br /&gt;
# Use SwapBitsRastPortClipRect(), directly on the display drawing area:&lt;br /&gt;
#* Render the menu in a back-up area off the display, then lock all of the on-display layers so that no task may use graphics routines to draw over the menu area on the display.&lt;br /&gt;
#* Next, swap the on-display bits with the off-display bits, making the menu appear.&lt;br /&gt;
#* When finished with the menu, swap again and unlock the layers.&lt;br /&gt;
&lt;br /&gt;
The second method is faster and leaves the clipping rectangles and most of the rest of the window data structures untouched.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=All of the layers must be locked while the menu is visible if you use the second method above. Any task that is using any of the layers for graphics output will be halted while the menu operations are taking place. If, on the other hand, the menu is rendered as a layer, no task need be halted while the menu is up because the lower layers need not be locked.}}&lt;br /&gt;
&lt;br /&gt;
=== Layers Example ===&lt;br /&gt;
&lt;br /&gt;
For the sake of brevity, the example is a single task. No Layer locking is done. Also note that the routine myLabelLayer() is used to redraw a given layer. It is called only when a layer needs refreshing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Layers.c */&lt;br /&gt;
&lt;br /&gt;
/* Force use of new variable names to help prevent errors */&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfxbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/layers.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define L_DELAY  (100)&lt;br /&gt;
#define S_DELAY   (50)&lt;br /&gt;
&lt;br /&gt;
#define DUMMY      (0L)&lt;br /&gt;
&lt;br /&gt;
#define RED_PEN    (1)&lt;br /&gt;
#define GREEN_PEN  (2)&lt;br /&gt;
#define BLUE_PEN   (3)&lt;br /&gt;
&lt;br /&gt;
#define SCREEN_D   (2)&lt;br /&gt;
#define SCREEN_W (320)&lt;br /&gt;
#define SCREEN_H (200)&lt;br /&gt;
&lt;br /&gt;
/* the starting size of example layers, offsets are used for placement */&lt;br /&gt;
#define W_H (50)&lt;br /&gt;
#define W_T (5)&lt;br /&gt;
#define W_B ((W_T+W_H)-1)&lt;br /&gt;
#define W_W (80)&lt;br /&gt;
#define W_L ((SCREEN_W/2) - (W_W/2))&lt;br /&gt;
#define W_R ((W_L+W_W)-1)&lt;br /&gt;
&lt;br /&gt;
/* size of the superbitmap */&lt;br /&gt;
#define SUPER_H SCREEN_H&lt;br /&gt;
#define SUPER_W SCREEN_W&lt;br /&gt;
&lt;br /&gt;
/* starting size of the message layer */&lt;br /&gt;
#define M_H (10)&lt;br /&gt;
#define M_T (SCREEN_H-M_H)&lt;br /&gt;
#define M_B ((M_T+M_H)-1)&lt;br /&gt;
#define M_W (SCREEN_W)&lt;br /&gt;
#define M_L (0)&lt;br /&gt;
#define M_R ((M_L+M_W)-1)&lt;br /&gt;
&lt;br /&gt;
struct GraphicsIFace *IGraphics = 0;&lt;br /&gt;
struct LayersIFace *ILayers = 0;&lt;br /&gt;
&lt;br /&gt;
/* global constant data for initializing the layers */&lt;br /&gt;
LONG  theLayerFlags[3] = { LAYERSUPER, LAYERSMART, LAYERSIMPLE };&lt;br /&gt;
UWORD colortable[]     = { 0x000, 0xf44, 0x4f4, 0x44f };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Clear the layer then draw in a text string.&lt;br /&gt;
*/&lt;br /&gt;
VOID myLabelLayer(struct Layer *layer, LONG color, UBYTE *string)&lt;br /&gt;
{&lt;br /&gt;
/* fill layer with color */&lt;br /&gt;
IGraphics-&amp;gt;SetRast(layer-&amp;gt;rp, color);&lt;br /&gt;
&lt;br /&gt;
/* set up for writing text into layer */&lt;br /&gt;
IGraphics-&amp;gt;SetDrMd(layer-&amp;gt;rp,JAM1);&lt;br /&gt;
IGraphics-&amp;gt;SetAPen(layer-&amp;gt;rp,0);&lt;br /&gt;
IGraphics-&amp;gt;Move(layer-&amp;gt;rp, 5, 7);&lt;br /&gt;
&lt;br /&gt;
/* write into layer */&lt;br /&gt;
IGraphics-&amp;gt;Text(layer-&amp;gt;rp, string, strlen(string));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** write a message into a layer with a delay.&lt;br /&gt;
*/&lt;br /&gt;
VOID pMessage(struct Layer *layer, UBYTE *string)&lt;br /&gt;
{&lt;br /&gt;
IDOS-&amp;gt;Delay(S_DELAY);&lt;br /&gt;
myLabelLayer(layer, GREEN_PEN, string);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** write an error message into a layer with a delay.&lt;br /&gt;
*/&lt;br /&gt;
VOID error(struct Layer *layer, UBYTE *string)&lt;br /&gt;
{&lt;br /&gt;
myLabelLayer(layer, RED_PEN, string);&lt;br /&gt;
IDOS-&amp;gt;Delay(L_DELAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** do some layers manipulations to demonstrate their abilities.&lt;br /&gt;
*/&lt;br /&gt;
VOID doLayers(struct Layer *msgLayer, struct Layer *layer_array[])&lt;br /&gt;
{&lt;br /&gt;
WORD ktr;&lt;br /&gt;
WORD ktr_2;&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Label all Layers&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[0], RED_PEN,   &amp;quot;Super&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[1], GREEN_PEN, &amp;quot;Smart&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN,  &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;MoveLayer 1 InFrontOf 0&amp;quot;);&lt;br /&gt;
if (!ILayers-&amp;gt;MoveLayerInFrontOf(layer_array[1], layer_array[0]))&lt;br /&gt;
    error(msgLayer, &amp;quot;MoveLayerInFrontOf() failed.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;MoveLayer 2 InFrontOf 1&amp;quot;);&lt;br /&gt;
if (!ILayers-&amp;gt;MoveLayerInFrontOf(layer_array[2], layer_array[1]))&lt;br /&gt;
    error(msgLayer, &amp;quot;MoveLayerInFrontOf() failed.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN, &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Incrementally MoveLayers...&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 30; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    if (!ILayers-&amp;gt;MoveLayer(DUMMY, layer_array[1], -1, 0))&lt;br /&gt;
        error(msgLayer, &amp;quot;MoveLayer() failed.&amp;quot;);&lt;br /&gt;
    if (!ILayers-&amp;gt;MoveLayer(DUMMY, layer_array[2], -2, 0))&lt;br /&gt;
        error(msgLayer, &amp;quot;MoveLayer() failed.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN, &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;make Layer 0 the UpfrontLayer&amp;quot;);&lt;br /&gt;
if (!ILayers-&amp;gt;UpfrontLayer(DUMMY, layer_array[0]))&lt;br /&gt;
    error(msgLayer, &amp;quot;UpfrontLayer() failed.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;make Layer 2 the BehindLayer&amp;quot;);&lt;br /&gt;
if (!ILayers-&amp;gt;BehindLayer(DUMMY, layer_array[2]))&lt;br /&gt;
    error(msgLayer, &amp;quot;BehindLayer() failed.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Incrementally MoveLayers again...&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 30; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    if (!ILayers-&amp;gt;MoveLayer(DUMMY, layer_array[1], 0, 1))&lt;br /&gt;
        error(msgLayer, &amp;quot;MoveLayer() failed.&amp;quot;);&lt;br /&gt;
    if (!ILayers-&amp;gt;MoveLayer(DUMMY, layer_array[2], 0, 2))&lt;br /&gt;
        error(msgLayer, &amp;quot;MoveLayer() failed.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN, &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Big MoveLayer&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 3; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    if (!ILayers-&amp;gt;MoveLayer(DUMMY, layer_array[ktr], -layer_array[ktr]-&amp;gt;bounds.MinX, 0))&lt;br /&gt;
        error(msgLayer, &amp;quot;MoveLayer() failed.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Incrementally increase size&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 5; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    for(ktr_2 = 0; ktr_2 &amp;lt; 3; ktr_2++)&lt;br /&gt;
        {&lt;br /&gt;
        if (!ILayers-&amp;gt;SizeLayer(DUMMY, layer_array[ktr_2], 1, 1))&lt;br /&gt;
            error(msgLayer, &amp;quot;SizeLayer() failed.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Smart Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[1], GREEN_PEN, &amp;quot;Smart&amp;quot;);&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN,  &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Big SizeLayer&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 3; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    if (!ILayers-&amp;gt;SizeLayer(DUMMY,layer_array[ktr],&lt;br /&gt;
                SCREEN_W-(layer_array[ktr]-&amp;gt;bounds.MaxX)-1,0))&lt;br /&gt;
        error(msgLayer, &amp;quot;SizeLayer() failed.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Smart Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[1], GREEN_PEN, &amp;quot;Smart&amp;quot;);&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN,  &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;ScrollLayer down&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 30; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    for(ktr_2 = 0; ktr_2 &amp;lt; 3; ktr_2++)&lt;br /&gt;
        {&lt;br /&gt;
        ILayers-&amp;gt;ScrollLayer(DUMMY, layer_array[ktr_2], 0, -1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Smart Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[1], GREEN_PEN, &amp;quot;Smart&amp;quot;);&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN,  &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;ScrollLayer up&amp;quot;);&lt;br /&gt;
for(ktr = 0; ktr &amp;lt; 30; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    for(ktr_2 = 0; ktr_2 &amp;lt; 3; ktr_2++)&lt;br /&gt;
        {&lt;br /&gt;
        ILayers-&amp;gt;ScrollLayer(DUMMY, layer_array[ktr_2], 0, 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Smart Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[1], GREEN_PEN, &amp;quot;Smart&amp;quot;);&lt;br /&gt;
pMessage(msgLayer, &amp;quot;Refresh Simple Refresh Layer&amp;quot;);&lt;br /&gt;
myLabelLayer(layer_array[2], BLUE_PEN,  &amp;quot;Simple&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
IDOS_&amp;gt;Delay(L_DELAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** delete the layer array created by allocLayers().&lt;br /&gt;
*/&lt;br /&gt;
VOID disposeLayers(struct Layer *msgLayer, struct Layer *layer_array[])&lt;br /&gt;
{&lt;br /&gt;
WORD ktr;&lt;br /&gt;
&lt;br /&gt;
for (ktr = 0; ktr &amp;lt; 3; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    if (layer_array[ktr] != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        if (!ILayers-&amp;gt;DeleteLayer(DUMMY, layer_array[ktr]))&lt;br /&gt;
            error(msgLayer, &amp;quot;Error deleting layer&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Create some hard-coded layers.  The first must be super-bitmap, with&lt;br /&gt;
** the bitmap passed as an argument.  The others must not be super-bitmap.&lt;br /&gt;
** The pointers to the created layers are returned in layer_array.&lt;br /&gt;
**&lt;br /&gt;
** Return FALSE on failure.  On a FALSE return, the layers are&lt;br /&gt;
** properly cleaned up.&lt;br /&gt;
*/&lt;br /&gt;
BOOL allocLayers(struct Layer *msgLayer, struct Layer *layer_array[],&lt;br /&gt;
    struct BitMap *super_bitmap, struct Layer_Info *theLayerInfo,&lt;br /&gt;
    struct BitMap *theBitMap)&lt;br /&gt;
{&lt;br /&gt;
WORD ktr;&lt;br /&gt;
BOOL create_layer_ok = TRUE;&lt;br /&gt;
&lt;br /&gt;
for (ktr = 0;&lt;br /&gt;
     (ktr &amp;lt; 3) &amp;amp;&amp;amp; (create_layer_ok);&lt;br /&gt;
     ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    pMessage(msgLayer, &amp;quot;Create BehindLayer&amp;quot;);&lt;br /&gt;
    if (ktr == 0)&lt;br /&gt;
        {&lt;br /&gt;
        if ((layer_array[ktr] = ILayers-&amp;gt;CreateBehindLayer(theLayerInfo, theBitMap,&lt;br /&gt;
                  W_L+(ktr*30), W_T+(ktr*30), W_R+(ktr*30), W_B+(ktr*30),&lt;br /&gt;
                  theLayerFlags[ktr], super_bitmap)) == NULL)&lt;br /&gt;
            create_layer_ok = FALSE;&lt;br /&gt;
        }&lt;br /&gt;
     else&lt;br /&gt;
        {&lt;br /&gt;
        if ((layer_array[ktr] = ILayers-&amp;gt;CreateBehindLayer(theLayerInfo, theBitMap,&lt;br /&gt;
                  W_L+(ktr*30), W_T+(ktr*30), W_R+(ktr*30), W_B+(ktr*30),&lt;br /&gt;
                  theLayerFlags[ktr], NULL)) == NULL)&lt;br /&gt;
            create_layer_ok = FALSE;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    if (create_layer_ok)&lt;br /&gt;
        {&lt;br /&gt;
        pMessage(msgLayer, &amp;quot;Fill the RastPort&amp;quot;);&lt;br /&gt;
        IGraphics-&amp;gt;SetRast(layer_array[ktr]-&amp;gt;rp, ktr + 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
if (!create_layer_ok)&lt;br /&gt;
    disposeLayers(msgLayer, layer_array);&lt;br /&gt;
&lt;br /&gt;
return(create_layer_ok);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Free the bitmap and all bitplanes created by allocBitMap().&lt;br /&gt;
*/&lt;br /&gt;
VOID disposeBitMap(struct BitMap *bitmap, LONG depth, LONG width, LONG height)&lt;br /&gt;
{&lt;br /&gt;
WORD ktr;&lt;br /&gt;
&lt;br /&gt;
if (NULL != bitmap)&lt;br /&gt;
    {&lt;br /&gt;
    for (ktr = 0; ktr &amp;lt; depth; ktr++)&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL != bitmap-&amp;gt;Planes[ktr])&lt;br /&gt;
            IGraphics-&amp;gt;FreeRaster(bitmap-&amp;gt;Planes[ktr], width, height);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeMem(bitmap, sizeof(*bitmap));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Allocate and initialize a bitmap structure.&lt;br /&gt;
*/&lt;br /&gt;
struct BitMap *allocBitMap(LONG depth, LONG width, LONG height)&lt;br /&gt;
{&lt;br /&gt;
WORD ktr;&lt;br /&gt;
BOOL bit_map_failed = FALSE;&lt;br /&gt;
struct BitMap *bitmap = NULL;&lt;br /&gt;
&lt;br /&gt;
if (NULL != (bitmap = IExec-&amp;gt;AllocMem(sizeof(*bitmap),NULL)))&lt;br /&gt;
    {&lt;br /&gt;
    IGraphics-&amp;gt;InitBitMap(bitmap,depth,width,height);&lt;br /&gt;
&lt;br /&gt;
    for (ktr = 0; ktr &amp;lt; depth; ktr++)&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL == (bitmap-&amp;gt;Planes[ktr] = (PLANEPTR)IGraphics-&amp;gt;AllocRaster(width,height)))&lt;br /&gt;
            bit_map_failed = TRUE;&lt;br /&gt;
        else&lt;br /&gt;
            IGraphics-&amp;gt;BltClear(bitmap-&amp;gt;Planes[ktr], RASSIZE(width,height), 1);&lt;br /&gt;
        }&lt;br /&gt;
    if (bit_map_failed)&lt;br /&gt;
        {&lt;br /&gt;
        disposeBitMap(bitmap,depth,width,height);&lt;br /&gt;
        bitmap = NULL;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
return(bitmap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Set up to run the layers example, doLayers(). Clean up when done.&lt;br /&gt;
*/&lt;br /&gt;
VOID startLayers(struct Layer_Info *theLayerInfo, struct BitMap *theBitMap)&lt;br /&gt;
{&lt;br /&gt;
struct Layer  *msgLayer;&lt;br /&gt;
struct BitMap *theSuperBitMap;&lt;br /&gt;
struct Layer  *theLayers[3] = { NULL, NULL, NULL, };&lt;br /&gt;
&lt;br /&gt;
if (NULL != (msgLayer = ILayers-&amp;gt;CreateUpfrontLayer(theLayerInfo, theBitMap,&lt;br /&gt;
                     M_L, M_T, M_R, M_B, LAYERSMART, NULL)))&lt;br /&gt;
    {&lt;br /&gt;
    pMessage(msgLayer, &amp;quot;Setting up Layers&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (NULL != (theSuperBitMap = allocBitMap(SCREEN_D, SUPER_W, SUPER_H)))&lt;br /&gt;
        {&lt;br /&gt;
        if (allocLayers(msgLayer, theLayers, theSuperBitMap, theLayerInfo, theBitMap))&lt;br /&gt;
            {&lt;br /&gt;
            doLayers(msgLayer, theLayers);&lt;br /&gt;
&lt;br /&gt;
            disposeLayers(msgLayer, theLayers);&lt;br /&gt;
            }&lt;br /&gt;
        disposeBitMap(theSuperBitMap, SCREEN_D, SUPER_W, SUPER_H);&lt;br /&gt;
        }&lt;br /&gt;
    if (!ILayers-&amp;gt;DeleteLayer(DUMMY, msgLayer))&lt;br /&gt;
        error(msgLayer, &amp;quot;Error deleting layer&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Set up a low-level graphics display for layers to work on.  Layers&lt;br /&gt;
** should not be built directly on Intuition screens, use a low-level&lt;br /&gt;
** graphics view.  If you need mouse or other events for the layers&lt;br /&gt;
** display, you have to get them directly from the input device.  The&lt;br /&gt;
** only supported method of using layers library calls with Intuition&lt;br /&gt;
** (other than the InstallClipRegion() call) is through Intuition windows.&lt;br /&gt;
**&lt;br /&gt;
** See graphics primitives articles for details on creating and using the&lt;br /&gt;
** low-level graphics calls.&lt;br /&gt;
*/&lt;br /&gt;
VOID runNewView(VOID)&lt;br /&gt;
{&lt;br /&gt;
struct View        theView;&lt;br /&gt;
struct View       *oldview;&lt;br /&gt;
struct ViewPort    theViewPort;&lt;br /&gt;
struct RasInfo     theRasInfo;&lt;br /&gt;
struct ColorMap   *theColorMap;&lt;br /&gt;
struct Layer_Info *theLayerInfo;&lt;br /&gt;
struct BitMap     *theBitMap;&lt;br /&gt;
UWORD             *colorpalette;&lt;br /&gt;
WORD               ktr;&lt;br /&gt;
&lt;br /&gt;
/* save current view, to be restored when done */&lt;br /&gt;
if (NULL != (oldview = GfxBase-&amp;gt;ActiView))&lt;br /&gt;
    {&lt;br /&gt;
    /* get a LayerInfo structure */&lt;br /&gt;
    if (NULL != (theLayerInfo = ILayers-&amp;gt;NewLayerInfo()))&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL != (theColorMap = IGraphics-&amp;gt;GetColorMap(4)))&lt;br /&gt;
            {&lt;br /&gt;
            colorpalette = (UWORD *)theColorMap-&amp;gt;ColorTable;&lt;br /&gt;
            for(ktr = 0; ktr &amp;lt; 4; ktr++)&lt;br /&gt;
                *colorpalette++ = colortable[ktr];&lt;br /&gt;
&lt;br /&gt;
            if (NULL != (theBitMap = allocBitMap(SCREEN_D, SCREEN_W, SCREEN_H)))&lt;br /&gt;
                {&lt;br /&gt;
                IGraphics-&amp;gt;InitView(&amp;amp;theView);&lt;br /&gt;
                IGraphics-&amp;gt;InitVPort(&amp;amp;theViewPort);&lt;br /&gt;
&lt;br /&gt;
                theView.ViewPort = &amp;amp;theViewPort;&lt;br /&gt;
&lt;br /&gt;
                theViewPort.DWidth   = SCREEN_W;&lt;br /&gt;
                theViewPort.DHeight  = SCREEN_H;&lt;br /&gt;
                theViewPort.RasInfo  = &amp;amp;theRasInfo;&lt;br /&gt;
                theViewPort.ColorMap = theColorMap;&lt;br /&gt;
&lt;br /&gt;
                theRasInfo.BitMap   = theBitMap;&lt;br /&gt;
                theRasInfo.RxOffset = 0;&lt;br /&gt;
                theRasInfo.RyOffset = 0;&lt;br /&gt;
                theRasInfo.Next     = NULL;&lt;br /&gt;
&lt;br /&gt;
                IGraphics-&amp;gt;MakeVPort(&amp;amp;theView, &amp;amp;theViewPort);&lt;br /&gt;
                IGraphics-&amp;gt;MrgCop(&amp;amp;theView);&lt;br /&gt;
                IGraphics-&amp;gt;LoadView(&amp;amp;theView);&lt;br /&gt;
                IGraphics-&amp;gt;WaitTOF();&lt;br /&gt;
&lt;br /&gt;
                startLayers(theLayerInfo, theBitMap);&lt;br /&gt;
&lt;br /&gt;
                /* put back the old view, wait for it to become&lt;br /&gt;
                ** active before freeing any of our display&lt;br /&gt;
                */&lt;br /&gt;
                IGraphics-&amp;gt;LoadView(oldview);&lt;br /&gt;
                IGraphics-&amp;gt;WaitTOF();&lt;br /&gt;
&lt;br /&gt;
                /* free dynamically created structures */&lt;br /&gt;
                IGraphics-&amp;gt;FreeVPortCopLists(&amp;amp;theViewPort);&lt;br /&gt;
                IGraphics-&amp;gt;FreeCprList(theView.LOFCprList);&lt;br /&gt;
&lt;br /&gt;
                disposeBitMap(theBitMap, SCREEN_D, SCREEN_W, SCREEN_H);&lt;br /&gt;
                }&lt;br /&gt;
            IGraphics-&amp;gt;FreeColorMap(theColorMap);       /* free the color map */&lt;br /&gt;
            }&lt;br /&gt;
        ILayers-&amp;gt;DisposeLayerInfo(theLayerInfo);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Open the libraries used by the example.  Clean up when done.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    ILayers = (struct LayersIFace*)IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL); &lt;br /&gt;
    &lt;br /&gt;
    if (IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL)&lt;br /&gt;
    {   &lt;br /&gt;
        runNewView();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ILayers);&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the layers library functions. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Layers Library Functions&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| NewLayerInfo()&lt;br /&gt;
| Allocating a Layer_Info structure.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeLayerInfo()&lt;br /&gt;
| Deallocating a Layer_Info structure.&lt;br /&gt;
|-&lt;br /&gt;
| CreateUpfrontLayer()&lt;br /&gt;
| Make a new layer in front of others.&lt;br /&gt;
|-&lt;br /&gt;
| CreateBehindLayer()&lt;br /&gt;
| Make a new layer behind others.&lt;br /&gt;
|-&lt;br /&gt;
| DeleteLayer()&lt;br /&gt;
| Remove and delete an existing layer.&lt;br /&gt;
|-&lt;br /&gt;
| MoveLayer()&lt;br /&gt;
| Change the position (not depth) of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| SizeLayer()&lt;br /&gt;
| Change the size of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| ScrollLayer()&lt;br /&gt;
| Change the internal coordinates of a layer.&lt;br /&gt;
|-&lt;br /&gt;
| BehindLayer()&lt;br /&gt;
| Depth arrange a layer behind others.&lt;br /&gt;
|-&lt;br /&gt;
| UpfrontLayer()&lt;br /&gt;
| Depth arrange a layer in front of others.&lt;br /&gt;
|-&lt;br /&gt;
| MoveLayerInFrontOf()&lt;br /&gt;
| Depth arrange a layer to a specific position.&lt;br /&gt;
|-&lt;br /&gt;
| WhichLayer()&lt;br /&gt;
| Find the frontmost layer at a position.&lt;br /&gt;
|-&lt;br /&gt;
| SwapBitsRastPortClipRect()&lt;br /&gt;
| Fast, non-layered and non-damaging display operation.&lt;br /&gt;
|-&lt;br /&gt;
| BeginUpdate()&lt;br /&gt;
| Synchronize optimized refreshing for layer.&lt;br /&gt;
|-&lt;br /&gt;
| EndUpdate()&lt;br /&gt;
| End optimized layer refresh.&lt;br /&gt;
|-&lt;br /&gt;
| LockLayer()&lt;br /&gt;
| Lock out rendering in a single layer.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayer()&lt;br /&gt;
| Release LockLayer() lock.&lt;br /&gt;
|-&lt;br /&gt;
| LockLayers()&lt;br /&gt;
| Lock out rendering in all layers of a display.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayers()&lt;br /&gt;
| Release LockLayers() lock.&lt;br /&gt;
|-&lt;br /&gt;
| LockLayerInfo()&lt;br /&gt;
| Gain exclusive access to the display‚Äôs layers.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerInfo()&lt;br /&gt;
| Release LockLayerInfo() lock.&lt;br /&gt;
|-&lt;br /&gt;
| InstallClipRegion()&lt;br /&gt;
| Add a clipping region to a layer.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Using_Crash-Logs_for_Debugging&amp;diff=12560</id>
		<title>Using Crash-Logs for Debugging</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Using_Crash-Logs_for_Debugging&amp;diff=12560"/>
		<updated>2025-01-26T19:33:28Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Debug]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Bugs in software are a fact of life. It is almost impossible to write software with zero bugs. To date, no-one has come up with a method of even proving that a computer program is bug-free. Given these facts, it would be desirable to obtain as much information as possible about any crash that occurs. This is essential for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
AmigaOS provides substantial information via the Grim-Reaper. The Grim-Reaper pops up whenever a program performs an illegal operation, and provides a whole host of information about the crash. Details that are listed include the name of the program/library in which the crash occurred is listed along with the state of the CPU, a stack trace, and other state information. This information could be used in order to isolate the specific line of code at which the program crashed. However, this requires suitable preparation.&lt;br /&gt;
&lt;br /&gt;
[[File:UsingCrashLogs1.jpg|center|frame|The Grim Reaper displaying a DSI error.]]&lt;br /&gt;
&lt;br /&gt;
It is possible to embed debug information into a binary. However, this is undesirable in software being released to consumers as the binaries are inflated to several times the original size (tens of megabytes in size is easily obtainable in this manner). From the customers perspective, this is completely wasted space. Also, it gives a wealth of information about the internals of your program, which is undesirable. As will be shown below, there is a methodt hat can be used to provide compact binaries to end-users whilst still being able to use their crash-logs in order to locate and fix bugs.&lt;br /&gt;
&lt;br /&gt;
= Enabling Debugging =&lt;br /&gt;
&lt;br /&gt;
It is very easy to enable debugging in GCC. Simply add the compiler flag &amp;quot;-gstabs&amp;quot; when compiling and linking. This tells GCC that you wish to include debugging symbols in the binary, and even works if optimizations are enabled. &amp;quot;Stabs&amp;quot; is a particular format in which the debugging symbol; &amp;quot;-ggdb&amp;quot; is another option that also adds debugging symbols, but in a different format. However, I have been told that there are issues with &amp;quot;-ggdb&amp;quot; and large applications, so it is best to stick to &amp;quot;-gstabs.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= Debug Binaries =&lt;br /&gt;
&lt;br /&gt;
As was mentioned previously, enabling debug symbols inflates the binray size significantly. What many people do not realize is that it is possible to create a separate file that contains all the debug information; here is an excerpt from the make-file that achieves this:&lt;br /&gt;
&lt;br /&gt;
 $(TARGET): $(OBJS)&lt;br /&gt;
     $(CC) $(CFLAGS) -o $@.debug $(OBJS) $(LIBS) $(LINK)&lt;br /&gt;
     $(STRIP) $@.debug -o $@ &lt;br /&gt;
&lt;br /&gt;
Where CFLAGS = -mcrt=newlib $(OPTIMIZE) -Wall -gstabs&lt;br /&gt;
&lt;br /&gt;
In the [[Media:usingcrashlogs.lha|example template]] this translates to:&lt;br /&gt;
 gcc -mcrt=newlib -O3 -Wall -gstabs -c -o badboy.o badboy.c&lt;br /&gt;
 gcc -mcrt=newlib -O3 -Wall -gstabs -o badboy.debug badboy.o&lt;br /&gt;
 strip badboy.debug badboy&lt;br /&gt;
&lt;br /&gt;
The first line compiles the single source-file, badboy.c. The next one links it, and creates an executable called badboy.debug, which contains all the debugging symbols. Finally, strip removes all the debugging symbols and creates a binary called badboy. This stripped binary can then be distributed to end-users; badboy.debug is kept by the developer for interpreting crash-logs.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Crash-Logs =&lt;br /&gt;
&lt;br /&gt;
This is best understood by example. Download and extract the [[Media:usingcrashlogs.lha|example template]]. Open a shell window and change to the directory containing the template code. Type make, and press enter. This will compile a program called badboy that deliberately performs an illegal operation. It performs a classical NULL-pointer dereferencing for a write operation; a very common bug. Now run badboy; the grim-reaper should pop up and display the error message below.&lt;br /&gt;
&lt;br /&gt;
[[File:UsingCrashLogs1.jpg|center|frame|The Grim Reaper displaying a DSI error.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note about the &amp;quot;redzone&amp;quot; message:&#039;&#039;&lt;br /&gt;
The lower part of the stack is made up of unmapped virtual pages that throw an exception when accessed, to prevent the program from wreaking havoc.&lt;br /&gt;
A redzone is a stack barrier that makes the program exhausting it&#039;s stack space crash. With memory, the term &amp;quot;electric fence&amp;quot; has been established for &amp;quot;hot&amp;quot; barriers (due to the name given to a tool under Linux that was used for this purpose).&lt;br /&gt;
&lt;br /&gt;
Now click on &amp;quot;More...&amp;quot; and select the &amp;quot;Stack Trace&amp;quot; tab. Clicking on the &amp;quot;Generate Stack Trace&amp;quot; button will generate a stack trace.&lt;br /&gt;
&lt;br /&gt;
[[File:UsingCrashLogs2.jpg|center|frame|A stack trace for the program &amp;quot;badboy&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
This shows that the crash occurred within &amp;quot;badboy&amp;quot; in section 5 at 0x608. Open a new shell window and change to the directory containing &amp;quot;badboy&amp;quot; once again. Enter the following line:&lt;br /&gt;
&lt;br /&gt;
 addr2line -e badboy.debug --section=.text 0x608 &lt;br /&gt;
&lt;br /&gt;
This performs a lookup of 0x608 inside &amp;quot;badboy.debug,&amp;quot; which contains the debug symbols. Addr2line will respond by giving the file and line number of this address.&lt;br /&gt;
&lt;br /&gt;
[[File:UsingCrashLogs3.jpg|center|frame|A stack trace for the program &amp;quot;badboy&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Line 25 performs a write to address zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int *a = NULL;&lt;br /&gt;
*a = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The procedure above has identified the exact source-line at which the crash occurred. In this case the crash was deliberate in order to present an easy to understand example. However, in a real application, the*.debug version of each release would be kept by the developer so that crash-logs submitted by users can be used in order to identify the source-line at which the crash occurred. If you look at the [http://hdrlab.org.nz/articles/amiga-os-articles/minigl-templates/ MiniGL templates] that I have written, you will notice that all of them use this technique.&lt;br /&gt;
&lt;br /&gt;
= Download =&lt;br /&gt;
&lt;br /&gt;
[[Media:usingcrashlogs.lha|Using crash-logs template and example]]&lt;br /&gt;
&lt;br /&gt;
= Wish-List =&lt;br /&gt;
&lt;br /&gt;
Whilst the technique above works, a more automated bug tracking system would be nice. The first component of such a system would be an automated crash-log submission system. If Grim Reaper provided a singl-click method of submitting a bug report to the developer via the internet, more crash-logs are likely to be submitted. Having a tool that would parse the crash-log and automatically find file names and line numbers for the entry in the crash-log would also be useful.&lt;br /&gt;
&lt;br /&gt;
= Author =&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2008 Hans de Ruiter.&amp;lt;br/&amp;gt;&lt;br /&gt;
Reproduced with permission.&amp;lt;br/&amp;gt;&lt;br /&gt;
See the original article [http://hdrlab.org.nz/articles/amiga-os-articles/using-crash-logs/ here].&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmiWest_Setup&amp;diff=12559</id>
		<title>AmiWest Setup</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmiWest_Setup&amp;diff=12559"/>
		<updated>2025-01-26T19:33:15Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Setup =&lt;br /&gt;
&lt;br /&gt;
Developing software on and for AmigaOS involves three major components:  the &#039;&#039;&#039;AmigaOS SDK&#039;&#039;&#039; or &amp;quot;Software Development Kit&amp;quot;, a source code &#039;&#039;&#039;Editor&#039;&#039;&#039; and your &#039;&#039;&#039;imagination&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The first part of this equation, the AmigaOS Software Development Kit or &#039;&#039;&#039;AmigaOS SDK&#039;&#039;&#039;, is a collection of files and tools that will convert your source code into an Amiga application.  The SDK consists of a collection of the latest documentation, &amp;quot;include&amp;quot; files, example code, utilities and the GCC compiler.  With each major version of AmigaOS a new SDK is issued that allows developers and their applications take advantage of the latest OS features.&lt;br /&gt;
&lt;br /&gt;
The second part of the equation is a means to edit and manage your programming project and its source code. While you can edit source code in the AmigaOS &#039;&#039;&#039;NotePad&#039;&#039;&#039; text editor, there are much better &amp;amp; easier ways. The most powerful programming environment on AmigaOS is called [http://codebench.co.uk/ CodeBench]. In addition to providing a source code editor that provide syntax highlighting, help as you type and context sensitive help, CodeBench takes care of building &amp;quot;makefiles&amp;quot;, runs the compiler, collects &amp;amp; presents errors and can interact to remote source code servers.&lt;br /&gt;
&lt;br /&gt;
Finally there is &#039;&#039;&#039;you&#039;&#039;&#039;!  Obviously, developing AmigaOS applications with the SDK requires a working understanding of the C language.  To take advantage of the specific powers of AmigaOS, there are more methodologies and functions to learn.  The SDK provides a number of example programs to learn from and a bounty of &amp;quot;autodocs&amp;quot; that document AmigaOS functions.  Every day there are new things to learn.&lt;br /&gt;
&lt;br /&gt;
== Installing the SDK ==&lt;br /&gt;
&lt;br /&gt;
The latest version of the SDK can be downloaded from the Hyperion Entertainment [http://www.hyperion-entertainment.biz/index.php?option=com_registration&amp;amp;view=files&amp;amp;parent=30&amp;amp;Itemid=63 SDK webpage].&lt;br /&gt;
&lt;br /&gt;
Once downloaded, you can double-click on the SDK archive file to use the AmigaOS &#039;&#039;&#039;Unarc&#039;&#039;&#039; utility to decompress the SDK to your RAM Disk. This will create a directory that includes all the SDK files to be installed.&lt;br /&gt;
&lt;br /&gt;
In that directory you will find the &#039;&#039;&#039;Install SDK&#039;&#039;&#039; installer program. If you run that, it will ask you what you want to install and for a location to install to.  Unless you have a severe limitation on hard disk space, it&#039;s recommended you make a &amp;quot;Full&amp;quot; install.  As part of the install process, a few commands will be added to your &amp;quot;user-startup&amp;quot; script to assign SDK: to your system.&lt;br /&gt;
&lt;br /&gt;
Once the SDK installation is complete, your SDK: path will include the following directories:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Documentation&#039;&#039;&#039; - This drawer contains various documentation, AutoDocs, and other information related to the SDK. It also contains documentation on the various tools and compilers.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Examples&#039;&#039;&#039; - This drawer contains example source code on how to program for AmigaOS 4.x. Examples are sorted by theme/topic.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Local&#039;&#039;&#039; - The Local drawer is our means of isolating the compilers and third-party additions. See SDK documentation PDF file for more information on the Local drawer.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;gcc&#039;&#039;&#039; - The gcc drawer contains the GNU C/C++ compiler. It is set up in a way that there are no user-serviceable parts inside. This makes it easy to exchange the compiler when later versions become available.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Include&#039;&#039;&#039; - This drawer contains system-level include files. Like the gcc drawer, it should not need to be modified.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Contrib&#039;&#039;&#039; - The Contrib drawer contains various files that where contributed to the SDK. Among other things, it contains the latest source code for the static C runtime library clib2.&lt;br /&gt;
&lt;br /&gt;
You can test the installation of SDK and its GCC compiler by opening a shell and typing the following command:&lt;br /&gt;
&lt;br /&gt;
 gcc --version&lt;br /&gt;
&lt;br /&gt;
This should cause a text to be printed by the compiler, including the version number.&lt;br /&gt;
&lt;br /&gt;
== Testing the SDK ==&lt;br /&gt;
&lt;br /&gt;
Open the &#039;&#039;&#039;NotePad&#039;&#039;&#039; text editor and type the following text:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
 IDOS-&amp;gt;Printf(&amp;quot;Hello, World\n&amp;quot;);&lt;br /&gt;
 return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then save the file to &amp;quot;hello_world.c&amp;quot; and go back to the shell, CD to the directory with your file and enter this command:&lt;br /&gt;
&lt;br /&gt;
 gcc -o hello_world hello_world.c&lt;br /&gt;
&lt;br /&gt;
After a few seconds, the gcc compiler should return. As long the source was correctly entered and no errors or warnings were displayed by GCC, then you will have successfully compiled your first Amiga program. Just type &amp;quot;hello_world&amp;quot; into the command line to see it run. Congratulations!&lt;br /&gt;
&lt;br /&gt;
== Installing CodeBench ==&lt;br /&gt;
&lt;br /&gt;
You may have used the &#039;&#039;&#039;NotePad&#039;&#039;&#039; text editor and your Shell console to create the sample C program above, but there are much better ways to edit and compile C programs. Such development systems provide more specialized editors suited for coding and means for managing compilation, handling warnings and errors, etc.&lt;br /&gt;
&lt;br /&gt;
On AmigaOS we have &#039;&#039;&#039;CodeBench&#039;&#039;&#039; to improve and automate our coding sessions. CodeBench provides a text editor with &amp;quot;syntax highlighting&amp;quot; which means that it color-codes text on the basis of what it is: comments, commands, strings, etc.  CodeBench also provides a GUI for GCC - click a button to compile, review the results in a window.  There are even more features for more serious projects.&lt;br /&gt;
&lt;br /&gt;
CodeBench can be downloaded from the  [http://codebench.co.uk/downloads.php website] of the developer, SImon Archer.  Once downloaded, you can double-click on the archive to open it in the AmigaOS &#039;&#039;&#039;Unarc&#039;&#039;&#039; utility and decompress the files to your RAM Disk. Once finished, you will find an &amp;quot;Install CodeBench&amp;quot; installer program icon. When you run the installer, it will confirm whether you are upgrading or making a new install and ask you for a location.&lt;br /&gt;
&lt;br /&gt;
Once this process is done, you&#039;re ready to start a project in CodeBench. CodeBench comes with comprehensive documentation on itself in web format (that can also be accessed [http://codebench.co.uk/docs/codebench.html here]). &lt;br /&gt;
&lt;br /&gt;
== Testing CodeBench: Hello World ==&lt;br /&gt;
&lt;br /&gt;
The first thing you can do with CodeBench is to start a test project to how things work.  At [http://codebench.co.uk/docs/started.html this page] you can find information on on starting a project.  Basically, it&#039;s a matter of clicking the Start Project button, setting a few names and variables and entering a path for your project.  &lt;br /&gt;
&lt;br /&gt;
Once the new project is established, then you can use the &amp;quot;Editor/Create New File...&amp;quot; menu item to start a new source file.  If you copy the above &amp;quot;Hello World&amp;quot; source code and paste it into the editor and save the source as &amp;quot;hello-world.c&amp;quot; then you will be ready for compiling.  &lt;br /&gt;
&lt;br /&gt;
In the CodeBench tool bar window, click the green &amp;quot;Build&amp;quot; button and CodeBench will automatically prepare, compile and link your project into an executable program.  &lt;br /&gt;
A Build window will appear that will show the status of the building of your project.  Assuming your code compiles without errors, you will then see a Run button with a play icon in the CodeBench toolbar.  Clicking that button will bring up a window to let you run your program.&lt;br /&gt;
&lt;br /&gt;
== Testing CodeBench: Hello with a GUI ==&lt;br /&gt;
&lt;br /&gt;
Once you start coding with CodeBench, you will find it provides a number of aids to help with coding: such as highlighting or coloring the source code by type, letting you work with a number of sources at one time, letting you access AmigaOS documents by shift-double-clicking on AmigaOS functions and providing a quick way to track compiling errors into the source code.&lt;br /&gt;
&lt;br /&gt;
With CodeBench you can easily jump into more advanced coding, such as our old &amp;quot;Hello World&amp;quot; program with a modern GUI.  Just copy and paste the following source into a new project and try to compile it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/label.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // Create a window with a label and button.&lt;br /&gt;
   &lt;br /&gt;
    Object* winobj = IIntuition-&amp;gt;NewObject(NULL, &amp;quot;window.class&amp;quot;,&lt;br /&gt;
        WA_Title, &amp;quot;Hello World Example&amp;quot;,&lt;br /&gt;
        WA_Activate, TRUE,&lt;br /&gt;
        WA_DepthGadget, TRUE,&lt;br /&gt;
        WA_DragBar, TRUE,&lt;br /&gt;
        WA_CloseGadget, TRUE,&lt;br /&gt;
        WA_SizeGadget, TRUE,&lt;br /&gt;
        WINDOW_Position, WPOS_CENTERMOUSE,&lt;br /&gt;
&lt;br /&gt;
        WINDOW_Layout, IIntuition-&amp;gt;NewObject(NULL, &amp;quot;layout.gadget&amp;quot;,&lt;br /&gt;
            LAYOUT_Orientation, LAYOUT_ORIENT_VERT,&lt;br /&gt;
            LAYOUT_SpaceOuter, TRUE,&lt;br /&gt;
            LAYOUT_DeferLayout, TRUE,&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddImage, IIntuition-&amp;gt;NewObject(NULL, &amp;quot;label.image&amp;quot;,&lt;br /&gt;
                LABEL_Text, &amp;quot;Hello, World&amp;quot;,&lt;br /&gt;
                TAG_END),&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddChild, IIntuition-&amp;gt;NewObject(NULL, &amp;quot;button.gadget&amp;quot;,&lt;br /&gt;
                GA_RelVerify, TRUE,&lt;br /&gt;
                GA_Text, &amp;quot;Quit&amp;quot;,&lt;br /&gt;
                TAG_END),&lt;br /&gt;
           TAG_END),&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (winobj != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        //  Open the window.&lt;br /&gt;
        struct Window *window = (struct Window *) IIntuition-&amp;gt;IDoMethod(winobj, WM_OPEN);&lt;br /&gt;
&lt;br /&gt;
        if (window != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            // Obtain the window wait signal mask.&lt;br /&gt;
&lt;br /&gt;
            uint32 signal = 0;&lt;br /&gt;
            IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, winobj, &amp;amp;signal);&lt;br /&gt;
&lt;br /&gt;
            // Input Event Loop&lt;br /&gt;
&lt;br /&gt;
            BOOL done = FALSE;&lt;br /&gt;
&lt;br /&gt;
            while (!done)&lt;br /&gt;
            {&lt;br /&gt;
                uint32 wait = IExec-&amp;gt;Wait(signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                if ( wait &amp;amp; SIGBREAKF_CTRL_C )&lt;br /&gt;
                {&lt;br /&gt;
                    done = TRUE;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if ( wait &amp;amp; signal )&lt;br /&gt;
                {&lt;br /&gt;
                    uint32 result = WMHI_LASTMSG;&lt;br /&gt;
                    int16 code = 0;&lt;br /&gt;
&lt;br /&gt;
                    while ((result = IIntuition-&amp;gt;IDoMethod(winobj, WM_HANDLEINPUT, &amp;amp;code)) != WMHI_LASTMSG)&lt;br /&gt;
                    {&lt;br /&gt;
                        switch (result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
                        {&lt;br /&gt;
                            case WMHI_GADGETUP:&lt;br /&gt;
                                done = TRUE;&lt;br /&gt;
                                break;&lt;br /&gt;
&lt;br /&gt;
                            case WMHI_CLOSEWINDOW:&lt;br /&gt;
                                window = NULL;&lt;br /&gt;
                                done = TRUE;&lt;br /&gt;
                                break;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Disposing of the window object will also close the window if it is&lt;br /&gt;
         * already opened and all attached objects.&lt;br /&gt;
         */&lt;br /&gt;
        IIntuition-&amp;gt;DisposeObject(winobj);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have built and run this example program the following GUI will be produced:&lt;br /&gt;
&lt;br /&gt;
[[File:HelloWorldWindow.png]]&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Borders&amp;diff=12558</id>
		<title>Intuition Borders</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Borders&amp;diff=12558"/>
		<updated>2025-01-26T19:32:23Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Creating Borders =&lt;br /&gt;
&lt;br /&gt;
This data type is called a Border since it was originally used to create border lines around display objects. It is actually a general purpose structure for drawing connected lines between any series of points.&lt;br /&gt;
&lt;br /&gt;
A Border is easier to use than an Image structure. Only the following need be specified to define a border:&lt;br /&gt;
&lt;br /&gt;
* An internal position component which is used in determining the final position of the border.&lt;br /&gt;
* A set of coordinate pairs for each vertex.&lt;br /&gt;
* A color for the lines.&lt;br /&gt;
* One of several drawing modes.&lt;br /&gt;
&lt;br /&gt;
== Border Structure Definition ==&lt;br /&gt;
&lt;br /&gt;
To use a border, the application must create one or more instances of the Border structure. Here is the specification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Border&lt;br /&gt;
    {&lt;br /&gt;
    WORD LeftEdge, TopEdge;&lt;br /&gt;
    UBYTE FrontPen, BackPen;&lt;br /&gt;
    UBYTE DrawMode;&lt;br /&gt;
    BYTE Count;&lt;br /&gt;
    WORD *XY;&lt;br /&gt;
    struct Border *NextBorder;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is a brief description of the fields of the Border structure.&lt;br /&gt;
&lt;br /&gt;
; LeftEdge, TopEdge&lt;br /&gt;
: These fields are used to determine the position of the Border relative to its base position (the base position is the upper left corner for requesters, menus, or gadgets and is specified in the call to DrawBorder() for windows and screens).&lt;br /&gt;
&lt;br /&gt;
; FrontPen, BackPen&lt;br /&gt;
: These fields contain color registers numbers. FrontPen is the color used to draw the lines. BackPen is currently unused.&lt;br /&gt;
&lt;br /&gt;
;DrawMode&lt;br /&gt;
: Set the DrawMode field to one of the following:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| JAM1&lt;br /&gt;
| Use FrontPen to draw the line.&lt;br /&gt;
|-&lt;br /&gt;
| COMPLEMENT&lt;br /&gt;
| Change the pixels within the lines to their complement color.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; Count&lt;br /&gt;
: Specify the number of data points used in this border. Each data point is described by two words of data in the XY array.&lt;br /&gt;
&lt;br /&gt;
; XY&lt;br /&gt;
: A pointer to an array of coordinate pairs, one pair for each point. These coordinates are measured relative to the position of the border.&lt;br /&gt;
&lt;br /&gt;
; NextBorder&lt;br /&gt;
: This field is a pointer to another instance of a Border structure. Set this field to NULL if this is the last Border structure in the linked list.&lt;br /&gt;
&lt;br /&gt;
= Directly Drawing the Borders =&lt;br /&gt;
&lt;br /&gt;
Borders may be directly drawn by the application by calling the function DrawBorder().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID DrawBorder( struct RastPort *rp, struct Border *border,&lt;br /&gt;
                 LONG leftOffset, LONG topOffset );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rp argument is a pointer to the RastPort into which the border should be drawn. This rastport may come from a Window or Screen structure.&lt;br /&gt;
&lt;br /&gt;
The border argument is a pointer to a list of Border structures which are to be rendered. The list may contain a single Border structure.&lt;br /&gt;
&lt;br /&gt;
The leftOffset and topOffset arguments are the external component, or base position, for this list of Borders. The LeftEdge and TopEdge values of each Border structure are added to these to determine the Border position.&lt;br /&gt;
&lt;br /&gt;
Borders may also be indirectly drawn by attaching them to gadgets, menus or requesters.&lt;br /&gt;
&lt;br /&gt;
= Border Example =&lt;br /&gt;
&lt;br /&gt;
The following example draws a double border using two pens to create a shadow effect. The border is drawn in two positions to show the flexibility in positioning borders, note that it could also be attached to a menu, gadget or requester.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** shadowborder.c - program to show the use of an Intuition Border.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase = NULL;&lt;br /&gt;
&lt;br /&gt;
#define MYBORDER_LEFT   (0)&lt;br /&gt;
#define MYBORDER_TOP    (0)&lt;br /&gt;
&lt;br /&gt;
/* This is the border data. */&lt;br /&gt;
int16 myBorderData[] =&lt;br /&gt;
{&lt;br /&gt;
  0,0, 50,0, 50,30, 0,30, 0,0,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** main routine. Open required library and window and draw the images.&lt;br /&gt;
** This routine opens a very simple window with no IDCMP.  See the&lt;br /&gt;
** articles on &amp;quot;Windows&amp;quot; and &amp;quot;Input and Output Methods&amp;quot; for more info.&lt;br /&gt;
** Free all resources when done.&lt;br /&gt;
*/&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct Screen   *screen;&lt;br /&gt;
  struct DrawInfo *drawinfo;&lt;br /&gt;
  struct Window   *win;&lt;br /&gt;
  struct Border    shineBorder;&lt;br /&gt;
  struct Border    shadowBorder;&lt;br /&gt;
&lt;br /&gt;
  uint32 mySHADOWPEN = 1;  /* set default values for pens */&lt;br /&gt;
  uint32 mySHINEPEN  = 2;  /* in case can&#039;t get info...   */&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  &lt;br /&gt;
  if (IIntuition != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (screen = IIntuition-&amp;gt;LockPubScreen(NULL))&lt;br /&gt;
    {&lt;br /&gt;
      if (drawinfo = IIntuition-&amp;gt;GetScreenDrawInfo(screen))&lt;br /&gt;
      {&lt;br /&gt;
        /* Get a copy of the correct pens for the screen.&lt;br /&gt;
        ** This is very important in case the user or the&lt;br /&gt;
        ** application has the pens set in a unusual way.&lt;br /&gt;
        */&lt;br /&gt;
        mySHADOWPEN = drawinfo-&amp;gt;dri_Pens[SHADOWPEN];&lt;br /&gt;
        mySHINEPEN  = drawinfo-&amp;gt;dri_Pens[SHINEPEN];&lt;br /&gt;
&lt;br /&gt;
        IIntuition-&amp;gt;FreeScreenDrawInfo(screen,drawinfo);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IIntuition-&amp;gt;UnlockPubScreen(NULL,screen);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* open a simple window on the workbench screen for displaying&lt;br /&gt;
    ** a border.  An application would probably never use such a&lt;br /&gt;
    ** window, but it is useful for demonstrating graphics...&lt;br /&gt;
    */&lt;br /&gt;
    if (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                        WA_PubScreen,  screen,&lt;br /&gt;
                        WA_RMBTrap,      TRUE,&lt;br /&gt;
                        TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
      /* set information specific to the shadow component of the border */&lt;br /&gt;
      shadowBorder.LeftEdge   = MYBORDER_LEFT + 1;&lt;br /&gt;
      shadowBorder.TopEdge    = MYBORDER_TOP + 1;&lt;br /&gt;
      shadowBorder.FrontPen   = mySHADOWPEN;&lt;br /&gt;
      shadowBorder.NextBorder = &amp;amp;shineBorder;&lt;br /&gt;
&lt;br /&gt;
      /* set information specific to the shine component of the border */&lt;br /&gt;
      shineBorder.LeftEdge    = MYBORDER_LEFT;&lt;br /&gt;
      shineBorder.TopEdge     = MYBORDER_TOP;&lt;br /&gt;
      shineBorder.FrontPen    = mySHINEPEN;&lt;br /&gt;
      shineBorder.NextBorder  = NULL;&lt;br /&gt;
&lt;br /&gt;
      /* the following attributes are the same for both borders. */&lt;br /&gt;
      shadowBorder.BackPen    = shineBorder.BackPen   = 0;&lt;br /&gt;
      shadowBorder.DrawMode   = shineBorder.DrawMode  = JAM1;&lt;br /&gt;
      shadowBorder.Count      = shineBorder.Count     = 5;&lt;br /&gt;
      shadowBorder.XY         = shineBorder.XY        = myBorderData;&lt;br /&gt;
&lt;br /&gt;
      /* Draw the border at 10,10 */&lt;br /&gt;
      IIntuition-&amp;gt;DrawBorder(win-&amp;gt;RPort,&amp;amp;shadowBorder,10,10);&lt;br /&gt;
&lt;br /&gt;
      /* Draw the border again at 100,10 */&lt;br /&gt;
      IIntuition-&amp;gt;DrawBorder(win-&amp;gt;RPort,&amp;amp;shadowBorder,100,10);&lt;br /&gt;
&lt;br /&gt;
      /* Wait a bit, then quit.&lt;br /&gt;
      ** In a real application, this would be an event loop, like the&lt;br /&gt;
      ** one described in the Intuition Input and Output Methods article.&lt;br /&gt;
      */&lt;br /&gt;
      IDOS-&amp;gt;Delay(200);&lt;br /&gt;
&lt;br /&gt;
      IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Border Colors and Drawing Modes =&lt;br /&gt;
&lt;br /&gt;
Borders can select their colors from the values set in the color registers for the screen in which they are rendered. The available number of colors and palette settings are screen attributes and may not be changed through border rendering.&lt;br /&gt;
&lt;br /&gt;
Two drawing modes pertain to border lines: JAM1 and COMPLEMENT. To draw the line in a specific color, use the JAM1 draw mode. This mode converts each pixel in the line to the color set in the FrontPen field.&lt;br /&gt;
&lt;br /&gt;
Selecting the COMPLEMENT draw mode causes the line to be drawn in an exclusive-or mode that inverts the color of each pixel within the line. The data bits of the pixel are changed to their binary complement. This complement is formed by reversing all bits in the binary representation of the color register number. In a three bitplane display, for example, color 6 is 110 in binary. In COMPLEMENT draw mode, if a pixel is color 6, it will be changed to the 001 (binary), which is color 1. Note that a border drawn in COMPLEMENT mode can be removed from a static display by drawing the border again in the same position.&lt;br /&gt;
&lt;br /&gt;
= Border Coordinates =&lt;br /&gt;
&lt;br /&gt;
Intuition draws lines between points that are specified as sets of X, Y coordinates. Border data does not have to be in Chip memory.&lt;br /&gt;
&lt;br /&gt;
The XY field contains a pointer to an array of coordinate pairs. All of these coordinates are offsets relative to the Border position, which is determined by the sum of the external and internal position components as described above. The coordinate pairs are ordered sequentially. The first two numbers make up the first coordinate pair, the next two numbers make up the second pair, and so on. Within a coordinate pair, the first number is the X offset and the second number is the Y offset.&lt;br /&gt;
&lt;br /&gt;
The first coordinate pair describes the starting point of the first line. When the Border is rendered, a line is drawn between each pair of points. The first line is drawn from point one to point two, the second line is drawn from point two to point three, and so on, until the final point is reached.&lt;br /&gt;
&lt;br /&gt;
The numbers specified in the XY array may be positive or negative. Negative values move up and to the left relative to the Border position, positive values move down and to the right. Again, the Border position is determined by adding the external position component and the internal position component. For example, a Border attached to a Gadget has an external component equal to the upper left corner of the gadget’s select box. The internal component is set within the Border structure itself. These two components are added together and offsets from the resulting position, specified within the XY array, determine where the lines of the Border will appear.&lt;br /&gt;
&lt;br /&gt;
Suppose the top left corner of the select box of the gadget is at window position (10,5). If the Border has LeftEdge set to 10 and TopEdge set to 10, then the Border is positioned at (10+10,5+10), that is (20,15). All XY coordinates will be relative to this Border position. If the XY array contains ‘0,5, 15,5, 15,0’, then the relative coordinates will be (0,5), (15,5) and (15,0). Adding each coordinate to the Border position gives the absolute position of the lines within the window. This Border will draw two lines in the window, one from (20,20) to (35,20) and the second from (35,20) to (35,15).&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig8-3.png|frame|center|Example of Border Relative Position]]&lt;br /&gt;
&lt;br /&gt;
To create a border that is outside the select box of a gadget, specify negative values in the internal component or use negative values for the initial XY values. For example, setting LeftEdge to -1 and TopEdge to -1 moves the position of the Border one pixel above and one pixel to the left of the gadget’s select box.&lt;br /&gt;
&lt;br /&gt;
= Linking Borders =&lt;br /&gt;
&lt;br /&gt;
The NextBorder field can point to another instance of a Border structure. This allows complex graphic objects to be created by linking together Border structures, each with its own data points, color and draw mode. This might be used, for instance, to draw a double border around a requester or gadget where the outer border is a second Border structure, linked to the first inner border.&lt;br /&gt;
&lt;br /&gt;
Note that the borders can share data. For instance, to create a border with a shadow, link two borders together each of which points to the same XY data. Set the first border to draw in a dark pen (such as the SHADOWPEN from the screen’s DrawInfo structure) and position the border down and to the right a few pixels by changing LeftEdge and TopEdge in the Border structure.&lt;br /&gt;
&lt;br /&gt;
The second border should be set to a bright pen (such as the SHINEPEN in the screen’s DrawInfo structure). When the border is drawn, the first border will draw in a dark color and then the second border will be drawn over it in a light color. Since they use the same data set, and the dark border is shifted down and to the right, the border will have a three dimensional appearance. This technique is demonstrated in the example listed earlier in this section.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Gameport_Device&amp;diff=12557</id>
		<title>Gameport Device</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Gameport_Device&amp;diff=12557"/>
		<updated>2025-01-26T19:32:20Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Devices|Gameport]]{{NeedUpdate}}&lt;br /&gt;
== Gameport Device ==&lt;br /&gt;
&lt;br /&gt;
The gameport device manages access to the Amiga gameport connectors for the operating system. It enables the Amiga to interface with various external pointing devices like mice (two and three button), joysticks, trackballs and light pens. There are two units in the gameport device, unit 0 and unit 1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Amiga Gameport Controllers&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! Unit 0&lt;br /&gt;
! Unit 1&lt;br /&gt;
|-&lt;br /&gt;
| A3000&lt;br /&gt;
| Front Connector&lt;br /&gt;
| Back Connector&lt;br /&gt;
|-&lt;br /&gt;
| A2000&lt;br /&gt;
| Left Connector&lt;br /&gt;
| Right Connector&lt;br /&gt;
|-&lt;br /&gt;
| A1000&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| A500&lt;br /&gt;
| 1 JOYSTICK&lt;br /&gt;
| 2 JOYSTICK&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Gameport Device Commands and Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command&lt;br /&gt;
! Command Operation&lt;br /&gt;
|-&lt;br /&gt;
| CMD_CLEAR&lt;br /&gt;
| Clear the gameport input buffer.&lt;br /&gt;
|-&lt;br /&gt;
| GPD_ASKCTYPE&lt;br /&gt;
| Return the type of gameport controller being used.&lt;br /&gt;
|-&lt;br /&gt;
| GPD_ASKTRIGGER&lt;br /&gt;
| Return the conditions that have been preset for triggering.&lt;br /&gt;
|-&lt;br /&gt;
| GPD_READEVENT&lt;br /&gt;
| Read one or more gameport events.&lt;br /&gt;
|-&lt;br /&gt;
| GPD_SETCTYPE&lt;br /&gt;
| Set the type of the controller to be used.&lt;br /&gt;
|-&lt;br /&gt;
| GPD_SETTRIGGER&lt;br /&gt;
| Preset the conditions that will trigger a gameport event.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Who Runs The Mouse?|text=When the input device or Intution is operating, unit 0 is usually dedicated to gathering mouse events. The input device uses the gameport device to read the mouse events. (For applications that take over the machine without starting up the input device or Intuition, unit 0 can perform the same functions as unit 1.) See [[Input_Device|Input Device]] for more information on the input device.}}&lt;br /&gt;
&lt;br /&gt;
== Device Interface ==&lt;br /&gt;
&lt;br /&gt;
The gameport device operates like the other Amiga devices. To use it, you must first open the gameport device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.&lt;br /&gt;
&lt;br /&gt;
The I/O request used by the gameport device is called IOStdReq.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IOStdReq&lt;br /&gt;
{&lt;br /&gt;
    struct  Message io_Message;&lt;br /&gt;
    struct  Device  *io_Device;     /* device node pointer  */&lt;br /&gt;
    struct  Unit    *io_Unit;       /* unit (driver private)*/&lt;br /&gt;
    UWORD   io_Command;             /* device command */&lt;br /&gt;
    UBYTE   io_Flags;&lt;br /&gt;
    BYTE    io_Error;               /* error or warning num */&lt;br /&gt;
    ULONG   io_Actual;              /* actual number of bytes transferred */&lt;br /&gt;
    ULONG   io_Length;              /* requested number bytes transferred*/&lt;br /&gt;
    APTR    io_Data;                /* points to data area */&lt;br /&gt;
    ULONG   io_Offset;              /* offset for block structured devices */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file exec/io.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Gameport Device ===&lt;br /&gt;
&lt;br /&gt;
Three primary steps are required to open the gameport device:&lt;br /&gt;
&lt;br /&gt;
* Create a message port using CreatePort(). Reply messages from the device must be directed to a message port.&lt;br /&gt;
* Create an I/O request structure of type IOStdReq. The IOStdReq structure is created by the CreateExtIO() function. CreateExtIO() will initialize the I/O request with your reply port.&lt;br /&gt;
* Open the gameport device. Call OpenDevice(), passing the I/O request and and indicating the unit you wish to use.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct MsgPort *GameMP;   /* Message port pointer */&lt;br /&gt;
struct IOStdReq *GameIO;  /* I/O request pointer */&lt;br /&gt;
&lt;br /&gt;
  /* Create port for gameport device communications */&lt;br /&gt;
if (!(GameMP = CreatePort(&amp;amp;quot;RKM_game_port&amp;amp;quot;,0)))&lt;br /&gt;
    cleanexit(&amp;amp;quot; Error: Can&#039;t create port\n&amp;amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
  /* Create message block for device I/O */&lt;br /&gt;
if (!(GameIO = CreateExtIO(GameMP,sizeof(struct IOStdReq))))&lt;br /&gt;
    cleanexit(&amp;amp;quot; Error: Can&#039;t create I/O request\n&amp;amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
  /* Open the right/back (unit 1, number 2) gameport.device unit */&lt;br /&gt;
if (error=OpenDevice(&amp;amp;quot;gameport.device&amp;amp;quot;,1,GameIO,0))&lt;br /&gt;
    cleanexit(&amp;amp;quot; Error: Can&#039;t open gameport.device\n&amp;amp;quot;,RETURN_FAIL);&amp;lt;/pre&amp;gt;&lt;br /&gt;
The gameport commands are unit specific. The unit number specified in the call to OpenDevice() determines which unit is acted upon.&lt;br /&gt;
&lt;br /&gt;
=== Gameport Device Controllers ===&lt;br /&gt;
&lt;br /&gt;
The Amiga has five gameport device controller types.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gameport Device Controllers&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Controller Type&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| GPCT_MOUSE&lt;br /&gt;
| Mouse controller&lt;br /&gt;
|-&lt;br /&gt;
| GPCT_ABSJOYSTICK&lt;br /&gt;
| Absolute (digital) joystick&lt;br /&gt;
|-&lt;br /&gt;
| GPCT_RELJOYSTICK&lt;br /&gt;
| Relative (digital) joystick&lt;br /&gt;
|-&lt;br /&gt;
| GPCT_ALLOCATED&lt;br /&gt;
| Custom controller&lt;br /&gt;
|-&lt;br /&gt;
| GPCT_NOCONTROLLER&lt;br /&gt;
| No controller&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To use the gameport device, you must define the type of device connected to the gameport and define how the device is to respond. The gameport device can be set up to return the controller status immediately or only when certain conditions have been met.&lt;br /&gt;
&lt;br /&gt;
When a gameport device unit reponds to a request for input, it creates an input event. The contents of the input event will vary based on the type of device and the trigger conditions you have declared.&lt;br /&gt;
&lt;br /&gt;
* A mouse controller can report input events for one, two, or three buttons and for positive or negative (x,y) movements. A trackball controller or car-driving controller is generally of the same type and can be declared as a mouse controller.&lt;br /&gt;
&lt;br /&gt;
* An absolute joystick reports one single event for each change of its current location. If, for example, the joystick is centered and the user pushes the stick forward and holds it in that position, only one single forward-switch event will be generated.&lt;br /&gt;
&lt;br /&gt;
* A relative joystick, on the other hand, is comparable to an absolute joystick with “autorepeat” installed. As long as the user holds the stick in a position other than centered, the gameport device continues to generate position reports.&lt;br /&gt;
&lt;br /&gt;
* There is currently no system software support for proportional joysticks or proportional controllers (e.g., paddles). If you write custom code to read proportional controllers or other controllers (e.g., light pen) make certain that you issue GPD_SETCTYPE (explained below) with controller type GPCT_ALLOCATED to insure that other applications know the connector is being used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;GPCT_NOCONTROLLER&#039;&#039; The controller type GPCT_NOCONTROLLER is not a controller at all, but a flag to indicate that the unit is not being used at the present time.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Closing the Gameport Device ===&lt;br /&gt;
&lt;br /&gt;
Each OpenDevice() must eventually be matched by a call to CloseDevice().&lt;br /&gt;
&lt;br /&gt;
All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO() and remove them with WaitIO().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if (!(CheckIO(GameIO)))&lt;br /&gt;
    {&lt;br /&gt;
    AbortIO(GameIO);  /* Ask device to abort request, if pending */&lt;br /&gt;
    }&lt;br /&gt;
WaitIO((GameIO);   /* Wait for abort, then clean up */&lt;br /&gt;
CloseDevice(GameIO);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gameport Events ==&lt;br /&gt;
&lt;br /&gt;
A gameport event is an InputEvent structure which describes the following:&lt;br /&gt;
&lt;br /&gt;
* The class of the event - always set to IECLASS_RAWMOUSE for the gameport device.&lt;br /&gt;
* The subclass of the event - 0 for the left port; 1 for the right port.&lt;br /&gt;
* The code - which button and its state. (No report = 0xFF)&lt;br /&gt;
* The qualifier - only button and relative mouse bits are set.&lt;br /&gt;
* The position - either a data address or mouse position count.&lt;br /&gt;
* The time stamp - delta time since last report, returned as frame count in tv_secs field.&lt;br /&gt;
* The next event - pointer to next event.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct InputEvent GameEV&lt;br /&gt;
{&lt;br /&gt;
    struct InputEvent *ie_NextEvent;  /* next event */&lt;br /&gt;
    UBYTE    ie_Class;                /* input event class */&lt;br /&gt;
    UBYTE    ie_SubClass;             /* subclass of the class */&lt;br /&gt;
    UWORD    ie_Code;                 /* input event code */&lt;br /&gt;
    UWORD    ie_Qualifier;            /* event qualifiers in effect */&lt;br /&gt;
       union&lt;br /&gt;
       {&lt;br /&gt;
         struct&lt;br /&gt;
         {&lt;br /&gt;
          WORD   ie_x;                /* x position for the event */&lt;br /&gt;
          WORD   ie_y;                /* y position for the event */&lt;br /&gt;
         }  ie_xy;&lt;br /&gt;
         APTR ie_addr;&lt;br /&gt;
       } ie_position;&lt;br /&gt;
    struct timeval ie_TimeStamp;      /* delta time since last report&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
See the include file devices/inputevent.h for the complete structure definition and listing of input event fields.&lt;br /&gt;
&lt;br /&gt;
=== Reading Gameport Events ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You read gameport events by passing an I/O request to the device with GPD_READEVENT set in io_Command, the address of the InputEvent structure to store events set in io_Data and the size of the structure set in io_Length.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct InputEvent  GameEV;&lt;br /&gt;
struct IOStdRequest *GameIO;  /* Must be initialized prior to using */&lt;br /&gt;
&lt;br /&gt;
void send_read_request()&lt;br /&gt;
{&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_READEVENT;  /* Read events */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length = sizeof (struct InputEvent);&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)&amp;amp;amp;GameEV;     /* put events in GameEV*/&lt;br /&gt;
SendIO(GameIO);                      /* Asynchronous */&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Setting Gameport Event Trigger Conditions ===&lt;br /&gt;
&lt;br /&gt;
You set the conditions that can trigger a gameport event by passing an I/O request to the device with GPD_SETTRIGGER set in io_Command and the address of a GamePortTrigger structure set in io_Data.&lt;br /&gt;
&lt;br /&gt;
The information needed for gameport trigger setting is placed into a GamePortTrigger data structure which is defined in the include file devices/gameport.h.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct GamePortTrigger&lt;br /&gt;
{&lt;br /&gt;
    UWORD    gpt_Keys;      /* key transition triggers */&lt;br /&gt;
    UWORD    gpt_Timeout;   /* time trigger (vertical blank units) */&lt;br /&gt;
    UWORD    gpt_XDelta;    /* X distance trigger */&lt;br /&gt;
    UWORD    gpt_YDelta;    /* Y distance trigger */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A few points to keep in mind with the GPD_SETTRIGGER command are:&lt;br /&gt;
&lt;br /&gt;
* Setting GPTF_UPKEYS enables the reporting of upward transitions. Setting GPTF_DOWNKEYS enables the reporting of downward transitions. These flags may both be specified.&lt;br /&gt;
&lt;br /&gt;
* The field gpt_Timeout specifies the time interval (in vertical blank units) between reports in the absence of another trigger condition. In other words, an event is generated every gpt_Timeout ticks. Vertical blank units may differ from country to country (e.g 60 Hz NTSC, 50 Hz PAL). To find out the exact frequency use this code fragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/execbase.h&amp;gt;&lt;br /&gt;
extern struct ExecBase *SysBase;&lt;br /&gt;
&lt;br /&gt;
UBYTE get_frequency(void)&lt;br /&gt;
{&lt;br /&gt;
  return((UBYTE)SysBase-&amp;gt;VBlankFrequency);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* The gpt_XDelta and gpt_YDelta fields specify the x and y distances which, if exceeded, trigger a report.&lt;br /&gt;
&lt;br /&gt;
For a mouse controller, you can trigger on a certain minimum-sized move in either the x or y direction, on up or down transitions of the mouse buttons, on a timed basis, or any combination of these conditions.&lt;br /&gt;
&lt;br /&gt;
For example, suppose you normally signal mouse events if the mouse moves at least 10 counts in either the x or y directions. If you are moving the cursor to keep up with mouse movements and the user moves the mouse less than 10 counts, after a period of time you will want to update the position of the cursor to exactly match the mouse position. Thus the timed report of current mouse counts would be preferred. The following structure would be used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define XMOVE 10&lt;br /&gt;
#define YMOVE 10&lt;br /&gt;
&lt;br /&gt;
struct GamePortTrigger GameTR =&lt;br /&gt;
{&lt;br /&gt;
    GPTF_UPKEYS | GPTF_DOWNKEYS,   /* trigger on all key transitions */&lt;br /&gt;
    1800,                          /* and every 36(PAL) or 30(NTSC) seconds */&lt;br /&gt;
    XMOVE,                         /* for any 10 in an x or y direction */&lt;br /&gt;
    YMOVE&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a joystick controller, you can select timed reports as well as button-up and button-down report trigger conditions. For an absolute joystick specify a value of one (1) for the GameTR_XDelta and GameTR_YDelta fields or you will not get any direction events. You set the trigger conditions by using the following code or its equivalent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct IOStdReq *GameIO;&lt;br /&gt;
&lt;br /&gt;
void set_trigger_conditions(struct GamePortTrigger *GameTR)&lt;br /&gt;
{&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_SETTRIGGER;    /* set trigger conditions */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)GameTR;         /* from GameTR */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length = sizeof(struct GamePortTrigger);&lt;br /&gt;
DoIO(GameIO);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;Triggers and Reads.&#039;&#039; If a task sets trigger conditions and does not ask for the position reports the gameport device will queue them up anyway. If the trigger conditions occur again and the gameport device buffer is filled, the additional triggers will be ignored until the buffer is read by a device read request (GPD_READEVENT) or a system CMD_CLEAR command flushes the buffer.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Determining the Trigger Conditions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You determine the conditions required for triggering gameport events by passing an I/O request to the device with GPD_ASKTRIGGER set in io_Command, the length of the GamePortTrigger structure set in io_Length and the address of the structure set in io_Data. The gameport device will respond with the event trigger conditions currently set.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct IOStdReq *GameIO;  /* Must be initialized prior to using */&lt;br /&gt;
&lt;br /&gt;
struct GamePortTrigger GameTR;&lt;br /&gt;
&lt;br /&gt;
void get_trigger_conditions(struct GamePortTrigger *GameTR)&lt;br /&gt;
{&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_ASKTRIGGER;    /* get type of triggers */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)GameTR;         /* place data here */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length= sizeof(GameTR);&lt;br /&gt;
DoIO(GameIO);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Setting and Reading the Controller Type ==&lt;br /&gt;
&lt;br /&gt;
=== Determining the Controller Type ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You determine the type of controller being used by passing an I/O request to the device with GPD_ASKCTYPE set in io_Command, 1 set in io_Length and the number of the unit set in io_Unit. The gameport device will respond with the type of controller being used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct IOStdReq *GameIO;  /* Must be initialized prior to using */&lt;br /&gt;
&lt;br /&gt;
BYTE GetControllerType()&lt;br /&gt;
{&lt;br /&gt;
BYTE controller_type = 0;&lt;br /&gt;
&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_ASKCTYPE;         /* get type of controller */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)&amp;amp;amp;controller_type;  /* place data here */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length = 1;&lt;br /&gt;
DoIO(GameIO);&lt;br /&gt;
return (controller_type);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
The BYTE value returned corresponds to one of the five controller types noted above.&lt;br /&gt;
&lt;br /&gt;
=== Setting the Controller Type ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You set the type of gameport controller by passing an I/O request to the device with GPD_SETCTYPE set in io_Command, 1 set in io_Length and the address of the byte variable describing the controller type set in io_Data.&lt;br /&gt;
&lt;br /&gt;
The gameport device is a shared device; many tasks may have it open at any given time. Hence, a high level protocol has been established to prevent multiple tasks from reading the same unit at the same time.&lt;br /&gt;
&lt;br /&gt;
==== Three Step Protocol for Using the Gameport Device ====&lt;br /&gt;
&lt;br /&gt;
Send GPD_ASKCTYPE to the device and check for a GPCT_NOCONTROLLER return. &#039;&#039;Never&#039;&#039; issue GPD_SETCTYPE without checking whether the desired gameport unit is in use.&lt;br /&gt;
&lt;br /&gt;
If GPCT_NOCONTROLLER is returned, you have access to the gameport. Set the allocation flag to GPCT_MOUSE, GPCT_ABSJOYSTICK or GPCT_RELJOYSTICK if you use a system supported controller, or GPCT_ALLOCATED if you use a custom controller.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct IOStdReq *GameIO;  /* Must be initialized prior to using */&lt;br /&gt;
&lt;br /&gt;
BOOL set_controller_type(type)&lt;br /&gt;
BYTE type;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
BOOL success = FALSE;&lt;br /&gt;
BYTE controller_type = 0;&lt;br /&gt;
&lt;br /&gt;
Forbid();                           /*critical section start */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_ASKCTYPE;  /* inquire current status */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length = 1;&lt;br /&gt;
GameIO-&amp;amp;gt;io_Flags = IOF_QUICK;&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)&amp;amp;amp;controller_type; /* put answer in here */&lt;br /&gt;
DoIO(GameIO);&lt;br /&gt;
&lt;br /&gt;
/* No one is using this device unit, let&#039;s claim it */&lt;br /&gt;
if (controller_type == GPCT_NOCONTROLLER)&lt;br /&gt;
    {&lt;br /&gt;
    GameIO-&amp;amp;gt;io_Command = GPD_SETCTYPE;/* set controller type */&lt;br /&gt;
    GameIO-&amp;amp;gt;io_Length = 1;&lt;br /&gt;
    GameIO-&amp;amp;gt;io_Data = (APTR)&amp;amp;amp;type;  /* set to input param */&lt;br /&gt;
    DoIO( GameIO);&lt;br /&gt;
    success = TRUE;&lt;br /&gt;
    UnitOpened = TRUE;&lt;br /&gt;
    }&lt;br /&gt;
Permit(); /* critical section end */&lt;br /&gt;
&lt;br /&gt;
/* success can be TRUE or FALSE, see above */&lt;br /&gt;
return(success);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The program must set the controller type back to GPCT_NOCONTROLLER upon exiting your program:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct IOStdReq *GameIO;  /* Must be initialized prior to using */&lt;br /&gt;
&lt;br /&gt;
void free_gp_unit()&lt;br /&gt;
{&lt;br /&gt;
BYTE type = GPCT_NOCONTROLLER;&lt;br /&gt;
GameIO-&amp;amp;gt;io_Command = GPD_SETCTYPE;  /* set controller type */&lt;br /&gt;
GameIO-&amp;amp;gt;io_Length = 1;&lt;br /&gt;
GameIO-&amp;amp;gt;io_Data = (APTR)&amp;amp;amp;type;      /* set to unused */&lt;br /&gt;
DoIO( GameIO);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This three step protocol allows applications to share the gameport device in a system compatible way.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;A Word About The Functions.&#039;&#039; The functions shown above are designed to be included in any application using the gameport device. The first function, set_controller_type(), would be the first thing done after opening the gameport device. The second function, free_gp_unit(), would be the last thing done before closing the device.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Joystick Example Program ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
 * Absolute_Joystick.c&lt;br /&gt;
 *&lt;br /&gt;
 * Gameport device absolute joystick example&lt;br /&gt;
 *&lt;br /&gt;
 * Compile with SAS 5.10  lc -b1 -cfistq -v -y -L&lt;br /&gt;
 *&lt;br /&gt;
 * Run from CLI only&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/io.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/intuition.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/exec.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/gameport.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/inputevent.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/dos_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/intuition_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void) { return(0); }     /* Disable SAS CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define JOY_X_DELTA (1)&lt;br /&gt;
#define JOY_Y_DELTA (1)&lt;br /&gt;
#define TIMEOUT_SECONDS (10)&lt;br /&gt;
&lt;br /&gt;
extern struct ExecBase *SysBase;&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** Routine to print out some information for the user.&lt;br /&gt;
*/&lt;br /&gt;
VOID printInstructions(VOID)&lt;br /&gt;
{&lt;br /&gt;
printf(&amp;amp;quot;\n &amp;amp;gt;&amp;amp;gt;&amp;amp;gt; gameport.device Absolute Joystick Demo &amp;amp;lt;&amp;amp;lt;&amp;amp;lt;\n\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (SysBase-&amp;amp;gt;VBlankFrequency==60)&lt;br /&gt;
    printf(&amp;amp;quot; Running on NTSC system (60 Hz).\n&amp;amp;quot;);&lt;br /&gt;
else if (SysBase-&amp;amp;gt;VBlankFrequency==50)&lt;br /&gt;
    printf(&amp;amp;quot; Running on PAL system (50 Hz).\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
printf( &amp;amp;quot; Attach joystick to rear connector (A3000) and (A1000).\n&amp;amp;quot;&lt;br /&gt;
        &amp;amp;quot; Attach joystick to right connector (A2000).\n&amp;amp;quot;&lt;br /&gt;
        &amp;amp;quot; Attach joystick to left connector (A500).\n&amp;amp;quot;&lt;br /&gt;
        &amp;amp;quot; Then move joystick and click its button(s).\n\n&amp;amp;quot;&lt;br /&gt;
        &amp;amp;quot; To exit program press and release fire button 3 consecutive times. \n&amp;amp;quot;&lt;br /&gt;
        &amp;amp;quot; The program also exits if no activity occurs for 1 minute.\n\n&amp;amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** print out information on the event received.&lt;br /&gt;
*/&lt;br /&gt;
BOOL check_move(struct InputEvent *game_event)&lt;br /&gt;
{&lt;br /&gt;
WORD xmove, ymove;&lt;br /&gt;
BOOL timeout=FALSE;&lt;br /&gt;
&lt;br /&gt;
xmove = game_event-&amp;amp;gt;ie_X;&lt;br /&gt;
ymove = game_event-&amp;amp;gt;ie_Y;&lt;br /&gt;
&lt;br /&gt;
if (xmove == 1)&lt;br /&gt;
    {&lt;br /&gt;
    if (ymove == 1) printf(&amp;amp;quot;RIGHT DOWN\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove == 0) printf(&amp;amp;quot;RIGHT\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove ==-1) printf(&amp;amp;quot;RIGHT UP\n&amp;amp;quot;);&lt;br /&gt;
    else printf(&amp;amp;quot;UNKNOWN Y\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else if (xmove ==-1)&lt;br /&gt;
    {&lt;br /&gt;
    if (ymove == 1) printf(&amp;amp;quot;LEFT DOWN\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove == 0) printf(&amp;amp;quot;LEFT\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove ==-1) printf(&amp;amp;quot;LEFT UP\n&amp;amp;quot;);&lt;br /&gt;
    else printf(&amp;amp;quot;UNKNOWN Y\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else if (xmove == 0)&lt;br /&gt;
    {&lt;br /&gt;
    if (ymove == 1) printf(&amp;amp;quot;DOWN\n&amp;amp;quot;);&lt;br /&gt;
    /* note that 0,0 can be a timeout, or a direction release. */&lt;br /&gt;
    else if (ymove == 0)&lt;br /&gt;
        {&lt;br /&gt;
        if (game_event-&amp;amp;gt;ie_TimeStamp.tv_secs &amp;amp;gt;=&lt;br /&gt;
                        (UWORD)(SysBase-&amp;amp;gt;VBlankFrequency) * TIMEOUT_SECONDS)&lt;br /&gt;
            {&lt;br /&gt;
            printf(&amp;amp;quot;TIMEOUT\n&amp;amp;quot;);&lt;br /&gt;
            timeout=TRUE;&lt;br /&gt;
            }&lt;br /&gt;
        else printf(&amp;amp;quot;RELEASE\n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    else if (ymove ==-1) printf(&amp;amp;quot;UP\n&amp;amp;quot;);&lt;br /&gt;
    else printf(&amp;amp;quot;UNKNOWN Y\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    printf(&amp;amp;quot;UNKNOWN X &amp;amp;quot;);&lt;br /&gt;
    if (ymove == 1) printf(&amp;amp;quot;unknown action\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove == 0) printf(&amp;amp;quot;unknown action\n&amp;amp;quot;);&lt;br /&gt;
    else if (ymove ==-1) printf(&amp;amp;quot;unknown action\n&amp;amp;quot;);&lt;br /&gt;
    else printf(&amp;amp;quot;UNKNOWN Y\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
return(timeout);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** send a request to the gameport to read an event.&lt;br /&gt;
*/&lt;br /&gt;
VOID send_read_request( struct InputEvent *game_event,&lt;br /&gt;
                        struct IOStdReq *game_io_msg)&lt;br /&gt;
{&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Command = GPD_READEVENT;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Flags   = 0;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Data    = (APTR)game_event;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Length  = sizeof(struct InputEvent);&lt;br /&gt;
SendIO(game_io_msg);  /* Asynchronous - message will return later */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** simple loop to process gameport events.&lt;br /&gt;
*/&lt;br /&gt;
VOID processEvents( struct IOStdReq *game_io_msg,&lt;br /&gt;
                    struct MsgPort  *game_msg_port)&lt;br /&gt;
{&lt;br /&gt;
BOOL timeout;&lt;br /&gt;
SHORT timeouts;&lt;br /&gt;
SHORT button_count;&lt;br /&gt;
BOOL  not_finished;&lt;br /&gt;
struct InputEvent game_event;   /* where input event will be stored */&lt;br /&gt;
&lt;br /&gt;
/* From now on, just read input events into the event buffer,&lt;br /&gt;
** one at a time.  READEVENT waits for the preset conditions.&lt;br /&gt;
*/&lt;br /&gt;
timeouts = 0;&lt;br /&gt;
button_count = 0;&lt;br /&gt;
not_finished = TRUE;&lt;br /&gt;
&lt;br /&gt;
while ((timeouts &amp;amp;lt; 6) &amp;amp;amp;&amp;amp;amp; (not_finished))&lt;br /&gt;
    {&lt;br /&gt;
    /* Send the read request */&lt;br /&gt;
    send_read_request(&amp;amp;amp;game_event,game_io_msg);&lt;br /&gt;
&lt;br /&gt;
    /* Wait for joystick action */&lt;br /&gt;
    Wait(1L &amp;amp;lt;&amp;amp;lt; game_msg_port-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
    while (NULL != GetMsg(game_msg_port))&lt;br /&gt;
        {&lt;br /&gt;
        timeout=FALSE;&lt;br /&gt;
        switch(game_event.ie_Code)&lt;br /&gt;
            {&lt;br /&gt;
            case IECODE_LBUTTON:&lt;br /&gt;
                printf(&amp;amp;quot; FIRE BUTTON PRESSED \n&amp;amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            case (IECODE_LBUTTON | IECODE_UP_PREFIX):&lt;br /&gt;
                printf(&amp;amp;quot; FIRE BUTTON RELEASED \n&amp;amp;quot;);&lt;br /&gt;
                if (3 == ++button_count)&lt;br /&gt;
                    not_finished = FALSE;&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            case IECODE_RBUTTON:&lt;br /&gt;
                printf(&amp;amp;quot; ALT BUTTON PRESSED \n&amp;amp;quot;);&lt;br /&gt;
                button_count = 0;&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            case (IECODE_RBUTTON | IECODE_UP_PREFIX):&lt;br /&gt;
                printf(&amp;amp;quot; ALT BUTTON RELEASED \n&amp;amp;quot;);&lt;br /&gt;
                button_count = 0;&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            case IECODE_NOBUTTON:&lt;br /&gt;
                /* Check for change in position */&lt;br /&gt;
                timeout = check_move(&amp;amp;amp;game_event);&lt;br /&gt;
                button_count = 0;&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            default:&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        if (timeout)&lt;br /&gt;
            timeouts++;&lt;br /&gt;
        else&lt;br /&gt;
            timeouts=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** allocate the controller if it is available.&lt;br /&gt;
** you allocate the controller by setting its type to something&lt;br /&gt;
** other than GPCT_NOCONTROLLER.  Before you allocate the thing&lt;br /&gt;
** you need to check if anyone else is using it (it is free if&lt;br /&gt;
** it is set to GPCT_NOCONTROLLER).&lt;br /&gt;
*/&lt;br /&gt;
BOOL set_controller_type(BYTE type, struct IOStdReq *game_io_msg)&lt;br /&gt;
{&lt;br /&gt;
BOOL success = FALSE;&lt;br /&gt;
BYTE controller_type = 0;&lt;br /&gt;
&lt;br /&gt;
/* begin critical section&lt;br /&gt;
** we need to be sure that between the time we check that the controller&lt;br /&gt;
** is available and the time we allocate it, no one else steals it.&lt;br /&gt;
*/&lt;br /&gt;
Forbid();&lt;br /&gt;
&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Command = GPD_ASKCTYPE;    /* inquire current status */&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Data    = (APTR)&amp;amp;amp;controller_type; /* put answer in here */&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Length  = 1;&lt;br /&gt;
DoIO(game_io_msg);&lt;br /&gt;
&lt;br /&gt;
/* No one is using this device unit, let&#039;s claim it */&lt;br /&gt;
if (controller_type == GPCT_NOCONTROLLER)&lt;br /&gt;
    {&lt;br /&gt;
    game_io_msg-&amp;amp;gt;io_Command = GPD_SETCTYPE;&lt;br /&gt;
    game_io_msg-&amp;amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
    game_io_msg-&amp;amp;gt;io_Data    = (APTR)&amp;amp;amp;type;&lt;br /&gt;
    game_io_msg-&amp;amp;gt;io_Length  = 1;&lt;br /&gt;
    DoIO( game_io_msg);&lt;br /&gt;
    success = TRUE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Permit(); /* critical section end */&lt;br /&gt;
return(success);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** tell the gameport when to trigger.&lt;br /&gt;
*/&lt;br /&gt;
VOID set_trigger_conditions(struct GamePortTrigger *gpt,&lt;br /&gt;
                            struct IOStdReq *game_io_msg)&lt;br /&gt;
{&lt;br /&gt;
/* trigger on all joystick key transitions */&lt;br /&gt;
gpt-&amp;amp;gt;gpt_Keys   = GPTF_UPKEYS | GPTF_DOWNKEYS;&lt;br /&gt;
gpt-&amp;amp;gt;gpt_XDelta = JOY_X_DELTA;&lt;br /&gt;
gpt-&amp;amp;gt;gpt_YDelta = JOY_Y_DELTA;&lt;br /&gt;
/* timeout trigger every TIMEOUT_SECONDS second(s) */&lt;br /&gt;
gpt-&amp;amp;gt;gpt_Timeout = (UWORD)(SysBase-&amp;amp;gt;VBlankFrequency) * TIMEOUT_SECONDS;&lt;br /&gt;
&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Command = GPD_SETTRIGGER;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Data    = (APTR)gpt;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Length  = (LONG)sizeof(struct GamePortTrigger);&lt;br /&gt;
DoIO(game_io_msg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** clear the buffer.  do this before you begin to be sure you&lt;br /&gt;
** start in a known state.&lt;br /&gt;
*/&lt;br /&gt;
VOID flush_buffer(struct IOStdReq *game_io_msg)&lt;br /&gt;
{&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Command = CMD_CLEAR;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Data    = NULL;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Length  = 0;&lt;br /&gt;
DoIO(game_io_msg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** free the unit by setting its type back to GPCT_NOCONTROLLER.&lt;br /&gt;
*/&lt;br /&gt;
VOID free_gp_unit(struct IOStdReq *game_io_msg)&lt;br /&gt;
{&lt;br /&gt;
BYTE type = GPCT_NOCONTROLLER;&lt;br /&gt;
&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Command = GPD_SETCTYPE;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Data    = (APTR)&amp;amp;amp;type;&lt;br /&gt;
game_io_msg-&amp;amp;gt;io_Length  = 1;&lt;br /&gt;
DoIO(game_io_msg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*-----------------------------------------------------------------------&lt;br /&gt;
** allocate everything and go.  On failure, free any resources that&lt;br /&gt;
** have been allocated.  this program fails quietly--no error messages.&lt;br /&gt;
*/&lt;br /&gt;
VOID main(int argc,char **argv)&lt;br /&gt;
{&lt;br /&gt;
struct GamePortTrigger   joytrigger;&lt;br /&gt;
struct IOStdReq         *game_io_msg;&lt;br /&gt;
struct MsgPort          *game_msg_port;&lt;br /&gt;
&lt;br /&gt;
/* Create port for gameport device communications */&lt;br /&gt;
if (game_msg_port = CreatePort(&amp;amp;quot;RKM_game_port&amp;amp;quot;,0))&lt;br /&gt;
    {&lt;br /&gt;
    /* Create message block for device IO */&lt;br /&gt;
    if (game_io_msg = (struct IOStdReq *)&lt;br /&gt;
                      CreateExtIO(game_msg_port,sizeof(*game_io_msg)))&lt;br /&gt;
        {&lt;br /&gt;
        game_io_msg-&amp;amp;gt;io_Message.mn_Node.ln_Type = NT_UNKNOWN;&lt;br /&gt;
&lt;br /&gt;
        /* Open the right/back (unit 1, number 2) gameport.device unit */&lt;br /&gt;
        if (!OpenDevice(&amp;amp;quot;gameport.device&amp;amp;quot;,1,game_io_msg,0))&lt;br /&gt;
            {&lt;br /&gt;
            /* Set controller type to joystick */&lt;br /&gt;
            if (set_controller_type(GPCT_ABSJOYSTICK,game_io_msg))&lt;br /&gt;
                {&lt;br /&gt;
                /* Specify the trigger conditions */&lt;br /&gt;
                set_trigger_conditions(&amp;amp;amp;joytrigger,game_io_msg);&lt;br /&gt;
&lt;br /&gt;
                printInstructions();&lt;br /&gt;
&lt;br /&gt;
                /* Clear device buffer to start from a known state.&lt;br /&gt;
                ** There might still be events left&lt;br /&gt;
                */&lt;br /&gt;
                flush_buffer(game_io_msg);&lt;br /&gt;
&lt;br /&gt;
                processEvents(game_io_msg,game_msg_port);&lt;br /&gt;
&lt;br /&gt;
                /* Free gameport unit so other applications can use it ! */&lt;br /&gt;
                free_gp_unit(game_io_msg);&lt;br /&gt;
                }&lt;br /&gt;
            CloseDevice(game_io_msg);&lt;br /&gt;
            }&lt;br /&gt;
        DeleteExtIO(game_io_msg);&lt;br /&gt;
        }&lt;br /&gt;
    DeletePort(game_msg_port);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Additional Information on the Gameport Device ==&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the gameport device can be found in the include files and the Autodocs for the gameport and input devices. Both are contained in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Includes&lt;br /&gt;
|-&lt;br /&gt;
| devices/gameport.h&lt;br /&gt;
|-&lt;br /&gt;
| devices/inputevent.h&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! AutoDocs&lt;br /&gt;
|-&lt;br /&gt;
| gameport.doc&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmiWest_2013_Lesson_4&amp;diff=12556</id>
		<title>AmiWest 2013 Lesson 4</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmiWest_2013_Lesson_4&amp;diff=12556"/>
		<updated>2025-01-26T19:31:42Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Simple IP Clients &amp;amp; Servers =&lt;br /&gt;
&lt;br /&gt;
Simple network access is easy!&lt;br /&gt;
&lt;br /&gt;
Developing applications that use TCP/IP &amp;quot;sockets&amp;quot; for network communications&lt;br /&gt;
have a reputation of being a challenging task.  But AmigaOS and its Roadshow &lt;br /&gt;
TCP/IP stack provides a couple of built-in shorthand mechanisms for easily&lt;br /&gt;
creating simple network clients and servers: the TCP: handler and Roadshow&#039;s&lt;br /&gt;
Superserver.&lt;br /&gt;
&lt;br /&gt;
Using these Roadshow shortcuts, creating simple network client and server&lt;br /&gt;
applications for single user or light-duty uses is no more complicated than&lt;br /&gt;
reading or writing a file.  The real challenge is understanding the protocol&lt;br /&gt;
to be used (f.e. HTTP communications with web browsers) and being careful not&lt;br /&gt;
to create a security risk on your or another user&#039;s Amiga.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==An Internet Client==&lt;br /&gt;
&lt;br /&gt;
First, we will look at creating a small client to retrieve some simple&lt;br /&gt;
information from an Internet website.  Such applications could be made&lt;br /&gt;
to retrieve basic information like stock quotes, email contents, &lt;br /&gt;
server conditions or other basic interactions.&lt;br /&gt;
&lt;br /&gt;
The Roadshow shortcut we will use is its built-in TCP: handler.  This is&lt;br /&gt;
mechanism that creates a virtual file system device  (&amp;quot;TCP:&amp;quot;) whenever&lt;br /&gt;
Roadhsow gets online.  To interact with a remote server, one3 opens a&lt;br /&gt;
file with the name being the URL of the server and port to be accessed.&lt;br /&gt;
&lt;br /&gt;
===Connect to the Server===&lt;br /&gt;
&lt;br /&gt;
When Roadshow gets online, it creates a virtual TCP: device.  To open&lt;br /&gt;
a connection, one opens a virtual file on the TCP: device to the URL and&lt;br /&gt;
port in question using this format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
TCP:&amp;lt;server URL&amp;gt;/&amp;lt;port number&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to connect to the website &amp;quot;www.wunderground.com&amp;quot; one would&lt;br /&gt;
use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	fileh = fopen(&amp;quot;TCP:www.wunderground.com/80&amp;quot;,&amp;quot;r+&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file name starts with TCP:, then the website URL and finally the&lt;br /&gt;
port number - here &amp;quot;80&amp;quot; is site&#039;s HTTP port (used by almost all websites).&lt;br /&gt;
&lt;br /&gt;
Typically, the port number reflects the type of port or protocol that&lt;br /&gt;
is to be used.  Such protocols could be FTP (file transfer), POP/SMTP&lt;br /&gt;
(email) or many others.  Port 80 is usually used for HTTP communications&lt;br /&gt;
with websites.&lt;br /&gt;
&lt;br /&gt;
If the file open request is successful, your Amiga is then connected to&lt;br /&gt;
that server and any further interaction with the &amp;quot;fileh&amp;quot; file handle&lt;br /&gt;
communicates directly with that port on that server.  In this case,&lt;br /&gt;
we would have connected to a web server of the &amp;quot;Weather Underground&amp;quot;&lt;br /&gt;
website.&lt;br /&gt;
&lt;br /&gt;
===Talk to the Server===&lt;br /&gt;
&lt;br /&gt;
Once one has connected to a server, knowledge of the communications&lt;br /&gt;
protocol for that type of connection is required.   &lt;br /&gt;
&lt;br /&gt;
For each of those, a server expects a certain format of interaction to&lt;br /&gt;
proceed and respond.  Fortunately, common protocols are publicly&lt;br /&gt;
documented.&lt;br /&gt;
&lt;br /&gt;
	http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers&lt;br /&gt;
&lt;br /&gt;
For each protocol there is usually an &amp;quot;RFC&amp;quot; document that describes how one&lt;br /&gt;
uses the protocol, interacts with the port and accomplishes that protocol&#039;s&lt;br /&gt;
goals.&lt;br /&gt;
&lt;br /&gt;
In the case of an  &amp;quot;HTTP&amp;quot; (or a &amp;quot;Hypertext Transfer Protocol&amp;quot;) connection to&lt;br /&gt;
a web server, we are usually expected to submit a &amp;quot;GET&amp;quot; request.  Here are&lt;br /&gt;
a couple more complete references on the HTTP protocol:&lt;br /&gt;
&lt;br /&gt;
	http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol&lt;br /&gt;
&lt;br /&gt;
	http://tools.ietf.org/html/rfc2616&lt;br /&gt;
&lt;br /&gt;
To keep things simple, lets mimic submitting a &amp;quot;GET&amp;quot; request (as if from IBrowse).&lt;br /&gt;
A simple HTTP version 1.1 GET request would include the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	GET /&amp;lt;path&amp;gt; HTTP/1.1&lt;br /&gt;
	host: &amp;lt;domain&amp;gt;&lt;br /&gt;
	user-agent: IBrowse/2.4 (AmigaOS 4.1; PPC; 68K build)&lt;br /&gt;
	Pragma: no-cache&lt;br /&gt;
	Accept-Language: en, *&lt;br /&gt;
	Accept: text/html;level=3&lt;br /&gt;
	Accept: text/html;version=3.0&lt;br /&gt;
	Accept: */*&lt;br /&gt;
	&amp;lt;blank line&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;&amp;lt;path&amp;gt;&#039;&#039;&#039; would be the rest of the URL (if any) on that system and&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;domain&amp;gt;&#039;&#039;&#039; is the name of the server.  &lt;br /&gt;
&lt;br /&gt;
Using the &amp;quot;WeatherUnderground&amp;quot; website URL from above and their path name for&lt;br /&gt;
searching for airport conditions, we can find the current conditions at&lt;br /&gt;
Washington&#039;s Dulles International Airport with these domain &amp;amp; path values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   URL = www.wunderground.com&lt;br /&gt;
   PATH = cgi-bin/findweather/getForecast?query=IAD&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Such a GET request syntax can be transcribed into C code which builds the entire&lt;br /&gt;
request in a string and then sends it to the server with a simple &amp;quot;fprintf&amp;quot; to our open&lt;br /&gt;
TCP: file handle, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	// build web req text&lt;br /&gt;
	strcpy(line,&amp;quot;GET /&amp;quot;);&lt;br /&gt;
	strcat(line,path);&lt;br /&gt;
	strcat(line,&amp;quot; HTTP/1.1\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;host: &amp;quot;);&lt;br /&gt;
	strcat(line,domain);&lt;br /&gt;
	strcat(line,&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;user-agent: IBrowse/2.4 (AmigaOS 4.1; PPC; 68K build)\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Pragma: no-cache\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept-Language: en, *\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: text/html;level=3\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: text/html;version=3.0\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: */*\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	// send web req line to server&lt;br /&gt;
	fprintf(fileh,&amp;quot;%s&amp;quot;,line);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, the variables &amp;quot;path&amp;quot; and &amp;quot;domain&amp;quot; are used to fill in some&lt;br /&gt;
blanks in the request.  The empty line at the end (with just a &amp;quot;\r\n&amp;quot;) completes the&lt;br /&gt;
GET request, after which the server will reply to your program.&lt;br /&gt;
&lt;br /&gt;
===Listen to the Server===&lt;br /&gt;
&lt;br /&gt;
Once the GET request has been sent to the web server, the server will start&lt;br /&gt;
sending back either the requested webpage (or other content) or an error page.&lt;br /&gt;
To see what sort of content is returned by the server in this example, you can&lt;br /&gt;
combine the domain and path values above and enter them in your web&lt;br /&gt;
browser&#039;s URL string gadget, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   www.wunderground.com/cgi-bin/findweather/getForecast?query=IAD&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the page is loaded in your browser, viewing the page source (f.e., IBrowse&lt;br /&gt;
menu item &amp;quot;Page/Display Source...&amp;quot;) will show you the same HTML text that your&lt;br /&gt;
program will receive after sending the GET request.&lt;br /&gt;
&lt;br /&gt;
As such, your program needs to start reading from the file handle&lt;br /&gt;
the request was sent with.  This loop will print out the first hundred&lt;br /&gt;
lines returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	// read in the response&lt;br /&gt;
	count = 0;&lt;br /&gt;
	while( (fgets(inStr,MAX_STR,fileh) != NULL) &amp;amp;&amp;amp; (count&amp;lt;100) )&lt;br /&gt;
	{&lt;br /&gt;
		++ count;&lt;br /&gt;
		printf(&amp;quot;line %ld = %s\n&amp;quot;,count,inStr);&lt;br /&gt;
		&lt;br /&gt;
		// process lines received here !&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Within this loop your application can parse the received lines for whatever&lt;br /&gt;
data your application is trying to obtain.  In the case of our example program below,&lt;br /&gt;
the program searches for a string precedes the weather information we want to find.&lt;br /&gt;
&lt;br /&gt;
After the returned content has been read, output or otherwise processed,&lt;br /&gt;
simply closing the file handle will close the connection to the server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	fclose(fileh);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===EXAMPLE ONE: IPClient.c===&lt;br /&gt;
&lt;br /&gt;
All of the above elements are combined in the following example program&lt;br /&gt;
&amp;quot;IPClient.c&amp;quot; that asks you for an IATA airport code (f.e., IAD = Washington&lt;br /&gt;
Dulles, FRA=Frankfurt, SYD=Sydney), then parses &amp;amp; prints out the weather&lt;br /&gt;
conditions from the returned WeatherUnderground.com web page.  &lt;br /&gt;
&lt;br /&gt;
Please Note: this example program worked with the wunderground.com&lt;br /&gt;
website in 2013-2014, as the served webpages change over time, it is &lt;br /&gt;
likely the parsing in this example is likely to fail and the program&lt;br /&gt;
no longer return useful information.  Should this happen, you get to use what&lt;br /&gt;
you learned above to upgrade or replace this example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************&lt;br /&gt;
**&lt;br /&gt;
** Created by: CodeBench 0.41 (12.10.2013)&lt;br /&gt;
** Project: IPclient&lt;br /&gt;
** Date: 12-10-2013 18:51:10&lt;br /&gt;
**&lt;br /&gt;
************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*****************************************************************************&lt;br /&gt;
 *		GLOBAL VARIABLES&lt;br /&gt;
 *****************************************************************************/&lt;br /&gt;
&lt;br /&gt;
STATIC CONST_STRPTR version USED = &amp;quot;$VER: IPclient v.02 (12.10.2013)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
char URL[200] = &amp;quot;www.wunderground.com/cgi-bin/findweather/getForecast?query=&amp;quot;;&lt;br /&gt;
char domain[200];&lt;br /&gt;
char path[200];&lt;br /&gt;
char fname[256] = &amp;quot;&amp;quot;;&lt;br /&gt;
#define MAX_STR 2056&lt;br /&gt;
char line[MAX_STR] = &amp;quot;&amp;quot;;&lt;br /&gt;
char inStr[MAX_STR];&lt;br /&gt;
uint16 uLen;&lt;br /&gt;
uint16 dLen;&lt;br /&gt;
uint16 count = 0;&lt;br /&gt;
char *found;&lt;br /&gt;
FILE *fileh;&lt;br /&gt;
&lt;br /&gt;
/*****************************************************************************&lt;br /&gt;
 *		PROGRAM START&lt;br /&gt;
 *****************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main(int argc,char **argv)&lt;br /&gt;
{&lt;br /&gt;
	// get URL from user&lt;br /&gt;
	printf(&amp;quot;IPclient example\n&amp;quot;);&lt;br /&gt;
	printf(&amp;quot;======================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;   enter airport code &amp;gt;&amp;quot;);&lt;br /&gt;
	fgets(line,sizeof(line),stdin);&lt;br /&gt;
	line[strlen(line)-1] = &#039;\0&#039;;		// strip \n off end of string&lt;br /&gt;
	printf(&amp;quot;======================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// append airport code on URLprefix&lt;br /&gt;
	strcat(URL,line);&lt;br /&gt;
	&lt;br /&gt;
	// parse domain from path&lt;br /&gt;
	uLen = strlen(URL) - 2;&lt;br /&gt;
	strcpy(domain,strtok(URL,&amp;quot;/\n&amp;quot;));&lt;br /&gt;
	dLen = strlen(domain);&lt;br /&gt;
	if (uLen&amp;gt;dLen)&lt;br /&gt;
		strcpy(path,strtok(NULL,&amp;quot;\n&amp;quot;));&lt;br /&gt;
	else&lt;br /&gt;
		printf(&amp;quot;   No chars remain for path\n&amp;quot;);&lt;br /&gt;
	printf(&amp;quot;   URL domain &amp;gt;%s&amp;lt;\n&amp;quot;,domain);&lt;br /&gt;
	printf(&amp;quot;   URL path &amp;gt;%s&amp;lt;\n&amp;quot;,path);&lt;br /&gt;
	&lt;br /&gt;
	// build URL filename from domain name&lt;br /&gt;
	strcpy(fname,&amp;quot;TCP:&amp;quot;);&lt;br /&gt;
	strcat(fname,domain);&lt;br /&gt;
	strcat(fname,&amp;quot;/80&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// open file access to URL&lt;br /&gt;
	printf(&amp;quot;      Opening URL &amp;gt;%s&amp;lt;\n&amp;quot;,fname);&lt;br /&gt;
	fileh = fopen(fname,&amp;quot;r+&amp;quot;);&lt;br /&gt;
	if (fileh == NULL)&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;Couldn&#039;t open connection domain server via \&amp;quot;%s\&amp;quot;\n&amp;quot;,fname);&lt;br /&gt;
		return RETURN_ERROR;&lt;br /&gt;
	};&lt;br /&gt;
	printf(&amp;quot;      TCP: file opened\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// build web req text&lt;br /&gt;
	strcpy(line,&amp;quot;GET /&amp;quot;);&lt;br /&gt;
	strcat(line,path);&lt;br /&gt;
	strcat(line,&amp;quot; HTTP/1.1\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;host: &amp;quot;);&lt;br /&gt;
	strcat(line,domain);&lt;br /&gt;
	strcat(line,&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;user-agent: IBrowse/2.4 (AmigaOS 4.1; PPC; 68K build)\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Pragma: no-cache\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept-Language: en, *\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: text/html;level=3\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: text/html;version=3.0\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;Accept: */*\r\n&amp;quot;);&lt;br /&gt;
	strcat(line,&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// send web req line to server&lt;br /&gt;
	fprintf(fileh,&amp;quot;%s&amp;quot;,line);&lt;br /&gt;
	strcpy(line,&amp;quot;&amp;quot;);&lt;br /&gt;
	printf(&amp;quot;      Web req sent\n&amp;quot;);&lt;br /&gt;
	printf(&amp;quot;======================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// read in the response&lt;br /&gt;
	while( (fgets(inStr,MAX_STR,fileh) != NULL) &amp;amp;&amp;amp; (count&amp;lt;100) )&lt;br /&gt;
	{&lt;br /&gt;
		++ count;&lt;br /&gt;
		//printf(&amp;quot;line %d = %s\n&amp;quot;,count,inStr);&lt;br /&gt;
		&lt;br /&gt;
		// look for name of airport&lt;br /&gt;
		found = strstr(inStr,&amp;quot;og:title&amp;quot;);&lt;br /&gt;
		if (found != NULL)&lt;br /&gt;
		{&lt;br /&gt;
			found = strtok(inStr,&amp;quot;=&amp;quot;);&lt;br /&gt;
			found = strtok(NULL,&amp;quot;=&amp;quot;);&lt;br /&gt;
			printf(&amp;quot;WeatherUnderground.com reports:\n&amp;quot;);&lt;br /&gt;
			printf(&amp;quot;   Airport   = %s\n&amp;quot;,strtok(NULL,&amp;quot;|&amp;quot;)+1);&lt;br /&gt;
			printf(&amp;quot;   Temp      =%s F\n&amp;quot;,strtok(NULL,&amp;quot;&amp;amp;&amp;quot;));&lt;br /&gt;
			found = strtok(NULL,&amp;quot;|&amp;quot;);&lt;br /&gt;
			printf(&amp;quot;   Condition =%s\n&amp;quot;,strtok(NULL,&amp;quot;\&amp;quot;&amp;quot;));			&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;======================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// close file&lt;br /&gt;
	fclose(fileh);&lt;br /&gt;
	&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Designing Your Network Client===&lt;br /&gt;
&lt;br /&gt;
As you see, the above code and the example program, the mechanics&lt;br /&gt;
of connecting to a internet server and obtaining data are relatively&lt;br /&gt;
trivial.  The real challenge for the developer lies in understanding&lt;br /&gt;
the protocol used, in formatting the request and processing&lt;br /&gt;
received data.&lt;br /&gt;
&lt;br /&gt;
Public web servers make for fairly rich and easy targets to interact&lt;br /&gt;
with.  You can explore simple interaction with any web browser and mimic&lt;br /&gt;
that interaction with your code.  In many cases, you can control the&lt;br /&gt;
feedback just by crafting the web request as we did above and then&lt;br /&gt;
parse the results.&lt;br /&gt;
&lt;br /&gt;
Naturally, the more complicated the interactions, like logging into&lt;br /&gt;
a website, would require much more knowledge of the protocols used&lt;br /&gt;
and more complicated code that is beyond the scope of this tutorial.&lt;br /&gt;
&lt;br /&gt;
But there are a number of basic topics you should consider when&lt;br /&gt;
designing a web client!&lt;br /&gt;
&lt;br /&gt;
====Server Suitability:====&lt;br /&gt;
&lt;br /&gt;
Unless you are connecting to your own server or one which invites such&lt;br /&gt;
connections (some even publish API&#039;s and example code for you to use),&lt;br /&gt;
your application&#039;s &amp;quot;visit&amp;quot; may not be welcome or even legal.&lt;br /&gt;
&lt;br /&gt;
At the very least, your program should &amp;quot;tread lightly&amp;quot; - do not abuse&lt;br /&gt;
the server with unnecessary or intrusive requests.  Furthermore, if &lt;br /&gt;
your client uses the resources of someone else&#039;s server, you should&lt;br /&gt;
credit the server within your application and documentation.&lt;br /&gt;
&lt;br /&gt;
====Server Changes:====&lt;br /&gt;
&lt;br /&gt;
As with any programming that interacts with external elements, your&lt;br /&gt;
code should provide for the possibility things may not&lt;br /&gt;
succeed or the returned results may not be what was expected.&lt;br /&gt;
&lt;br /&gt;
As servers and websites change over time, the processing and&lt;br /&gt;
parsing of your program may often have to change accordingly.  &lt;br /&gt;
To maintain your program and deal with such changes, you may want&lt;br /&gt;
to consider how to make modifying those things easy as possible (even&lt;br /&gt;
by the user?).&lt;br /&gt;
&lt;br /&gt;
For example, the parsing strings could be kept in the program&#039;s tooltype&lt;br /&gt;
or a config text file, you could provide a GUI for managing the strings, or&lt;br /&gt;
employ an external, editable ARexx macro for processing.&lt;br /&gt;
&lt;br /&gt;
====Protocol Details &amp;amp; Vagaries:====&lt;br /&gt;
&lt;br /&gt;
Our example code presented a very simple interaction with a web&lt;br /&gt;
server using the a widely supported version of the HTTP protocol.&lt;br /&gt;
As one develops an application and uses a protocol, one should get&lt;br /&gt;
familiar with the details of that protocol.  Typically a &amp;quot;RFC&amp;quot;&lt;br /&gt;
document (as linked above) will describe the details of how things&lt;br /&gt;
are to work and how they might fail.  Your application should be&lt;br /&gt;
careful to comply with the details of the protocol (&amp;quot;Are we sending&lt;br /&gt;
a CR-LF or LF-CR?!&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
===Ideas===&lt;br /&gt;
&lt;br /&gt;
The above example was just a quick, simple exercise in demonstrating&lt;br /&gt;
the use of the AmigaOS Roadshow TCP: handler to access a web server.&lt;br /&gt;
There are many ways applications could be created to have simple&lt;br /&gt;
interactions with all sorts internet services, such as:&lt;br /&gt;
&lt;br /&gt;
* Check stock quotes from a financial site.&lt;br /&gt;
* Get package tracking information from a shipping site.&lt;br /&gt;
* Check for new emails on a POP email server.&lt;br /&gt;
* Convert currencies with a financial or travel web site.&lt;br /&gt;
* Check for files on an FTP server.&lt;br /&gt;
* Send messages with a SMTP email server.&lt;br /&gt;
* Look up words on a dictionary website.&lt;br /&gt;
&lt;br /&gt;
You could also write your own server for another Amiga and interact&lt;br /&gt;
with your own client.   A remote controlled media player?  The AmiNet &lt;br /&gt;
also has many examples of simple clients (with C source code or in&lt;br /&gt;
readable ARexx) that can be studied for means and methods.&lt;br /&gt;
&lt;br /&gt;
==An Internet Server==&lt;br /&gt;
&lt;br /&gt;
Next we will look at creating a simple internet server program that relies on AmigaOS Roadshow&#039;s &amp;quot;SuperServer&amp;quot; to receive the incoming&lt;br /&gt;
internet connection and share it with our application.&lt;br /&gt;
&lt;br /&gt;
Simply speaking, once Roadshow is configured to recognize the incoming&lt;br /&gt;
request and that your application is there to handle it; our program&lt;br /&gt;
just has to deal with another case of simple file-like interaction.&lt;br /&gt;
&lt;br /&gt;
For this example, we will create a simple server that accepts an HTTP&lt;br /&gt;
protocol request (like from IBrowse) and then responds to it with a&lt;br /&gt;
simple webpage.  Of course, you could write a server to serve an&lt;br /&gt;
interactive webpage, some machine information, media files, etc.&lt;br /&gt;
Just be careful.&lt;br /&gt;
&lt;br /&gt;
===Receive the Request===&lt;br /&gt;
&lt;br /&gt;
When Roadshow receives an internet request on the designated port that&lt;br /&gt;
corresponds to our server, it starts our server program and routes the&lt;br /&gt;
network connection to our program using the &amp;quot;standard&amp;quot; input stream&lt;br /&gt;
(otherwise known as &amp;quot;stdin&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Our application then reads the stream almost like reading from any&lt;br /&gt;
other file or user input, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	// read in the response&lt;br /&gt;
	while( (fgets(inStr,MAX_STR,(FILE *)stdin) != NULL) &amp;amp;&amp;amp; (count&amp;lt;100) )&lt;br /&gt;
	{&lt;br /&gt;
		if (strlen(inStr)&amp;lt;3)&lt;br /&gt;
		{&lt;br /&gt;
			IExec-&amp;gt;DebugPrintF(&amp;quot;   received nearly empty line, ending read loop.\n&amp;quot;,NULL,NULL);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		++ count;&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;   %s\n&amp;quot;,inStr,NULL,NULL,NULL);&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
		&lt;br /&gt;
As you can see, we loop reading incoming lines until we encounter a&lt;br /&gt;
line with two or fewer characters (essentially an empty line with&lt;br /&gt;
what should be a CR-LF).  There is also a counter that kicks us out&lt;br /&gt;
of the loop if we read 100 lines - that&#039;s a sign something else is &lt;br /&gt;
wrong, we should never see so long a request with the HTTP protocol.&lt;br /&gt;
&lt;br /&gt;
With the HTTP protocol, the first line is where we would see what is&lt;br /&gt;
being requested.  Were our server to use another protocol, the processing of&lt;br /&gt;
the received data would likely happe3n elsewhere.&lt;br /&gt;
&lt;br /&gt;
As you see, we do all our diagnostics output using the &amp;quot;DebugPrintf&amp;quot;. Which&lt;br /&gt;
means we either need to run Sashimi or watch output on the serial port&lt;br /&gt;
(with your second Amiga, naturally).  With the above code, the diagnostics&lt;br /&gt;
output will show the entire incoming request on the serial port.  Of course,&lt;br /&gt;
there&#039;s a reason why we have to use &amp;quot;DebugPrintf&amp;quot; for diagnostics output...&lt;br /&gt;
&lt;br /&gt;
===Respond to the Visitor===&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve read the (almost) empty line at the end of the incoming&lt;br /&gt;
web request, we will use another &amp;quot;standard&amp;quot; stream (know as &amp;quot;stdout&amp;quot;) to respond to our&lt;br /&gt;
internet visitor.&lt;br /&gt;
&lt;br /&gt;
Anything we output to &amp;quot;stdout&amp;quot; will be sent by Roadshow back to our&lt;br /&gt;
internet visitor.  This is the reason why we used the serial port for&lt;br /&gt;
diagnostic print outs, since a simple &amp;quot;printf&amp;quot; would have gone to&lt;br /&gt;
our web visitor.&lt;br /&gt;
&lt;br /&gt;
In this example, we create a basic loop that feeds a predefined series of strings (in the &#039;&#039;&#039;lines&#039;&#039;&#039; array)&lt;br /&gt;
of a very simple webpage through Roadshow, to our web visitor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
	//loop thru text writing to stdout&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		if (strlen(lines[l]) &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			IExec-&amp;gt;DebugPrintF(&amp;quot;   printing line = %s\n&amp;quot;,lines[l],NULL,NULL);			&lt;br /&gt;
			if (fprintf(stdout,&amp;quot;%s\n&amp;quot;,lines[l]) &amp;lt; 0)&lt;br /&gt;
			{&lt;br /&gt;
				IExec-&amp;gt;DebugPrintF(&amp;quot;ERROR printing line\n&amp;quot;,NULL,NULL);&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
			fflush(stdout);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			IExec-&amp;gt;DebugPrintF(&amp;quot;   empty line, loop end\n&amp;quot;,NULL,NULL);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		++l;&lt;br /&gt;
	} while (l &amp;lt; 25);		// emergency loop escape to stop endlessness&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, our loop ends when we hit an empty line in our webpage&lt;br /&gt;
definition or a maximum of 25 lines (a fail-safe).  Then we &amp;quot;flush&amp;quot; the output&lt;br /&gt;
stream and quit our program.&lt;br /&gt;
&lt;br /&gt;
While our two samples of code simply receive and respond to an incoming web request,&lt;br /&gt;
one can imagine where this code can be expanded provide useful service and more&lt;br /&gt;
complex interactions.&lt;br /&gt;
&lt;br /&gt;
===EXAMPLE TWO:  IPserver.c===&lt;br /&gt;
&lt;br /&gt;
We can see all these pieces and the definition of our simple webpage in&lt;br /&gt;
the full program here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************&lt;br /&gt;
**&lt;br /&gt;
** Created by: CodeBench 0.41 (12.10.2013)&lt;br /&gt;
** Project: IPserver&lt;br /&gt;
** Date: 13-10-2013 23:41:17&lt;br /&gt;
**&lt;br /&gt;
************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*****************************************************************************&lt;br /&gt;
 *		GLOBAL VARIABLES&lt;br /&gt;
 *****************************************************************************/&lt;br /&gt;
&lt;br /&gt;
STATIC CONST_STRPTR version USED = &amp;quot;$VER: IPserver 0.14 (13.10.2013)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// define maximum web request lines &amp;amp; length&lt;br /&gt;
#define MAX_STR 2048&lt;br /&gt;
&lt;br /&gt;
// ARS path &amp;amp; file name&lt;br /&gt;
uint16 count = 0;&lt;br /&gt;
char inStr[MAX_STR];&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR lines[] =&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;HTTP/1.0\015&amp;quot;,&lt;br /&gt;
	&amp;quot;Content-Type: text/html\015&amp;quot;,&lt;br /&gt;
	&amp;quot;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;TITLE&amp;gt;IPserver&amp;lt;/TITLE&amp;gt;&amp;lt;/HEAD&amp;gt;&amp;lt;BODY BGCOLOR=\&amp;quot;cccccc\&amp;quot;&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;TABLE WIDTH=100%&amp;gt;&amp;lt;TD ALIGN=\&amp;quot;center\&amp;quot; BGCOLOR=\&amp;quot;cccccc\&amp;quot;&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;BR&amp;gt;&amp;lt;TABLE WIDTH=90%&amp;gt;&amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;&amp;lt;B&amp;gt;&amp;lt;H1&amp;gt;WELCOME&amp;lt;/H1&amp;gt;&amp;lt;/B&amp;gt;&amp;lt;BR&amp;gt;&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&amp;lt;/TABLE&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;TR&amp;gt;&amp;lt;TD BGCOLOR=\&amp;quot;6688ee\&amp;quot;&amp;gt;&amp;lt;H3&amp;gt;&amp;lt;P&amp;gt;&amp;lt;P&amp;gt;&amp;lt;FONT COLOR=\&amp;quot;white\&amp;quot;&amp;gt;&amp;lt;B&amp;gt;AmigaOS IPserver example&amp;lt;/B&amp;gt;&amp;lt;/FONT&amp;gt;&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;TR&amp;gt;&amp;lt;TD ALIGN=\&amp;quot;center\&amp;quot; BGCOLOR=\&amp;quot;cccccc\&amp;quot;&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;BR&amp;gt;&amp;lt;TABLE CELLSPACING=0 WIDTH=90%&amp;gt;&amp;lt;TR BGCOLOR=\&amp;quot;ffffff\&amp;quot;&amp;gt;&amp;lt;TD WIDTH=100&amp;gt;&amp;lt;H4&amp;gt;&amp;lt;B&amp;gt;And so it begins...&amp;lt;/B&amp;gt;&amp;lt;/TD&amp;gt;&amp;lt;TD&amp;gt; Your Amiga internet server! &amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&amp;lt;/TABLE&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;lt;/TR&amp;gt;&amp;lt;/TD&amp;gt;&amp;lt;/TABLE&amp;gt;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;\015&amp;quot;,&lt;br /&gt;
	&amp;quot;&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
uint16 l = 0;&lt;br /&gt;
&lt;br /&gt;
/*****************************************************************************&lt;br /&gt;
 *		PROGRAM START&lt;br /&gt;
 *****************************************************************************/&lt;br /&gt;
&lt;br /&gt;
// Starting program&lt;br /&gt;
int main(int argc,char **argv)&lt;br /&gt;
{&lt;br /&gt;
	&lt;br /&gt;
	IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
	IExec-&amp;gt;DebugPrintF(&amp;quot;IPserver starting\n&amp;quot;);&lt;br /&gt;
	IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	//		Was program started from shell or WB ?&lt;br /&gt;
	if (argc &amp;gt; 0)&lt;br /&gt;
	{&lt;br /&gt;
		&lt;br /&gt;
		// read in the response&lt;br /&gt;
		while( (fgets(inStr,MAX_STR,(FILE *)stdin) != NULL) &amp;amp;&amp;amp; (count&amp;lt;100) )&lt;br /&gt;
		{&lt;br /&gt;
			//  ####   Test for CR-LR only line&lt;br /&gt;
			if (strlen(inStr)&amp;lt;3)&lt;br /&gt;
			{&lt;br /&gt;
				IExec-&amp;gt;DebugPrintF(&amp;quot;   received nearly empty line, ending read loop.\n&amp;quot;,NULL,NULL);&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			++ count;&lt;br /&gt;
			IExec-&amp;gt;DebugPrintF(&amp;quot;   %s\n&amp;quot;,inStr,NULL,NULL,NULL);&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;FINISHED READING WEB REQUEST\n&amp;quot;);&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;SERVING WEB PAGE...\n&amp;quot;);&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		//loop thru text writing to stdout&lt;br /&gt;
		do&lt;br /&gt;
		{&lt;br /&gt;
			if (strlen(lines[l]) &amp;gt; 0)&lt;br /&gt;
			{&lt;br /&gt;
				IExec-&amp;gt;DebugPrintF(&amp;quot;   printing line = %s\n&amp;quot;,lines[l],NULL,NULL);&lt;br /&gt;
				&lt;br /&gt;
				if (fprintf(stdout,&amp;quot;%s\n&amp;quot;,lines[l]) &amp;lt; 0)&lt;br /&gt;
				{&lt;br /&gt;
					IExec-&amp;gt;DebugPrintF(&amp;quot;ERROR printing line\n&amp;quot;,NULL,NULL);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				fflush(stdout);&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				IExec-&amp;gt;DebugPrintF(&amp;quot;   empty line, loop end\n&amp;quot;,NULL,NULL);&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
			++l;&lt;br /&gt;
		} while (l &amp;lt; 25);		// emergency loop escape to stop endlessness&lt;br /&gt;
		&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
		IExec-&amp;gt;DebugPrintF(&amp;quot;Web service finished... \n&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf(&amp;quot;IPserver started from Workbench - Don&#039;t do it again, this is a Roadshow app!\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	IExec-&amp;gt;DebugPrintF(&amp;quot;IPserver Quitting!\n&amp;quot;);&lt;br /&gt;
	IExec-&amp;gt;DebugPrintF(&amp;quot;======================================\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see there&#039;s also a bit of housekeeping code at the beginning&lt;br /&gt;
to check if the program was run from the Workbench and to tell the user not to.&lt;br /&gt;
As it stands, this is program is only meant to be used by Roadshow for&lt;br /&gt;
serving your Amiga&#039;s internet visitors.&lt;br /&gt;
&lt;br /&gt;
But how do we test this server out?  First we have to let Roadshow know that&lt;br /&gt;
we&#039;ve created this server and where visitors will find it.&lt;br /&gt;
&lt;br /&gt;
===Roadshow Configuration===&lt;br /&gt;
&lt;br /&gt;
There are two areas where Roadshow needs to be told about your server&lt;br /&gt;
and the service it is going to provide, as described below.  Both of these can&lt;br /&gt;
be configured using Internet Prefs.  You can also make this configuration by&lt;br /&gt;
manually editing the &amp;quot;servers&amp;quot; and &amp;quot;services&amp;quot; files in the &amp;quot;DEVS:internet/&amp;quot; directory.&lt;br /&gt;
Configuration of these files is described in the file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   SYS:Documentation/Roadshow/README&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to spare your user from having to do either of those&lt;br /&gt;
chores, your application can even modify those files.  As soon as&lt;br /&gt;
Roadshow sees those files have been modified, it will reconfigure itself&lt;br /&gt;
accordingly.&lt;br /&gt;
&lt;br /&gt;
====Services====&lt;br /&gt;
&lt;br /&gt;
First, we need to tell Roadshow about the &amp;quot;service&amp;quot; we are providing.&lt;br /&gt;
If one opens Internet Prefs and clicks on the &amp;quot;Services&amp;quot; page, one will&lt;br /&gt;
see a list of standard internet services with their TCPIP port numbers,&lt;br /&gt;
types and aliases.&lt;br /&gt;
&lt;br /&gt;
Click the &amp;quot;New...&amp;quot; button to define a new Service.  An &amp;quot;Add service&amp;quot;&lt;br /&gt;
window will open where we can define how our server will be accessed.&lt;br /&gt;
Critically, we need to pick a port number that is not already in use.&lt;br /&gt;
Such settings could be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   Name = IPserver&lt;br /&gt;
   Port = 7600&lt;br /&gt;
   Type = tcp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we click &amp;quot;Use&amp;quot; to accept our new values.  As soon as one clicks&lt;br /&gt;
&amp;quot;Save&amp;quot; in Internet Prefs, this service will be recognized by Roadshow.&lt;br /&gt;
&lt;br /&gt;
The same configuration could manually be added to Roadshow by adding this line&lt;br /&gt;
(in port number location) into the file &amp;quot;DEVS:Internet/Services&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   IPServer         7600/tcp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as one saves the file, Roadshow will be notified and adjust&lt;br /&gt;
itself for the service.&lt;br /&gt;
&lt;br /&gt;
====Servers====&lt;br /&gt;
&lt;br /&gt;
Before we can use the service above, we also need to tell Roadshow&lt;br /&gt;
about our server app and where to find it.  Again, this can be done&lt;br /&gt;
while in Internet Prefs.  Click on the &amp;quot;Servers&amp;quot; page and one will see&lt;br /&gt;
any servers configured with the services they provide (as set above),&lt;br /&gt;
their type, wait method and program path.&lt;br /&gt;
&lt;br /&gt;
Click on the &amp;quot;New...&amp;quot; button to define a new Server.  In the &amp;quot;Add server&amp;quot;&lt;br /&gt;
window we can define what sort of service our server is for, the&lt;br /&gt;
characteristics of how it will be called and where it is located on&lt;br /&gt;
our Amiga system.  Such settings could be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   Service = IPserver&lt;br /&gt;
   Type = Stream&lt;br /&gt;
   Stack = 65536&lt;br /&gt;
   Program = data:Projects/C/OS4ex-IPserver/IPserver.debug&lt;br /&gt;
   Arguments = &amp;lt;empty&amp;gt;&lt;br /&gt;
   Active = &amp;lt;checked&amp;gt;&lt;br /&gt;
   Wait for completion = &amp;lt;not checked&amp;gt;&lt;br /&gt;
   Use socket I/O streams = &amp;lt;checked&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Naturally, you should enter the server program path &amp;amp; name for your own system.&lt;br /&gt;
Again we click &amp;quot;Use&amp;quot; to accept our new Server entry and saving Internet&lt;br /&gt;
Prefs will adjust Roadshow.&lt;br /&gt;
&lt;br /&gt;
We can otherwise make this adjustment to Roadshow&#039;s server list by manually adding the&lt;br /&gt;
following line to the &amp;quot;DEVS:Internet/servers&amp;quot; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   IPServer        stream     dos stack=65536 data:Projects/C/OS4ex-IPserver/IPserver.debug&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again, saving the file is automatically noticed by Roadhsow.&lt;br /&gt;
&lt;br /&gt;
Once both the service and server are correctly configured, Roadshow is ready&lt;br /&gt;
to use our new server.&lt;br /&gt;
&lt;br /&gt;
===Let&#039;s See Our Server!===&lt;br /&gt;
&lt;br /&gt;
To test our new server example, you can access it by connecting to your&lt;br /&gt;
Amiga and the designated IP port number.  On the same machine, you can&lt;br /&gt;
enter this in your browser URL line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   http://localhost:7600/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From another machine, you will need to have the IP address or defined host&lt;br /&gt;
name for your Amiga running the test server program.  The URL should&lt;br /&gt;
look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   http://192.168.1.07:7600/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In any case, the browser will connect to your Amiga (at the port number&lt;br /&gt;
following the colon - &amp;quot;:&amp;quot;) and Roadshow will call up your server program&lt;br /&gt;
and let it talk to your browser and send it the preset webpage.&lt;br /&gt;
&lt;br /&gt;
===Where to go?   Webpages?  More?===&lt;br /&gt;
&lt;br /&gt;
Clearly the above example does little more than say &amp;quot;Hello World&amp;quot; to a&lt;br /&gt;
visiting web browser user.  It offers no interactivity, performs no task&lt;br /&gt;
and serves no media.  &lt;br /&gt;
&lt;br /&gt;
When this web server receives the incoming web request, the first line&lt;br /&gt;
(starting with &amp;quot;GET&amp;quot;) tells the server what the browser is looking for.&lt;br /&gt;
For example, if you watch the serial output and connect with a newer browser than&lt;br /&gt;
IBrowse, you can see most browsers actually make two connections to the&lt;br /&gt;
server, with one of them saying:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   GET /favicon.ico HTTP/1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In that case, the browser is requesting your server send an image file&lt;br /&gt;
that it can display on the browser tab.  Just like that, anything in the URL&lt;br /&gt;
after the machine&#039;s network name (or IP address) and the port number&lt;br /&gt;
is taken as a requested path and your server will receive it after &amp;quot;GET&amp;quot; in&lt;br /&gt;
the first line of the request.&lt;br /&gt;
&lt;br /&gt;
This just scratches the surface of requests your server may receive and&lt;br /&gt;
how it can process them and respond to web visitors.  To dig deeper you can&lt;br /&gt;
search the many sources online or in your local bookstore on HTTP communications&lt;br /&gt;
and the formatting of HTML webpages.  Of course, you should make sure your&lt;br /&gt;
server can optionally send diagnostics output so you know what requests are&lt;br /&gt;
being received and how they are responded to.&lt;br /&gt;
&lt;br /&gt;
===Just Webpages?===&lt;br /&gt;
&lt;br /&gt;
There&#039;s also no reason why your server needs to be limited to dealing&lt;br /&gt;
with browsers and sending webpages.  The internet is full of other&lt;br /&gt;
protocols and services that you could write a server for.  Or you could&lt;br /&gt;
create your own online client and server pair of applications that&lt;br /&gt;
talked with each other.&lt;br /&gt;
&lt;br /&gt;
Just as we discussed with creating an Internet client, there are many&lt;br /&gt;
topics you should consider when designing an internet server program.&lt;br /&gt;
It&#039;s not just about the coding!&lt;br /&gt;
&lt;br /&gt;
====Security====&lt;br /&gt;
&lt;br /&gt;
Obviously, opening your Amiga with an internet server represents more of a&lt;br /&gt;
risk than if you had done nothing.  So it is critically important one&lt;br /&gt;
considers security when creating a server.  While it&#039;s beyond the scope&lt;br /&gt;
of this article to inclusively discuss all the issues involved, here are some&lt;br /&gt;
basics to consider.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t blindly serve sensitive information to visitors.  How can you be&lt;br /&gt;
sure who&#039;s at the other end of the connection and who might be watching&lt;br /&gt;
along the way?  Even if randomized path names and requests are used by&lt;br /&gt;
your server, there&#039;s no guarantee the connection is 100% secure.&lt;br /&gt;
&lt;br /&gt;
Limit commands and functions served to visitors.  Obviously, creating a&lt;br /&gt;
means of offering anything like command line access or unfettered file &lt;br /&gt;
editing/deleting powers to a web visitor would represent dangerous hole&lt;br /&gt;
in any system&#039;s security.  But one also has to make sure no whatsoever holes&lt;br /&gt;
or bugs exist in the server design that would let such access slip&lt;br /&gt;
through.&lt;br /&gt;
&lt;br /&gt;
====Server Instances &amp;amp; Load====&lt;br /&gt;
&lt;br /&gt;
One has to keep in mind the &amp;quot;SuperServer&amp;quot; method where Roadshow runs&lt;br /&gt;
your server also means your server program will be executed with each and every&lt;br /&gt;
request Roadshow receives for that port.  In each case, your server&lt;br /&gt;
will do its duty and then be expected to quit.  That should be fine for&lt;br /&gt;
light duty, personal server tasks.&lt;br /&gt;
&lt;br /&gt;
But one should focus server development on keeping things as&lt;br /&gt;
&amp;quot;lightweight&amp;quot; and simple as possible.  If the server needs to&lt;br /&gt;
read data files or perform other complicated initialization, this may&lt;br /&gt;
slow reaction time for each incoming request to an unacceptable level.&lt;br /&gt;
&lt;br /&gt;
To avoid time consuming chores, one might considering using inter-process&lt;br /&gt;
communications (such as ARexx messages) to interact with a separate&lt;br /&gt;
master program that handles all the housekeeping and remains continuously running&lt;br /&gt;
while each instance of the internet server handles each incoming request, quickly&lt;br /&gt;
relays the necessary information from the master program and quits.  &lt;br /&gt;
&lt;br /&gt;
Naturally, at some point usage requirements may require one bypasses the&lt;br /&gt;
Roadshow  SuperServer method of running servers and goes to using &lt;br /&gt;
longhand socket programming that is better suited to heavy duty use&lt;br /&gt;
- like running a serious public server.  That&#039;s beyond the scope of this tutorial.&lt;br /&gt;
&lt;br /&gt;
Furthermore, there are whole areas of study and technology involved with the&lt;br /&gt;
handling of network user load that should be explored in implementing a serious&lt;br /&gt;
server system.&lt;br /&gt;
&lt;br /&gt;
====Protocols and Compatibility====&lt;br /&gt;
&lt;br /&gt;
As one develops a more a complex or interactive server meant to handle&lt;br /&gt;
a wider group of users (even those on lesser platforms!), one needs&lt;br /&gt;
to pay much closer attention to the protocols involved.  With each new client&lt;br /&gt;
and platform encountered, the more important implementation details of a&lt;br /&gt;
protocol are likely to be.&lt;br /&gt;
&lt;br /&gt;
As mentioned in elsewhere in this text, there are RFC&#039;s that describe the&lt;br /&gt;
formal protocols and there are likely countless webpages that address&lt;br /&gt;
idiosyncrasies of implementing the protocols.&lt;br /&gt;
&lt;br /&gt;
Beyond all those, a developer will need to do serious testing with as&lt;br /&gt;
wide a pool of possible clients to refine a server&#039;s operation.  &amp;quot;You&lt;br /&gt;
aren&#039;t in Kansas anymore!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Issues==&lt;br /&gt;
&lt;br /&gt;
Whether creating a server or a client program, there are many general&lt;br /&gt;
issues that one should consider in developing such programs.&lt;br /&gt;
&lt;br /&gt;
====Complexity and Speed====&lt;br /&gt;
&lt;br /&gt;
These days it seems like few developers on lesser platforms care terribly&lt;br /&gt;
much about optimization and speed.  Those platforms typically just throw&lt;br /&gt;
more horsepower &amp;amp; memory at their chores and try to go about their business.&lt;br /&gt;
&lt;br /&gt;
In the Amiga world, without the glut of idle horsepower, development of&lt;br /&gt;
any applications and internet clients &amp;amp; servers in particular should be&lt;br /&gt;
mindful of overhead.  For every client or server transaction, there is&lt;br /&gt;
likely some user that clicked a button and is waiting for an answer.&lt;br /&gt;
Keep things simple and optimize!&lt;br /&gt;
&lt;br /&gt;
====Incomplete transactions====&lt;br /&gt;
&lt;br /&gt;
Since all these programs are dependent on communications over many links,&lt;br /&gt;
these programs need to have a robustness for failed links.  Transfers can&lt;br /&gt;
be interrupted mid-stream.  Received data can be incomplete or corrupted.&lt;br /&gt;
These applications need to have the error trapping to deal with such real&lt;br /&gt;
possibilities - that isn&#039;t reflected in the simple examples above.&lt;br /&gt;
&lt;br /&gt;
====Statelessness====&lt;br /&gt;
&lt;br /&gt;
Given the nature of internet communications and web browsing in particular,&lt;br /&gt;
servers and clients need to be &amp;quot;stateless&amp;quot; as possible.  While a visitor&lt;br /&gt;
may have just &amp;quot;logged in&amp;quot; to your server, it&#039;s not guaranteed &lt;br /&gt;
the next transaction your server receives is that logged-in user, that&lt;br /&gt;
the user hasn&#039;t left or hit the back or reload button on their browser&lt;br /&gt;
(reloading the log-in page again).  So a server should avoid making&lt;br /&gt;
assumptions about the state of the connection and status of a&lt;br /&gt;
visitor whenever possible or provide explicit means for addressing such&lt;br /&gt;
things, if possible.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_GUI_Toolkit_ReAction&amp;diff=12555</id>
		<title>Programming AmigaOS 4: GUI Toolkit ReAction</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_GUI_Toolkit_ReAction&amp;diff=12555"/>
		<updated>2025-01-26T19:31:21Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;This article was adapted from Amiga Future magazine&#039;s series on developing for AmigaOS....&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We mainly worked with shell programs or simple windows in the previous parts of this workshop. However, today&#039;s topic will be how to create a completely interactive GUI.&lt;br /&gt;
&lt;br /&gt;
For this purpose we will use ReAction, which provides great flexibility for defined windows and controls that lay themselves out automatically. But before we dive into this subject, let us take a look at the past and see how interface development on the Amiga has changed over the years.&lt;br /&gt;
&lt;br /&gt;
= History =&lt;br /&gt;
&lt;br /&gt;
With Kickstart 1.x you had to create gadgets (the controls, which were back then - a button, a panel and a slide bar) directly as a structure and connect them with each other as well as calculate their positions. This procedure was very time consuming and a different font or another window size could sometimes cause significant trouble. Therefore, the gadtools.library, which was supplied with Kickstart 2.x, greatly simplified the creation of gadgets and menus. Additionally, a great number of new gadget types (e.g. ListView, Checkbox, Cycle) was launched. However, there was still a problem with the fixed positions. In this step the BOOPSI (Basic Object Oriented Programming System for Intuition) was also created. It offered an object-orientated approach, that could readily be used with Workbench 3.5 in the form of ReAction gadgets. This made it possible for windows to lay themselves out correctly automatically. That means that the gadgets automatically re-position themselves and their size is adjusted, if needed, when the window size is changed. The calculation of the minimized window size when opening a window is handled automatically by the system.&lt;br /&gt;
&lt;br /&gt;
= The end of Gadtools =&lt;br /&gt;
&lt;br /&gt;
By introducing ReAction all gadget types from the gadtools.library became superfluous. The same procedure is still used with AmigaOS 4, but this time it is used to create menus. All the functions required to create and layout menus are now available in the window class and no longer need to be made automatically. In other words, gadtools.library no longer has this role to perform!&lt;br /&gt;
&lt;br /&gt;
A side note: the MUI (Magic User Interface) is used as a second GUI library and it is provided with the Operating System since AmigaOS 4. However, it remains an external toolkit. Its standard appearance has been made in the style of AmigaOS 4 so it would not look like an &#039;alien element&#039;. In principle, it applies the same approach as for ReAction, but it is programmed differently.&lt;br /&gt;
&lt;br /&gt;
= ReAction step by step =&lt;br /&gt;
&lt;br /&gt;
After that brief introduction it is now time for our first example, a window with a gadget and a menu. What used to take a long time, is now done in just a few steps. These necessary steps will be explained here in detail, because you can create big applications with them as well.&lt;br /&gt;
&lt;br /&gt;
All ReAction objects (gadgets, window and Requester) are set up with IIntuition-&amp;gt;NewObject(). The result is a generic &#039;Object&#039; pointer. However, most of the old functions still expect a pointer on a gadget structure (or a window structure), so that you must decide whether you want to cast the return value immediately, or once you activate the respective functions. There is no standard notation in SDK at the moment. Right now, casting the result pointer is the best option. This way you can immediately see what it is about, in case you have selected an unfavourable name for the variable. You can create a button as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct Gadget *gb_ButtonGad;&lt;br /&gt;
  #define GAD_ID_Quit 2&lt;br /&gt;
&lt;br /&gt;
  if((gb_ButtonGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
      GA_ID,        GAD_ID_Quit,&lt;br /&gt;
      GA_RelVerify, TRUE,&lt;br /&gt;
      GA_Text,      &amp;quot;_end&amp;quot;,&lt;br /&gt;
      TAG_DONE)))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is an example for various gadget classes at the end of this article. The GA_xxx tags are general and applicable to all kinds of gadgets. A specific gadget for a button would be, for instance, BUTTON_BevelStyle, which you can find under &amp;quot;gadgets/...&amp;quot;.  With the underscore in the button text the shortcut is defined, which can start the button without a mouse.&lt;br /&gt;
&lt;br /&gt;
You can also use the same procedure to create a window, but this time you must use the WINDOW_xxx Defines from &amp;quot;classes/window.h&amp;quot;. In addition, you can also use a great number of old WA tags.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  Object *gb_WindowObj;&lt;br /&gt;
  if((gb_WindowObj = (Object *) IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),NULL,&lt;br /&gt;
    WA_IDCMP,        IDCMP_GADGETUP | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK,&lt;br /&gt;
    WA_SizeGadget,   TRUE,&lt;br /&gt;
    WA_DepthGadget,  TRUE,&lt;br /&gt;
    WA_DragBar,      TRUE,&lt;br /&gt;
    WA_CloseGadget,  TRUE,&lt;br /&gt;
    WA_Activate,     TRUE,&lt;br /&gt;
    WA_Title,        &amp;quot;Example with reaction gadgets and window class&amp;quot;,&lt;br /&gt;
    WINDOW_Position, WPOS_CENTERSCREEN,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A ReAction window requires implicitly a layout object, which automatically handles the size adjustment and distribution of the individual gadgets. In our example with only one gadget we need a central layout method. The layout object serves the horizontal and vertical arrangement of several gadgets within the window. This way, you can also realise very complex layouts by dividing them into horizontal and vertical groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct Gadget *gb_MainLayout;&lt;br /&gt;
  WINDOW_ParentGroup,   gb_MainLayout = (struct Gadget *)&lt;br /&gt;
                    IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
    LAYOUT_Orientation, LAYOUT_ORIENT_VERT,&lt;br /&gt;
    LAYOUT_SpaceOuter,  TRUE,&lt;br /&gt;
    LAYOUT_SpaceInner,  TRUE,&lt;br /&gt;
    LAYOUT_AddChild,    gb_ButtonGad,&lt;br /&gt;
    TAG_DONE),&lt;br /&gt;
  TAG_DONE)))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As already mentioned, creating menus has become a very simple task in the meantime. All you need to do is create the NewMenu structure (from &amp;quot;libraries/gadtools.h&amp;quot;) dating from gadtools times, which describes the individual menu points. This structure is given then when you create the window object. You no longer need to take care of deleting the menu structure; this happens automatically when the window object is released.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct NewMenu gb_MenuDescribe[] =&lt;br /&gt;
  {&lt;br /&gt;
    { NM_TITLE, &amp;quot;Project&amp;quot;,   NULL, 0, 0, NULL },&lt;br /&gt;
    { NM_ITEM,  &amp;quot;Quit&amp;quot;,      NULL, 0, 0, NULL },&lt;br /&gt;
    { NM_END, NULL, NULL, 0, 0, NULL }&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  #define MENU_ID_Quit FULLMENUNUM(0,0,NOSUB)&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),&lt;br /&gt;
      WINDOW_NewMenu, gb_MenuDescribe,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since AmigaOS 4&#039;s release the ReAction gadgets have been included in the BubbleHelp. If you move the mouse cursor over such an element, a bubble with help text will be displayed soon afterwards.  You need to do just a little bit of programming for this. Mainly, you must fill out the HintInfo structure from &amp;quot;classes/window.h&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct HintInfo gb_HintInfo[] =&lt;br /&gt;
  {&lt;br /&gt;
    { GAD_ID_Help, -1, &amp;quot;activate/deactivate the BubbleHelp hints&amp;quot;, 0 },&lt;br /&gt;
    { GAD_ID_Quit, -1, &amp;quot;quit program&amp;quot;, 0 },&lt;br /&gt;
    { -1, -1, NULL, 0 }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fourth value is reserved for the future flags and a 0 is assigned to it at the moment. The structure must be indicated when you create the window object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),&lt;br /&gt;
    WINDOW_HintInfo,      gb_HintInfo,&lt;br /&gt;
    WINDOW_GadgetHelp,    TRUE,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the WINDOW_GadgetHelp you can turn on/off the help. With &#039;WINDOW_GadgetHelp, FALSE&#039; you can deactivate it, so it will not be displayed again. With TRUE you can re-activate it. You can change this tag during runtime. Other settings such as influencing the waiting time until the display, the display duration, the colour, etc. are still not possible, but users should be able to change them by using a global Prefs adjuster in the future.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IIntuition-&amp;gt;SetAttrs(gb_WindowObj, WINDOW_GadgetHelp, FALSE, TAG_DONE);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will add a checkbox to the sample program in order to put this possibility to use. We can turn on and off the help any time with it. In reality, you will set this option on a menu point or in the program settings, if there are any. A tooltype in the icon information is also an alternative.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  struct Gadget *gb_CheckboxGad;&lt;br /&gt;
  #define GAD_ID_Help 1&lt;br /&gt;
&lt;br /&gt;
  if((gb_CheckboxGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ICheckBox-&amp;gt;CHECKBOX_GetClass(),NULL,&lt;br /&gt;
      GA_ID,          GAD_ID_Help,&lt;br /&gt;
      GA_RelVerify,   TRUE,&lt;br /&gt;
      GA_Text,        &amp;quot;Bubble_Help active&amp;quot;,&lt;br /&gt;
      TAG_DONE)))&lt;br /&gt;
&lt;br /&gt;
  WINDOW_ParentGroup,&lt;br /&gt;
    LAYOUT_AddChild,     gb_CheckboxGad,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All GUI elements are now created, but they are still not visible. You should certainly check, whether the window pointer is present. If not, a gadget could probably not be created. To display the window you can use the RA_OpenWindow() macros from &amp;quot;reaction/reaction_macros.h&amp;quot;, to close it RA_CloseWindow() and to edit news RA_HandleInput().&lt;br /&gt;
&lt;br /&gt;
Since these are macros, you cannot use the typical interface notation. The news editing loop looks in practice as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
  if((gb_Win = RA_OpenWindow(gb_WindowObj)))&lt;br /&gt;
  {&lt;br /&gt;
    ULONG winsig;&lt;br /&gt;
    IIntuition-&amp;gt;GetAttr(WINDOW_SigMask,gb_WindowObj,&amp;amp;winsig);&lt;br /&gt;
&lt;br /&gt;
    BOOL run = TRUE;&lt;br /&gt;
    while(run)&lt;br /&gt;
    {&lt;br /&gt;
      ULONG result, code, val;&lt;br /&gt;
      &lt;br /&gt;
      const ULONG sigs = IExec-&amp;gt;Wait(winsig | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
      while((result = RA_HandleInput(gb_WindowObj,&amp;amp;code)) != WMHI_LASTMSG)&lt;br /&gt;
      {&lt;br /&gt;
        switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
        {&lt;br /&gt;
          case WMHI_GADGETUP:&lt;br /&gt;
               switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
               {&lt;br /&gt;
                 case GAD_ID_Help:&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CHECKBOX_Checked,gb_CheckboxGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IIntuition-&amp;gt;SetAttrs(gb_WindowObj,&lt;br /&gt;
                                           WINDOW_GadgetHelp, val,&lt;br /&gt;
                                           TAG_DONE);&lt;br /&gt;
                      break;&lt;br /&gt;
&lt;br /&gt;
                 case GAD_ID_Quit:&lt;br /&gt;
                      run = FALSE;&lt;br /&gt;
                      break;&lt;br /&gt;
               }&lt;br /&gt;
               break;&lt;br /&gt;
&lt;br /&gt;
          case WMHI_CLOSEWINDOW:&lt;br /&gt;
               run = FALSE;&lt;br /&gt;
               break;&lt;br /&gt;
&lt;br /&gt;
          case WMHI_MENUPICK:&lt;br /&gt;
               switch(result &amp;amp; WMHI_MENUMASK)&lt;br /&gt;
               {&lt;br /&gt;
                 case MENU_ID_Quit:&lt;br /&gt;
                      run = FALSE;&lt;br /&gt;
                      break;&lt;br /&gt;
               }&lt;br /&gt;
               break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if(sigs &amp;amp; SIGBREAKF_CTRL_C) run = FALSE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    RA_CloseWindow(gb_WindowObj);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see the various news types like press gadget (WMHI_GADGETUP), select menu (WMHI_MENUPICK) and the closing symbol for the window (WMHI_CLOSEWINDOW). But you can only get news, which has been defined explicitly as IDCMP_xxx Bits when creating the window.&lt;br /&gt;
&lt;br /&gt;
To close the program, that is, to release all created objects all you need to do is activate IIntuition-&amp;gt;DisposeObject(). It automatically makes sure that all elements included in the object are also released. If you specify the window pointer here, all included gadgets, menus and helptexts will be also deleted and can no longer be used!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IIntuition-&amp;gt;DisposeObject(gb_WindowObj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What is missing now are only the Includes and a main function. Then the program is complete and can be found as an example below.&lt;br /&gt;
&lt;br /&gt;
== ReAction Example ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * ReActionEx.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc ReActionEx.c -o ReActionEx -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction_macros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/checkbox.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/checkbox.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MENU_ID_Quit       FULLMENUNUM(0,0,NOSUB)&lt;br /&gt;
&lt;br /&gt;
#define GAD_ID_Help        1&lt;br /&gt;
#define GAD_ID_Quit        2&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: ReActionEx 1.00 (16.12.2004) - (c) Dec.2004 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
Object        *gb_WindowObj;&lt;br /&gt;
struct Window *gb_Win;&lt;br /&gt;
struct Gadget *gb_MainLayout;&lt;br /&gt;
struct Gadget *gb_ButtonGad;&lt;br /&gt;
struct Gadget *gb_CheckboxGad;&lt;br /&gt;
&lt;br /&gt;
struct NewMenu gb_MenuDescribe[] =&lt;br /&gt;
{&lt;br /&gt;
  { NM_TITLE, &amp;quot;Project&amp;quot;,   NULL, 0, 0, NULL },&lt;br /&gt;
  { NM_ITEM,  &amp;quot;Quit&amp;quot;,      NULL, 0, 0, NULL },&lt;br /&gt;
&lt;br /&gt;
  { NM_END, NULL, NULL, 0, 0, NULL }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct HintInfo gb_HintInfo[] =&lt;br /&gt;
{&lt;br /&gt;
 	{ GAD_ID_Help, -1, &amp;quot;Activate/Deactive BubbleHelp Tips&amp;quot;, 0 },&lt;br /&gt;
 	{ GAD_ID_Quit, -1, &amp;quot;Quit Program&amp;quot;, 0 },&lt;br /&gt;
 	{ -1, -1, NULL, 0 }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  /* create a Checkbox Gadget */&lt;br /&gt;
  if((gb_CheckboxGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ICheckBox-&amp;gt;CHECKBOX_GetClass(),NULL,&lt;br /&gt;
      GA_ID,          GAD_ID_Help,&lt;br /&gt;
      GA_RelVerify,   TRUE,&lt;br /&gt;
      GA_Text,        &amp;quot;Bubble_Help Active&amp;quot;,&lt;br /&gt;
      TAG_DONE)))&lt;br /&gt;
  {&lt;br /&gt;
    /* create the Button */&lt;br /&gt;
    if((gb_ButtonGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
        GA_ID,          GAD_ID_Quit,&lt;br /&gt;
        GA_RelVerify,   TRUE,&lt;br /&gt;
        GA_Text,        &amp;quot;_Quit&amp;quot;,&lt;br /&gt;
        TAG_DONE)))&lt;br /&gt;
    {&lt;br /&gt;
      /* create the Window Object */&lt;br /&gt;
      if((gb_WindowObj = (Object *) IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),NULL,&lt;br /&gt;
        WA_IDCMP,             IDCMP_GADGETUP | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK,&lt;br /&gt;
        WA_SizeGadget,        TRUE,&lt;br /&gt;
        WA_DepthGadget,       TRUE,&lt;br /&gt;
        WA_DragBar,           TRUE,&lt;br /&gt;
        WA_CloseGadget,       TRUE,&lt;br /&gt;
        WA_Activate,          TRUE,&lt;br /&gt;
        WA_Title,             &amp;quot;Example with ReAction gadgets and window class&amp;quot;,&lt;br /&gt;
        WINDOW_Position,      WPOS_CENTERSCREEN,&lt;br /&gt;
        WINDOW_NewMenu,       gb_MenuDescribe,&lt;br /&gt;
        WINDOW_HintInfo,      gb_HintInfo,&lt;br /&gt;
        WINDOW_GadgetHelp,    FALSE,&lt;br /&gt;
  &lt;br /&gt;
        /* create the Layout Object and include it into the Window */&lt;br /&gt;
        WINDOW_ParentGroup,   gb_MainLayout = (struct Gadget *)&lt;br /&gt;
                          IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_Orientation,  LAYOUT_ORIENT_VERT,&lt;br /&gt;
          LAYOUT_SpaceOuter,   TRUE,&lt;br /&gt;
          LAYOUT_SpaceInner,   TRUE,&lt;br /&gt;
          LAYOUT_AddChild,     gb_CheckboxGad,&lt;br /&gt;
          LAYOUT_AddChild,     gb_ButtonGad,&lt;br /&gt;
          TAG_DONE),&lt;br /&gt;
        TAG_DONE)))&lt;br /&gt;
      {&lt;br /&gt;
        /* open the Window */&lt;br /&gt;
        if((gb_Win = RA_OpenWindow(gb_WindowObj)))&lt;br /&gt;
        {&lt;br /&gt;
          /* query the Signal Bit of the Window */&lt;br /&gt;
          ULONG winsig;&lt;br /&gt;
          IIntuition-&amp;gt;GetAttr(WINDOW_SigMask,gb_WindowObj,&amp;amp;winsig);&lt;br /&gt;
  &lt;br /&gt;
          BOOL laufen = TRUE;&lt;br /&gt;
          while(laufen)&lt;br /&gt;
          {&lt;br /&gt;
            ULONG result, code, val;&lt;br /&gt;
            &lt;br /&gt;
            /* wait till either Button or Close Icon are clicked */&lt;br /&gt;
            const ULONG sigs = IExec-&amp;gt;Wait(winsig | SIGBREAKF_CTRL_C);&lt;br /&gt;
  &lt;br /&gt;
            /* process the received Messages */&lt;br /&gt;
            while((result = RA_HandleInput(gb_WindowObj,&amp;amp;code)) != WMHI_LASTMSG)&lt;br /&gt;
            {&lt;br /&gt;
              /* mask out the Messages part */&lt;br /&gt;
              switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
              {&lt;br /&gt;
                /* process the Gadget Messages */&lt;br /&gt;
                case WMHI_GADGETUP:&lt;br /&gt;
                     switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
                     {&lt;br /&gt;
                       case GAD_ID_Help:&lt;br /&gt;
                            IIntuition-&amp;gt;GetAttr(CHECKBOX_Checked,&lt;br /&gt;
                                                (Object*)gb_CheckboxGad,&lt;br /&gt;
                                                (ULONG*)&amp;amp;val);&lt;br /&gt;
                            IIntuition-&amp;gt;SetAttrs(gb_WindowObj,&lt;br /&gt;
                                                 WINDOW_GadgetHelp, val,&lt;br /&gt;
                                                 TAG_DONE);&lt;br /&gt;
                            break;&lt;br /&gt;
&lt;br /&gt;
                       case GAD_ID_Quit:&lt;br /&gt;
                            laufen = FALSE;&lt;br /&gt;
                            break;&lt;br /&gt;
                     }&lt;br /&gt;
                     break;&lt;br /&gt;
  &lt;br /&gt;
                /* query the Window Close Icon */&lt;br /&gt;
                case WMHI_CLOSEWINDOW:&lt;br /&gt;
                     laufen = FALSE;&lt;br /&gt;
                     break;&lt;br /&gt;
  &lt;br /&gt;
                /* query the Menu Selection */&lt;br /&gt;
                case WMHI_MENUPICK:&lt;br /&gt;
                     switch(result &amp;amp; WMHI_MENUMASK)&lt;br /&gt;
                     {&lt;br /&gt;
                       case MENU_ID_Quit:&lt;br /&gt;
                            laufen = FALSE;&lt;br /&gt;
                            break;&lt;br /&gt;
                     }&lt;br /&gt;
                     break;&lt;br /&gt;
              }&lt;br /&gt;
            }&lt;br /&gt;
  &lt;br /&gt;
            /* query if CTRL-C received from the Shell */&lt;br /&gt;
            if(sigs &amp;amp; SIGBREAKF_CTRL_C) laufen = FALSE;&lt;br /&gt;
          }&lt;br /&gt;
  &lt;br /&gt;
          /* close the Window */&lt;br /&gt;
          RA_CloseWindow(gb_WindowObj);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* release Window Object, dump also Layout and Gadget Objects */&lt;br /&gt;
    IIntuition-&amp;gt;DisposeObject(gb_WindowObj);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF110_reactionex.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= AllGadgets Example =&lt;br /&gt;
&lt;br /&gt;
We can deal with programming with ReAction here only in theory. But for this purpose we have put &amp;quot;AllGadgets.c&amp;quot; as a bonus. Here all available gadgets are used and you can see how they can be created and queried. The use of page gadgets is also shown. In conjunction with the Clicktab gadget you can also realise tabs, which always show another &#039;page&#039; with gadgets.  For instance, you can divide comprehensive setting windows into individual pages.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * AllGadgets.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc AllGadgets.c -o AllGadgets -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/requester.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction_macros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/macros.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;gadgets/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/space.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/checkbox.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/listbrowser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/integer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/radiobutton.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/scroller.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/getfile.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/getfont.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/getscreenmode.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/getcolor.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/chooser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/texteditor.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/colorwheel.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/gradientslider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/datebrowser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/fuelgauge.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/listview.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/clicktab.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/page.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/palette.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/virtual.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;images/label.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/bevel.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/bitmap.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/drawlist.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/filler.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/glyph.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/penmap.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/asl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/locale.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/application.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/timer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/scroller.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/space.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/integer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/listbrowser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/clicktab.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/chooser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/radiobutton.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/checkbox.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/palette.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/datebrowser.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/fuelgauge.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/getfile.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/getfont.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/getscreenmode.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/getcolor.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/texteditor.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/colorwheel.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/bevel.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/bitmap.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/drawlist.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/filler.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/glyph.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/penmap.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: AllGadgets 1.01 (14.04.2014) - (c) Apr.2014 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
enum GADIDs&lt;br /&gt;
{&lt;br /&gt;
  GAD_ID_ButtonV,&lt;br /&gt;
  GAD_ID_Button,&lt;br /&gt;
  GAD_ID_String,&lt;br /&gt;
  GAD_ID_Integer,&lt;br /&gt;
  GAD_ID_Slider,&lt;br /&gt;
  GAD_ID_Scroller,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Chooser,&lt;br /&gt;
  GAD_ID_Chooser2,&lt;br /&gt;
  GAD_ID_Chooser3,&lt;br /&gt;
  GAD_ID_Checkbox,&lt;br /&gt;
  GAD_ID_Radio,&lt;br /&gt;
  GAD_ID_Listbrowser,&lt;br /&gt;
  GAD_ID_Palette,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_File,&lt;br /&gt;
  GAD_ID_Font,&lt;br /&gt;
  GAD_ID_Screenmode,&lt;br /&gt;
  GAD_ID_Color,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Fuelgauge,&lt;br /&gt;
  GAD_ID_Colorwheel,&lt;br /&gt;
  GAD_ID_Gradientslider,&lt;br /&gt;
  GAD_ID_Texteditor,&lt;br /&gt;
  GAD_ID_Datebrowser,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Space,&lt;br /&gt;
  GAD_ID_Page,&lt;br /&gt;
  GAD_ID_Clicktab,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Pages,&lt;br /&gt;
  GAD_ID_PagesClicktab,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct List gb_List;&lt;br /&gt;
&lt;br /&gt;
struct ColumnInfo gb_ListbrowserColumn[] =&lt;br /&gt;
{&lt;br /&gt;
   {   30, &amp;quot;Nummer&amp;quot;, 0 },&lt;br /&gt;
   {  170, &amp;quot;Eintrag&amp;quot;, 0 },&lt;br /&gt;
   { -1, (STRPTR)~0, (ULONG)-1 }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Hook gb_RenderHook;&lt;br /&gt;
struct Hook gb_IDCMPHook;&lt;br /&gt;
&lt;br /&gt;
STRPTR gb_ChooserLabels[] = { &amp;quot;Amiga 1000&amp;quot;, &amp;quot;Amiga 1200&amp;quot;, &amp;quot;AmigaOne&amp;quot;, NULL };&lt;br /&gt;
STRPTR gb_Chooser2Labels[] = { &amp;quot;Amiga 1000&amp;quot;, &amp;quot;Amiga 1200&amp;quot;, &amp;quot;AmigaOne&amp;quot;, NULL };&lt;br /&gt;
STRPTR gb_Chooser3Labels[] = { &amp;quot;Amiga 1000&amp;quot;, &amp;quot;Amiga 1200&amp;quot;, &amp;quot;AmigaOne&amp;quot;, NULL };&lt;br /&gt;
&lt;br /&gt;
STRPTR gb_RadioLabels[] = { &amp;quot;Amiga 1000&amp;quot;, &amp;quot;Amiga 1200&amp;quot;, &amp;quot;AmigaOne&amp;quot;, NULL };&lt;br /&gt;
&lt;br /&gt;
STRPTR gb_ClicktabLabels[] = { &amp;quot;Amiga 1000&amp;quot;, &amp;quot;Amiga 1200&amp;quot;, &amp;quot;AmigaOne&amp;quot;, NULL };&lt;br /&gt;
&lt;br /&gt;
STRPTR gb_PagesLabels[] = { &amp;quot;_Grundtypen&amp;quot;, &amp;quot;_Auswahllisten&amp;quot;, &amp;quot;ASL-Aus_wahl&amp;quot;,&lt;br /&gt;
                            &amp;quot;Sonstige _Typen&amp;quot;, &amp;quot;_Seitengestalltung&amp;quot;,&lt;br /&gt;
                            &amp;quot;_Images&amp;quot;, NULL };&lt;br /&gt;
&lt;br /&gt;
UWORD gb_GradientArray[] = { ~0, ~0, ~0, ~0, ~0, ~0, ~0 };&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct Gadget *gb_ButtonVGad;&lt;br /&gt;
struct Gadget *gb_ButtonGad;&lt;br /&gt;
struct Gadget *gb_StringGad;&lt;br /&gt;
struct Gadget *gb_IntegerGad;&lt;br /&gt;
struct Gadget *gb_SliderGad;&lt;br /&gt;
struct Gadget *gb_ScrollerGad;&lt;br /&gt;
struct Gadget *gb_ChooserGad;&lt;br /&gt;
struct Gadget *gb_Chooser2Gad;&lt;br /&gt;
struct Gadget *gb_Chooser3Gad;&lt;br /&gt;
struct Gadget *gb_CheckboxGad;&lt;br /&gt;
struct Gadget *gb_RadioGad;&lt;br /&gt;
struct Gadget *gb_ListbrowserGad;&lt;br /&gt;
struct Gadget *gb_PaletteGad;&lt;br /&gt;
struct Gadget *gb_FileGad;&lt;br /&gt;
struct Gadget *gb_FontGad;&lt;br /&gt;
struct Gadget *gb_ScreenmodeGad;&lt;br /&gt;
struct Gadget *gb_ColorGad;&lt;br /&gt;
struct Gadget *gb_FuelgaugeGad;&lt;br /&gt;
struct Gadget *gb_GradientsliderGad;&lt;br /&gt;
struct Gadget *gb_ColorwheelGad;&lt;br /&gt;
struct Gadget *gb_TexteditorGad;&lt;br /&gt;
struct Gadget *gb_DatebrowserGad;&lt;br /&gt;
struct Gadget *gb_SpaceGad;&lt;br /&gt;
struct Gadget *gb_PageGad;&lt;br /&gt;
struct Gadget *gb_ClicktabGad;&lt;br /&gt;
&lt;br /&gt;
struct Image  *gb_LabelImg;&lt;br /&gt;
struct Image  *gb_BevelImg;&lt;br /&gt;
struct Image  *gb_BitmapImg;&lt;br /&gt;
struct Image  *gb_BitmapSImg;&lt;br /&gt;
struct Image  *gb_BitmapDImg;&lt;br /&gt;
struct Image  *gb_DrawlistImg;&lt;br /&gt;
struct Image  *gb_FillerImg;&lt;br /&gt;
struct Image  *gb_GlyphImg[27];&lt;br /&gt;
struct Image  *gb_PenmapImg;&lt;br /&gt;
&lt;br /&gt;
struct Gadget *gb_MainLayout;&lt;br /&gt;
struct Gadget *gb_Page1Layout;&lt;br /&gt;
struct Gadget *gb_Page2Layout;&lt;br /&gt;
struct Gadget *gb_Page3Layout;&lt;br /&gt;
struct Gadget *gb_Page4Layout;&lt;br /&gt;
struct Gadget *gb_Page5Layout;&lt;br /&gt;
struct Gadget *gb_Page6Layout;&lt;br /&gt;
struct Gadget *gb_PagesLayout;&lt;br /&gt;
struct Gadget *gb_PagesClicktab;&lt;br /&gt;
&lt;br /&gt;
Object        *gb_WindowObj;&lt;br /&gt;
struct Window *gb_Win;&lt;br /&gt;
struct Screen *gb_Scr;&lt;br /&gt;
&lt;br /&gt;
ULONG   image_object_palette[] =&lt;br /&gt;
{&lt;br /&gt;
   2,&lt;br /&gt;
   0x00000000, 0x00000000, 0x00000000,&lt;br /&gt;
   0xEEEEEEEE, 0xDDDDDDDD, 0x00000000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
UBYTE   happy_data[] =&lt;br /&gt;
{&lt;br /&gt;
   0,16, 0,14,&lt;br /&gt;
   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,&lt;br /&gt;
   0,0,0,1,1,2,2,2,2,2,2,1,1,0,0,0,&lt;br /&gt;
   0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,&lt;br /&gt;
   0,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,&lt;br /&gt;
   0,1,2,2,2,1,1,2,2,1,1,2,2,2,1,0,&lt;br /&gt;
   1,2,2,2,2,1,1,2,2,1,1,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,1,1,2,2,1,1,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,&lt;br /&gt;
   0,1,2,2,2,1,2,2,2,2,1,2,2,2,1,0,&lt;br /&gt;
   0,1,2,2,2,2,1,1,1,1,2,2,2,2,1,0,&lt;br /&gt;
   0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,&lt;br /&gt;
   0,0,0,1,1,2,2,2,2,2,2,1,1,0,0,0,&lt;br /&gt;
   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
UBYTE   scared_data[] =&lt;br /&gt;
{&lt;br /&gt;
   0,16, 0,14,&lt;br /&gt;
   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,&lt;br /&gt;
   0,0,0,1,1,2,2,2,2,2,2,1,1,0,0,0,&lt;br /&gt;
   0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,&lt;br /&gt;
   0,1,2,2,2,1,1,2,2,1,1,2,2,2,1,0,&lt;br /&gt;
   0,1,2,2,1,2,2,2,2,2,2,1,2,2,1,0,&lt;br /&gt;
   1,2,2,2,2,1,1,2,2,1,1,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,1,1,2,2,1,1,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,&lt;br /&gt;
   1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,&lt;br /&gt;
   0,1,2,2,2,2,1,1,1,1,2,2,2,2,1,0,&lt;br /&gt;
   0,1,2,2,2,1,1,1,1,1,1,2,2,2,1,0,&lt;br /&gt;
   0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,&lt;br /&gt;
   0,0,0,1,1,2,2,2,2,2,2,1,1,0,0,0,&lt;br /&gt;
   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct DrawList gb_DrawList[] =&lt;br /&gt;
{&lt;br /&gt;
  { DLST_LINE, 0,30,15,0, 1},&lt;br /&gt;
  { DLST_LINE, 30,30,15,0, 1},&lt;br /&gt;
  { DLST_LINE, 9,15,23,15, 1},&lt;br /&gt;
  { DLST_END }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
BOOL CreateEntry(ULONG num, CONST_STRPTR number, CONST_STRPTR text)&lt;br /&gt;
{&lt;br /&gt;
  /* create a List entry */&lt;br /&gt;
&lt;br /&gt;
  /* ATTENTION:&lt;br /&gt;
  ** Use this function only if the List is *not* assigned to the ListView!&lt;br /&gt;
  ** If applicable, deselect via&lt;br /&gt;
  ** SetGadgetAttrs(gad,win,NULL, LISTBROWSER_Labels,~0, TAG_END);&lt;br /&gt;
  ** then modify the List, then assign the List via&lt;br /&gt;
  ** SetGadgetAttrs(gad,win,NULL, LISTBROWSER_Labels,liste, TAG_END);&lt;br /&gt;
  ** back to the ListView.&lt;br /&gt;
&lt;br /&gt;
  struct Node *node;&lt;br /&gt;
&lt;br /&gt;
  if((node = IListBrowser-&amp;gt;AllocListBrowserNode(2,&lt;br /&gt;
              LBNA_Column,   0,&lt;br /&gt;
                      LBNCA_Text,          number,&lt;br /&gt;
                      LBNCA_Justification, LCJ_LEFT,&lt;br /&gt;
                      LBNCA_Editable,      FALSE,&lt;br /&gt;
                      LBNCA_CopyText,      TRUE,&lt;br /&gt;
                      LBNCA_MaxChars,      4,&lt;br /&gt;
              LBNA_Column,   1,&lt;br /&gt;
                      LBNCA_Text,          text,&lt;br /&gt;
                      LBNCA_Justification, LCJ_LEFT,&lt;br /&gt;
                      LBNCA_Editable,      FALSE,&lt;br /&gt;
                      LBNCA_CopyText,      TRUE,&lt;br /&gt;
                      LBNCA_MaxChars,      100,&lt;br /&gt;
              TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    node-&amp;gt;ln_Pri = num;&lt;br /&gt;
    IExec-&amp;gt;AddTail(&amp;amp;gb_List,node);&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Out of memory while creating list entry\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return( node ? TRUE : FALSE );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void FreigebenList()&lt;br /&gt;
{&lt;br /&gt;
  /* release the List with all its data */&lt;br /&gt;
&lt;br /&gt;
  /* ATTENTION:&lt;br /&gt;
  ** Use this function only if the List is *not* assigned to the ListView!&lt;br /&gt;
  ** If applicable, deselect via&lt;br /&gt;
  ** SetGadgetAttrs(gad,win,NULL, LISTBROWSER_Labels,~0, TAG_END);&lt;br /&gt;
  ** then modify the List, then assign the (empty) List via&lt;br /&gt;
  ** SetGadgetAttrs(gad,win,NULL, LISTBROWSER_Labels,liste, TAG_END);&lt;br /&gt;
  ** back to the ListView.&lt;br /&gt;
  */&lt;br /&gt;
&lt;br /&gt;
  struct Node *node, *nextnode;&lt;br /&gt;
  node = gb_List.lh_Head;&lt;br /&gt;
&lt;br /&gt;
  while((nextnode = node-&amp;gt;ln_Succ))&lt;br /&gt;
  {&lt;br /&gt;
    IExec-&amp;gt;Remove(node);&lt;br /&gt;
    IListBrowser-&amp;gt;FreeListBrowserNode(node);&lt;br /&gt;
&lt;br /&gt;
    node = nextnode;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void RenderHookFunc(struct Hook *hook, Object *obj, struct gpRender *gpr)&lt;br /&gt;
{&lt;br /&gt;
  /*&lt;br /&gt;
  ** Hook Function to refresh the Space Gadgets&lt;br /&gt;
  */&lt;br /&gt;
&lt;br /&gt;
  /* only if the Window is visible we can draw into it */&lt;br /&gt;
  if(gb_Win)&lt;br /&gt;
  {&lt;br /&gt;
    /* determine the size of our Output Area */&lt;br /&gt;
    struct IBox *gb_DrawArea;&lt;br /&gt;
    IIntuition-&amp;gt;GetAttr(SPACE_AreaBox,(Object*)gb_SpaceGad,(ULONG *)&amp;amp;gb_DrawArea);&lt;br /&gt;
&lt;br /&gt;
    /* empty the complete Area with Background Color */&lt;br /&gt;
    IGraphics-&amp;gt;SetAPen(gpr-&amp;gt;gpr_RPort,0);&lt;br /&gt;
    IGraphics-&amp;gt;RectFill(gpr-&amp;gt;gpr_RPort,gb_DrawArea-&amp;gt;Left,gb_DrawArea-&amp;gt;Top,&lt;br /&gt;
     gb_DrawArea-&amp;gt;Left+gb_DrawArea-&amp;gt;Width-1,gb_DrawArea-&amp;gt;Top+gb_DrawArea-&amp;gt;Height-1);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void IDCMPHookFunc(struct Hook *hook, Object *winobj, struct IntuiMessage *msg)&lt;br /&gt;
{&lt;br /&gt;
  /*&lt;br /&gt;
  ** Hook Function for listening to IDCMP_UPDATE Messages&lt;br /&gt;
  */&lt;br /&gt;
&lt;br /&gt;
  struct TagItem *taglist = (struct TagItem *) msg-&amp;gt;IAddress;&lt;br /&gt;
  const LONG gadid = IUtility-&amp;gt;GetTagData(GA_ID,-1,taglist);&lt;br /&gt;
&lt;br /&gt;
  if(msg-&amp;gt;Class == IDCMP_GADGETDOWN)&lt;br /&gt;
  {&lt;br /&gt;
    /* Press Mouse Button: Set start position without drawing */&lt;br /&gt;
    IGraphics-&amp;gt;SetAPen(gb_Win-&amp;gt;RPort,4);&lt;br /&gt;
    IGraphics-&amp;gt;Move(gb_Win-&amp;gt;RPort,msg-&amp;gt;MouseX,msg-&amp;gt;MouseY);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if(msg-&amp;gt;Class == IDCMP_IDCMPUPDATE &amp;amp;&amp;amp; gadid == GAD_ID_Space)&lt;br /&gt;
  {&lt;br /&gt;
    //ULONG mx, my;&lt;br /&gt;
    //IIntuition-&amp;gt;GetAttr(SPACE_MouseX,gb_SpaceGad,(ULONG*)&amp;amp;mx);&lt;br /&gt;
    //IIntuition-&amp;gt;GetAttr(SPACE_MouseY,gb_SpaceGad,(ULONG*)&amp;amp;my);&lt;br /&gt;
&lt;br /&gt;
    /* determine the size of our Output Area */&lt;br /&gt;
    /* so we don&#039;t draw outside of this area */&lt;br /&gt;
    struct IBox *gb_DrawArea;&lt;br /&gt;
    IIntuition-&amp;gt;GetAttr(SPACE_AreaBox,(Object*)gb_SpaceGad,(ULONG *)&amp;amp;gb_DrawArea);&lt;br /&gt;
&lt;br /&gt;
    const LONG mx = msg-&amp;gt;MouseX;&lt;br /&gt;
    const LONG my = msg-&amp;gt;MouseY;&lt;br /&gt;
&lt;br /&gt;
    if(mx &amp;gt;= gb_DrawArea-&amp;gt;Left &amp;amp;&amp;amp;&lt;br /&gt;
       my &amp;gt;= gb_DrawArea-&amp;gt;Top &amp;amp;&amp;amp;&lt;br /&gt;
       mx &amp;lt;= gb_DrawArea-&amp;gt;Left+gb_DrawArea-&amp;gt;Width-1 &amp;amp;&amp;amp;&lt;br /&gt;
       my &amp;lt;= gb_DrawArea-&amp;gt;Top+gb_DrawArea-&amp;gt;Height-1)&lt;br /&gt;
    {&lt;br /&gt;
      IGraphics-&amp;gt;Draw(gb_Win-&amp;gt;RPort,msg-&amp;gt;MouseX,msg-&amp;gt;MouseY);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
BOOL CreateAll()&lt;br /&gt;
{&lt;br /&gt;
  int i;&lt;br /&gt;
&lt;br /&gt;
  /* Screen Pointer is needed for single Gadgets */&lt;br /&gt;
  gb_Scr = IIntuition-&amp;gt;LockPubScreen(NULL);&lt;br /&gt;
&lt;br /&gt;
  if(gb_Scr)&lt;br /&gt;
  {&lt;br /&gt;
    /* request and set pens for the Gradient Slider */&lt;br /&gt;
    struct ColorMap *cm = gb_Scr-&amp;gt;ViewPort.ColorMap;&lt;br /&gt;
&lt;br /&gt;
    gb_GradientArray[0] = IGraphics-&amp;gt;ObtainPen(cm,~0,0xFF,0xFF,0xFF,PENF_EXCLUSIVE);&lt;br /&gt;
    gb_GradientArray[1] = IGraphics-&amp;gt;ObtainPen(cm,~0,0xCC,0xCC,0xCC,PENF_EXCLUSIVE);&lt;br /&gt;
    gb_GradientArray[2] = IGraphics-&amp;gt;ObtainPen(cm,~0,0x99,0x99,0x99,PENF_EXCLUSIVE);&lt;br /&gt;
    gb_GradientArray[3] = IGraphics-&amp;gt;ObtainPen(cm,~0,0x66,0x66,0x66,PENF_EXCLUSIVE);&lt;br /&gt;
    gb_GradientArray[4] = IGraphics-&amp;gt;ObtainPen(cm,~0,0x33,0x33,0x33,PENF_EXCLUSIVE);&lt;br /&gt;
    gb_GradientArray[5] = IGraphics-&amp;gt;ObtainPen(cm,~0,0x00,0x00,0x00,PENF_EXCLUSIVE);&lt;br /&gt;
&lt;br /&gt;
    struct ViewPort *vp = &amp;amp;gb_Scr-&amp;gt;ViewPort;&lt;br /&gt;
&lt;br /&gt;
    if((WORD)gb_GradientArray[0] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[0],0xFF,0xFF,0xFF);&lt;br /&gt;
    if((WORD)gb_GradientArray[1] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[1],0xCC,0xCC,0xCC);&lt;br /&gt;
    if((WORD)gb_GradientArray[2] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[2],0x99,0x99,0x99);&lt;br /&gt;
    if((WORD)gb_GradientArray[3] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[3],0x66,0x66,0x66);&lt;br /&gt;
    if((WORD)gb_GradientArray[4] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[4],0x33,0x33,0x33);&lt;br /&gt;
    if((WORD)gb_GradientArray[5] != ~0) IGraphics-&amp;gt;SetRGB4(vp,gb_GradientArray[5],0x00,0x00,0x00);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* create Listbrowser entries */&lt;br /&gt;
  IExec-&amp;gt;NewList(&amp;amp;gb_List);&lt;br /&gt;
  CreateEntry(0,&amp;quot;1&amp;quot;,&amp;quot;Amiga 1000&amp;quot;);&lt;br /&gt;
  CreateEntry(1,&amp;quot;2&amp;quot;,&amp;quot;Amiga 1200&amp;quot;);&lt;br /&gt;
  CreateEntry(2,&amp;quot;3&amp;quot;,&amp;quot;AmigaOne&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  /* initialize RenderHook */&lt;br /&gt;
  gb_RenderHook.h_Entry    = (ULONG (*)())RenderHookFunc;&lt;br /&gt;
  gb_RenderHook.h_SubEntry = NULL;&lt;br /&gt;
&lt;br /&gt;
  /* initialize IDCMPHook for listening to IDCMP_UPDATE Messages */&lt;br /&gt;
  gb_IDCMPHook.h_Entry    = (ULONG (*)())IDCMPHookFunc;&lt;br /&gt;
  gb_IDCMPHook.h_SubEntry = NULL;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* create Gadget Object */&lt;br /&gt;
  gb_ButtonVGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
      BUTTON_BevelStyle,          BVS_THIN,&lt;br /&gt;
      BUTTON_Justification,       BCJ_LEFT,&lt;br /&gt;
      GA_ID,                      GAD_ID_ButtonV,&lt;br /&gt;
      GA_Text,                    &amp;quot;Info-Anzeige&amp;quot;,&lt;br /&gt;
      GA_ReadOnly,                TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ButtonGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Button,&lt;br /&gt;
      GA_Text,                    &amp;quot;Button&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_StringGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IString-&amp;gt;STRING_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_String,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      STRINGA_MaxChars,           30+1,&lt;br /&gt;
      STRINGA_MinVisible,         30,&lt;br /&gt;
      STRINGA_TextVal,            &amp;quot;Amiga 1000&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_IntegerGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IInteger-&amp;gt;INTEGER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Integer,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      INTEGER_MaxChars,           4,&lt;br /&gt;
      INTEGER_Arrows,             TRUE,&lt;br /&gt;
      INTEGER_MinVisible,         3,&lt;br /&gt;
      INTEGER_Minimum,            1,&lt;br /&gt;
      INTEGER_Maximum,            100,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_SliderGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ISlider-&amp;gt;SLIDER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Slider,&lt;br /&gt;
      GA_Immediate,               TRUE,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      SLIDER_Min,                 0,&lt;br /&gt;
      SLIDER_Max,                 100,&lt;br /&gt;
      SLIDER_Level,               10,&lt;br /&gt;
      SLIDER_Orientation,         SLIDER_HORIZONTAL,&lt;br /&gt;
      SLIDER_Ticks,               11,&lt;br /&gt;
      SLIDER_KnobDelta,           50,&lt;br /&gt;
      SLIDER_LevelFormat,         &amp;quot;%3ld&amp;quot;,&lt;br /&gt;
      SLIDER_LevelMaxLen,         3+1,&lt;br /&gt;
      SLIDER_LevelPlace,          PLACETEXT_IN,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ScrollerGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IScroller-&amp;gt;SCROLLER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Scroller,&lt;br /&gt;
      GA_Immediate,               TRUE,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      SCROLLER_Top,               1,&lt;br /&gt;
      SCROLLER_Visible,           5,&lt;br /&gt;
      SCROLLER_Total,             105,&lt;br /&gt;
      SCROLLER_Orientation,       SCROLLER_HORIZONTAL,&lt;br /&gt;
      SCROLLER_Arrows,            TRUE,&lt;br /&gt;
      SCROLLER_Stretch,           TRUE,&lt;br /&gt;
      SCROLLER_ArrowDelta,        10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ChooserGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IChooser-&amp;gt;CHOOSER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Chooser,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_Underscore,              &#039;#&#039;,&lt;br /&gt;
      CHOOSER_PopUp,              TRUE,&lt;br /&gt;
      CHOOSER_DropDown,           FALSE,&lt;br /&gt;
      CHOOSER_LabelArray,         gb_ChooserLabels,&lt;br /&gt;
      CHOOSER_Selected,           0,&lt;br /&gt;
      CHOOSER_MaxLabels,          10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Chooser2Gad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IChooser-&amp;gt;CHOOSER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Chooser2,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_Underscore,              &#039;#&#039;,&lt;br /&gt;
      CHOOSER_PopUp,              FALSE,&lt;br /&gt;
      CHOOSER_DropDown,           TRUE,&lt;br /&gt;
      CHOOSER_Title,              &amp;quot;Eintrâ°ge&amp;quot;,&lt;br /&gt;
      CHOOSER_LabelArray,         gb_Chooser2Labels,&lt;br /&gt;
      CHOOSER_Selected,           0,&lt;br /&gt;
      CHOOSER_MaxLabels,          10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Chooser3Gad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IChooser-&amp;gt;CHOOSER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Chooser3,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_Underscore,              &#039;#&#039;,&lt;br /&gt;
      CHOOSER_PopUp,              FALSE,&lt;br /&gt;
      CHOOSER_DropDown,           TRUE,&lt;br /&gt;
      CHOOSER_LabelArray,         gb_Chooser3Labels,&lt;br /&gt;
      CHOOSER_Selected,           0,&lt;br /&gt;
      CHOOSER_MaxLabels,          10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_CheckboxGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ICheckBox-&amp;gt;CHECKBOX_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Checkbox,&lt;br /&gt;
      GA_Text,                    &amp;quot;Anwahl&amp;quot;,&lt;br /&gt;
      CHECKBOX_TextPlace,         PLACETEXT_RIGHT,&lt;br /&gt;
      CHECKBOX_Checked,           TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_RadioGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IRadioButton-&amp;gt;RADIOBUTTON_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Radio,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_Text,                    gb_RadioLabels,&lt;br /&gt;
      //RADIOBUTTON_LabelArray,     gb_RadioLabels,&lt;br /&gt;
      RADIOBUTTON_Selected,       2,&lt;br /&gt;
      RADIOBUTTON_LabelPlace,     PLACETEXT_RIGHT,&lt;br /&gt;
      RADIOBUTTON_Spacing,        4,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ListbrowserGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IListBrowser-&amp;gt;LISTBROWSER_GetClass(),NULL,&lt;br /&gt;
      GA_ReadOnly,                FALSE,&lt;br /&gt;
      GA_ID,                      GAD_ID_Listbrowser,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      LISTBROWSER_ColumnInfo,     &amp;amp;gb_ListbrowserColumn,&lt;br /&gt;
      LISTBROWSER_ColumnTitles,   TRUE,&lt;br /&gt;
      LISTBROWSER_Labels,         &amp;amp;gb_List,&lt;br /&gt;
      LISTBROWSER_VertSeparators, TRUE,&lt;br /&gt;
      LISTBROWSER_Spacing,        1,&lt;br /&gt;
      LISTBROWSER_ShowSelected,   TRUE,&lt;br /&gt;
      LISTBROWSER_AutoFit,        TRUE,&lt;br /&gt;
      LISTBROWSER_Editable,       FALSE,&lt;br /&gt;
      LISTBROWSER_MinVisible,     2,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_PaletteGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IPalette-&amp;gt;PALETTE_GetClass(),NULL,&lt;br /&gt;
      GA_ReadOnly,                FALSE,&lt;br /&gt;
      GA_ID,                      GAD_ID_Palette,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      PALETTE_Colour,             1,&lt;br /&gt;
      PALETTE_ColourOffset,       0,&lt;br /&gt;
      PALETTE_NumColours,         8,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_FileGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IGetFile-&amp;gt;GETFILE_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_File,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      GETFILE_TitleText,          &amp;quot;&amp;quot;Select File ...&amp;quot;,&lt;br /&gt;
      GETFILE_Pattern,            &amp;quot;#?&amp;quot;,&lt;br /&gt;
      GETFILE_DoSaveMode,         FALSE,&lt;br /&gt;
      GETFILE_RejectIcons,        TRUE,&lt;br /&gt;
      GETFILE_ReadOnly,           TRUE,&lt;br /&gt;
      GETFILE_DrawersOnly,        FALSE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_FontGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IGetFont-&amp;gt;GETFONT_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Font,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GETFONT_TitleText,          &amp;quot;Select Font ...&amp;quot;,&lt;br /&gt;
      GETFONT_MinHeight,          6,&lt;br /&gt;
      GETFONT_MaxHeight,          30,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ScreenmodeGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IGetScreenMode-&amp;gt;GETSCREENMODE_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Screenmode,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GETSCREENMODE_TitleText,    &amp;quot;Select Screen Mode ...&amp;quot;,&lt;br /&gt;
      GETSCREENMODE_MinDepth,     2,&lt;br /&gt;
      GETSCREENMODE_MaxDepth,     8,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ColorGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IGetColor-&amp;gt;GETCOLOR_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Color,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GETCOLOR_TitleText,         &amp;quot;Choose Color ...&amp;quot;,&lt;br /&gt;
      GETCOLOR_Screen,            gb_Scr,&lt;br /&gt;
      GETCOLOR_Color,             0x00aaccee,&lt;br /&gt;
      GETCOLOR_ColorWheel,        TRUE,&lt;br /&gt;
      GETCOLOR_RGBSliders,        TRUE,&lt;br /&gt;
      GETCOLOR_ShowRGB,           TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_FuelgaugeGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IFuelGauge-&amp;gt;FUELGAUGE_GetClass(),NULL,&lt;br /&gt;
      GA_ReadOnly,                FALSE,&lt;br /&gt;
      GA_ID,                      GAD_ID_Fuelgauge,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      FUELGAUGE_Min,              0,&lt;br /&gt;
      FUELGAUGE_Max,              100,&lt;br /&gt;
      FUELGAUGE_Level,            10,&lt;br /&gt;
      FUELGAUGE_Orientation,      FGORIENT_HORIZ,&lt;br /&gt;
      FUELGAUGE_Percent,          TRUE,&lt;br /&gt;
      FUELGAUGE_Ticks,            10,&lt;br /&gt;
      FUELGAUGE_ShortTicks,       10,&lt;br /&gt;
      FUELGAUGE_Justification,    FGJ_CENTER,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_GradientsliderGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(NULL,&amp;quot;gradientslider.gadget&amp;quot;,&lt;br /&gt;
      GA_ID,                      GAD_ID_Gradientslider,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      GRAD_PenArray,              gb_GradientArray,&lt;br /&gt;
      PGA_Freedom,                LORIENT_VERT,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  /* set Color Wheel to this RGB value */&lt;br /&gt;
  struct ColorWheelRGB rgbval = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };&lt;br /&gt;
&lt;br /&gt;
  gb_ColorwheelGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(NULL,&amp;quot;colorwheel.gadget&amp;quot;,&lt;br /&gt;
      GA_ID,                      GAD_ID_Colorwheel,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      WHEEL_Screen,               gb_Scr,&lt;br /&gt;
      //WHEEL_GradientSlider,       gb_GradientsliderGad,&lt;br /&gt;
      WHEEL_BevelBox,             FALSE,&lt;br /&gt;
      WHEEL_RGB,                  rgbval,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_TexteditorGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ITextEditor-&amp;gt;TEXTEDITOR_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Texteditor,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      GA_TEXTEDITOR_FixedFont,    FALSE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_DatebrowserGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IDateBrowser-&amp;gt;DATEBROWSER_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Datebrowser,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      GA_TabCycle,                TRUE,&lt;br /&gt;
      DATEBROWSER_Day,            27,&lt;br /&gt;
      DATEBROWSER_Month,          10,&lt;br /&gt;
      DATEBROWSER_Year,           2007,&lt;br /&gt;
      DATEBROWSER_ShowTitle,      TRUE,&lt;br /&gt;
      DATEBROWSER_ShowWeek,       TRUE,&lt;br /&gt;
      DATEBROWSER_MultiSelect,    FALSE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_SpaceGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ISpace-&amp;gt;SPACE_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Space,&lt;br /&gt;
      GA_RelVerify,               TRUE,  /* send Gadget Up */&lt;br /&gt;
      GA_Immediate,               TRUE,  /* send Gadget Down */&lt;br /&gt;
      GA_ReadOnly,                FALSE,&lt;br /&gt;
      SPACE_Transparent,          TRUE,  /* Background not deleted automatically */&lt;br /&gt;
      SPACE_MinWidth,             10,&lt;br /&gt;
      SPACE_MinHeight,            10,&lt;br /&gt;
      SPACE_BevelStyle,           BVS_DROPBOX,&lt;br /&gt;
      SPACE_RenderHook,           &amp;amp;gb_RenderHook,&lt;br /&gt;
      ICA_TARGET,                 ICTARGET_IDCMP,  /* send a Message to the Program */&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_PageGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;PAGE_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Page,&lt;br /&gt;
      LAYOUT_Orientation,         LAYOUT_VERTICAL,&lt;br /&gt;
      LAYOUT_FixedHoriz,          FALSE,&lt;br /&gt;
      LAYOUT_FixedVert,           FALSE,&lt;br /&gt;
      LAYOUT_SpaceInner,          TRUE,&lt;br /&gt;
&lt;br /&gt;
      PAGE_Add, IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_AddChild, IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
              BUTTON_BevelStyle, BVS_NONE, BUTTON_Transparent, TRUE, GA_Text, &amp;quot;Amiga 1000&amp;quot;, GA_ReadOnly, TRUE, TAG_END),&lt;br /&gt;
              TAG_END),&lt;br /&gt;
      PAGE_Add, IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_AddChild, IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
              BUTTON_BevelStyle, BVS_NONE, BUTTON_Transparent, TRUE, GA_Text, &amp;quot;Amiga 1200&amp;quot;, GA_ReadOnly, TRUE, TAG_END),&lt;br /&gt;
              TAG_END),&lt;br /&gt;
      PAGE_Add, IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_AddChild, IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
              BUTTON_BevelStyle, BVS_NONE, BUTTON_Transparent, TRUE, GA_Text, &amp;quot;AmigaOne&amp;quot;, GA_ReadOnly, TRUE, TAG_END),&lt;br /&gt;
              TAG_END),&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ClicktabGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IClickTab-&amp;gt;CLICKTAB_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                      GAD_ID_Clicktab,&lt;br /&gt;
      GA_Text,                    &amp;amp;gb_ClicktabLabels,&lt;br /&gt;
      GA_RelVerify,               TRUE,&lt;br /&gt;
      CLICKTAB_PageGroup,         gb_PageGad,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  gb_LabelImg = (struct Image *) IIntuition-&amp;gt;NewObject(ILabel-&amp;gt;LABEL_GetClass(),NULL,&lt;br /&gt;
      LABEL_Text,                 &amp;quot;Beschriftung&amp;quot;,&lt;br /&gt;
      //LABEL_SoftStyle,            FSF_BOLD,&lt;br /&gt;
      //LABEL_Justification,        LJ_LEFT,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_BevelImg = (struct Image *) IIntuition-&amp;gt;NewObject(IBevel-&amp;gt;BEVEL_GetClass(),NULL,&lt;br /&gt;
      BEVEL_Style,                BVS_BOX,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_BitmapImg = (struct Image *) IIntuition-&amp;gt;NewObject(IBitMap-&amp;gt;BITMAP_GetClass(),NULL,&lt;br /&gt;
      BITMAP_Screen,              gb_Scr,&lt;br /&gt;
      BITMAP_Masking,             FALSE,&lt;br /&gt;
      BITMAP_SourceFile,          &amp;quot;amiga1000-normal.iff&amp;quot;,&lt;br /&gt;
      BITMAP_SelectSourceFile,    &amp;quot;amiga1000-selected.iff&amp;quot;,&lt;br /&gt;
      BITMAP_DisabledSourceFile,  &amp;quot;amiga1000-disabled.iff&amp;quot;,&lt;br /&gt;
      IA_SupportsDisable,         TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_BitmapSImg = (struct Image *) IIntuition-&amp;gt;NewObject(IBitMap-&amp;gt;BITMAP_GetClass(),NULL,&lt;br /&gt;
      BITMAP_Screen,              gb_Scr,&lt;br /&gt;
      BITMAP_Masking,             FALSE,&lt;br /&gt;
      BITMAP_SourceFile,          &amp;quot;amiga1000-selected.iff&amp;quot;,&lt;br /&gt;
      IA_SupportsDisable,         TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_BitmapDImg = (struct Image *) IIntuition-&amp;gt;NewObject(IBitMap-&amp;gt;BITMAP_GetClass(),NULL,&lt;br /&gt;
      BITMAP_Screen,              gb_Scr,&lt;br /&gt;
      BITMAP_Masking,             FALSE,&lt;br /&gt;
      BITMAP_SourceFile,          &amp;quot;amiga1000-disabled.iff&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_DrawlistImg = (struct Image *) IIntuition-&amp;gt;NewObject(IDrawList-&amp;gt;DRAWLIST_GetClass(),NULL,&lt;br /&gt;
      DRAWLIST_Directives,        gb_DrawList,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_FillerImg = (struct Image *) IIntuition-&amp;gt;NewObject(IFiller-&amp;gt;FILLER_GetClass(),NULL,&lt;br /&gt;
      FILLER_Screen,              gb_Scr,&lt;br /&gt;
      FILLER_BackgroundColor,     0xffaaaaaa,&lt;br /&gt;
      FILLER_ImageFilename,       &amp;quot;amigaball.iff&amp;quot;,&lt;br /&gt;
      FILLER_ImageMode,           FILLER_MODE_TILED,&lt;br /&gt;
      IA_Width,                   30,&lt;br /&gt;
      IA_Height,                  30,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_PenmapImg = (struct Image *) IIntuition-&amp;gt;NewObject(IPenMap-&amp;gt;PENMAP_GetClass(),NULL,&lt;br /&gt;
      PENMAP_RenderData,          happy_data,&lt;br /&gt;
      PENMAP_Palette,             image_object_palette,&lt;br /&gt;
      PENMAP_Screen,              gb_Scr,&lt;br /&gt;
      PENMAP_Transparent,         TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  for(i=1; i&amp;lt;=26; i++)&lt;br /&gt;
  {&lt;br /&gt;
    /* 13-17 and 19 do not exist */&lt;br /&gt;
    gb_GlyphImg[i] = (struct Image *) IIntuition-&amp;gt;NewObject(IGlyph-&amp;gt;GLYPH_GetClass(),NULL,&lt;br /&gt;
        GLYPH_Glyph,                i, /*GLYPH_xxx */&lt;br /&gt;
        IA_Width,                   20,&lt;br /&gt;
        IA_Height,                  20,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* create the single pages of the Drag Bar */&lt;br /&gt;
  gb_Page1Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;Grundtypen&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_ButtonVGad,&lt;br /&gt;
      CHILD_WeightedHeight, 80,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ButtonGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
      LAYOUT_AddChild,      gb_StringGad,&lt;br /&gt;
      LAYOUT_AddChild,      gb_IntegerGad,&lt;br /&gt;
      LAYOUT_AddChild,      gb_SliderGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ScrollerGad,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Page2Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;Auswahllisten&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,&lt;br /&gt;
          LAYOUT_AddChild,      gb_ChooserGad,&lt;br /&gt;
          LAYOUT_AddChild,      gb_Chooser2Gad,&lt;br /&gt;
          LAYOUT_AddChild,      gb_Chooser3Gad,&lt;br /&gt;
          TAG_END),&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_CheckboxGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      LAYOUT_AddChild,      gb_RadioGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ListbrowserGad,&lt;br /&gt;
      CHILD_WeightedHeight, 90,&lt;br /&gt;
      LAYOUT_AddChild,      gb_PaletteGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Page3Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;ASL-Auswahl&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_FileGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      LAYOUT_AddChild,      gb_FontGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ScreenmodeGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ColorGad,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Page4Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;Sonstige Typen&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_FuelgaugeGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,&lt;br /&gt;
          LAYOUT_AddChild,      gb_ColorwheelGad,&lt;br /&gt;
          //CHILD_WeightedWidth,  0,&lt;br /&gt;
          //CHILD_WeightedHeight, 0,&lt;br /&gt;
          LAYOUT_AddChild,      gb_GradientsliderGad,&lt;br /&gt;
          LAYOUT_AddChild,      IIntuition-&amp;gt;NewObject(ISpace-&amp;gt;SPACE_GetClass(),NULL,&lt;br /&gt;
              SPACE_Transparent,          FALSE,&lt;br /&gt;
              SPACE_MinWidth,             10,&lt;br /&gt;
              SPACE_MinHeight,            10,&lt;br /&gt;
              TAG_END),&lt;br /&gt;
          TAG_END),&lt;br /&gt;
      CHILD_WeightedHeight, 70,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_TexteditorGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
      LAYOUT_AddChild,      gb_DatebrowserGad,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Page5Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;Seitengestalltung&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      gb_SpaceGad,&lt;br /&gt;
      LAYOUT_AddChild,      gb_ClicktabGad,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_Page6Layout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,&lt;br /&gt;
      LAYOUT_SpaceOuter,    TRUE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
      LAYOUT_BevelStyle,    BVS_GROUP,&lt;br /&gt;
      LAYOUT_Label,         &amp;quot;Images&amp;quot;,&lt;br /&gt;
      LAYOUT_LabelPlace,    BVJ_TOP_CENTER,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddImage,      gb_LabelImg,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddImage,      gb_BevelImg,&lt;br /&gt;
      CHILD_MinHeight,      10,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,&lt;br /&gt;
          LAYOUT_AddImage,      gb_BitmapImg,&lt;br /&gt;
          LAYOUT_AddImage,      gb_BitmapSImg,&lt;br /&gt;
          LAYOUT_AddImage,      gb_BitmapDImg,&lt;br /&gt;
          TAG_END),&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddImage,      gb_DrawlistImg,&lt;br /&gt;
      CHILD_MinWidth,       30,&lt;br /&gt;
      CHILD_MinHeight,      30,&lt;br /&gt;
      CHILD_WeightedWidth,  0,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddImage,      gb_FillerImg,&lt;br /&gt;
      CHILD_MinWidth,       30,&lt;br /&gt;
      CHILD_MinHeight,      30,&lt;br /&gt;
      CHILD_MaxWidth,       200,&lt;br /&gt;
      CHILD_MaxHeight,      60,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddImage,      gb_PenmapImg,&lt;br /&gt;
      CHILD_WeightedWidth,  0,&lt;br /&gt;
      CHILD_WeightedHeight, 0,&lt;br /&gt;
&lt;br /&gt;
      LAYOUT_AddChild,      IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
          LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,&lt;br /&gt;
&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 1],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 2],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 3],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 4],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 5],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 6],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 7],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 8],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[ 9],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[10],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[11],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[12],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[18],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[20],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[21],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[22],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[23],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[24],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[25],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          LAYOUT_AddImage,      gb_GlyphImg[26],&lt;br /&gt;
      CHILD_MinWidth,       20,&lt;br /&gt;
          TAG_END),&lt;br /&gt;
      CHILD_MinHeight,      20,&lt;br /&gt;
      CHILD_WeightedHeight, 10,&lt;br /&gt;
&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_PagesLayout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;PAGE_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                GAD_ID_Pages,&lt;br /&gt;
      LAYOUT_Orientation,   LAYOUT_VERTICAL,&lt;br /&gt;
      LAYOUT_FixedHoriz,    FALSE,&lt;br /&gt;
      LAYOUT_FixedVert,     FALSE,&lt;br /&gt;
      LAYOUT_SpaceInner,    TRUE,&lt;br /&gt;
&lt;br /&gt;
      PAGE_Add,             gb_Page1Layout,&lt;br /&gt;
      PAGE_Add,             gb_Page2Layout,&lt;br /&gt;
      PAGE_Add,             gb_Page3Layout,&lt;br /&gt;
      PAGE_Add,             gb_Page4Layout,&lt;br /&gt;
      PAGE_Add,             gb_Page5Layout,&lt;br /&gt;
      PAGE_Add,             gb_Page6Layout,&lt;br /&gt;
&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_PagesClicktab = (struct Gadget *) IIntuition-&amp;gt;NewObject(IClickTab-&amp;gt;CLICKTAB_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                GAD_ID_PagesClicktab,&lt;br /&gt;
      GA_Text,              &amp;amp;gb_PagesLabels,&lt;br /&gt;
      CLICKTAB_PageGroup,   gb_PagesLayout,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  IIntuition-&amp;gt;UnlockPubScreen(NULL,gb_Scr);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* create Layout Object */&lt;br /&gt;
  gb_MainLayout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
    LAYOUT_Orientation,  LAYOUT_ORIENT_VERT,&lt;br /&gt;
    LAYOUT_SpaceOuter,   TRUE,&lt;br /&gt;
    LAYOUT_SpaceInner,   TRUE,&lt;br /&gt;
&lt;br /&gt;
    LAYOUT_AddChild,     gb_PagesClicktab,&lt;br /&gt;
&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  if(gb_MainLayout)&lt;br /&gt;
  {&lt;br /&gt;
&lt;br /&gt;
    /* create the Window Object */&lt;br /&gt;
    if((gb_WindowObj = (Object *) IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),NULL,&lt;br /&gt;
      WA_IDCMP,             IDCMP_GADGETUP | IDCMP_GADGETDOWN |&lt;br /&gt;
                            IDCMP_MOUSEMOVE |&lt;br /&gt;
                            IDCMP_VANILLAKEY | IDCMP_RAWKEY |&lt;br /&gt;
                            IDCMP_CLOSEWINDOW,&lt;br /&gt;
      WA_SizeGadget,        TRUE,&lt;br /&gt;
      WA_DepthGadget,       TRUE,&lt;br /&gt;
      WA_DragBar,           TRUE,&lt;br /&gt;
      WA_Activate,          TRUE,&lt;br /&gt;
      WA_Title,             &amp;quot;All ReAction Gadgets&amp;quot;,&lt;br /&gt;
      WA_InnerWidth,        100,&lt;br /&gt;
      WA_InnerHeight,       100,&lt;br /&gt;
      WA_AutoAdjust,        TRUE,&lt;br /&gt;
      WA_CloseGadget,       TRUE,&lt;br /&gt;
      WA_RMBTrap,           TRUE,&lt;br /&gt;
      WINDOW_ParentGroup,   gb_MainLayout,&lt;br /&gt;
      WINDOW_IDCMPHook,     &amp;amp;gb_IDCMPHook,&lt;br /&gt;
      WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE | IDCMP_GADGETDOWN,&lt;br /&gt;
      TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      /* everything created correctly */&lt;br /&gt;
&lt;br /&gt;
      /* the Window can be shown now */&lt;br /&gt;
      gb_Win = RA_OpenWindow(gb_WindowObj);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gb_WindowObj ? TRUE : FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void DestroyAll()&lt;br /&gt;
{&lt;br /&gt;
  /* close Window and release included Objects */&lt;br /&gt;
  RA_CloseWindow(gb_WindowObj);&lt;br /&gt;
  IIntuition-&amp;gt;DisposeObject(gb_WindowObj);&lt;br /&gt;
&lt;br /&gt;
  /* release Listview entries */&lt;br /&gt;
  FreigebenList();&lt;br /&gt;
&lt;br /&gt;
  /* release the reserved pens */&lt;br /&gt;
  struct ColorMap *cm = (gb_Scr ? gb_Scr-&amp;gt;ViewPort.ColorMap : NULL);&lt;br /&gt;
&lt;br /&gt;
  if((WORD)gb_GradientArray[0] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[0]);&lt;br /&gt;
  if((WORD)gb_GradientArray[1] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[1]);&lt;br /&gt;
  if((WORD)gb_GradientArray[2] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[2]);&lt;br /&gt;
  if((WORD)gb_GradientArray[3] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[3]);&lt;br /&gt;
  if((WORD)gb_GradientArray[4] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[4]);&lt;br /&gt;
  if((WORD)gb_GradientArray[5] != ~0) IGraphics-&amp;gt;ReleasePen(cm,gb_GradientArray[5]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void MsgLoop()&lt;br /&gt;
{&lt;br /&gt;
  ULONG winsig;&lt;br /&gt;
  ULONG result, code;&lt;br /&gt;
  BOOL laufen = TRUE;&lt;br /&gt;
&lt;br /&gt;
  /* save Signal Bits of the Window */&lt;br /&gt;
  IIntuition-&amp;gt;GetAttr(WINDOW_SigMask,gb_WindowObj,&amp;amp;winsig);&lt;br /&gt;
&lt;br /&gt;
  while(laufen)&lt;br /&gt;
  {&lt;br /&gt;
    /* wait for user actions */&lt;br /&gt;
    const ULONG sigs = IExec-&amp;gt;Wait(winsig | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
    /* process the received Messages */&lt;br /&gt;
    while((result = RA_HandleInput(gb_WindowObj,&amp;amp;code)) != WMHI_LASTMSG)&lt;br /&gt;
    {&lt;br /&gt;
      /* mask out the Messages part */&lt;br /&gt;
      switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
      {&lt;br /&gt;
        /* process Gadget Messages */&lt;br /&gt;
        case WMHI_GADGETUP:&lt;br /&gt;
             switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
             {&lt;br /&gt;
               case GAD_ID_ButtonV:&lt;br /&gt;
                    {&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Button:&lt;br /&gt;
                    {&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Button: [released]\n&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_String:&lt;br /&gt;
                    {&lt;br /&gt;
                      STRPTR val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(STRINGA_TextVal,(Object*)gb_StringGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;String: [%s]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Integer:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(INTEGER_Number,(Object*)gb_IntegerGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Integer: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Slider:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(SLIDER_Level,(Object*)gb_SliderGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Slieder: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Scroller:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(SCROLLER_Top,(Object*)gb_ScrollerGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Scroller: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Chooser:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CHOOSER_Selected,(Object*)gb_ChooserGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Chooser: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Chooser2:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CHOOSER_Selected,(Object*)gb_Chooser2Gad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Chooser2: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Chooser3:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CHOOSER_Selected,(Object*)gb_Chooser3Gad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Chooser3: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Checkbox:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GA_Selected,(Object*)gb_CheckboxGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Checkbox: [%s]\n&amp;quot;,(val ? &amp;quot;selectet&amp;quot; : &amp;quot;not selected&amp;quot;));&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Radio:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(RADIOBUTTON_Selected,(Object*)gb_RadioGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Radio: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Listbrowser:&lt;br /&gt;
                    {&lt;br /&gt;
                      //ULONG action, column;&lt;br /&gt;
                      struct Node *node;&lt;br /&gt;
                      //IIntuition-&amp;gt;GetAttr(LISTBROWSER_RelEvent,(Object*)gb_WordlistGad,(ULONG*)&amp;amp;action);&lt;br /&gt;
                      //IIntuition-&amp;gt;GetAttr(LISTBROWSER_RelColumn,(Object*)gb_WordlistGad,(ULONG*)&amp;amp;column);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(LISTBROWSER_SelectedNode,(Object*)gb_ListbrowserGad,(ULONG*)&amp;amp;node);&lt;br /&gt;
                      //struct UserDictEntry *ude = LBNode2UserDictNode(node);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Listview: [%ld]\n&amp;quot;,node-&amp;gt;ln_Pri);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Palette:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(PALETTE_Colour,(Object*)gb_PaletteGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Palette: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_File:&lt;br /&gt;
                    {&lt;br /&gt;
                      STRPTR val;&lt;br /&gt;
                      ULONG res = gfRequestFile((Object *)gb_FileGad,gb_Win);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETFILE_FullFile,(Object*)gb_FileGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      if(res) IDOS-&amp;gt;Printf(&amp;quot;File: [%s]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Font:&lt;br /&gt;
                    {&lt;br /&gt;
                      struct TextAttr *val;&lt;br /&gt;
                      ULONG res = gfRequestFont((Object *)gb_FontGad,gb_Win);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETFONT_TextAttr,(Object*)gb_FontGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      if(res) IDOS-&amp;gt;Printf(&amp;quot;Font: [%s %ld]\n&amp;quot;,val-&amp;gt;ta_Name,val-&amp;gt;ta_YSize);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Screenmode:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG id, width, height, depth;&lt;br /&gt;
                      ULONG res = RequestScreenMode((Object *)gb_ScreenmodeGad,gb_Win);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETSCREENMODE_DisplayID,(Object*)gb_ScreenmodeGad,(ULONG*)&amp;amp;id);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETSCREENMODE_DisplayWidth,(Object*)gb_ScreenmodeGad,(ULONG*)&amp;amp;width);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETSCREENMODE_DisplayHeight,(Object*)gb_ScreenmodeGad,(ULONG*)&amp;amp;height);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETSCREENMODE_DisplayDepth,(Object*)gb_ScreenmodeGad,(ULONG*)&amp;amp;depth);&lt;br /&gt;
                      if(res) IDOS-&amp;gt;Printf(&amp;quot;Screenmode: [$%lx %ld x %ld x %ld]\n&amp;quot;,id,width,height,depth);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Color:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      ULONG res = RequestColor((Object *)gb_ColorGad,gb_Win);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GETCOLOR_Color,(Object*)gb_ColorGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      if(res) IDOS-&amp;gt;Printf(&amp;quot;Color: [$%08lx]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Fuelgauge:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(FUELGAUGE_Level,(Object*)gb_FuelgaugeGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Fuelgauge: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Colorwheel:&lt;br /&gt;
                    {&lt;br /&gt;
                      struct ColorWheelRGB val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(WHEEL_RGB,(Object*)gb_ColorwheelGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Colorwheel: [red=%08lx green=%08lx blue=%08lx]\n&amp;quot;,val.cw_Red,val.cw_Green,val.cw_Blue);&lt;br /&gt;
&lt;br /&gt;
                      /* reset the Gradient Slider depending on the new color selection */&lt;br /&gt;
                      struct ViewPort *vp = &amp;amp;gb_Scr-&amp;gt;ViewPort;&lt;br /&gt;
&lt;br /&gt;
                      if((WORD)gb_GradientArray[0] != ~0)&lt;br /&gt;
                       IGraphics-&amp;gt;SetRGB32(vp,gb_GradientArray[0],val.cw_Red,val.cw_Green,val.cw_Blue);&lt;br /&gt;
                      if((WORD)gb_GradientArray[1] != ~0)&lt;br /&gt;
                       IGraphics-&amp;gt;SetRGB32(vp,gb_GradientArray[1],val.cw_Red/15*12,val.cw_Green/15*12,val.cw_Blue/15*12);&lt;br /&gt;
                      if((WORD)gb_GradientArray[2] != ~0)&lt;br /&gt;
                       IGraphics-&amp;gt;SetRGB32(vp,gb_GradientArray[2],val.cw_Red/15*9,val.cw_Green/15*9,val.cw_Blue/15*9);&lt;br /&gt;
                      if((WORD)gb_GradientArray[3] != ~0)&lt;br /&gt;
                       IGraphics-&amp;gt;SetRGB32(vp,gb_GradientArray[3],val.cw_Red/15*6,val.cw_Green/15*6,val.cw_Blue/15*6);&lt;br /&gt;
                      if((WORD)gb_GradientArray[4] != ~0)&lt;br /&gt;
                       IGraphics-&amp;gt;SetRGB32(vp,gb_GradientArray[4],val.cw_Red/15*3,val.cw_Green/15*3,val.cw_Blue/15*3);&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshGadgets(gb_GradientsliderGad,gb_Win,NULL);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Gradientslider:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GRAD_CurVal,(Object*)gb_GradientsliderGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Gradientslieder: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Texteditor:&lt;br /&gt;
                    {&lt;br /&gt;
                      STRPTR val;&lt;br /&gt;
                      val = (STRPTR) IIntuition-&amp;gt;DoGadgetMethod(gb_TexteditorGad,gb_Win,NULL,GM_TEXTEDITOR_ExportText,NULL);&lt;br /&gt;
                      if(val) IDOS-&amp;gt;Printf(&amp;quot;Texteditor: [%s]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Datebrowser:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG day, month, year;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(DATEBROWSER_Day,(Object*)gb_DatebrowserGad,(ULONG*)&amp;amp;day);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(DATEBROWSER_Month,(Object*)gb_DatebrowserGad,(ULONG*)&amp;amp;month);&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(DATEBROWSER_Year,(Object*)gb_DatebrowserGad,(ULONG*)&amp;amp;year);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Datebrowser: [%ld.%ld.%ld]\n&amp;quot;,day,month,year);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Space:&lt;br /&gt;
                    {&lt;br /&gt;
                    //  ULONG mx, my;&lt;br /&gt;
                    //  IIntuition-&amp;gt;GetAttr(SPACE_MouseX,(Object*)gb_SpaceGad,(ULONG*)&amp;amp;mx);&lt;br /&gt;
                    //  IIntuition-&amp;gt;GetAttr(SPACE_MouseY,(Object*)gb_SpaceGad,(ULONG*)&amp;amp;my);&lt;br /&gt;
                    //  IDOS-&amp;gt;Printf(&amp;quot;Space: [%ld : %ld]\n&amp;quot;,mx,my);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Page:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(PAGE_Current,(Object*)gb_PageGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Page: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Clicktab:&lt;br /&gt;
                    {&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CLICKTAB_Current,(Object*)gb_ClicktabGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Clicktab: [%ld]\n&amp;quot;,val);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
        /* RawKey of pressed key */&lt;br /&gt;
        case WMHI_RAWKEY:&lt;br /&gt;
             {&lt;br /&gt;
               //struct InputEvent *ie;&lt;br /&gt;
               //IIntuition-&amp;gt;GetAttr(WINDOW_InputEvent,gb_WindowObj,(ULONG *)&amp;amp;ie);&lt;br /&gt;
               // code = result &amp;amp; WMHI_KEYMASK&lt;br /&gt;
               // qualifier = ie-&amp;gt;ie_Qualifier&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
        /* ASCII code of pressed key */&lt;br /&gt;
        case WMHI_MENUPICK:&lt;br /&gt;
             switch(result &amp;amp; WMHI_MENUMASK)&lt;br /&gt;
             {&lt;br /&gt;
               //case MENU_ID_xxx:&lt;br /&gt;
               //     break;&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
        /* Window Close Icon clicked */&lt;br /&gt;
        case WMHI_CLOSEWINDOW:&lt;br /&gt;
             {&lt;br /&gt;
               /* Window Close Icon executed */&lt;br /&gt;
               laufen = FALSE;&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    /* CTRL-C typed into the Shell or received as signal */&lt;br /&gt;
    if(sigs &amp;amp; SIGBREAKF_CTRL_C) laufen = FALSE;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  if( CreateAll() )&lt;br /&gt;
  {&lt;br /&gt;
    MsgLoop();&lt;br /&gt;
&lt;br /&gt;
    DestroyAll();&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Createing GUI failed !\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the support graphics files to go with it: [[Media:allgadgets-gfx.lha|allgadgets-gfx.lha]]&lt;br /&gt;
&lt;br /&gt;
[[File:AF110_allgadgets.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
Reactor&#039;s approach, which you could use to &#039;click together&#039; graphically a GUI under the Workbench 3.5 and 3.9, hasn&#039;t been developed any further. However, as shown in the previous example, you can basically copy the required GUI elements in your own sources with copy &amp;amp; paste.&lt;br /&gt;
&lt;br /&gt;
= ClickTab-Test Example =&lt;br /&gt;
&lt;br /&gt;
Another bonus is &amp;quot;ClickTab-Test&amp;quot; which deals exclusively with the Clicktab gadget. As I already mentioned, you can use it to create tabs. The tabs can in the meantime be dynamically added and deleted again. Moreover, you can use both tags, so all tags have the same width and the tab labeling is not shortened.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * ClickTab-Test.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc ClickTab-Test.c -o ClickTab-Test -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/requester.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction_macros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/checkbox.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/clicktab.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/page.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/label.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/button.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/clicktab.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/checkbox.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: ClickTab-Test 1.0 (13.02.2009) - (c) Feb.2009 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
enum GADID&lt;br /&gt;
{&lt;br /&gt;
  GAD_ID_Name = 1,&lt;br /&gt;
  GAD_ID_Add,&lt;br /&gt;
  GAD_ID_Del,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Evensize,&lt;br /&gt;
  GAD_ID_Labeltruncate,&lt;br /&gt;
&lt;br /&gt;
  GAD_ID_Clicktab,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Gadget *gb_NameGad;&lt;br /&gt;
struct Gadget *gb_AddGad;&lt;br /&gt;
struct Gadget *gb_DelGad;&lt;br /&gt;
struct Gadget *gb_EvensizeGad;&lt;br /&gt;
struct Gadget *gb_LabeltruncateGad;&lt;br /&gt;
struct Gadget *gb_ClicktabGad;&lt;br /&gt;
struct Gadget *gb_MainLayout;&lt;br /&gt;
&lt;br /&gt;
#define MAX_CLICKTABS 15&lt;br /&gt;
STRPTR gb_ClicktabLabels[MAX_CLICKTABS + 1] = { NULL };&lt;br /&gt;
&lt;br /&gt;
Object        *gb_WindowObj;&lt;br /&gt;
struct Window *gb_Win;&lt;br /&gt;
struct Screen *gb_Scr;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
BOOL CreateGUI()&lt;br /&gt;
{&lt;br /&gt;
  if((gb_ClicktabLabels[0] = (STRPTR) IExec-&amp;gt;AllocVecTags(31, AVT_Type, MEMF_SHARED, TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    IUtility-&amp;gt;Strlcpy(gb_ClicktabLabels[0], &amp;quot;_Muster&amp;quot;, 30);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  gb_NameGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IString-&amp;gt;STRING_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Name,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      GA_TabCycle,            TRUE,&lt;br /&gt;
      STRINGA_MaxChars,       30+1,&lt;br /&gt;
      STRINGA_MinVisible,     30,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_AddGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Add,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      GA_Text,                &amp;quot;_Add&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_DelGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IButton-&amp;gt;BUTTON_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Del,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      GA_Text,                &amp;quot;_Del&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_EvensizeGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ICheckBox-&amp;gt;CHECKBOX_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Evensize,&lt;br /&gt;
      GA_Text,                &amp;quot;EvenSize&amp;quot;,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      CHECKBOX_TextPlace,     PLACETEXT_RIGHT,&lt;br /&gt;
      CHECKBOX_Checked,       FALSE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_LabeltruncateGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(ICheckBox-&amp;gt;CHECKBOX_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Labeltruncate,&lt;br /&gt;
      GA_Text,                &amp;quot;LabelTruncate&amp;quot;,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      CHECKBOX_TextPlace,     PLACETEXT_RIGHT,&lt;br /&gt;
      CHECKBOX_Checked,       TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_ClicktabGad = (struct Gadget *) IIntuition-&amp;gt;NewObject(IClickTab-&amp;gt;CLICKTAB_GetClass(),NULL,&lt;br /&gt;
      GA_ID,                  GAD_ID_Clicktab,&lt;br /&gt;
      GA_Text,                &amp;amp;gb_ClicktabLabels,&lt;br /&gt;
      GA_RelVerify,           TRUE,&lt;br /&gt;
      CLICKTAB_EvenSize,      FALSE,&lt;br /&gt;
      CLICKTAB_LabelTruncate, TRUE,&lt;br /&gt;
      //CLICKTAB_PageGroup,   gb_PagesLayout,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
  gb_MainLayout = (struct Gadget *) IIntuition-&amp;gt;NewObject(ILayout-&amp;gt;LAYOUT_GetClass(),NULL,&lt;br /&gt;
    LAYOUT_Orientation,  LAYOUT_VERTICAL,&lt;br /&gt;
    LAYOUT_SpaceOuter,   TRUE,&lt;br /&gt;
    LAYOUT_SpaceInner,   TRUE,&lt;br /&gt;
&lt;br /&gt;
    LAYOUT_AddChild,     gb_NameGad,&lt;br /&gt;
    LAYOUT_AddChild,     gb_AddGad,&lt;br /&gt;
    LAYOUT_AddChild,     gb_DelGad,&lt;br /&gt;
    LAYOUT_AddChild,     gb_EvensizeGad,&lt;br /&gt;
    LAYOUT_AddChild,     gb_LabeltruncateGad,&lt;br /&gt;
    LAYOUT_AddChild,     gb_ClicktabGad,&lt;br /&gt;
&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if(gb_MainLayout)&lt;br /&gt;
  {&lt;br /&gt;
&lt;br /&gt;
    /* Create the window object */&lt;br /&gt;
    if((gb_WindowObj = (Object *) IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),NULL,&lt;br /&gt;
      WA_IDCMP,             IDCMP_GADGETUP |&lt;br /&gt;
                            IDCMP_VANILLAKEY |&lt;br /&gt;
                            IDCMP_CLOSEWINDOW,&lt;br /&gt;
      WA_SizeGadget,        TRUE,&lt;br /&gt;
      WA_DepthGadget,       TRUE,&lt;br /&gt;
      WA_DragBar,           TRUE,&lt;br /&gt;
      WA_Activate,          TRUE,&lt;br /&gt;
      WA_Title,             &amp;quot;Variable Number of Tabs&amp;quot;,&lt;br /&gt;
      WA_InnerWidth,        150,&lt;br /&gt;
      WA_InnerHeight,       100,&lt;br /&gt;
      WA_AutoAdjust,        TRUE,&lt;br /&gt;
      WA_CloseGadget,       TRUE,&lt;br /&gt;
      WINDOW_ParentGroup,   gb_MainLayout,&lt;br /&gt;
      TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Everything created successfully */&lt;br /&gt;
&lt;br /&gt;
      /* The window can be opened now */&lt;br /&gt;
      gb_Win = RA_OpenWindow(gb_WindowObj);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return ( gb_Win ? TRUE : FALSE );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void FreeGUI()&lt;br /&gt;
{&lt;br /&gt;
  RA_CloseWindow(gb_WindowObj);&lt;br /&gt;
  IIntuition-&amp;gt;DisposeObject(gb_WindowObj);&lt;br /&gt;
&lt;br /&gt;
  ULONG i;&lt;br /&gt;
  for(i=0; i&amp;lt;MAX_CLICKTABS; i++)&lt;br /&gt;
  {&lt;br /&gt;
    if(gb_ClicktabLabels[i]) IExec-&amp;gt;FreeVec(gb_ClicktabLabels[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
void MsgLoop()&lt;br /&gt;
{&lt;br /&gt;
  ULONG winsig;&lt;br /&gt;
  ULONG result, code;&lt;br /&gt;
  BOOL running = TRUE;&lt;br /&gt;
&lt;br /&gt;
  /* Get the window signal bit mask */&lt;br /&gt;
  IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, gb_WindowObj, &amp;amp;winsig);&lt;br /&gt;
&lt;br /&gt;
  while(running)&lt;br /&gt;
  {&lt;br /&gt;
    /* Wait for user actions */&lt;br /&gt;
    const ULONG sigs = IExec-&amp;gt;Wait(winsig | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
    /* Process all messages */&lt;br /&gt;
    while((result = RA_HandleInput(gb_WindowObj, &amp;amp;code)) != WMHI_LASTMSG)&lt;br /&gt;
    {&lt;br /&gt;
      /* Mask out the message class ! */&lt;br /&gt;
      switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
      {&lt;br /&gt;
        /* Handle gadget message */&lt;br /&gt;
        case WMHI_GADGETUP:&lt;br /&gt;
             switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
             {&lt;br /&gt;
               case GAD_ID_Name:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* Update caption */&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CLICKTAB_Current,(Object*)gb_ClicktabGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      STRPTR name;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(STRINGA_TextVal,(Object*)gb_NameGad,(ULONG*)&amp;amp;name);&lt;br /&gt;
                      IUtility-&amp;gt;Strlcpy(gb_ClicktabLabels[val],name,30);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Update CT [%ld] &amp;lt;%s&amp;gt;\n&amp;quot;,val,name);&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, GA_Text,gb_ClicktabLabels, TAG_END);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Add:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* Add a new tab */&lt;br /&gt;
                      ULONG max;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CLICKTAB_Total,(Object*)gb_ClicktabGad,(ULONG*)&amp;amp;max);&lt;br /&gt;
                      if(max &amp;lt; MAX_CLICKTABS-1)&lt;br /&gt;
                      {&lt;br /&gt;
                        STRPTR name;&lt;br /&gt;
                        IIntuition-&amp;gt;GetAttr(STRINGA_TextVal,(Object*)gb_NameGad,(ULONG*)&amp;amp;name);&lt;br /&gt;
                        if(name[0])&lt;br /&gt;
                        {&lt;br /&gt;
                          IDOS-&amp;gt;Printf(&amp;quot;Add CT [%ld] &amp;lt;%s&amp;gt;\n&amp;quot;,max,name);&lt;br /&gt;
                          if((gb_ClicktabLabels[max] = (STRPTR) IExec-&amp;gt;AllocVecTags(31,AVT_Type,MEMF_SHARED,TAG_END)))&lt;br /&gt;
                          {&lt;br /&gt;
                            IUtility-&amp;gt;Strlcpy(gb_ClicktabLabels[max],name,30);&lt;br /&gt;
                          }&lt;br /&gt;
                          else IDOS-&amp;gt;Printf(&amp;quot;out of memory\n&amp;quot;);&lt;br /&gt;
                          IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, GA_Text,gb_ClicktabLabels, TAG_END);&lt;br /&gt;
                        }&lt;br /&gt;
                        else IDOS-&amp;gt;Printf(&amp;quot;Name for tab required\n&amp;quot;);&lt;br /&gt;
                      }&lt;br /&gt;
                      else IDOS-&amp;gt;Printf(&amp;quot;No more tabs possible\n&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Del:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* Erase the current tab */&lt;br /&gt;
                      ULONG val, i;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CLICKTAB_Current,(Object*)gb_ClicktabGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Del CT [%ld] &amp;lt;%s&amp;gt;\n&amp;quot;,val,gb_ClicktabLabels[val]);&lt;br /&gt;
                      IExec-&amp;gt;FreeVec(gb_ClicktabLabels[val]);&lt;br /&gt;
                      for(i=val; i&amp;lt;MAX_CLICKTABS; i++)&lt;br /&gt;
                      {&lt;br /&gt;
                        gb_ClicktabLabels[i] = gb_ClicktabLabels[i + 1];&lt;br /&gt;
                      }&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, GA_Text,gb_ClicktabLabels, TAG_END);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Evensize:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* All tabs have the same width */&lt;br /&gt;
                      /* V53: WORKS ONLY DURING INITIALIZATION ! */&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GA_Selected,(Object*)gb_EvensizeGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, CLICKTAB_EvenSize,val, TAG_END);&lt;br /&gt;
                      /* !!! MUST DO to layout the changed tag !!! */&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, GA_Text,gb_ClicktabLabels, TAG_END);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Labeltruncate:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* Truncates label if necessary */&lt;br /&gt;
                      /* V53: WORKS ONLY DURING INITIALIZATION ! */&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(GA_Selected,(Object*)gb_LabeltruncateGad,(ULONG*)&amp;amp;val);&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, CLICKTAB_LabelTruncate,val, TAG_END);&lt;br /&gt;
                      /* !!! MUST DO to layout the changed tag !!! */&lt;br /&gt;
                      IIntuition-&amp;gt;RefreshSetGadgetAttrs(gb_ClicktabGad,gb_Win,NULL, GA_Text,gb_ClicktabLabels, TAG_END);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
               case GAD_ID_Clicktab:&lt;br /&gt;
                    {&lt;br /&gt;
                      /* Use tab name for editing */&lt;br /&gt;
                      ULONG val;&lt;br /&gt;
                      IIntuition-&amp;gt;GetAttr(CLICKTAB_Current,(Object*)gb_ClicktabGad, (ULONG*)&amp;amp;val);&lt;br /&gt;
                      IDOS-&amp;gt;Printf(&amp;quot;Update STR [%ld] &amp;lt;%s&amp;gt;\n&amp;quot;, val, gb_ClicktabLabels[val]);&lt;br /&gt;
                      IIntuition-&amp;gt;SetGadgetAttrs(gb_NameGad,gb_Win,NULL, STRINGA_TextVal,gb_ClicktabLabels[val], TAG_END);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
        case WMHI_VANILLAKEY:&lt;br /&gt;
             {&lt;br /&gt;
               const LONG code = result &amp;amp; WMHI_KEYMASK;&lt;br /&gt;
&lt;br /&gt;
               if(code == &#039; &#039;)&lt;br /&gt;
               {&lt;br /&gt;
                 IDOS-&amp;gt;Printf(&amp;quot;---- Label List:\n&amp;quot;);&lt;br /&gt;
                 ULONG i;&lt;br /&gt;
                 for(i=0; i&amp;lt;MAX_CLICKTABS; i++)&lt;br /&gt;
                 {&lt;br /&gt;
                   if(gb_ClicktabLabels[i]) IDOS-&amp;gt;Printf(&amp;quot;[%ld] &amp;lt;%s&amp;gt; $%lx\n&amp;quot;,i,gb_ClicktabLabels[i],gb_ClicktabLabels[i]);&lt;br /&gt;
                 }&lt;br /&gt;
                 IDOS-&amp;gt;Printf(&amp;quot;----------------\n&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
&lt;br /&gt;
        /* Window close gadget clicked */&lt;br /&gt;
        case WMHI_CLOSEWINDOW:&lt;br /&gt;
             {&lt;br /&gt;
               running = FALSE;&lt;br /&gt;
             }&lt;br /&gt;
             break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    /* CTRL-C typed in the shell or via a signal */&lt;br /&gt;
    if(sigs &amp;amp; SIGBREAKF_CTRL_C) laufen = FALSE;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
  /* AmigaOS 4.1 is required */&lt;br /&gt;
  if(SysBase-&amp;gt;lib_Version &amp;lt; 53)&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Program requires AmigaOS 4.1\n&amp;quot;);&lt;br /&gt;
    return( 20 );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if( CreateGUI() )&lt;br /&gt;
  {&lt;br /&gt;
    MsgLoop();&lt;br /&gt;
&lt;br /&gt;
    FreeGUI();&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;GUI creation failed !\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF110_clicktab-test.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
Irrespective of the programing side, the user can influence the appearance and partly the behaviour of gadgets and windows system-wide using the Prefs options. Angular or rounded edges, the thickness of the window frame, and also the complex colour scheme are all widely configurable. There are ready-made styles so everything works perfectly in the visual sense.&lt;br /&gt;
&lt;br /&gt;
= Authors =&lt;br /&gt;
&lt;br /&gt;
Written by Michael Christoph and Aleksandra Schmidt-Pendarovska&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright (c) 2013 Michael Christoph&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=ANIM_IFF_CEL_Animations&amp;diff=12554</id>
		<title>ANIM IFF CEL Animations</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=ANIM_IFF_CEL_Animations&amp;diff=12554"/>
		<updated>2025-01-26T19:30:52Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ANIM =&lt;br /&gt;
&lt;br /&gt;
An IFF Format For CEL Animations&lt;br /&gt;
&lt;br /&gt;
Revision date:  6 June 2013&lt;br /&gt;
&lt;br /&gt;
Originally prepared by:&lt;br /&gt;
: SPARTA Inc.&lt;br /&gt;
: 23041 de la Carlota&lt;br /&gt;
: Laguna Hills, Calif 92653&lt;br /&gt;
: (714) 768-8161&lt;br /&gt;
: Contact: Gary Bonham&lt;br /&gt;
&lt;br /&gt;
Also by:&lt;br /&gt;
: Aegis Development Co.&lt;br /&gt;
: 2115 Pico Blvd.&lt;br /&gt;
: Santa Monica, Calif 90405&lt;br /&gt;
: (213) 392-9972&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
   &lt;br /&gt;
The ANIM IFF format was developed at Sparta originally for the production of animated video sequences on the Amiga computer.  The intent was to be able to store, and play back, sequences of frames and to minimize both the storage space on disk (through compression) and playback time (through efficient decompression algorithms). It was desired to maintain maximum compatibility with existing IFF formats and to be able to display the initial frame as a normal still IFF picture.&lt;br /&gt;
   &lt;br /&gt;
Several compression schemes have been introduced in the ANIM format. Most of these are strictly of historical interest as the only one currently being placed in new code is the vertical run length encoded byte encoding developed by Jim Kent.&lt;br /&gt;
   &lt;br /&gt;
== ANIM Format Overview ==&lt;br /&gt;
      &lt;br /&gt;
The general philosophy of ANIMs is to present the initial frame as a normal, run-length-encoded, IFF picture.  Subsequent&lt;br /&gt;
frames are then described by listing only their differences from a previous frame.  Normally, the &amp;amp;quot;previous&amp;amp;quot; frame is two frames back as that is the frame remaining in the hidden screen buffer when double-buffering is used.  To better understand this, suppose one has two screens, called A and B, and the ability to instantly switch the display from one to the other.  The normal playback mode is to load the initial frame into A and duplicate it into B.  Then frame A is displayed on the screen.  Then the differences for frame 2 are used to alter screen B and it is displayed.  Then the differences for frame 3 are used to alter screen A and it is displayed, and so on.  Note that frame 2 is stored as differences from frame 1, but all other frames are stored as differences from two frames back.&lt;br /&gt;
      &lt;br /&gt;
ANIM is an IFF FORM and its basic format is as follows (this assumes the reader has a basic understanding of IFF format&lt;br /&gt;
files):&lt;br /&gt;
                      FORM ANIM&lt;br /&gt;
                      . FORM ILBM         first frame&lt;br /&gt;
                      . . BMHD                normal type IFF data&lt;br /&gt;
                      . . ANHD                optional animation header&lt;br /&gt;
                                              chunk for timing of 1st frame.&lt;br /&gt;
                      . . CMAP&lt;br /&gt;
                      . . BODY&lt;br /&gt;
                      . FORM ILBM         frame 2&lt;br /&gt;
                      . . ANHD                animation header chunk&lt;br /&gt;
                      . . DLTA                delta mode data&lt;br /&gt;
                      . FORM ILBM         frame 3&lt;br /&gt;
                      . . ANHD&lt;br /&gt;
                      . . DLTA&lt;br /&gt;
                           ...&lt;br /&gt;
      &lt;br /&gt;
The initial FORM ILBM can contain all the normal ILBM chunks, such as CRNG, etc.  The BODY will normally be a standard&lt;br /&gt;
run-length-encoded data chunk (but may be any other legal compression mode as indicated by the BMHD).  If desired, an ANHD&lt;br /&gt;
chunk can appear here to provide timing data for the first frame.  If it is here, the operation field should be =0.&lt;br /&gt;
      &lt;br /&gt;
The subsequent FORMs ILBM contain an ANHD, instead of a BMHD, which duplicates some of BMHD and has additional parameters&lt;br /&gt;
pertaining to the animation frame.  The DLTA chunk contains the data for the delta compression modes.  If the older XOR compression mode is used, then a BODY chunk will be here.  In addition, other chunks may be placed in each of these as deemed necessary (and as code is placed in player programs to utilize them).  A good example would be CMAP chunks to alter the color palette.  A basic assumption in ANIMs is that the size of the bitmap, and the display mode (e.g. HAM) will not change through the animation.  Take care when playing an ANIM that if a CMAP occurs with a frame, then the change must be applied to both buffers.&lt;br /&gt;
      &lt;br /&gt;
Note that the DLTA chunks are not interleaved bitmap representations, thus the use of the ILBM form is inappropriate for these frames. However, this inconsistency was not noted until there were a number of commercial products either released or close to release which generated/played this format.  Therefore, this is probably an inconsistency which will have to stay with us.&lt;br /&gt;
&lt;br /&gt;
== Recording ANIMs ==&lt;br /&gt;
&lt;br /&gt;
To record an ANIM will require three bitmaps - one for creation of the next frame, and two more for a &amp;amp;quot;history&amp;amp;quot; of the previous two frames for performing the compression calculations (e.g. the delta mode calculations).&lt;br /&gt;
&lt;br /&gt;
There are five frame-to-frame compression methods currently defined.  The first three are mainly for historical interest.&lt;br /&gt;
The product Aegis VideoScape 3D utilizes the third method in version 1.0, but switched to method 5 on 2.0.  This is the only instance known of a commercial product generating ANIMs of any of the first three methods.  The fourth method is a general short or long word compression scheme which has several options including whether the compression is horizontal or vertical, and whether or not it is XOR format.  This offers a choice to the user for the optimization of file size and/or playback speed.  The fifth method is the byte vertical run length encoding as designed by Jim Kent.  Do not confuse this with Jim&#039;s RIFF file format which is different than ANIM. Here we utilized his compression/decompression routines within the ANIM file structure.&lt;br /&gt;
&lt;br /&gt;
The following paragraphs give a general outline of each of the methods of compression currently included in this spec.&lt;br /&gt;
&lt;br /&gt;
=== XOR mode ===&lt;br /&gt;
    &lt;br /&gt;
This mode is the original and is included here for historical interest.  In general, the delta modes are far superior. The creation of XOR mode is quite simple.  One simply performs an exclusive-or (XOR) between all corresponding bytes of the new frame and two frames back.  This results in a new bitmap with 0 bits wherever the two frames were identical, and 1 bits where they are different.  Then this new bitmap is saved using run-length-encoding.  A major obstacle of this mode is in the time consumed in performing the XOR upon reconstructing the image.&lt;br /&gt;
    &lt;br /&gt;
=== Long Delta mode ===&lt;br /&gt;
    &lt;br /&gt;
This mode stores the actual new frame long-words which are different, along with the offset in the bitmap.  The exact format is shown and discussed in section 2 below. Each plane is handled separately, with no data being saved if no changes take place in a given plane.  Strings of 2 or more long-words in a row which change can be run together so offsets do not have to be saved for each one.&lt;br /&gt;
&lt;br /&gt;
Constructing this data chunk usually consists of having a buffer to hold the data, and calculating the data as one compares the new frame, long-word by long-word, with two frames back.&lt;br /&gt;
   &lt;br /&gt;
=== Short Delta mode ===&lt;br /&gt;
   &lt;br /&gt;
This mode is identical to the Long Delta mode except that short-words are saved instead of long-words.  In most instances, this mode results in a smaller DLTA chunk. The Long Delta mode is mainly of interest in improving the playback speed when used on a 32-bit 68020 Turbo Amiga.&lt;br /&gt;
   &lt;br /&gt;
=== General Delta mode ===&lt;br /&gt;
&lt;br /&gt;
The above two delta compression modes were hastily put together. This mode was an attempt to provide a well-thought-out delta compression scheme.  Options provide for both short and long word compression, either vertical or horizontal compression, XOR mode (which permits reverse playback), etc.  About the time this was being finalized, the fifth mode, below, was developed by Jim Kent.  In practice the short-vertical-run-length-encoded deltas in this mode play back faster than the fifth mode (which is in essence a byte-vertical-run-length-encoded delta mode) but does not compress as well - especially for very noisy data such as digitized images.  In most cases, playback speed not being terrifically slower, the better compression (sometimes 2x) is preferable due to limited storage media in most machines.&lt;br /&gt;
&lt;br /&gt;
Details on this method are contained in section 2.2.2 below.&lt;br /&gt;
&lt;br /&gt;
=== Byte Vertical Compression ===&lt;br /&gt;
&lt;br /&gt;
This method does not offer the many options that method 4 offers, but is very successful at producing decent compression even for very noisy data such as digitized images.  The method was devised by Jim Kent and is utilized in his RIFF file format which is different than the ANIM format.  The description of this method in this document is taken from Jim&#039;s writings.  Further, he has released both compression and decompression code to public domain.&lt;br /&gt;
&lt;br /&gt;
Details on this method are contained in section 2.2.3 below.&lt;br /&gt;
&lt;br /&gt;
== Playing ANIMs ==&lt;br /&gt;
   &lt;br /&gt;
Playback of ANIMs will usually require two buffers, as mentioned above, and double-buffering between them.  The frame data from the ANIM file is used to modify the hidden frame to the next frame to be shown.  When using the XOR mode, the usual run-length-decoding routine can be easily modified to do the exclusive-or operation required.  Note that runs of zero bytes,&lt;br /&gt;
which will be very common, can be ignored, as an exclusive or of any byte value to a byte of zero will not alter the original byte value.&lt;br /&gt;
&lt;br /&gt;
The general procedure, for all compression techniques, is to first decode the initial ILBM picture into the hidden buffer and double-buffer it into view.  Then this picture is copied to the other (now hidden) buffer.  At this point each frame is displayed with the same procedure.  The next frame is formed in the hidden buffer by applying the DLTA data (or the XOR data from the BODY chunk in the case of the first XOR method) and the new frame is double-buffered into view.  This process continues to the end of the file.&lt;br /&gt;
&lt;br /&gt;
A master colormap should be kept for the entire ANIM which would be initially set from the CMAP chunk in the initial ILBM.  This colormap should be used for each frame.  If a CMAP chunk appears in one of the frames, then this master colormap is updated and the new colormap applies to all frames until the occurrance of another CMAP chunk.&lt;br /&gt;
&lt;br /&gt;
Looping ANIMs may be constructed by simply making the last two frames identical to the first two.  Since the first two frames are special cases (the first being a normal ILBM and the second being a delta from the first) one can continually loop the animation by repeating from frame three.  In this case the delta for creating frame three will modify the next to the last frame which is in the hidden buffer (which is identical to the first frame), and the delta for creating frame four will modify the last frame which is identical to the second frame.&lt;br /&gt;
&lt;br /&gt;
Multi-File ANIMs are also supported so long as the first two frames of a subsequent file are identical to the last two frames of the preceeding file.  Upon reading subsequent files, the ILBMs for the first two frames are simply ignored, and the remaining frames are simply appended to the preceding frames.  This permits splitting ANIMs across multiple floppies and also permits playing each section independently and/or editing it independent of the rest of the ANIM.&lt;br /&gt;
      &lt;br /&gt;
Timing of ANIM playback is easily achieved using the vertical blank interrupt of the Amiga.  There is an example of setting up such a timer in the SDK.  Be sure to remember the timer value when a frame is flipped up, so the next frame can be flipped up relative to that time.  This will make the playback independent of how long it takes to decompress a frame (so long as there is enough time between frames to accomplish this decompression).&lt;br /&gt;
&lt;br /&gt;
= Chunk Formats =&lt;br /&gt;
&lt;br /&gt;
== ANHD Chunk ==&lt;br /&gt;
&lt;br /&gt;
The ANHD chunk consists of the following data structure:&lt;br /&gt;
&lt;br /&gt;
; UBYTE operation&lt;br /&gt;
: The compression method:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| 0 || set directly (normal ILBM BODY)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || XOR ILBM mode&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Long Delta mode&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Short Delta mode&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Generalized short/long Delta mode&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Byte Vertical Delta mode&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Stereo op 5 (third party)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || short/long Vertical Delta mode&lt;br /&gt;
|-&lt;br /&gt;
| 74 (ASCII &#039;J&#039;) || reserved for Eric Graham&#039;s compression technique (details to be released later).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; UBYTE mask&lt;br /&gt;
: (XOR mode only - plane mask where each bit is set =1 if there is data and =0 if not.)&lt;br /&gt;
&lt;br /&gt;
; UWORD w,h&lt;br /&gt;
: (XOR mode only - width and height of the area represented by the BODY to eliminate unnecessary unchanged data)&lt;br /&gt;
&lt;br /&gt;
; WORD  x,y&lt;br /&gt;
: (XOR mode only - position of rectangular area represented by the BODY)&lt;br /&gt;
&lt;br /&gt;
; ULONG abstime&lt;br /&gt;
: (currently unused - timing for a frame relative to the time the first frame was displayed - in jiffies (1/60 sec))&lt;br /&gt;
&lt;br /&gt;
; ULONG reltime&lt;br /&gt;
: (timing for frame relative to time previous frame was displayed - in jiffies (1/60 sec))&lt;br /&gt;
&lt;br /&gt;
; UBYTE interleave&lt;br /&gt;
: (unused so far - indicates how may frames back this data is to modify.  =0 defaults to indicate two frames back (for double buffering). =n indicates n frames back. The main intent here is to allow values of =1 for special applications where frame data would modify the immediately previous frame)&lt;br /&gt;
&lt;br /&gt;
; UBYTE pad0&lt;br /&gt;
: Pad byte, not used at present.&lt;br /&gt;
&lt;br /&gt;
; ULONG bits&lt;br /&gt;
: 32 option bits used by options=4 and 5. At present only 6 are identified, but the rest are set =0 so they can be used to implement future ideas. These are defined for option 4 only at this point. It is recommended that all bits be set =0 for option 5 and that any bit settings used in the future (such as for XOR mode) be compatible with the option 4 bit settings.   Player code should check undefined bits in options 4 and 5 to assure they are zero.&lt;br /&gt;
&lt;br /&gt;
: The six bits for current use are:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! bit # !! set =0 !! set =1&lt;br /&gt;
|-&lt;br /&gt;
| 0 || short data || long data&lt;br /&gt;
|-&lt;br /&gt;
| 1 || set || XOR&lt;br /&gt;
|-&lt;br /&gt;
| 2 || separate info for each plane || one info list for all planes&lt;br /&gt;
|-&lt;br /&gt;
| 3 || not RLC || RLC (run length coded)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || horizontal || vertical&lt;br /&gt;
|-&lt;br /&gt;
| 5 || short info offsets || long info offsets&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; UBYTE pad[16]&lt;br /&gt;
: This is a pad for future use for future compression modes.&lt;br /&gt;
&lt;br /&gt;
== DLTA Chunk ==&lt;br /&gt;
      &lt;br /&gt;
This chunk is the basic data chunk used to hold delta compression data. The format of the data will be dependent upon the exact compression format selected.  At present there are two basic formats for the overall structure of this chunk.&lt;br /&gt;
&lt;br /&gt;
=== Format for methods 2 &amp;amp; 3 ===&lt;br /&gt;
&lt;br /&gt;
This chunk is a basic data chunk used to hold the delta compression data.  The minimum size of this chunk is 32 bytes as the first 8 long-words are byte pointers into the chunk for the data for each of up to 8 bit planes.  The pointer for the&lt;br /&gt;
plane data starting immediately following these 8 pointers will have a value of 32 as the data starts in the 33-rd byte of the chunk (index value of 32 due to zero-base indexing).&lt;br /&gt;
&lt;br /&gt;
The data for a given plane consists of groups of data words.  In&lt;br /&gt;
Long Delta mode, these groups consist of both short and long&lt;br /&gt;
words - short words for offsets and numbers, and long words for&lt;br /&gt;
the actual data.  In Short Delta mode, the groups are identical&lt;br /&gt;
except data words are also shorts so all data is short words.&lt;br /&gt;
Each group consists of a starting word which is an offset.  If&lt;br /&gt;
the offset is positive then it indicates the increment in long&lt;br /&gt;
or short words (whichever is appropriate) through the bit plane.&lt;br /&gt;
In other words, if you were reconstructing the plane, you would&lt;br /&gt;
start a pointer (to shorts or longs depending on the mode) to&lt;br /&gt;
point to the first word of the bit plane.  Then the offset would&lt;br /&gt;
be added to it and the following data word would be placed at&lt;br /&gt;
that position.  Then the next offset would be added to the&lt;br /&gt;
pointer and the following data word would be placed at that&lt;br /&gt;
position.  And so on...  The data terminates with an offset&lt;br /&gt;
equal to 0xFFFF.&lt;br /&gt;
&lt;br /&gt;
A second interpretation is given if the offset is negative.  In&lt;br /&gt;
that case, the absolute value is the offset+2.  Then the &lt;br /&gt;
following short-word indicates the number of data words that&lt;br /&gt;
follow.  Following that is the indicated number of contiguous&lt;br /&gt;
data words (longs or shorts depending on mode) which are to&lt;br /&gt;
be placed in contiguous locations of the bit plane.&lt;br /&gt;
&lt;br /&gt;
If there are no changed words in a given plane, then the pointer&lt;br /&gt;
in the first 32 bytes of the chunk is =0.&lt;br /&gt;
&lt;br /&gt;
=== Format for method 4 ===&lt;br /&gt;
         &lt;br /&gt;
The DLTA chunk is modified slightly to have 16 long pointers at&lt;br /&gt;
the start.  The first 8 are as before - pointers to the start of&lt;br /&gt;
the data for each of the bit planes (up to a theoretical max of 8&lt;br /&gt;
planes).  The next 8 are pointers to the start of the offset/numbers&lt;br /&gt;
data list.  If there is only one list of offset/numbers for all&lt;br /&gt;
planes, then the pointer to that list is repeated in all positions&lt;br /&gt;
so the playback code need not even be aware of it.  In fact, one&lt;br /&gt;
could get fancy and have some bit planes share lists while others&lt;br /&gt;
have different lists, or no lists (the problems in these schemes&lt;br /&gt;
lie in the generation, not in the playback).&lt;br /&gt;
&lt;br /&gt;
The best way to show the use of this format is in a sample playback&lt;br /&gt;
routine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   SetDLTAshort(bm,deltaword)&lt;br /&gt;
   struct BitMap *bm;&lt;br /&gt;
   WORD *deltaword;&lt;br /&gt;
   {&lt;br /&gt;
      int i;&lt;br /&gt;
      LONG *deltadata;&lt;br /&gt;
      WORD *ptr,*planeptr;&lt;br /&gt;
      register int s,size,nw;&lt;br /&gt;
      register WORD *data,*dest;&lt;br /&gt;
&lt;br /&gt;
      deltadata = (LONG *)deltaword;&lt;br /&gt;
      nw = bm-&amp;amp;gt;BytesPerRow &amp;amp;gt;&amp;amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
      for (i=0;i&amp;amp;lt;bm-&amp;amp;gt;Depth;i++) {&lt;br /&gt;
         planeptr = (WORD *)(bm-&amp;amp;gt;Planes[i]);&lt;br /&gt;
         data = deltaword + deltadata[i];&lt;br /&gt;
         ptr  = deltaword + deltadata[i+8];&lt;br /&gt;
         while (*ptr != 0xFFFF) {&lt;br /&gt;
            dest = planeptr + *ptr++;&lt;br /&gt;
            size = *ptr++;&lt;br /&gt;
            if (size &amp;amp;lt; 0) {&lt;br /&gt;
               for (s=size;s&amp;amp;lt;0;s++) {&lt;br /&gt;
                  *dest = *data;&lt;br /&gt;
                  dest += nw;&lt;br /&gt;
               }&lt;br /&gt;
               data++;&lt;br /&gt;
            }&lt;br /&gt;
            else {&lt;br /&gt;
               for (s=0;s&amp;amp;lt;size;s++) {&lt;br /&gt;
                  *dest = *data++;&lt;br /&gt;
                  dest += nw;&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      return(0);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above routine is for short word vertical compression with&lt;br /&gt;
run length compression.  The most efficient way to support &lt;br /&gt;
the various options is to replicate this routine and make &lt;br /&gt;
alterations for, say, long word or XOR.  The variable nw&lt;br /&gt;
indicates the number of words to skip to go down the vertical&lt;br /&gt;
column.  This one routine could easily handle horizontal&lt;br /&gt;
compression by simply setting nw=1.  For ultimate playback&lt;br /&gt;
speed, the core, at least, of this routine should be coded in&lt;br /&gt;
assembly language.&lt;br /&gt;
&lt;br /&gt;
=== Format for method 5 ===&lt;br /&gt;
&lt;br /&gt;
In this method the same 16 pointers are used as in option 4.&lt;br /&gt;
The first 8 are pointers to the data for up to 8 planes.&lt;br /&gt;
The second set of 8 are not used but were retained for several&lt;br /&gt;
reasons.  First to be somewhat compatible with code for option&lt;br /&gt;
4 (although this has not proven to be of any benefit) and &lt;br /&gt;
second, to allow extending the format for more bit planes (code&lt;br /&gt;
has been written for up to 12 planes).  &lt;br /&gt;
&lt;br /&gt;
Compression/decompression is performed on a plane-by-plane basis.&lt;br /&gt;
For each plane, compression can be handled by the skip.c code&lt;br /&gt;
(provided Public Domain by Jim Kent) and decompression can be&lt;br /&gt;
handled by unvscomp.asm (also provided Public Domain by Jim Kent).&lt;br /&gt;
&lt;br /&gt;
Compression/decompression is performed on a plane-by-plane basis.&lt;br /&gt;
The following description of the method is taken directly from&lt;br /&gt;
Jim Kent&#039;s code with minor re-wording.  Please refer to Jim&#039;s&lt;br /&gt;
code (skip.c and unvscomp.asm) for more details:&lt;br /&gt;
&lt;br /&gt;
Each column of the bit plane is compressed separately.&lt;br /&gt;
A 320x200 bit plane would have 40 columns of 200 bytes each.&lt;br /&gt;
Each column starts with an op-count followed by a number&lt;br /&gt;
of ops.  If the op-count is zero, that&#039;s OK, it just means&lt;br /&gt;
there&#039;s no change in this column from the last frame.&lt;br /&gt;
The ops are of three classes, and followed by a varying&lt;br /&gt;
amount of data depending on which class:&lt;br /&gt;
&lt;br /&gt;
# Skip ops - this is a byte with the hi bit clear that says how many rows to move the &amp;amp;quot;dest&amp;amp;quot; pointer forward, i.e. to skip. It is non-zero.&lt;br /&gt;
# Uniq ops - this is a byte with the hi bit set. The hi bit is masked down and the remainder is a count of the number of bytes of data to copy literally. It&#039;s of course followed by the data to copy.&lt;br /&gt;
# Same ops - this is a 0 byte followed by a count byte, followed by a byte value to repeat count times.&lt;br /&gt;
&lt;br /&gt;
Do bear in mind that the data is compressed vertically rather&lt;br /&gt;
than horizontally, so to get to the next byte in the destination&lt;br /&gt;
we add the number of bytes per row instead of one!&lt;br /&gt;
&lt;br /&gt;
= ANIM.op6 =&lt;br /&gt;
&lt;br /&gt;
== OpCode 6 Addition to ANIM IFF Format ==&lt;br /&gt;
&lt;br /&gt;
Stereo (3D) Animations&lt;br /&gt;
&lt;br /&gt;
Submitted by William J. Coldwell (08/23/91)&lt;br /&gt;
&lt;br /&gt;
Revision Date: 20.8.91&lt;br /&gt;
&lt;br /&gt;
Prepared by:&lt;br /&gt;
: Cryogenic Software&lt;br /&gt;
: 13045 SouthEast Stark St&lt;br /&gt;
: Suite 144&lt;br /&gt;
: Portland, OR 97233-1557&lt;br /&gt;
: Contact:  William J. Coldwell&lt;br /&gt;
&lt;br /&gt;
: Voice: (503) 254-8147 (11a-4p PDT/PST)&lt;br /&gt;
: Data: (503) 257-4823 (EMail to SYSOP)&lt;br /&gt;
: Portal: Cryogenic&lt;br /&gt;
: UUCP: uunet!m2xenix!percy!cryo!billc&lt;br /&gt;
: Internet: billc@cryo.rain.com&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In 1989, we added support into one of our commercial products to&lt;br /&gt;
support the Haitex X-Specs glasses.  This documentation will not&lt;br /&gt;
go into a detailed description of this product.  Contact Haitex&lt;br /&gt;
for more information concerning the hardware:&lt;br /&gt;
&lt;br /&gt;
: Haitex Resources, Inc.&lt;br /&gt;
: Post Office Box 20609&lt;br /&gt;
: Charleston, SC 29413&lt;br /&gt;
: Voice: (803) 881-7518&lt;br /&gt;
: Fax: (803) 881-7522&lt;br /&gt;
: Contact: Shawn Glisson&lt;br /&gt;
&lt;br /&gt;
We found that there was not a supported way to display stereo&lt;br /&gt;
animations using the current IFF ANIM OpCode 5 specification.&lt;br /&gt;
&lt;br /&gt;
Cryogenic supported OpCode 6 as an internal format in our&lt;br /&gt;
commercial programs (see below) and provided Public Domain&lt;br /&gt;
players.  It is our intention at this time, to release this&lt;br /&gt;
format to other developers wishing to support stereo animations&lt;br /&gt;
using this OpCode.&lt;br /&gt;
&lt;br /&gt;
When we first started this project, the current Amiga machines&lt;br /&gt;
had a 512K of CHIP RAM maximum.  This caused some memory problems&lt;br /&gt;
with some of the higher resolution stereo animations, since the&lt;br /&gt;
Quad Buffers were in CHIP RAM for our players.  It was our&lt;br /&gt;
intention to attempt to do some memory magic to require only&lt;br /&gt;
two of the four bitmaps to be in CHIP RAM at one time.  It was&lt;br /&gt;
our feeling that this would have caused the animations to slow&lt;br /&gt;
down, due to data swapping that may or may not have needed to&lt;br /&gt;
be done.  By the end of 1989, all development had stopped on&lt;br /&gt;
OpCode 6.  This left all buffers in CHIP, and the format has&lt;br /&gt;
remained the same since then.&lt;br /&gt;
&lt;br /&gt;
== OpCode 6 Additions to OpCode 5 ==&lt;br /&gt;
&lt;br /&gt;
The format is exactly the same as OpCode 5 but is QUAD buffered&lt;br /&gt;
instead of DOUBLE buffered.  This allows the player to show 2&lt;br /&gt;
screens at one time for the X-Specs Glasses.  Each picture MUST&lt;br /&gt;
be viewed for 1/60th of a second, therefore to see a 3-D Picture&lt;br /&gt;
the viewer can only play ANIMs at 30 frames per second.&lt;br /&gt;
(2 pictures = 1 frame).&lt;br /&gt;
&lt;br /&gt;
The IFF file is stored exactly the same except that instead&lt;br /&gt;
of having each DLTA (delta) modify bitmap two frames back, it&lt;br /&gt;
modifies the bitmap four frames back.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |                      |&lt;br /&gt;
 |   BMHD               |&lt;br /&gt;
 |                      |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (1)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (2)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (3)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (4)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (5)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (6)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
            .&lt;br /&gt;
            .&lt;br /&gt;
            .&lt;br /&gt;
 ------------------------&lt;br /&gt;
 |   DLTA  (x)          |&lt;br /&gt;
 ------------------------&lt;br /&gt;
&lt;br /&gt;
== Playing OpCode 6 ANIMs ==&lt;br /&gt;
&lt;br /&gt;
Four bitmaps are allocated.  Bitmaps 1 and 3 are the left views,&lt;br /&gt;
and bitmaps 2 and 4 are the right.&lt;br /&gt;
&lt;br /&gt;
The First bitmap is gets its image from the bitmap in the file&lt;br /&gt;
(BMHD).  The Second bitmap is a copy of the first with DLTA (1)&lt;br /&gt;
performed on it.  The Third Bitmap is a copy of the first with&lt;br /&gt;
DLTA (2) performed on it.  The Fourth Bitmap is a copy of the&lt;br /&gt;
first with DLTA (3) performed on it.&lt;br /&gt;
&lt;br /&gt;
We now have the first two 3-D Pictures:&lt;br /&gt;
&lt;br /&gt;
One in bitmaps 1 and 2 and the other in bitmaps 3 and 4&lt;br /&gt;
&lt;br /&gt;
DLTA (6) is used to create the third left view from bitmap 1.&lt;br /&gt;
&lt;br /&gt;
DLTA (7) is used to create the third right view from bitmap 2.&lt;br /&gt;
&lt;br /&gt;
DLTA (8) is used to create the forth left view from bitmap 3.&lt;br /&gt;
&lt;br /&gt;
DLTA (9) is used to create the forth right view from bitmap 4.&lt;br /&gt;
&lt;br /&gt;
NOTE:  This technique requires 4 Loop frames at the end to&lt;br /&gt;
perform looping.&lt;br /&gt;
&lt;br /&gt;
== Chunk Changes ==&lt;br /&gt;
&lt;br /&gt;
In the ANHDChunk structure the only differences between OpCode&lt;br /&gt;
5 and OpCode 6 are the _Operation_ field which should be set to&lt;br /&gt;
6, and the _Interleave_ field which should be set to 4.&lt;br /&gt;
&lt;br /&gt;
== Supporting Software ==&lt;br /&gt;
&lt;br /&gt;
* 3-D Professional 1.0+ (Progressive Peripherals and Software)&lt;br /&gt;
* View 1.7 and above (Public Domain) on a Fred Fish Disk&lt;br /&gt;
* MSA: Make Stereo ANIM (internal) Available upon request.&lt;br /&gt;
* PSA: Play Stereo ANIM (internal) Available upon request.&lt;br /&gt;
&lt;br /&gt;
= ANIM.op7 =&lt;br /&gt;
&lt;br /&gt;
== Appendix for Anim7 Formats ==&lt;br /&gt;
&lt;br /&gt;
Anim7 (July 92) by:&lt;br /&gt;
: Wolfgang Hofer&lt;br /&gt;
: A-2722 Winzendorf&lt;br /&gt;
: Wr. Neustaedterstr. 140&lt;br /&gt;
&lt;br /&gt;
Anim method 7 is designed for maximum playback speed and acceptable packing rates (packing usually not as good as method 5, but more efficient than methods 1 -- 4)&lt;br /&gt;
&lt;br /&gt;
Method 7 was not originally in the IFF specification but supported by the Public Domain Programs AAP/AAC.&lt;br /&gt;
&lt;br /&gt;
== Chunk Sequence ==&lt;br /&gt;
&lt;br /&gt;
Method 7 Anims should use the same Chunk Sequence as methods 1..5. Alternatively the first frame may have a DLTA chunk instead of the BODY chunk.&lt;br /&gt;
&lt;br /&gt;
In that case the DLTA is the difference to a &#039;black frame&#039;. A player has to clear all bit planes of the first bitmap to zero, and then call his DLTA unpack routines for this frame.&lt;br /&gt;
&lt;br /&gt;
      FORM ANIM&lt;br /&gt;
      . FORM ILBM         first frame&lt;br /&gt;
      . . BMHD                normal type IFF data&lt;br /&gt;
      . . ANHD                optional animation header chunk for timing of 1st frame.&lt;br /&gt;
      . . CMAP&lt;br /&gt;
      . . { BODY | DLTA }     full picture or difference to &#039;black frame&#039;&lt;br /&gt;
      . FORM ILBM         frame 2&lt;br /&gt;
      . . ANHD                animation header chunk&lt;br /&gt;
      . . DLTA                delta mode data&lt;br /&gt;
      . . [CMAP]&lt;br /&gt;
      . FORM ILBM         frame 3&lt;br /&gt;
      . . ANHD&lt;br /&gt;
      . . DLTA&lt;br /&gt;
      . . [CMAP]&lt;br /&gt;
          ...&lt;br /&gt;
&lt;br /&gt;
The initial FORM ILBM can contain all the normal ILBM chunks, such as CRNG, etc.  The BODY will normally be a standard&lt;br /&gt;
run-length-encoded data chunk (but may be any other legal compression mode as indicated by the BMHD).  If desired, an ANHD chunk can appear here to provide timing data for the first frame.  If it is here, the operation field should be =0.&lt;br /&gt;
&lt;br /&gt;
If the initial FORM ILBM uses a DLTA chunk, the ANHD chunk must appear, and the operation field must be set to the according anim method.&lt;br /&gt;
&lt;br /&gt;
== Chunk Formats ==&lt;br /&gt;
&lt;br /&gt;
=== ANHD Chunk for method 7 ===&lt;br /&gt;
&lt;br /&gt;
The ANHD chunk consists of the following data structure:&lt;br /&gt;
&lt;br /&gt;
  UBYTE operation    The compression method=7 short/long Vertical Delta mode&lt;br /&gt;
  UBYTE mask         unused&lt;br /&gt;
  UWORD w,h          unused&lt;br /&gt;
  WORD  x,y          unused&lt;br /&gt;
  ULONG abstime      unused&lt;br /&gt;
  ULONG reltime      (timing for frame relative to time previous frame was displayed - in jiffies (1/60 sec))&lt;br /&gt;
  UBYTE interleave   = 0 (see ANHD description above)&lt;br /&gt;
  UBYTE pad0         unused&lt;br /&gt;
  ULONG bits         32 option bits used by method=4 and 5.&lt;br /&gt;
                     method 7 uses only bit #0&lt;br /&gt;
&lt;br /&gt;
                     bit #          set =0               set =1&lt;br /&gt;
                     ===============================================&lt;br /&gt;
                     0              short data           long data&lt;br /&gt;
&lt;br /&gt;
  UBYTE pad[16]       unused&lt;br /&gt;
&lt;br /&gt;
=== DLTA Chunk ===&lt;br /&gt;
&lt;br /&gt;
The DLTA Chunks of method 7 consists of&lt;br /&gt;
* 8 pointers    to opcode lists&lt;br /&gt;
* 8 pointers    to data lists&lt;br /&gt;
* data lists    (long/short)&lt;br /&gt;
* opcode lists  (bytes)&lt;br /&gt;
&lt;br /&gt;
In this method the DLTA Chunk begins with 16 pointers. The first 8 longwords are pointers to the opcode lists for up to 8 planes. The second set of 8 longwords are pointers to the corresponding data lists. If there are less than 8 Planes all unused pointers are set to zero.&lt;br /&gt;
&lt;br /&gt;
Compression/decompression is performed on a plane-by-plane basis. The following description of the method is similar to&lt;br /&gt;
Jim Kent&#039;s method 5, except that data is stored in a separated data list (long or short, depending on bit#0 of the ANHD bits) and doesn&#039;t follow immediate after the opcode.&lt;br /&gt;
&lt;br /&gt;
In method 7 the bit plane is split into vertical columns. Each column of the bit plane is compressed separately. A 320x200 bitplane would have 20 columns of 200 short datas each. (or 10 columns of 200 long datas)&lt;br /&gt;
&lt;br /&gt;
Each column starts with an op-count followed by a number of ops.  If the op-count is zero, that&#039;s OK, it just means&lt;br /&gt;
there&#039;s no change in this column from the last frame. The ops are of three classes. The ops refer to a varying&lt;br /&gt;
amount of data (to fetch from the corresponding data list) depending on which class:&lt;br /&gt;
&lt;br /&gt;
# Skip ops - this is a byte with the hi bit clear that says how many rows to move the &amp;quot;dest&amp;quot; pointer forward, i.e. to skip. It is non-zero. Skip ops have no corresponding data-items in the data list.&lt;br /&gt;
# Uniq ops - this is a byte with the hi bit set.  The hi bit is masked down and the remainder is a count of the number of data to copy literally from the data list to the &amp;quot;dest&amp;quot; pointer column. (Each data item to the next destination row) Data items may be long or short organized.&lt;br /&gt;
# Same ops - this is a 0 byte followed by a count byte. The count byte says how many rows of the current column are to be set to the same data-item. the data-item (long or short) is fetched from the data list.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Do bear in mind that the data is compressed vertically rather than horizontally, so to get to the next address in the destination we have to add the number of bytes per row instead of 2 (or 4)!}}&lt;br /&gt;
&lt;br /&gt;
= ANIM.op8 =&lt;br /&gt;
&lt;br /&gt;
== Appendix for Anim8 ==&lt;br /&gt;
&lt;br /&gt;
Joe Porkka 10-jan-92&lt;br /&gt;
&lt;br /&gt;
Anim method 8 is designed for maximum playback speed and acceptable packing rates (packing usually not as good as method 5, but more efficient than methods 1 -- 4). In addition, it is easier to convert existing Anim5 code to support Anim8 than Anim7.&lt;br /&gt;
&lt;br /&gt;
== Chunk Sequence ==&lt;br /&gt;
&lt;br /&gt;
Method 8 Anims should use the same Chunk Sequence as methods 1..5. Alternatively the first frame may have a DLTA chunk instead of the BODY chunk.&lt;br /&gt;
&lt;br /&gt;
In that case the DLTA is the difference to a &#039;black frame&#039;. A player has to clear all bit planes of the first bitmap to zero, and then call his DLTA unpack routines for this frame. The same rules about copying the first frame into both frame buffers still applies in this case.&lt;br /&gt;
&lt;br /&gt;
                   FORM ANIM&lt;br /&gt;
                   . FORM ILBM         first frame&lt;br /&gt;
                   . . BMHD                normal type IFF data&lt;br /&gt;
                   . . ANHD                optional animation header chunk for timing of 1st frame.&lt;br /&gt;
                   . . CMAP&lt;br /&gt;
                   . . { BODY | DLTA }     full picture or difference to &#039;black frame&#039;&lt;br /&gt;
                   . FORM ILBM         frame 2&lt;br /&gt;
                   . . ANHD                animation header chunk&lt;br /&gt;
                   . . DLTA                delta mode data&lt;br /&gt;
                   . . [CMAP]&lt;br /&gt;
                   . FORM ILBM         frame 3&lt;br /&gt;
                   . . ANHD&lt;br /&gt;
                   . . DLTA&lt;br /&gt;
                   . . [CMAP]&lt;br /&gt;
                        ...&lt;br /&gt;
&lt;br /&gt;
The initial FORM ILBM can contain all the normal ILBM chunks, such as CRNG, etc.  The BODY will normally be a standard run-length-encoded data chunk (but may be any other legal compression mode as indicated by the BMHD).  If desired, an ANHD chunk can appear here to provide timing data for the first frame.  If it is here, the operation field should be =0.&lt;br /&gt;
&lt;br /&gt;
If the initial FORM ILBM uses a DLTA chunk, the ANHD chunk must appear, and the operation field must be set to the&lt;br /&gt;
according anim method.&lt;br /&gt;
&lt;br /&gt;
Each of the frames from frame 2 on up may use an anhd-&amp;gt;operation of 0, 5 or 8. Note that only for the first frame in the file do you copy the image data into two buffers, not every time you get an ANHD-&amp;gt;operation==0.&lt;br /&gt;
&lt;br /&gt;
== Chunk Formats ==&lt;br /&gt;
&lt;br /&gt;
=== ANHD Chunk for method 8 ===&lt;br /&gt;
&lt;br /&gt;
The ANHD chunk consists of the following data structure:&lt;br /&gt;
&lt;br /&gt;
     UBYTE operation  The compression method=8 short/long Vertical Delta mode&lt;br /&gt;
     UBYTE mask         unused&lt;br /&gt;
     UWORD w,h          unused&lt;br /&gt;
     WORD  x,y          unused&lt;br /&gt;
     ULONG abstime      unused&lt;br /&gt;
     ULONG reltime     (timing for frame relative to time previous frame was displayed - in jiffies (1/60 sec))&lt;br /&gt;
     UBYTE interleave = 0 (see ANHD description above)&lt;br /&gt;
     UBYTE pad0         unused&lt;br /&gt;
     ULONG bits         32 option bits used by method=4 and 5.&lt;br /&gt;
                        method 8 uses only bit #0&lt;br /&gt;
                       bit #              set =0              set =1&lt;br /&gt;
                       =============================================&lt;br /&gt;
                       0              short data           long data&lt;br /&gt;
     UBYTE pad[16]       unused&lt;br /&gt;
&lt;br /&gt;
=== DLTA Chunk ===&lt;br /&gt;
&lt;br /&gt;
The DLTA Chunks of method8 consists of&lt;br /&gt;
* 16 pointers   same as in method 5&lt;br /&gt;
&lt;br /&gt;
In this method the DLTA Chunk begins with 16 pointers. The first 8 longwords are pointers to the opcode lists for up to 8&lt;br /&gt;
planes. The  second set of 8 longwords are unused.  If there are less than 8 Planes all unused pointers are set to zero.&lt;br /&gt;
&lt;br /&gt;
Compression/decompression is performed on a plane-by-plane basis. The following description of the method is similar to Jim Kent&#039;s methode 5, except that data is either in WORDs or LONGS, depending on bit 0 of the ANHD bits.&lt;br /&gt;
&lt;br /&gt;
In method 8 the bit plane is split into vertical columns. Each column of the bit plane is compressed separately. A 320x200 bit plane would have 20 columns of 200 short data each (or 10 columns of 200 long data).&lt;br /&gt;
&lt;br /&gt;
Each column of the bit plane is compressed separately.  A 320x200 bit plane would have 20 (WORD) or 10 (LONG)columns of 200 bytes each. Each column starts with an op-count followed by a number of ops.  If the op-count is zero, that&#039;s OK, it just means there&#039;s no change in this column from the last frame.  The ops are of three classes, and followed by a varying amount of data depending on which class:&lt;br /&gt;
&lt;br /&gt;
# Skip ops - this is a word or long with the hi bit clear that says how many rows to move the &amp;quot;dest&amp;quot; pointer forward, i.e. to skip. It is non-zero. Note that the range of values is much larger for word and long data, 0x7fff and 0x7fffffff.&lt;br /&gt;
# Uniq ops - this is a word or long with the hi bit set. The hi bit is masked down and the remainder is a count of the number of bytes of data to copy literally. It&#039;s of course followed by the data to copy. Note that the range of values is much larger for word and long data, 0x7fff and 0x7fffffff.&lt;br /&gt;
# Same ops - this is a 0 word or long followed by a count word or long, followed by a word or long value to repeat count times. Note that the range of values is much larger for word and long data, 0xffff and 0xffffffff.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Do bear in mind that the data is compressed vertically rather than horizontally, so to get to the next word or long in the destination we add the number of bytes per row instead of one!}}&lt;br /&gt;
&lt;br /&gt;
There is a slight complication in the case of long data. Normally an Amiga BitMap is and even number of 16bit WORDs wide, so it is possible to have an image which is not an even number or LONGs wide. For example, an image which is 336 pixels wide is 42 bytes wide, 21 words wide, and 10.5 longs wide. In the case that the data is not an even number of longs wide, and the data is to be long compressed, then the last column of data is to be word compressed instead. So, that 336 pixel wide image would be compress as 10 long columns and 1 word column.&lt;br /&gt;
&lt;br /&gt;
= ANIM.brush =&lt;br /&gt;
&lt;br /&gt;
Dpaint Anim Brush IFF Format&lt;br /&gt;
&lt;br /&gt;
From a description by the author of DPaint, Dan Silva, Electronic Arts.&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;quot;Anim Brushes&amp;amp;quot; of DPaint III are saved on disk in the IFF &amp;amp;quot;ANIM&amp;amp;quot; format.  Basically, an ANIM Form consists of an initial ILBM which is the first frame of the animation, and any number of subsequent &amp;amp;quot;ILBM&amp;amp;quot;S (which aren&#039;t really ILBM&#039;s) each of which contains an ANHD animation header chunk and a DLTA chunk comprised of the encoded difference between a frame and a previous one.&lt;br /&gt;
&lt;br /&gt;
To use ANIM terminology (for a description of the ANIM format, see the IFF Anim Spec, by Gary Bonham). Anim Brushes use a &amp;amp;quot;type 5&amp;amp;quot; encoding, which is a vertical, byte-oriented delta encoding (based on Jim Kent&#039;s RIFF).  The deltas have an interleave of 1, meaning deltas are computed between adjacent frames, rather than between frames 2 apart, which is the usual ANIM custom for the purpose of fast hardware page-flipping.  Also, the deltas use Exclusive Or to allow reversable play.&lt;br /&gt;
&lt;br /&gt;
However, to my knowledge, all the existing Anim players in the Amiga world will only play type 5 &amp;amp;quot;Anim&amp;amp;quot;s which have an interleave of 0 (i.e. 2) and which use a Store operation rather than Exclusive Or, so no existing programs will read Anim Brushes anyway.  The job of modifying existing Anim readers to read Anim Brushes should be simplified, however.&lt;br /&gt;
&lt;br /&gt;
Here is an outline of the structure of the IFF Form output by DPaint III as an &amp;amp;quot;Anim Brush&amp;amp;quot;. The IFF Reader should of course be flexible enough to tolerate variation in what chunks actually appear in the initial ILBM.&lt;br /&gt;
&lt;br /&gt;
 FORM ANIM&lt;br /&gt;
     . FORM ILBM         first frame&lt;br /&gt;
     . . BMHD        &lt;br /&gt;
     . . CMAP&lt;br /&gt;
     . . DPPS&lt;br /&gt;
     . . GRAB&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . CRNG&lt;br /&gt;
     . . DPAN     my own little chunk.&lt;br /&gt;
     . . CAMG&lt;br /&gt;
     . . BODY&lt;br /&gt;
     &lt;br /&gt;
     . FORM ILBM         frame 2&lt;br /&gt;
     . . ANHD                animation header chunk&lt;br /&gt;
     . . DLTA                delta mode data&lt;br /&gt;
     &lt;br /&gt;
     . FORM ILBM         frame 3&lt;br /&gt;
     . . ANHD                animation header chunk&lt;br /&gt;
     . . DLTA                delta mode data&lt;br /&gt;
     &lt;br /&gt;
     . FORM ILBM         frame 4&lt;br /&gt;
     . . ANHD                animation header chunk&lt;br /&gt;
     . . DLTA                delta mode data&lt;br /&gt;
     &lt;br /&gt;
     ...&lt;br /&gt;
     &lt;br /&gt;
  . FORM ILBM         frame N&lt;br /&gt;
     . . ANHD                animation header chunk&lt;br /&gt;
     . . DLTA                delta mode data&lt;br /&gt;
&lt;br /&gt;
== DPAN ==&lt;br /&gt;
&lt;br /&gt;
Here is the format of the DPAN chunk:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
 UWORD version;   /* current version=4 */&lt;br /&gt;
 UWORD nframes;   /* number of frames in the animation.*/&lt;br /&gt;
 ULONG flags;   /* Not used */&lt;br /&gt;
 } DPAnimChunk;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The version number was necessary during development. At present all I look at is &amp;amp;quot;nframes&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== ANHD ==&lt;br /&gt;
&lt;br /&gt;
Here is the ANHD chunk format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
 UBYTE operation;  /* =0  set directly&lt;br /&gt;
       =1  XOR ILBM mode,&lt;br /&gt;
       =2 Long Delta mode,&lt;br /&gt;
       =3 Short Delta mode&lt;br /&gt;
       =4 Generalize short/long Delta mode,&lt;br /&gt;
       =5 Byte Vertical Delta (riff)&lt;br /&gt;
       =74 (Eric Grahams compression mode)&lt;br /&gt;
   */&lt;br /&gt;
 UBYTE mask;      /* XOR ILBM only: plane mask where data is*/&lt;br /&gt;
 UWORD w,h;  &lt;br /&gt;
 WORD x,y;&lt;br /&gt;
 ULONG abstime;&lt;br /&gt;
 ULONG reltime;&lt;br /&gt;
 UBYTE interleave; /* 0 defaults to 2 */&lt;br /&gt;
 UBYTE pad0;   /* not used */&lt;br /&gt;
 ULONG bits;   /* meaning of bits:&lt;br /&gt;
     bit#    =0         =1&lt;br /&gt;
    0  short data      long data&lt;br /&gt;
    1  store         XOR&lt;br /&gt;
    2  separate info       one info for&lt;br /&gt;
      for each plane     for all planes&lt;br /&gt;
    3  not RLC    RLC (run length encoded)&lt;br /&gt;
    4  horizontal   vertical&lt;br /&gt;
    5  short info offsets long info offsets&lt;br /&gt;
   -------------------------*/&lt;br /&gt;
 UBYTE pad[16];&lt;br /&gt;
 } AnimHdr;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for Anim Brushes, I set:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
animHdr.operation = 5;  /* RIFF encoding */&lt;br /&gt;
animHdr.interleave = 1;&lt;br /&gt;
animHdr.w = curAnimBr.bmob.pict.box.w; &lt;br /&gt;
animHdr.h = curAnimBr.bmob.pict.box.h; &lt;br /&gt;
animHdr.reltime = 1;&lt;br /&gt;
animHdr.abstime = 0;&lt;br /&gt;
animHdr.bits = 4; /* indicating XOR */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
everything else is set to 0.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=The &amp;amp;quot;bits&amp;amp;quot; field was actually intended ( by the original creator of the ANIM format, Gary Bonham of SPARTA, Inc.) for use with only with compression method 4. I am using bit 2 of the bits field to indicate the Exclusive OR operation in the context of method 5, which seems like a reasonable generalization.}}&lt;br /&gt;
&lt;br /&gt;
== DLTA ==&lt;br /&gt;
&lt;br /&gt;
For an Anim Brush with 10 frames, there will be an initial frame followed by 10 Delta&#039;s (i.e ILBMS containing ANHD and DLTA &lt;br /&gt;
chunks).  Applying the first Delta to the initial frame generates the second frame, applying the second Delta to the second frame &lt;br /&gt;
generates the third frame, etc.  Applying the last Delta thus brings back the first frame.  &lt;br /&gt;
&lt;br /&gt;
The DLTA chunk begins with 16 LONG plane offets, of which DPaint only uses the first 6 (at most).  These plane offsets are either &lt;br /&gt;
the offset (in bytes ) from the beginning of the DLTA chunk to the data for the corresponding plane, or Zero, if there was no &lt;br /&gt;
change in that plane.  Thus the first plane offset is either 0 or 64.&lt;br /&gt;
&lt;br /&gt;
(The following description of the method is based on Gary Bonham&#039;s rewording of Jim Kent&#039;s RIFF documentation.)&lt;br /&gt;
&lt;br /&gt;
Compression/decompression is performed on a plane-by-plane basis.  &lt;br /&gt;
&lt;br /&gt;
Each byte-column of the bit plane is compressed separately.  A 320x200 bit plane would have 40 columns of 200 bytes each.  In &lt;br /&gt;
general, the bit planes are always an even number of bytes wide, so for instance a 17x20 bit plane would have 4 columns of 20 &lt;br /&gt;
bytes each.&lt;br /&gt;
&lt;br /&gt;
Each column starts with an op-count followed by a number of ops.  If the op-count is zero, that&#039;s OK, it just means there&#039;s no change in this column from the last frame.  The ops are of three kinds, and followed by a varying amount of data depending on which kind:&lt;br /&gt;
&lt;br /&gt;
# SKIP - this is a byte with the hi bit clear that says how many rows to move the &amp;amp;quot;dest&amp;amp;quot; pointer forward, i.e. to skip. It is non-zero.&lt;br /&gt;
# DUMP - this is a byte with the hi bit set. The hi bit is masked off and the remainder is a count of the number of bytes of data to XOR directly. It is followed by the bytes to copy.&lt;br /&gt;
# RUN - this is a 0 byte followed by a count byte, followed by a byte value to repeat &amp;amp;quot;count&amp;amp;quot; times, XOR&#039;ing it into the destination.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Bear in mind that the data is compressed vertically rather than horizontally, so to get to the next byte in the destination you add the number of bytes per row instead of one.}}&lt;br /&gt;
&lt;br /&gt;
The Format of DLTA chunks is as described in section 2.2.2 of the Anim Spec. The encoding for type 5 is described in section 2.2.3 of the Anim Spec.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Window_Display_Preservation&amp;diff=12553</id>
		<title>Window Display Preservation</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Window_Display_Preservation&amp;diff=12553"/>
		<updated>2025-01-26T19:30:48Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Preserving the Window Display =&lt;br /&gt;
&lt;br /&gt;
The layers library is what allows the display and manipulation of multiple overlapping rectangles, or &#039;&#039;layers&#039;&#039;. Intuition uses the layers library to manage its windows, by associating a layer to each window.&lt;br /&gt;
&lt;br /&gt;
Each window is a virtual display. When rendering, the application does not have to worry about the current size or position of its window, and what other windows might be partly or fully obscuring its window. The window&#039;s RastPort is the handle to the its virtual display space. Intuition and graphics library rendering calls will recognize that this RastPort belongs to a layer, and act accordingly.&lt;br /&gt;
&lt;br /&gt;
As windows are moved, resized, rearranged, opened, or closed, the on-screen representation changes. When part of a window which was visible now needs to appear in a new location, the layers library will move that imagery without involving the application. However, when part of a window that was previously obscured is revealed, or when a window is made larger, the imagery for the newly-visible part of the window needs to be redrawn. Intuition, through layers, offers three choices for how this is managed, trading off speed, memory usage, and application complexity.&lt;br /&gt;
&lt;br /&gt;
* The most basic type of window is called &#039;&#039;Simple Refresh&#039;&#039;. When any graphics operation takes place in this kind of window, the visible parts are updated, but rendering to the obscured parts is discarded. When the window arrangement changes to reveal a previously obscured part of such a window, the application must refresh that area.&lt;br /&gt;
&lt;br /&gt;
* Alternately, a window may be made &#039;&#039;Smart Refresh&#039;&#039;, which means that when rendering occurs, the system will not only update the visible parts of the window, but it will maintain the obscured parts as well, by using off-screen buffers. This means that when an obscured part of the window is revealed, the system will restore the imagery that belongs there. The application needs only to refresh parts of the window that appear when the window is made bigger. Smart Refresh windows use more memory than Simple Refresh windows (for the storage of obscured areas), but they are faster.&lt;br /&gt;
&lt;br /&gt;
* The third kind of window is called &#039;&#039;SuperBitMap&#039;&#039;. In such a window, the system can refresh the window even when it is sized bigger. For this to work, the application must store a complete bitmap for the window&#039;s maximum size. Such a window is more work to manage, and uses yet more memory. SuperBitMap windows are used less often than the other two types.&lt;br /&gt;
&lt;br /&gt;
Intuition helps your application manage window refresh. First, Intuition will take care of redrawing the window border and any system and application gadgets in the window. Your application never has to worry about that. Second, Intuition will notify your application when it needs to refresh its window (by sending the IDCMP_REFRESHWINDOW event). Third, Intuition provides functions that restrict your rendering to the newly-revealed (damaged) areas only, which speeds up your refresh rendering and makes it look cleaner.&lt;br /&gt;
&lt;br /&gt;
The Intuition, layers, and graphics libraries work together to make rendering into and managing windows easy. You obtain your windows through Intuition, which uses the Layers library to manage the overlapping, resizing, and re-positioning of the window layers. The layers library is responsible for identifying the areas of each window that are visible, obscured but preserved off-screen, or obscured and not preserved. The rendering functions in the graphics library and Intuition library know how to render into the multiple areas that layers library establishes.&lt;br /&gt;
&lt;br /&gt;
Note that you may not directly manipulate layers on an Intuition screen. You cannot create your own layers on an Intuition screen, nor can you use the layers movement, sizing, or arrangement functions on Intuition windows. Use the corresponding Intuition calls instead. Some other Layers library calls (such as the locking calls) are sometimes used on Intuition screens and windows.&lt;br /&gt;
&lt;br /&gt;
= Damage Regions =&lt;br /&gt;
&lt;br /&gt;
The layers library and Intuition maintain a &#039;&#039;damage region&#039;&#039; for each window, which is the part of the window whose imagery is in need of repair, or refreshing. Several things can add areas of the window to the damage region:&lt;br /&gt;
&lt;br /&gt;
* Revealing an obscured part of a Simple Refresh window adds that area to the damage region&lt;br /&gt;
* Sizing a Simple or Smart Refresh window bigger along either axis adds the new area to the damage region&lt;br /&gt;
* Resizing a Simple or Smart Refresh window (smaller or bigger) adds the old and new border areas, and the areas occupied by certain gadgets (those whose position or size depend on window size) to the damage region.&lt;br /&gt;
&lt;br /&gt;
= Refreshing Intuition Windows =&lt;br /&gt;
&lt;br /&gt;
When the user or an application performs an Intuition operation which causes damage to a window, Intuition notifies that window&#039;s application. It does this by sending a message of the class IDCMP_REFRESHWINDOW to that window&#039;s IDCMP.&lt;br /&gt;
&lt;br /&gt;
In response to this message, your application should update the damaged areas. Rendering proceeds faster and looks cleaner if it is restricted to the damaged areas only. The BeginRefresh()/EndRefresh() pair achieve that. The application should call BeginRefresh() for the window, and then do its rendering. Any rendering that would have gone into undamaged areas of the window is automatically discarded; only the area in need of repair is affected. Finally, the application should call EndRefresh(), which removes the restriction on rendering, and informs the system that the damage region has been dealt with. Even if your application intends to do no rendering, it must at least call BeginRefresh()/EndRefresh(), to inform the system that the damage region is no longer needed. If your application never needs to render in response to a refresh event, it can avoid having to call BeginRefresh()/EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.&lt;br /&gt;
&lt;br /&gt;
Note that by the time that your application receives notification that refresh is needed, Intuition will have already refreshed your window&#039;s border and all gadgets in the window, as needed. Thus, it is unnecessary to use any of the gadget-refreshing functions in response to an IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Avoid RefreshGList() or RefreshGadgets(), or you risk deadlocking the computer. Avoid calls that may lock the LayerInfo or get complicated in Intuition, since BeginRefresh() leaves the window&#039;s layer or layers locked. Avoid AutoRequest() and EasyRequest(), and therefore all direct or indirect disk related DOS calls. See the [[Intuition_Gadgets|Intuition Gadgets]] section for more information on gadget restrictions with BeginRefresh() and EndRefresh().&lt;br /&gt;
&lt;br /&gt;
== Simple Refresh ==&lt;br /&gt;
&lt;br /&gt;
For a Simple Refresh window, only those pixels actually on-screen are maintained by the system. When part of a Simple Refresh window is obscured, the imagery that was there is lost. As well, any rendering into obscured portions of such a window is discarded.&lt;br /&gt;
&lt;br /&gt;
When part of the window is newly revealed (either because the window was just made larger, or because that part used to be obscured by another window), the application must refresh any rendering it wishes to appear into that part. The application will learn that refresh is needed because Intuition sends an IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
== Smart Refresh ==&lt;br /&gt;
&lt;br /&gt;
If a window is of the Smart Refresh type, then the system will not only preserve those pixels which are actually on-screen, but it will save all obscured pixels that are within the current window&#039;s size. The system will refresh those parts of the window revealed by changes in the overlapping with other windows on the screen, without involving the application. However, any part of the window revealed through the sizing of the window must be redrawn by the application. Again, Intuition will notify the application through the IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
Because the obscured areas are kept in off-screen buffers, Smart Refresh windows are refreshed faster than Simple Refresh windows are, and often without involving the application. Of course, for the same reason, they use more display memory.&lt;br /&gt;
&lt;br /&gt;
== SuperBitMap Refresh ==&lt;br /&gt;
&lt;br /&gt;
The SuperBitMap refresh type allows the application to provide and maintain bitmap memory for graphics in the window. The bitmap can be any size as long as the window sizing limits respect the maximum size of the bitmap.&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows have their own memory for maintaining all obscured parts of the window up to the size of the defined bitmap, including those parts outside of the current window. Intuition will update all parts of the window that are revealed through changes in sizing and changes in window overlapping. The application never needs to redraw portions of the window that were revealed by sizing or positioning windows in the screen.&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows require the application to allocate a bitmap for use as off-screen memory, instead of using Intuition managed buffers. This bitmap must be as large as, or larger than, the inner window&#039;s maximum dimensions (that is, the window&#039;s outside dimensions less the border sizes).&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows are almost always WFLG_GIMMEZEROZERO, which renders the borders and system gadgets in a separate bitmap. If the application wishes to create a SuperBitMap window that is not GimmeZeroZero, it must make the window borderless with no system gadgets, so that no border imagery is rendered by Intuition into the application&#039;s bitmap.&lt;br /&gt;
&lt;br /&gt;
= Intuition Refresh Events =&lt;br /&gt;
&lt;br /&gt;
When using a Simple Refresh or a Smart Refresh windows, the program may receive refresh events, informing it to update the display. See the above discussion for information on when refresh events are sent.&lt;br /&gt;
&lt;br /&gt;
A message of the class IDCMP_REFRESHWINDOW arrives at the IDCMP, informing the program of the need to update the display. The program must take some action when it receives a refresh event, even if it is just the acceptable minimum action described below.&lt;br /&gt;
&lt;br /&gt;
On receiving a refresh event, BeginRefresh() must be called, then the program should redraw its display, and, finally, call EndRefresh(). The minimum required action is to call the BeginRefresh()/EndRefresh() pair. This allows Intuition and the Layers library keep things sorted and organized.&lt;br /&gt;
&lt;br /&gt;
= Optimized Window Refreshing =&lt;br /&gt;
&lt;br /&gt;
Bracketing the display updating in the BeginRefresh()/EndRefresh() pair automatically restricts all rendering to the &amp;quot;damaged&amp;quot; areas.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID BeginRefresh( struct Window *window );&lt;br /&gt;
VOID EndRefresh  ( struct Window *window, LONG complete );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions makes sure that refreshing is done in the most efficient way, only redrawing those portions of the window that really need to be redrawn. The rest of the rendering commands are discarded.&lt;br /&gt;
&lt;br /&gt;
Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Calls to RefreshGadgets() are not permitted. Avoid calls that may lock the LayerInfo, or get complicated in Intuition, since BeginRefresh() leaves the window&#039;s layer or layers locked. Avoid AutoRequest(), and therefore all direct or indirect disk related DOS calls. See [[Intuition_Gadgets|Intuition Gadgets]] for more information on gadget restrictions with BeginRefresh()/EndRefresh().&lt;br /&gt;
&lt;br /&gt;
Certain applications do not need to receive refresh events, and can avoid having to call BeginRefresh() and EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.&lt;br /&gt;
&lt;br /&gt;
The EndRefresh() function takes a boolean value as an argument (complete in the prototype above). This value determines whether refreshing is completely finished. When set to FALSE, further refreshing may be performed between subsequent BeginRefresh()/ EndRefresh() pairs. Set the boolean to TRUE for the last call to EndRefresh().&lt;br /&gt;
&lt;br /&gt;
It is critical that applications performing multiple BeginRefresh() and EndRefresh() pairs using EndRefresh(win,FALSE) hold layers locked through the entire process. The layer lock may only be released after the final call to EndRefresh(win,TRUE). See [[Layers_Library|Layers Library]] for more details.&lt;br /&gt;
&lt;br /&gt;
The procedures outlined in this section take care of refreshing what is inside the window. Another function named RefreshWindowFrame() refreshes window borders, including the title region and gadgets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID RefreshWindowFrame( struct Window *window );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Applications can use this function to update window borders after overwriting them with graphics.&lt;br /&gt;
&lt;br /&gt;
= Setting up a SuperBitMap Window =&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows are created by setting the WFLG_SUPER_BITMAP flag, or by specifying the WA_SuperBitMap tag in the OpenWindowTagList() call. A pointer to an allocated and initialized BitMap structure must be provided.&lt;br /&gt;
&lt;br /&gt;
A SuperBitMap window requires the application to allocate and initialize its own bitmap. This entails allocating a BitMap structure, initializing the structure and allocating memory for the bit planes.&lt;br /&gt;
&lt;br /&gt;
Allocate a BitMap structure with the Exec AllocMem() function. Then use the graphics function InitBitMap() to initialize the BitMap structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID InitBitMap( struct BitMap *bitMap, LONG depth, LONG width, LONG height );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
InitBitMap() fills in fields in the BitMap structure describing how a linear memory area is organized as a series of one or more rectangular bit-planes.&lt;br /&gt;
&lt;br /&gt;
Once you have allocated and initialized the BitMap structure, use the graphics library function AllocRaster() to allocate the memory space for all the bit planes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
PLANEPTR AllocRaster( ULONG width, ULONG height );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example listed in the next section shows how to allocate a BitMap structure, initialize it with InitBitMap() and use AllocRaster() function to set up memory for the bitplanes.&lt;br /&gt;
&lt;br /&gt;
= Graphics and Layers Functions for SuperBitMap Windows =&lt;br /&gt;
&lt;br /&gt;
The portion of the bitmap showing within a SuperBitMap window is controlled by the application. Initially, the window shows the bitmap starting from its origin (0,0) and clipped to fit within the window layer. The visible portion of the bitmap can be scrolled around within the window using the layers library ScrollLayer() function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID ScrollLayer(LONG unused, struct Layer *layer, LONG dx, LONG dy)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pass this function a pointer to the window&#039;s layer in layer and the scroll offsets in dx and dy. (A pointer to the window&#039;s layer can be obtained from Window.RPort-&amp;amp;gt;Layer.)&lt;br /&gt;
&lt;br /&gt;
When rendering operations are performed in a SuperBitMap window, any rendering that falls outside window boundaries is done in the application&#039;s bitmap. Rendering that falls within window bounds is done in the screen&#039;s bitmap. Before performing an operation such as a save on the application bitmap, the graphics library function SyncSBitMap() should be called:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID SyncSBitMap(struct Layer *layer)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pass this function a pointer to the window&#039;s layer. SyncSBitMap() copies the window contents to the corresponding part of the application bitmap, bringing it up to date. (If no rendering operations have been performed this call is not necessary.)&lt;br /&gt;
&lt;br /&gt;
Similarly, after making any changes to the application bitmap such as loading a new one, the window&#039;s layer should be locked and the CopySBitMap() function should be called.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID CopySBitMap(struct Layer *)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function copies the new information in the appropriate area of the underlying bitmap to the window&#039;s layer.&lt;br /&gt;
&lt;br /&gt;
For more information about bitmaps and layers, see the [[Graphics_Primitives|Graphics Primitives]] and [[Layers_Library|Layers Library]]. Also see the &amp;amp;lt;graphics/clip.h&amp;amp;gt;, &amp;amp;lt;graphics/gfx.h&amp;amp;gt;, &amp;amp;lt;graphics/layers.h &amp;amp;gt; files in the SDK.&lt;br /&gt;
&lt;br /&gt;
== SuperBitMap Window Example ==&lt;br /&gt;
&lt;br /&gt;
This example shows how to implement a superbitmap, and uses a host of Intuition facilities. Further reading of other Intuition, BOOPSI and graphics chapters may be required for a complete understanding of this example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 **  lines.c -- Window SuperBitMap example.&lt;br /&gt;
 **&lt;br /&gt;
 **  gcc -o lines lines.c&lt;br /&gt;
 ** &lt;br /&gt;
 **/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/scroller.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/layout.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define WIDTH_SUPER (800)&lt;br /&gt;
#define HEIGHT_SUPER (600)&lt;br /&gt;
#define MINWIDTH (150)&lt;br /&gt;
#define MINHEIGHT (150)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LAYERXOFFSET(x) (x-&amp;gt;RPort-&amp;gt;Layer-&amp;gt;Scroll_X)&lt;br /&gt;
#define LAYERYOFFSET(x) (x-&amp;gt;RPort-&amp;gt;Layer-&amp;gt;Scroll_Y)&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
    WID_MAIN = 0,&lt;br /&gt;
    WID_LAST&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
    OID_MAIN = 0,&lt;br /&gt;
    OID_VPROP,&lt;br /&gt;
    OID_HPROP,&lt;br /&gt;
    OID_LAST&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct LayersIFace *ILayers;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
static struct Window *windows[WID_LAST];&lt;br /&gt;
static Object *objects[OID_LAST];&lt;br /&gt;
static struct Hook idcmphook;&lt;br /&gt;
&lt;br /&gt;
/* Prototypes for our functions */&lt;br /&gt;
&lt;br /&gt;
void initBorderProps(struct Screen *screen);&lt;br /&gt;
void doNewSize();&lt;br /&gt;
void doDrawStuff();&lt;br /&gt;
void doMsgLoop();&lt;br /&gt;
void superWindow(struct Screen *screen);&lt;br /&gt;
void slideBitMap(struct Window *win, int16 dX, int16 dY);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct Screen *screen;&lt;br /&gt;
&lt;br /&gt;
    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    IIntuition = (struct IntuitionIFace *) IExec-&amp;gt;GetInterface(IntuitionBase,&lt;br /&gt;
        &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace *) IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
    ILayers = (struct LayersIFace *) IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL) {&lt;br /&gt;
        if ((screen = IIntuition-&amp;gt;LockPubScreen(NULL)) != NULL) {&lt;br /&gt;
            superWindow(screen);&lt;br /&gt;
            IIntuition-&amp;gt;UnlockPubScreen(NULL, screen);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) ILayers);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) IGraphics);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void IDCMPHook(struct Hook *hook, Object *object, struct IntuiMessage *msg)&lt;br /&gt;
{&lt;br /&gt;
    if (msg-&amp;gt;Class == IDCMP_IDCMPUPDATE) {&lt;br /&gt;
        int16 dX = 0;&lt;br /&gt;
        int16 dY = 0;&lt;br /&gt;
        int32 pos;&lt;br /&gt;
&lt;br /&gt;
        struct Window *win = windows[WID_MAIN];&lt;br /&gt;
        if (win != NULL) {&lt;br /&gt;
            uint32 gadgetid = IUtility-&amp;gt;GetTagData(GA_ID, 0, (struct TagItem *) msg-&amp;gt;IAddress);&lt;br /&gt;
            if (IIntuition-&amp;gt;GetAttr(SCROLLER_Top, objects[gadgetid], (uint32 *) &amp;amp;pos)) {&lt;br /&gt;
                switch (gadgetid) {&lt;br /&gt;
                case OID_VPROP:&lt;br /&gt;
                    dY = pos - LAYERYOFFSET(win);&lt;br /&gt;
                    break;&lt;br /&gt;
                case OID_HPROP:&lt;br /&gt;
                    dX = pos - LAYERXOFFSET(win);&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                if (dX || dY) {&lt;br /&gt;
                    slideBitMap(win, dX, dY);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void superWindow(struct Screen *myscreen)&lt;br /&gt;
{&lt;br /&gt;
    struct BitMap *bigBitMap = IGraphics-&amp;gt;AllocBitMapTags(WIDTH_SUPER, HEIGHT_SUPER, myscreen-&amp;gt;BitMap.Depth,&lt;br /&gt;
            BMATags_Friend, myscreen-&amp;gt;BitMap, BMATags_Displayable, TRUE, BMATags_Clear, 0, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (bigBitMap != NULL) {&lt;br /&gt;
        struct MsgPort *AppPort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_DONE);&lt;br /&gt;
&lt;br /&gt;
        if ( AppPort != NULL )&lt;br /&gt;
        {&lt;br /&gt;
            idcmphook.h_Entry = (uint32 (*)())IDCMPHook;&lt;br /&gt;
            idcmphook.h_SubEntry = NULL;&lt;br /&gt;
&lt;br /&gt;
            if ((objects[OID_MAIN] = IIntuition-&amp;gt;NewObject(NULL, &amp;quot;window.class&amp;quot;,&lt;br /&gt;
                WA_ScreenTitle, &amp;quot;SuperBitmap&amp;quot;,&lt;br /&gt;
                WA_Title, &amp;quot;Lines&amp;quot;,&lt;br /&gt;
                WA_Activate, TRUE,&lt;br /&gt;
                WA_DepthGadget, TRUE,&lt;br /&gt;
                WA_DragBar, TRUE,&lt;br /&gt;
                WA_CloseGadget, TRUE,&lt;br /&gt;
                WA_SizeGadget, TRUE,&lt;br /&gt;
                WA_Top, 10,&lt;br /&gt;
                WA_Left, 10,&lt;br /&gt;
                WA_MinWidth, MINWIDTH,&lt;br /&gt;
                WA_MinHeight, MINHEIGHT,&lt;br /&gt;
                WA_Width, MINWIDTH,&lt;br /&gt;
                WA_Height, MINHEIGHT,&lt;br /&gt;
                WA_IDCMP, IDCMP_NEWSIZE | IDCMP_IDCMPUPDATE,&lt;br /&gt;
                WA_PubScreen, myscreen, WA_SuperBitMap, bigBitMap,&lt;br /&gt;
                WA_NoCareRefresh, TRUE,&lt;br /&gt;
                WA_GimmeZeroZero, TRUE,     &lt;br /&gt;
                WINDOW_IDCMPHook, &amp;amp;idcmphook,&lt;br /&gt;
                WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE,&lt;br /&gt;
                WINDOW_HorizProp, 1,&lt;br /&gt;
                WINDOW_VertProp, 1,&lt;br /&gt;
                WINDOW_AppPort, AppPort, TAG_DONE)) != NULL) {&lt;br /&gt;
                //  Open the window.&lt;br /&gt;
                if (windows[WID_MAIN] = (struct Window *) IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_OPEN)) {   &lt;br /&gt;
                    /* adjust props to represent portion visible */&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();&lt;br /&gt;
                    /* process the window, return on IDCMP_CLOSEWINDOW */&lt;br /&gt;
                    doMsgLoop();&lt;br /&gt;
                }&lt;br /&gt;
                IIntuition-&amp;gt;DisposeObject(objects[OID_MAIN]);&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, AppPort);&lt;br /&gt;
        }   &lt;br /&gt;
        IGraphics-&amp;gt;FreeBitMap(bigBitMap);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void slideBitMap(struct Window *win, int16 Dx, int16 Dy)&lt;br /&gt;
{&lt;br /&gt;
    ILayers-&amp;gt;ScrollLayer(0, win-&amp;gt;RPort-&amp;gt;Layer, Dx, Dy);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Update the prop gadgets and bitmap positioning when the size changes.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void doNewSize()&lt;br /&gt;
{&lt;br /&gt;
    struct Window *win = windows[WID_MAIN];&lt;br /&gt;
    if (win != NULL) {&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_HorizObject, objects[OID_MAIN], (uint32 *) &amp;amp;objects[OID_HPROP]);&lt;br /&gt;
        if (objects[OID_HPROP]) {       &lt;br /&gt;
            if (win-&amp;gt;GZZWidth &amp;gt;= WIDTH_SUPER)&lt;br /&gt;
                slideBitMap(win, -LAYERXOFFSET(win), 0);&lt;br /&gt;
            IIntuition-&amp;gt;RefreshSetGadgetAttrs((APTR) objects[OID_HPROP], win, NULL,&lt;br /&gt;
                GA_ID, OID_HPROP,&lt;br /&gt;
                SCROLLER_Top, LAYERXOFFSET(win),&lt;br /&gt;
                SCROLLER_Total, WIDTH_SUPER,&lt;br /&gt;
                SCROLLER_Visible, win-&amp;gt;GZZWidth,&lt;br /&gt;
                ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
                TAG_DONE); &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_VertObject, objects[OID_MAIN], (uint32 *) &amp;amp;objects[OID_VPROP]);&lt;br /&gt;
        if (objects[OID_VPROP]) {&lt;br /&gt;
            if (win-&amp;gt;GZZHeight &amp;gt;= HEIGHT_SUPER)&lt;br /&gt;
                slideBitMap(win, 0, -LAYERYOFFSET(win));&lt;br /&gt;
&lt;br /&gt;
            IIntuition-&amp;gt;RefreshSetGadgetAttrs((APTR) objects[OID_VPROP], win, NULL,&lt;br /&gt;
                GA_ID, OID_VPROP,&lt;br /&gt;
                SCROLLER_Top, LAYERYOFFSET(win),&lt;br /&gt;
                SCROLLER_Total, HEIGHT_SUPER,&lt;br /&gt;
                SCROLLER_Visible, win-&amp;gt;GZZHeight,&lt;br /&gt;
                ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
                TAG_DONE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int16 RangeRand(uint32 maxValue)&lt;br /&gt;
{&lt;br /&gt;
    static struct RandomState range = {0xFFFF, 0};&lt;br /&gt;
    return IUtility-&amp;gt;Random(&amp;amp;range) % maxValue;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void doDrawStuff()&lt;br /&gt;
{&lt;br /&gt;
    struct Window *win = windows[WID_MAIN];&lt;br /&gt;
    if (win != NULL) {&lt;br /&gt;
        /* clear the bitplanes */&lt;br /&gt;
        IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 0);&lt;br /&gt;
        IGraphics-&amp;gt;SetDrMd(win-&amp;gt;RPort, JAM1);&lt;br /&gt;
        int16 x1, y1, x2, y2;&lt;br /&gt;
        int16 pen, ncolors, deltx, delty;&lt;br /&gt;
        ncolors = 1 &amp;lt;&amp;lt; win-&amp;gt;WScreen-&amp;gt;BitMap.Depth;&lt;br /&gt;
        deltx = RangeRand(6) + 2;&lt;br /&gt;
        delty = RangeRand(6) + 2;&lt;br /&gt;
        pen = RangeRand(ncolors - 1) + 1;&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(win-&amp;gt;RPort, pen);&lt;br /&gt;
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;&lt;br /&gt;
            x1 &amp;lt; WIDTH_SUPER; x1 += deltx, x2 -= deltx) {&lt;br /&gt;
            IGraphics-&amp;gt;Move(win-&amp;gt;RPort, x1, y1);&lt;br /&gt;
            IGraphics-&amp;gt;Draw(win-&amp;gt;RPort, x2, y2);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        pen = RangeRand(ncolors - 1) + 1;&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(win-&amp;gt;RPort, pen);&lt;br /&gt;
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;&lt;br /&gt;
             y1 &amp;lt; HEIGHT_SUPER; y1 += delty, y2 -= delty) {&lt;br /&gt;
            IGraphics-&amp;gt;Move(win-&amp;gt;RPort, x1, y1);&lt;br /&gt;
            IGraphics-&amp;gt;Draw(win-&amp;gt;RPort, x2, y2);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void doMsgLoop()&lt;br /&gt;
{                   &lt;br /&gt;
    uint32 signal = 0;&lt;br /&gt;
    BOOL done = FALSE;&lt;br /&gt;
    &lt;br /&gt;
    // Obtain the window wait signal mask.&lt;br /&gt;
    IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, objects[OID_MAIN], &amp;amp;signal);&lt;br /&gt;
&lt;br /&gt;
    // Input Event Loop&lt;br /&gt;
    while (!done) {&lt;br /&gt;
        uint32 wait = IExec-&amp;gt;Wait(signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
        if ( wait &amp;amp; SIGBREAKF_CTRL_C ) {&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( wait &amp;amp; signal ) {&lt;br /&gt;
            uint32 result = WMHI_LASTMSG;&lt;br /&gt;
            int16 code = 0;&lt;br /&gt;
            while ((result = IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_HANDLEINPUT, &amp;amp;code)) != WMHI_LASTMSG) {&lt;br /&gt;
                switch (result &amp;amp; WMHI_CLASSMASK) {&lt;br /&gt;
                case WMHI_NEWSIZE:&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_CLOSEWINDOW:&lt;br /&gt;
                    windows[WID_MAIN] = NULL;&lt;br /&gt;
                    done = TRUE;&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_ICONIFY:&lt;br /&gt;
                    IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_ICONIFY);&lt;br /&gt;
                    windows[WID_MAIN] = NULL;&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_UNICONIFY:&lt;br /&gt;
                    windows[WID_MAIN] = (struct Window *) IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_OPEN);&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();     &lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Sprites,_Bobs_and_Animation&amp;diff=12552</id>
		<title>Graphics Sprites, Bobs and Animation</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Sprites,_Bobs_and_Animation&amp;diff=12552"/>
		<updated>2025-01-26T19:30:36Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP}}&lt;br /&gt;
== Graphics Sprites, Bobs and Animation ==&lt;br /&gt;
&lt;br /&gt;
This article describes how to use the functions provided by the graphics library to manipulate and animate Graphic Elements (also called GELs). It is divided into six sections:&lt;br /&gt;
&lt;br /&gt;
* An overview of the GELs animation system, including fundamental terms and structures&lt;br /&gt;
* Explanation of simple (hardware) Sprites and an example showing their usage&lt;br /&gt;
* Explanation of VSprites and an example showing their usage&lt;br /&gt;
* Explanation of Bobs and an example showing their usage&lt;br /&gt;
* Discussion of topics that apply to all GELs such as collision detection and data structure extensions.&lt;br /&gt;
* Discussion of animation, using AnimComps and AnimObs and an example showing their usage&lt;br /&gt;
&lt;br /&gt;
== About the GELs System ==&lt;br /&gt;
&lt;br /&gt;
Before going into details, a quick glossary is in order. A &#039;&#039;playfield&#039;&#039; forms the background that GELs operate in. It encompasses the View, ViewPort, and RastPort data structures. (VSprites appear &#039;&#039;over&#039;&#039;, and Bobs appear &#039;&#039;in&#039;&#039; the playfield.) Playfields can be created and controlled at several levels. Refer to [[Graphics_Primitives|Graphics Primitives]] and [[Layers_Library|Layers Library]] for details on lower-level playfield control. [[Intuition_Screens|Intuition Screens]] explains how to get higher-level access to playfields.&lt;br /&gt;
&lt;br /&gt;
GELs, or graphic elements, are special graphic objects that appear in the foreground and can be moved easily around the display. They are software constructs based on the Amiga&#039;s sprite and blitter hardware. The GELs system is compatible with all playfield modes, including dual-playfield. All the various types of GELs are defined by data structures found in &amp;amp;lt;graphics/gels.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Types of GELs ===&lt;br /&gt;
&lt;br /&gt;
The GEL types are (in order of increasing complexity):&lt;br /&gt;
&lt;br /&gt;
; VSprites&lt;br /&gt;
: for Virtual Sprites. These are represented by the VSprite data structure and implemented with sprite hardware.&lt;br /&gt;
&lt;br /&gt;
; Bobs&lt;br /&gt;
: Blitter Objects. These are represented by the VSprite and Bob data structures and implemented with blitter hardware.&lt;br /&gt;
&lt;br /&gt;
; AnimComps&lt;br /&gt;
: Animation Components. These are represented by the VSprite, Bob and AnimComp data structures and implemented with blitter hardware.&lt;br /&gt;
&lt;br /&gt;
; AnimObjs&lt;br /&gt;
: Animation Objects. These are used to group AnimComps. They are not strictly GELs, but are described here.&lt;br /&gt;
&lt;br /&gt;
==== Simple Sprites ====&lt;br /&gt;
&lt;br /&gt;
Simple Sprites (also known as hardware sprites) are not really part of the GELs system but are the basis for VSprites. Simple Sprites are graphic objects implemented in hardware that are easy to define and easy to animate. The Amiga hardware has the ability to handle up to eight such sprite objects. Each Simple Sprite is produced by one of the Amiga&#039;s eight sprite DMA channels. They are 16-bits wide and arbitrarily tall.&lt;br /&gt;
&lt;br /&gt;
The Amiga system software offers a choice of how to use these hardware sprites. After a sprite DMA channel has displayed the last line of a Simple Sprite, the system can reuse the channel for a different sprite lower on the screen. This is how VSprites are implemented; as a software construct based on the sprite hardware.&lt;br /&gt;
&lt;br /&gt;
Hence, Simple Sprites are not really part of the animation system (they are &#039;&#039;not&#039;&#039; GELs). In fact, if Simple Sprites and GELs are used in the same display, the GELs system must be told specifically which Simple Sprites to avoid. Simple Sprites are described in this article because they are alternatives to VSprites.&lt;br /&gt;
&lt;br /&gt;
==== VSprites ====&lt;br /&gt;
&lt;br /&gt;
The VSprite, or virtual sprite, is the simplest type of GEL. The VSprite data structure contains just a bit more information than is needed to define a hardware sprite. VSprites take advantage of the system&#039;s ability to reuse sprite DMA channels - each VSprite can be temporarily assigned to a hardware sprite, as needed. This makes it appear to an application program that it has a &#039;&#039;virtually&#039;&#039; unlimited supply of VSprites.&lt;br /&gt;
&lt;br /&gt;
Since VSprites are based on hardware sprites, rules that apply to hardware sprites apply to VSprites too. VSprites are not rendered into the underlying BitMap of the playfield and so do not affect any bits in the BitMap. Because they are hardware based, &#039;&#039;they are positioned at absolute display coordinates and are not affected by the movement of screens&#039;&#039;. The starting position of a sprite must not occur before scanline 20, because of certain hardware DMA time constraints. VSprites have the same size limitations as hardware sprites, they are 16-bits wide and arbitrarily tall.&lt;br /&gt;
&lt;br /&gt;
The VSprite data structure also serves as the root structure of more complex GEL types: Bobs and AnimComps.&lt;br /&gt;
&lt;br /&gt;
==== Bobs and AnimComps ====&lt;br /&gt;
&lt;br /&gt;
Like VSprites, Bobs and AnimComps are graphics objects that make animation easier. They are rendered using the blitter. The blitter is a special Amiga hardware component used to move data quickly and efficiently, optionally performing logical operations as it does. It can be used to move any kind of data but is especially well suited to moving rectangular blocks of display data.&lt;br /&gt;
&lt;br /&gt;
It is important to keep in mind that Bobs and AnimComps are based on the blitter hardware while VSprites use the sprite hardware. However all three GEL types use the VSprite structure as their root data structure. The system uses pointers to link the VSprite, Bob and AnimComp structures, &amp;quot;extending&amp;quot; the VSprite structure to include all GEL types.&lt;br /&gt;
&lt;br /&gt;
Since Bobs and AnimComps are rendered with the blitter they actually change the underlying playfield BitMap. The BitMap area where the GEL is rendered can be saved. By moving the GEL to new locations in small increments while also saving and restoring the Bitmap as you proceed, you can create an animation effect. Bobs and AnimComps use the same coordinates as the playfield and can be any size.&lt;br /&gt;
&lt;br /&gt;
==== AnimObs ====&lt;br /&gt;
&lt;br /&gt;
The AnimOb (Animation Object) is a data structure that is used to group one or more AnimComps for convenient movement. For example, an AnimOb could be created that consists of two AnimComps, one that looks like a planet and another containing a sequence that describes orbiting moons. By moving just the AnimOb the image of the planet can be moved across the display and the moons will travel along with it, orbiting the planet the entire time. The system automatically manages the movement of all the AnimComps associated with the AnimOb.&lt;br /&gt;
&lt;br /&gt;
==== VSprites vs. Bobs ====&lt;br /&gt;
&lt;br /&gt;
If you are going to manage the movement and sequencing of GELS yourself, you need to decide if sprite animation (VSprites) or blitter animation (Bobs, AnimComps and AnimObs) best suit your needs. If you&#039;ve got simple requirements or lots of coding time, you may even opt to use only Simple Sprites, and control them yourself. On the other hand if you want the system to manage your animations, AnimComps &#039;&#039;must&#039;&#039; be used and they are Bobs at heart.&lt;br /&gt;
&lt;br /&gt;
Some fundamental differences between VSprites and Bobs are:&lt;br /&gt;
&lt;br /&gt;
* VSprite images and coordinates currently low-resolution pixels, even on a high resolution display. Bob images and coordinates have the same resolution as the playfield they are rendered into.&lt;br /&gt;
&lt;br /&gt;
* VSprites have a maximum width of 16 (low resolution) pixels. Bobs can be any width (although large Bobs tend to slow down the system). The height of either VSprites or Bobs can be as tall as the display.&lt;br /&gt;
&lt;br /&gt;
* VSprites have a maximum of three colors (Simple Sprites can have fifteen if they&#039;re attached). Because the system uses the Copper to control VSprite colors on the fly, the colors are not necessarily the same as those in the background playfield. Bobs can use any or all of the colors in the background playfield. Limiting factors include playfield resolution and display time. Bobs with more colors take longer to display.&lt;br /&gt;
&lt;br /&gt;
* VSprites are positioned using absolute display coordinates, and don&#039;t move with screens. Bobs follow screen movement.&lt;br /&gt;
&lt;br /&gt;
In general, VSprites offer speed, while Bobs offer flexibility.&lt;br /&gt;
&lt;br /&gt;
The following figure shows how the various GEL data structures, VSprites, Bobs and AnimComps are linked together.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-1.png|frame|center|GEL Structure Layout]]&lt;br /&gt;
&lt;br /&gt;
=== The GELs System ===&lt;br /&gt;
&lt;br /&gt;
Before you can use the GELs system, you must set up a playfield. The GELs system requires access to a View, ViewPort, and RastPort structure. These structures may be set up through the graphics library or Intuition. For most examples in this article, the Intuition library is used for this purpose.&lt;br /&gt;
&lt;br /&gt;
All GELs have a VSprite structure at their core. The system keeps track of all the GELs that it will display (the active GELs) by using a standard Exec list structure to link the VSprites. This list is accessed via the GelsInfo data structure, which in turn is associated with the RastPort. The GelsInfo structure is defined in the file &amp;amp;lt;graphics/rastport.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
A new GEL is introduced to the system by calling AddGel() to link it into the GelsInfo list. The new GEL is added immediately ahead of the first existing GEL whose &#039;&#039;x,y&#039;&#039; value is greater than or equal to that of the new GEL, always trying to keep the list sorted.&lt;br /&gt;
&lt;br /&gt;
As GELs are moved about the screen, their &#039;&#039;x,y&#039;&#039; values are constantly changing. SortGList() re-sorts this list by the &#039;&#039;x,y&#039;&#039; values. (Although this is a list of VSprite structures, bear in mind that some or all may really be Bobs or AnimComps.)&lt;br /&gt;
&lt;br /&gt;
The basic set up of the GelsInfo structure requires three important fields: sprRsrvd, gelHead and gelTail. The sprRsrvd field tells the system which hardware sprites not to use when managing true VSprites. For instance, Intuition uses sprite 0 for the mouse pointer so this hardware sprite is not available for assignment to a VSprite. The gelHead and gelTail are VSprite structures that are used to manage the list of GELs. They are never displayed. To activate or deactivate a GEL, a system call is made to add it to or delete it from this list.&lt;br /&gt;
&lt;br /&gt;
Other fields must be set up to provide for collision detection, color optimization, and other features. A complete example for setting up the GelsInfo structure is shown in the animtools.c lisitng at the end of this article.&lt;br /&gt;
&lt;br /&gt;
==== Initializing the GEL System ====&lt;br /&gt;
&lt;br /&gt;
To initialize the animation system, call the system function InitGels(). It takes the form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct VSprite  *vsHead;&lt;br /&gt;
struct VSprite  *vsTail;&lt;br /&gt;
struct GelsInfo *gInfo;&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;InitGels(vsHead, vsTail, gInfo);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The vsHead argument is a pointer to the VSprite structure to be used as the GEL list head. (You must allocate an actual VSprite structure for vsHead to point to.) The vsTail argument is a pointer to the VSprite structure to be used as the GEL list tail. (You must allocate an actual VSprite structure for vsTail to point to.) The gInfo argument is a pointer to the GelsInfo structure to be initialized.&lt;br /&gt;
&lt;br /&gt;
InitGels() forms these structures into a linked list of GELs that is empty except for these two dummy elements (the head and tail). It gives the head VSprite the maximum negative &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; positions and the tail VSprite the maximum positive &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; positions. This is to aid the system in keeping the list sorted by &#039;&#039;x, y&#039;&#039; values, so GELs that are closer to the top and left of the display are nearer the head of the list. The memory space that the VSprites and Gelsinfo structures take up must already have been allocated. This can be done either by declaring them statically or explicitly allocating memory for them.&lt;br /&gt;
&lt;br /&gt;
Once the GelsInfo structure has been allocated and initialized, GELs can be added to the system. Refer to the setupGelSys() and cleanupGelsys() functions in the &amp;quot;animtools.c&amp;quot; lisitng at the end of the article for examples of allocating, initializing and freeing a GelsInfo structure.&lt;br /&gt;
&lt;br /&gt;
== Collisions and GEL Structure Extensions ==&lt;br /&gt;
&lt;br /&gt;
This section covers two topics that are applicable to all GELs: how to extend GEL data structures for your own purposes and how to detect collisions between GELs and other graphics objects.&lt;br /&gt;
&lt;br /&gt;
==== Detecting GEL Collisions ====&lt;br /&gt;
&lt;br /&gt;
All GELs, including VSprites, can participate in the software collision detection features of the graphics library. Simple Sprites must use &#039;&#039;hardware&#039;&#039; collision detection. See the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; for information about hardware collision detection.&lt;br /&gt;
&lt;br /&gt;
Two kinds of collisions are handled by the system routines: GEL-to-boundary hits and GEL-to-GEL hits. You can set up as many as 16 different routines to handle different collision combinations; one routine to handle the boundary hits, and up to fifteen more to handle different inter-GEL hits.&lt;br /&gt;
&lt;br /&gt;
You supply the actual collision handling routines, and provide their addresses to the system so that it can call them as needed (when the hits are detected). These addresses are kept in a collision handler table pointed to by the CollHandler field of the GelsInfo list. Which routine is called depends on the 16-bit MeMask and HitMask members of the VSprite structures involved in the collision.&lt;br /&gt;
&lt;br /&gt;
When you call DoCollision(), the system goes through the GelsInfo list which, is constantly kept sorted by &#039;&#039;x,y&#039;&#039; position. If a GEL intersects the display boundaries and the GELs HitMask indicates it is appropriate, the boundary collision routine is called. When DoCollision() finds that two GELs overlap, it compares the MeMask of one with the HitMask of the other. If corresponding bits are set in both, it calls the appropriate inter-GEL collision routine at the table position corresponding to the bits in the HitMask and MeMask, as outlined below.&lt;br /&gt;
&lt;br /&gt;
==== Preparing for Collision Detection ====&lt;br /&gt;
&lt;br /&gt;
Before you can use the system to detect collisions between GELS, you must allocate and initialize a table of collision-detection routines and place the address of the table in the GelsInfo.CollHandler field. This table is an array of pointers to the actual routines that you have provided for your collision types. You must also prepare some members of the VSprite structure: CollMask, BorderLine, HitMask, and MeMask.&lt;br /&gt;
&lt;br /&gt;
==== Building a Table of Collision Routines ====&lt;br /&gt;
&lt;br /&gt;
The collision handler table is a structure, CollTable, defined in &amp;amp;lt;graphics/gels.h&amp;amp;gt;. It is accessed as the CollHandler member of the GelsInfo structure. The table only needs to be as large as the number of bits for which you wish to provide collision processing. It is safest, though, to allocate space for all 16 entries, considering the small amount of space required.&lt;br /&gt;
&lt;br /&gt;
Call the routine SetCollision() to initialize the table entries that correspond to the HitMask and MeMask bits that you plan to use. Do not set any of the table entries directly, instead give the address to SetCollision() routine and let it handle the set up of the GelsInfo.CollTable field.&lt;br /&gt;
&lt;br /&gt;
For example, SetCollision() could be called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG            num;&lt;br /&gt;
VOID           (*routine)();&lt;br /&gt;
struct GelsInfo *GInfo;&lt;br /&gt;
&lt;br /&gt;
VOID myCollisionRoutine(GELA, GELB)   /* sample collision routine */&lt;br /&gt;
struct VSprite *GELA;&lt;br /&gt;
struct VSprite *GELB;&lt;br /&gt;
{&lt;br /&gt;
    /* process GELs here - GELA and GELB point to the base VSprites of */&lt;br /&gt;
    /* the GELs, you can use the user extensions to identify what hit  */&lt;br /&gt;
    /* (if you need the info).                                         */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* GelsInfo must be allocated and initialized */&lt;br /&gt;
&lt;br /&gt;
routine = myCollisionRoutine;&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;SetCollision(num, routine, GInfo)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The num argument is the collision table vector number (0-15). The (*routine)() argument is a pointer to your collision routine. And the GInfo argument is a pointer to the GelsInfo structure.&lt;br /&gt;
&lt;br /&gt;
==== VSprite Collision Mask ====&lt;br /&gt;
&lt;br /&gt;
The CollMask member of the VSprite is a pointer to a memory area allocated for holding the collision mask of that GEL. This area must be in Chip memory and its size is the equivalent of one bitplane of the GEL&#039;s image. The collision mask is &#039;&#039;usually&#039;&#039; the same as the shadow mask of the GEL, formed from a logical-&#039;&#039;&#039;OR&#039;&#039;&#039; combination of all planes of the image. The following figure shows an example collision mask.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-3.png|frame|center|A Collision Mask]]&lt;br /&gt;
&lt;br /&gt;
Alternatively, you may have a collision mask that is not derived from the image. In this case, the actual image isn&#039;t relevant. The system will not register collisions unless the other objects touch the collision mask. If the collision mask is smaller than the image, other objects will pass through the edges without a collision.&lt;br /&gt;
&lt;br /&gt;
==== VSprite BorderLine ====&lt;br /&gt;
&lt;br /&gt;
For faster collision detection, the system uses the BorderLine member of the VSprite structure. BorderLine specifies the location of the horizontal logical-&#039;&#039;&#039;OR&#039;&#039;&#039; combination of all of the bits of the object. It may be compared to taking the whole object&#039;s shadow/collision mask and squishing it down into a single horizontal line. You provide the system with a place to store this line. The size of the data area you allocate must be at least as large as the image width.&lt;br /&gt;
&lt;br /&gt;
In other words, if it takes three 16-bit words to hold one line of a GEL, then you must reserve three words for the BorderLine. In the VSprite examples, the routine makeVSprite() correctly allocates and initializes the collision mask and borderline. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
WORD myBorderLineData[3];    /* reserve space for BorderLine for this Bob */&lt;br /&gt;
&lt;br /&gt;
myVSprite.BorderLine = myBorderLineData;   /* tell the system where it is */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is a sample of an object and its BorderLine image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
011000001100    Object&lt;br /&gt;
001100011000&lt;br /&gt;
001100011000&lt;br /&gt;
000110110000&lt;br /&gt;
000010100000&lt;br /&gt;
&lt;br /&gt;
011110111100    BorderLine image&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using this squished image, the system can quickly determine if the image is touching the left or rightmost boundary of the drawing area.&lt;br /&gt;
&lt;br /&gt;
To establish default BorderLine and CollMask data, call the InitMasks() function.&lt;br /&gt;
&lt;br /&gt;
==== VSprite HitMask and MeMask ====&lt;br /&gt;
&lt;br /&gt;
Software collision detection is independently enabled and disabled for each GEL. Further, you can specify which of 16 possible collision routines you wish to have automatically executed. DoCollision(), in addition to sensing an overlap between objects, uses these masks to determine which routine (if any) the system will call when a collision occurs.&lt;br /&gt;
&lt;br /&gt;
When the system determines a collision, it performs a logical-&#039;&#039;&#039;AND&#039;&#039;&#039; of the HitMask of the upper-leftmost object in the colliding pair with the MeMask of the lower-rightmost object of the pair. The bits that are 1s after the logical-&#039;&#039;&#039;AND&#039;&#039;&#039; operation choose which one of the 16 possible collision routines to perform.&lt;br /&gt;
&lt;br /&gt;
* If the collision is with the boundary, bit 0 is always a 1 and the system calls the collision handling routine number 0. Always assign the routine that handles boundary collisions to vector 0 in the collision handling table.&lt;br /&gt;
&lt;br /&gt;
* The system uses the flag called BORDERHIT to indicate that an object has landed on or moved beyond the outermost bounds of the drawing area (the edge of the clipping region). The VSprite example earlier in this article uses collision detection to check for border hits.&lt;br /&gt;
&lt;br /&gt;
* If any one of the other bits (1 to 15) is set, then the system calls your collision handling routine corresponding to the bit set.&lt;br /&gt;
&lt;br /&gt;
* If more than one bit is set in both masks, the system calls the vector corresponding to the rightmost (the least significant) bit &#039;&#039;only&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Using HitMask and MeMask ====&lt;br /&gt;
&lt;br /&gt;
This section illustrates the use of the HitMask and MeMask to define one type of collision.&lt;br /&gt;
&lt;br /&gt;
Suppose there are two classes of objects that you wish to control on the display: ENEMYTANK and MYMISSILE. Objects of class ENEMYTANK should be able to pass across one another without registering any collisions. Objects of class MYMISSILE should also be able to pass across one another without collisions. However, when MYMISSILE and ENEMYTANK collide, the system should generate a call to a collision routine.&lt;br /&gt;
&lt;br /&gt;
Choose a pair of collision detect bits not yet assigned within MeMask, one to represent ENEMYTANK, the other to represent MYMISSILE. You will use the same two bits in the corresponding HitMask. In this example, bit 1 represents ENEMYTANK objects and bit 2 represents MYMISSLE objects.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot;|MeMask&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot;|HitMask&lt;br /&gt;
|-&lt;br /&gt;
! Bit Number !! 2 !! 1 !! 2 !! 1&lt;br /&gt;
|-&lt;br /&gt;
| ENEMYTANK 1 || align=&amp;quot;center&amp;quot;|0 || align=&amp;quot;center&amp;quot;|1 || align=&amp;quot;center&amp;quot;|1 || align=&amp;quot;center&amp;quot;|0&lt;br /&gt;
|-&lt;br /&gt;
| ENEMYTANK 2 || align=&amp;quot;center&amp;quot;|0 || align=&amp;quot;center&amp;quot;|1 || align=&amp;quot;center&amp;quot;|1 || align=&amp;quot;center&amp;quot;|0&lt;br /&gt;
|-&lt;br /&gt;
| MYMISSLE || align=&amp;quot;center&amp;quot;|1 || align=&amp;quot;center&amp;quot;|0 || align=&amp;quot;center&amp;quot;|0 || align=&amp;quot;center&amp;quot;|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In the MeMask, bit 1 is set to indicate that this object is an ENEMYTANK. Bit 2 is clear (zero) indicating this object is &#039;&#039;not&#039;&#039; a MYMISSILE object. In the HitMask for ENEMYTANK objects, bit 1 is clear (zero) which means, &amp;quot;I will not register collisions with other ENEMYTANK objects.&amp;quot; However, bit 2 is set (one) which means, &amp;quot;I &#039;&#039;will&#039;&#039; register collisions with MYMISSILE objects.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Thus when a call to DoCollision() occurs, for any objects that appear to be colliding, the system &#039;&#039;&#039;AND&#039;&#039;&#039;s the MeMask of one object with the HitMask of the other object. If there are non-zero bits present, the system will call one of your collision routines.&lt;br /&gt;
&lt;br /&gt;
In this example, suppose that the system senses a collision between ENEMYTANK 1 and ENEMYTANK 2. Suppose also that ENEMYTANK 1 is the top/leftmost object of the pair. Here is the way that the collision testing routine performs the test to see if the system will call any collision-handling routines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit Number !! 2 !! 1&lt;br /&gt;
|-&lt;br /&gt;
| ENEMYTANK 1 MeMask || 0 || 1&lt;br /&gt;
|-&lt;br /&gt;
| ENEMYTANK 2 HitMask || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
| Result of logical-AND || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Therefore, the system does not call a collision routine. But suppose that DoCollision() finds an overlap between ENEMYTANK 1 and MYMISSILE, where MYMISSILE is the top/leftmost of the pair:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit Number !! 2 !! 1&lt;br /&gt;
|-&lt;br /&gt;
| MYMISSLE MeMask || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
| ENEMYTANK 2 HitMask || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
| Result of logical-AND || 1 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Therefore, the system calls the collision routine at position 2 in the table of collision-handling routines.&lt;br /&gt;
&lt;br /&gt;
=== Setting up for Boundary Collisions ===&lt;br /&gt;
&lt;br /&gt;
To specify the region in the playfield that the system will use to define the outermost limits of the GEL boundaries, you use these GelsInfo members: topmost, bottommost, leftmost, and rightmost.&lt;br /&gt;
&lt;br /&gt;
The DoCollision() routine tests these boundaries when determining boundary collisions within this RastPort. They have nothing whatsoever to do with graphical clipping. Graphical clipping makes use of the RastPort&#039;s clipping rectangle.&lt;br /&gt;
&lt;br /&gt;
Here is a typical program segment that assigns the members correctly (for boundaries 50, 100, 80, 240). It assumes that you already have a RastPort structure pointer named myRastPort.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myRastPort-&amp;gt;GelsInfo-&amp;gt;topmost    = 50;&lt;br /&gt;
myRastPort-&amp;gt;GelsInfo-&amp;gt;bottommost = 100;&lt;br /&gt;
myRastPort-&amp;gt;GelsInfo-&amp;gt;leftmost   = 80;&lt;br /&gt;
myRastPort-&amp;gt;GelsInfo-&amp;gt;rightmost  = 240;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Parameters To Your Boundary Collision Routine ====&lt;br /&gt;
&lt;br /&gt;
During the operation of the DoCollision() routine, if you have enabled boundary collisions for a GEL (by setting the least significant bit of its HitMask) and it has crossed a boundary, the system calls the boundary routine you have defined. The system will call the routine once for every GEL that has hit, or gone outside of the boundary. The system will call your routine with the following two arguments:&lt;br /&gt;
&lt;br /&gt;
* A pointer to the VSprite structure of the GEL that hit the boundary&lt;br /&gt;
* A flag word containing one to four bits set, representing top, bottom, left and right boundaries, telling you which of the boundaries it has hit or exceeded.&lt;br /&gt;
* To test these bits, compare to the constants TOPHIT, BOTTOMHIT, LEFTHIT, and RIGHTHIT.&lt;br /&gt;
&lt;br /&gt;
See the VSprite example given earlier for an example of using boundary collision.&lt;br /&gt;
&lt;br /&gt;
==== Parameters To Your Inter-GEL Collision Routines ====&lt;br /&gt;
&lt;br /&gt;
If, instead of a GEL-to-boundary collision, DoCollision() senses a GEL-to-GEL collision, the system calls your collision routine with the following two arguments:&lt;br /&gt;
&lt;br /&gt;
* Address of the VSprite that is the uppermost (or leftmost if &#039;&#039;y&#039;&#039; coordinates are identical) GEL of a colliding pair.&lt;br /&gt;
* Address of the VSprite that is the lowermost (or rightmost if &#039;&#039;y&#039;&#039; coordinates are identical) GEL of the pair.&lt;br /&gt;
&lt;br /&gt;
==== Handling Multiple Collisions ====&lt;br /&gt;
&lt;br /&gt;
When multiple elements collide within the same display field, the following set of sequential calls to the collision routines occurs:&lt;br /&gt;
&lt;br /&gt;
* The system issues each call in a sorted order for GELs starting at the upper left-hand corner of the screen and proceeding to the right and down the screen.&lt;br /&gt;
* For any colliding GEL pair, the system issues only one call, to the collision routine for the object that is the topmost and leftmost of the pair.&lt;br /&gt;
&lt;br /&gt;
=== Adding User Extensions to GEL Data Structures ===&lt;br /&gt;
&lt;br /&gt;
This section describes how to expand the size and scope of the VSprite, Bob and AnimOb data structures. In the definition for these structures, there is an item called UserExt at the end of each. If you want to expand these structures (to hold your own special data), you define the UserExt member before the &amp;amp;lt;graphics/gels.h&amp;amp;gt; file is included. If this member has already been defined when the &amp;amp;lt;graphics/gels.h&amp;amp;gt; file is parsed, the compiler preprocessor will extend the structure definition automatically. If these members have not been defined, the system will make them SHORTs, and you may still consider these as being reserved for your private use.&lt;br /&gt;
&lt;br /&gt;
To show the kind of use you can make of this feature, the example below adds speed and acceleration figures to each GEL by extending the VSprite structure. When your collision routine is called, it could use these values to transfer energy between the two colliding objects (say, billiard balls). You would have to set up additional routines, executed between calls to DoCollision(), that would add the values to the GELs position appropriately. You could do this with code similar o the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct myInfo&lt;br /&gt;
    {&lt;br /&gt;
    short xvelocity;&lt;br /&gt;
    short yvelocity;&lt;br /&gt;
    short xaccel;&lt;br /&gt;
    short yaccel;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These members are for example only. You may use any definition for your user extensions. You would then also provide the following line, to extend the VSprites structure, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Redefine VUserStuff for my own use. */&lt;br /&gt;
#define VUserStuff struct myInfo&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To extend the Bobs structure, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define BUserStuff struct myInfo&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To extend the AnimObs structure, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define AUserStuff struct myInfo&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the system is compiling the &amp;amp;lt;graphics/gels.h&amp;amp;gt; file with your program, the compiler preprocessor substitutes &amp;quot;struct myInfo&amp;quot; everywhere that UserExt is used in the header. The structure is thereby customized to include the items you wish to associate with it.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Typedef Cannot Be Used|text=You cannot use the C-language construct typedef for the above statements. If you want to substitute your own data type for one of the UserStuff variables, you must use a #define.}}&lt;br /&gt;
&lt;br /&gt;
== Animation with GELs ==&lt;br /&gt;
&lt;br /&gt;
An animation sequence is composed of a series of drawings. Each drawing differs from the preceding one so that when they are arranged in a stack and viewed sequentially, the images appear to flow naturally.&lt;br /&gt;
&lt;br /&gt;
In classic film animation, image drawing is done in two stages. The background for each scene is painted just once. Then, the cartoon characters and any other foreground objects are painted on transparent sheets of celluloid called &#039;&#039;cells&#039;&#039; which are placed over the background. With cells, animation can be achieved by redrawing only the parts of the scene that move while the background stays the same. Animation on the Amiga works similarly. The background is formed by the playfield while the objects that move can be conveniently handled with the GELs system.&lt;br /&gt;
&lt;br /&gt;
=== Animation Data Structures ===&lt;br /&gt;
&lt;br /&gt;
There are two main data structures involved in Amiga animation: AnimComp and AnimOb.&lt;br /&gt;
&lt;br /&gt;
The AnimComp (Animation Component), is an extension of the Bob structure discussed in the previous section. An AnimComp provides a convenient way to link together a series of images so that they can be sequenced automatically, and so multiple sequences can be grouped together. An AnimComp is analogous to one sheet of celluloid representing a single image to be placed over the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct AnimComp&lt;br /&gt;
{&lt;br /&gt;
    WORD Flags;                 /* AnimComp flags for system &amp;amp;amp; user */&lt;br /&gt;
    WORD Timer;&lt;br /&gt;
    WORD TimeSet;&lt;br /&gt;
    struct AnimComp  *NextComp;&lt;br /&gt;
    struct AnimComp  *PrevComp;&lt;br /&gt;
    struct AnimComp  *NextSeq;&lt;br /&gt;
    struct AnimComp  *PrevSeq;&lt;br /&gt;
    WORD (*AnimCRoutine)();&lt;br /&gt;
    WORD YTrans;&lt;br /&gt;
    WORD XTrans;&lt;br /&gt;
    struct AnimOb    *HeadOb;   /* Pointer back to the controlling AnimOb     */&lt;br /&gt;
    struct Bob       *AnimBob;  /* Underlying Bob structure for this AnimComp */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The AnimComp structure contains pointers, PrevSeq and NextSeq, that lets you group these cells into stacks that will be viewed sequentially. The AnimComp structure also has PrevComp and NextComp pointers that let you group stacks into complex objects containing multiple independently moving parts.&lt;br /&gt;
&lt;br /&gt;
The second animation data structure is the AnimOb which provides the variables needed for overall control over a group of AnimComps. The AnimOb itself contains no imagery; it simply provides a common reference point for the sequenced images and specifies how the system should move that point.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct AnimOb&lt;br /&gt;
{&lt;br /&gt;
    struct AnimOb    *NextOb, *PrevOb;&lt;br /&gt;
    LONG Clock;&lt;br /&gt;
    WORD AnOldY, AnOldX;            /* old y,x coordinates          */&lt;br /&gt;
    WORD AnY, AnX;                  /* y,x coordinates of the AnimOb*/&lt;br /&gt;
    WORD YVel, XVel;                /* velocities of this object    */&lt;br /&gt;
    WORD YAccel, XAccel;            /* accelerations of this object */&lt;br /&gt;
    WORD RingYTrans, RingXTrans;    /* ring translation values      */&lt;br /&gt;
    WORD (*AnimORoutine)();         /* address of user procedure    */&lt;br /&gt;
    struct AnimComp  *HeadComp;     /* pointer to first component   */&lt;br /&gt;
    AUserStuff AUserExt;            /* AnimOb user extension        */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These structures can be used in various ways. A simple animation of a rotating ball could be created with three or four AnimComps linked together in a circular list by their NextSeq and PrevSeq fields. The system displays the initial AnimComp (the &amp;quot;top of the stack&amp;quot;), then switches to the AnimComp pointed to by NextSeq, and then switches to &#039;&#039;its&#039;&#039; NextSeq and so on until it reaches the end of the sequence. The sequence starts over again automatically if the last AnimComp.NextSeq points back to the first AnimComp in the stack.&lt;br /&gt;
&lt;br /&gt;
For a more complex animation of a walking human, you could use five stacks, i.e., five circular lists of AnimComps; four stacks for the arms and legs and a single stack for the head and torso. To group these stacks into one cohesive unit showing a human figure, you use the PrevComp and NextComp pointers in the AnimComp structure. All the stacks would also share a common AnimOb, so that the combined sequences can be moved as a single object.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-4.png|frame|center|Linking AnimComps For a Multiple Component AnimOb]]&lt;br /&gt;
&lt;br /&gt;
=== Animation Types ===&lt;br /&gt;
&lt;br /&gt;
The GELs system provides several ways of setting up automatic animation, loosely based on some categories of movement in real life. Some things (like balls or arrows) can move independently of the background, and look even more realistic if they tumble or rotate as they move; other things (like worms, wheels, and people) must be anchored to the background, or they will appear to &#039;&#039;slide&#039;&#039; unnaturally.&lt;br /&gt;
&lt;br /&gt;
The system software allows these types of animation through simple motion control, motion control with sequenced drawing, and sequenced drawing using Ring Motion Control.&lt;br /&gt;
&lt;br /&gt;
==== Simple Motion Control ====&lt;br /&gt;
&lt;br /&gt;
To produce motion of a simple object, such as a ball, the object is simply moved relative to a background display, a little at a time. This is simple motion control, and can be accomplished with one AnimComp and one AnimOb, by simply changing the AnimOb&#039;s position every N video frames. The apparent speed of the object is a combination of how often it is moved (every frame, every other frame, etc.) and how far it is moved (how much the AnimOb&#039;s AnX and AnY are changed).&lt;br /&gt;
&lt;br /&gt;
==== Sequenced Drawing ====&lt;br /&gt;
&lt;br /&gt;
To make the ball appear to rotate is a little more complex. To produce apparent movement within the image, sequencing is used. This is done by having a stack of AnimComp&#039;s that are laid down one after the other, a frame at a time. The stack can be arranged in a circular list for repeating movement. So, when you combine a sequence of drawings using AnimComps with simple motion control using an AnimOb, you can perform more complex animations such as having a rotating ball bounce around.&lt;br /&gt;
&lt;br /&gt;
==== Ring Motion Control ====&lt;br /&gt;
&lt;br /&gt;
Making a worm appear to crawl is similar to the rotating ball. There is still a stack of AnimComps that are sequenced automatically, and one controlling AnimOb. But each AnimCop image is drawn so that it appears to move relative to an internal point that remains stationary throughout the stack. So instead of the Anim&#039;s common reference point moving in each frame, you tell the system how far to move only &#039;&#039;at the end&#039;&#039; of each AnimComp sequence.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-5.png|frame|center|Ring Motion Control]]&lt;br /&gt;
&lt;br /&gt;
As illustrated in the figure, the sequence of events for Ring Motion Control look like this:&lt;br /&gt;
&lt;br /&gt;
Draw AnimComp1, Draw AnimComp2, Draw AnimComp3, Move AnimOb,&amp;lt;br /&amp;gt;&lt;br /&gt;
Draw AnimComp1, Draw AnimComp2, Draw AnimComp3, Move AnimOb,&amp;lt;br /&amp;gt;&lt;br /&gt;
Draw AnimComp1 ...&lt;br /&gt;
&lt;br /&gt;
=== Specifying Animation Components ===&lt;br /&gt;
&lt;br /&gt;
For each AnimComp, you initially specify:&lt;br /&gt;
&lt;br /&gt;
* A pointer to the AnimComp&#039;s controlling AnimOb.&lt;br /&gt;
* Initial and alternate views, their timing and order.&lt;br /&gt;
* The initial inter-component drawing priorities (for multiple AnimComp sequences, this specifies which sequence to display frontmost).&lt;br /&gt;
* A pointer to a special animation routine related to this component (optional).&lt;br /&gt;
* Your own extensions to this structure (optional).&lt;br /&gt;
&lt;br /&gt;
==== Sequencing AnimComps ====&lt;br /&gt;
&lt;br /&gt;
To specify the sequencing of AnimComp images, the pointers called PrevSeq and NextSeq are used to build a doubly-linked list. The sequence can be made circular (and usually is) by linking the first and last AnimComps in the sequence: the NextSeq of the last AnimComp must point back to the first AnimComp, and the PrevSeq of the first AnimComp must point to the last AnimComp. If the list is a loop, then the system will continue to cycle through the list until it is stopped. If the list is not a loop, then the program must act to restart the sequence after the last item is displayed. The AnimCRoutine field of the last AnimComp can be used to do this.&lt;br /&gt;
&lt;br /&gt;
==== Position of an AnimComp ====&lt;br /&gt;
&lt;br /&gt;
To specify the placement of each AnimComp relative to its controlling AnimOb, you set the AnimComp members XTrans and YTrans. These values can be positive or negative.&lt;br /&gt;
&lt;br /&gt;
The system is designed so that only one of the AnimComps in any given sequence is &amp;quot;active&amp;quot; (being displayed) at a given point in time. It is the only image in the sequence that is (or is about to be) linked into the GelsInfo list. The Timer determines how long each Component in the sequence remains active, as described below.&lt;br /&gt;
&lt;br /&gt;
==== Specifying Time for Each Image ====&lt;br /&gt;
&lt;br /&gt;
The AnimComp members Timer and TimeSet are used to specify how long the system should keep each sequential image on the screen.&lt;br /&gt;
&lt;br /&gt;
When the system makes an animation component active, it copies the value you have put in the TimeSet member into the Timer member. As the animation proceeds, the system decrements Timer; as long as it is greater than zero, then that AnimComp remains active. When the Timer value reaches zero, the system makes the next AnimComp in the sequence active, and the process repeats.&lt;br /&gt;
&lt;br /&gt;
If you initialize the value in TimeSet to zero, the system will &#039;&#039;not sequence this component at all&#039;&#039; (and Timer will remain zero).&lt;br /&gt;
&lt;br /&gt;
==== Linking Multiple AnimComp Sequences ====&lt;br /&gt;
&lt;br /&gt;
When an AnimOb is built from multiple AnimComp sequences, the sequences are linked together by the the PrevComp and NextComp fields of the AnimComps. These pointers must be initialized &#039;&#039;only in the initial&#039;&#039; AnimComp &#039;&#039;of each sequence&#039;&#039;. The other components that are not initially active should have their PrevComp and NextComp pointers set to NULL.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do Not Use Empty Fields|text=You cannot store data in the empty PrevComp and NextComp fields. As the system cycles through the AnimComps, the NextComp and PrevComp fields are set to NULL when an old AnimComps is replaced by a new AnimComp. The new AnimComp is then linked in to the list of sequences &#039;&#039;in place of the old one&#039;&#039;.}}&lt;br /&gt;
&lt;br /&gt;
==== Component Ordering ====&lt;br /&gt;
&lt;br /&gt;
The PrevSeq, NextSeq, PrevComp and NextComp linkages have no bearing on the order in which AnimComps in any given video frame are drawn. To specify the inter-component priorities (so that the closest objects appear frontmost) the Before and After pointers in the initially active AnimComp&#039;s underlying Bob structure are linked in to the rest of the system, as described previously in the discussion of Bobs.&lt;br /&gt;
&lt;br /&gt;
This setup needs to be done once, &#039;&#039;for the initially active AnimComps of the AnimOb only&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The animation system adjusts the Before and After pointers of all the underlying Bob structures to constantly maintain the inter-component drawing sequence, even though different components are being made active as sequencing occurs.&lt;br /&gt;
&lt;br /&gt;
These pointers also assure that one complete &#039;&#039;object&#039;&#039; always has priority over another object. The Bob Before and After pointers are used to link together the last AnimComp&#039;s Bob of one AnimOb to the first AnimComp&#039;s Bob of the next AnimOb.&lt;br /&gt;
&lt;br /&gt;
=== Specifying the Animation Object ===&lt;br /&gt;
&lt;br /&gt;
For each AnimOb, you initially specify:&lt;br /&gt;
&lt;br /&gt;
* The starting position of this object&lt;br /&gt;
* Its velocity and acceleration (optional).&lt;br /&gt;
* A pointer to the first of its AnimComps.&lt;br /&gt;
* A pointer to a special animation routine related to this object (optional).&lt;br /&gt;
* Your own extensions to this structure (optional).&lt;br /&gt;
&lt;br /&gt;
==== Linking the AnimComp Sequences to the AnimOb ====&lt;br /&gt;
&lt;br /&gt;
Within each AnimOb there may be one or more AnimComp sequences. The HeadComp of the AnimOb points to the first AnimComp in the list of sequences.&lt;br /&gt;
&lt;br /&gt;
Each sequence is identified by its &amp;quot;active&amp;quot; AnimComp. There can only be one active AnimComp in each sequence. The sequences are linked together by their active AnimComps; for each of these the NextComp and PrevComp fields link the sequences together to create a list. The first sequence in the list (HeadComp of the AnimOb), has its PrevComp set to NULL. The last sequence in the list has its NextComp set to NULL. None of the inactive AnimComps should have NextComp or PrevComp fields set.&lt;br /&gt;
&lt;br /&gt;
To find the active AnimComp at run time, you can look in the AnimOb&#039;s HeadComp field. To find the active AnimComp from any another AnimComp, use the HeadOb field to find the controlling AnimOb first and then look in its HeadComp field to find the active AnimComp.&lt;br /&gt;
&lt;br /&gt;
The figure below shows all the linkages in data structures needed to create the animation GELs.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-6.png|frame|center|Linking of an AnimOb]]&lt;br /&gt;
&lt;br /&gt;
==== Position of an AnimOb ====&lt;br /&gt;
&lt;br /&gt;
To position the object and its component parts, use the AnimOb structure members AnX and AnY. The following figure illustrates how each component has its own offset from the AnimOb&#039;s common reference point.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig28-7.png|frame|center|Specifying an AnimOb Position]]&lt;br /&gt;
&lt;br /&gt;
When you change the animation object&#039;s AnX and AnY, all of the component parts will be redrawn relative to it the next time DrawGList() is called.&lt;br /&gt;
&lt;br /&gt;
==== Setting Up Simple Motion Control ====&lt;br /&gt;
&lt;br /&gt;
In this form of animation, you can specify objects that have independently controllable velocities and accelerations in the &#039;&#039;X&#039;&#039; and &#039;&#039;Y&#039;&#039; directions. Components can still sequence.&lt;br /&gt;
&lt;br /&gt;
The variables that control this motion are located in the AnimOb structure and are called:&lt;br /&gt;
&lt;br /&gt;
; YVel, XVel&lt;br /&gt;
: the velocities in the &#039;&#039;y&#039;&#039; and &#039;&#039;x&#039;&#039; directions. These values are added to the position values on each call to Animate() (see below).&lt;br /&gt;
&lt;br /&gt;
; YAccel, XAccel&lt;br /&gt;
: the accelerations in the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; directions. These values are added to the velocity values on each call to Animate() (see below). The velocity values are updated &#039;&#039;before&#039;&#039; the position values.&lt;br /&gt;
&lt;br /&gt;
==== Setting Up Ring Motion Control ====&lt;br /&gt;
&lt;br /&gt;
To make a given component &#039;&#039;trigger&#039;&#039; a move of the AnimOb you set the RINGTRIGGER bit of that AnimComp&#039;s Flags field.&lt;br /&gt;
&lt;br /&gt;
When the system software encounters this flag, it adds the values of RingXTrans and RingYTrans to the AnX and AnY values of the controlling AnimOb. The &#039;&#039;next&#039;&#039; time you execute DrawGList(), the drawing sequence will use the new position.&lt;br /&gt;
&lt;br /&gt;
You usually set RINGTRIGGER in only one of the animation components in a sequence (the last one); however, you can use this flag and the translation variables any way you wish.&lt;br /&gt;
&lt;br /&gt;
==== Using Sequenced Drawing and Motion Control ====&lt;br /&gt;
&lt;br /&gt;
If you are using Ring Motion Control, you will probably set the velocity and acceleration variables to zero. For instance, consider the example of a person walking. With Ring Motion Control, as each foot falls it is positioned on the ground exactly where originally drawn. If you included a velocity value, the person&#039;s foot would not be stationary with respect to the ground, and the person would appear to &amp;quot;skate&amp;quot; rather than walk. If you set the velocity and acceleration variables at zero, you avoid this problem.&lt;br /&gt;
&lt;br /&gt;
When the system activates a new AnimComp, it checks the Flags field to see if the RINGTRIGGER bit is set. If so, the system adds RingYTrans and RingXTrans to AnY and AnX respectively.&lt;br /&gt;
&lt;br /&gt;
=== The AnimKey ===&lt;br /&gt;
&lt;br /&gt;
The system uses one pointer, known as the AnimKey, to keep track of all the AnimObs via their PrevOb and NextOb linkage fields. The AnimKey acts as the anchor for the list of AnimObs you are using and is initialized with code such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct AnimOb *animKey;&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;InitAnimate(&amp;amp;animKey);  /* Only do this once to initialize the AnimOb list */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As each new object is added (via AddAnimOb()), it is linked in at the beginning of the list, so AnimKey will always point to the object most recently added to the list. To search forward through the list, start with the AnimKey and move forward on the NextOb link. Continue to move forward until the NextOb is NULL, indicating the end of the list. The PrevOb link will allow you to move back to a previous object.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Set Up PrevOb and NextOb Correctly|text=It is important that the NextOb link of the last object is NULL, and that the PrevOb of the first object is NULL. In fact, the system expects the animation object lists to be &#039;&#039;exactly&#039;&#039; the way that they are described above. If they are not, the system will produce unexpected results.}}&lt;br /&gt;
&lt;br /&gt;
=== Adding Animation Objects ===&lt;br /&gt;
&lt;br /&gt;
Use the routine AddAnimOb() to add animation objects to the controlled object list. This routine will link the PrevOb and NextOb pointers to chain all the AnimObs that the system is controlling.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RastPort myRPort;&lt;br /&gt;
struct AnimOb myAnimOb;&lt;br /&gt;
struct AnimOb *animKey;  /* Must be initialized with InitAnimate() */&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;AddAnimOb(&amp;amp;myAnimOb, &amp;amp;animKey, &amp;amp;myRPort);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Moving the Objects ===&lt;br /&gt;
&lt;br /&gt;
When you have defined all of the structures and have established all of the links, you can call the Animate() routine to move the objects. Animate() adjusts the positions of the objects as described above, and calls the various subroutines (AnimCRoutines and AnimORoutines) that you have specified.&lt;br /&gt;
&lt;br /&gt;
After the system has completed the Animate() routine, some GELs may have been moved, so the GelsInfo list order may possibly be incorrect. Therefore, the list must be re-sorted with SortGList() before passing it to a system routine.&lt;br /&gt;
&lt;br /&gt;
If you are using collision detection, you then perform DoCollision(). Your collision routines may also have an effect on the relative position of the GELs. Therefore, you should again call SortGList() to assure that the system correctly orders the objects before you call DrawGList().&lt;br /&gt;
&lt;br /&gt;
When you call DrawGList(), the system renders all the GELs it finds in the GelsInfo list and any changes caused by the previous call to Animate() can then be seen.&lt;br /&gt;
&lt;br /&gt;
This is illustrated in the following typical call sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct AnimOb **myAnimKey;&lt;br /&gt;
struct RastPort *rp;&lt;br /&gt;
struct ViewPort *vp;&lt;br /&gt;
&lt;br /&gt;
/* ... setup of graphics elements and objects */&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;Animate(myAnimKey, rp);       /* &amp;amp;quot;move&amp;amp;quot; objects per instructions */&lt;br /&gt;
IGraphics-&amp;gt;SortGList(rp);                /*  put them in order */&lt;br /&gt;
IGraphics-&amp;gt;DoCollision(rp);              /*  software collision detect/action */&lt;br /&gt;
IGraphics-&amp;gt;SortGList(rp);                /*  put them back into right order */&lt;br /&gt;
IGraphics-&amp;gt;DrawGList(vp, rp);            /*  draw into current RastPort */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Your own Animation Routine Calls ====&lt;br /&gt;
&lt;br /&gt;
The AnimOb and AnimComp structures can include pointers for your own routines that you want the system to call. These pointers are stored in the AnimOb&#039;s AnimORoutine field and in the AnimComp&#039;s AnimCRoutine field, respectively.&lt;br /&gt;
&lt;br /&gt;
When Animate() is called, the system performs the following steps for every AnimOb in the AnimKey list:&lt;br /&gt;
&lt;br /&gt;
* Updates the AnimOb&#039;s location and velocities.&lt;br /&gt;
* Calls the AnimOb.AnimORoutine routine if one is supplied.&lt;br /&gt;
* Then for each AnimComp of the AnimOb:&lt;br /&gt;
** If this sequence times out, switches to the new AnimComp.&lt;br /&gt;
** Calls the AnimComp.AnimCRoutine if one is supplied.&lt;br /&gt;
** Sets the underlying VSprite&#039;s &#039;&#039;x,y&#039;&#039; coordinates.&lt;br /&gt;
&lt;br /&gt;
If you want a routine to be called, you put the address of the routine in either AnimComp.AnimCRoutine or AnimOb.AnimORoutine member as needed. If no routine is to be called, you must set these fields to NULL. Your routines will be passed one parameter, a pointer to the AnimOb or AnimComp it was related to. You can use the user structure extensions discussed earlier to hold the variables you need for your own routines.&lt;br /&gt;
&lt;br /&gt;
For example, if you provide a routine such as this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID MyOCode(struct AnimOb *anOb)&lt;br /&gt;
{&lt;br /&gt;
/* whatever needs to be done */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, if you put the address of the routine in an AnimOb structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myAnimOb.AnimORoutine = MyOCode;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
MyOCode() will be called with the address of this AnimOb when Animate() processes this AnimOb.&lt;br /&gt;
&lt;br /&gt;
=== Standard GEL Rules Still Apply ===&lt;br /&gt;
&lt;br /&gt;
Before you use the animation system, you must have called the routine InitGels(). The section called &amp;quot;Bob Priorities&amp;quot; describes how the system maintains the list of GELs to draw on the screen according to their various data fields. The animation system selectively adds GELs to and removes GELs from this list of screen objects during the Animate() routine. On the next call to DrawGList(), the system will draw the GELs in the list into the selected RastPort.&lt;br /&gt;
&lt;br /&gt;
=== Animations Special Numbering System ===&lt;br /&gt;
&lt;br /&gt;
Velocities and accelerations can be either positive or negative. The system treats the velocity, acceleration and Ring values as fixed-point binary fractions, with the decimal point at position 6 in the word. That is: vvvvvvvvvv.ffffff where v stands for actual values that you add to the x or y (AnX, AnY) positions of the object for each call to Animate(), and f stands for the fractional part. By using a fractional part, you can specify the speed of an object in increments as precise as 1/64th of an interval.&lt;br /&gt;
&lt;br /&gt;
If you set the value of XVel at 0x0001, it will take 64 calls to the Animate() routine before the system will modify the object&#039;s &#039;&#039;x&#039;&#039; coordinate position by a step of one.&lt;br /&gt;
&lt;br /&gt;
The system constant ANFRACSIZE can be used to shift values correctly. So if you set the value to (1 &amp;lt;&amp;lt; ANFRACSIZE), it will be set to 0x0040, the value required to move the object one step per call to Animate().&lt;br /&gt;
&lt;br /&gt;
The system constant ANIMHALF can be used if you want the object to move every other call to Animate().&lt;br /&gt;
&lt;br /&gt;
Each call you make to Animate() simply adds the value of XAccel to the current value of XVel, and YAccel to the current value of YVel, modifying these values accordingly.&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;animtools.h&amp;quot; and &amp;quot;animtools.c&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Here is the listing of the &amp;quot;animtools.h&amp;quot; header file and the &amp;quot;animtools.c&amp;quot; link file used by the examples in this article. The makeSeq() and makeComp() subroutines here demonstrate how to use the GELs animation system.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* animtools.h */&lt;br /&gt;
#ifndef GELTOOLS_H&lt;br /&gt;
#define GELTOOLS_H&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** These data structures are used by the functions in animtools.c to&lt;br /&gt;
** allow for an easier interface to the animation system.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/* Data structure to hold information for a new VSprite.                */&lt;br /&gt;
typedef struct newVSprite {&lt;br /&gt;
        WORD           *nvs_Image;      /* image data for the vsprite   */&lt;br /&gt;
        WORD           *nvs_ColorSet;   /* color array for the vsprite  */&lt;br /&gt;
        SHORT           nvs_WordWidth;  /* width in words               */&lt;br /&gt;
        SHORT           nvs_LineHeight; /* height in lines              */&lt;br /&gt;
        SHORT           nvs_ImageDepth; /* depth of the image           */&lt;br /&gt;
        SHORT           nvs_X;          /* initial x position           */&lt;br /&gt;
        SHORT           nvs_Y;          /* initial y position           */&lt;br /&gt;
        SHORT           nvs_Flags;      /* vsprite flags                */&lt;br /&gt;
        USHORT          nvs_HitMask;    /* Hit mask.                    */&lt;br /&gt;
        USHORT          nvs_MeMask;     /* Me mask.                     */&lt;br /&gt;
        } NEWVSPRITE;&lt;br /&gt;
&lt;br /&gt;
/* Data structure to hold information for a new Bob.                */&lt;br /&gt;
typedef struct newBob {&lt;br /&gt;
        WORD       *nb_Image;       /* image data for the bob       */&lt;br /&gt;
        SHORT       nb_WordWidth;   /* width in words               */&lt;br /&gt;
        SHORT       nb_LineHeight;  /* height in lines              */&lt;br /&gt;
        SHORT       nb_ImageDepth;  /* depth of the image           */&lt;br /&gt;
        SHORT       nb_PlanePick;   /* planes that get image data   */&lt;br /&gt;
        SHORT       nb_PlaneOnOff;  /* unused planes to turn on     */&lt;br /&gt;
        SHORT       nb_BFlags;      /* bob flags                    */&lt;br /&gt;
        SHORT       nb_DBuf;        /* 1=double buf, 0=not          */&lt;br /&gt;
        SHORT       nb_RasDepth;    /* depth of the raster          */&lt;br /&gt;
        SHORT       nb_X;           /* initial x position           */&lt;br /&gt;
        SHORT       nb_Y;           /* initial y position           */&lt;br /&gt;
        USHORT      nb_HitMask;     /* Hit mask.                    */&lt;br /&gt;
        USHORT      nb_MeMask;      /* Me mask.                     */&lt;br /&gt;
        } NEWBOB ;&lt;br /&gt;
&lt;br /&gt;
/* Data structure to hold information for a new animation component.       */&lt;br /&gt;
typedef struct newAnimComp {&lt;br /&gt;
        WORD  (*nac_Routine)(); /* routine called when Comp is displayed.   */&lt;br /&gt;
        SHORT   nac_Xt;         /* initial delta offset position.           */&lt;br /&gt;
        SHORT   nac_Yt;         /* initial delta offset position.           */&lt;br /&gt;
        SHORT   nac_Time;       /* Initial Timer value.                     */&lt;br /&gt;
        SHORT   nac_CFlags;     /* Flags for the Component.                 */&lt;br /&gt;
        } NEWANIMCOMP;&lt;br /&gt;
&lt;br /&gt;
/* Data structure to hold information for a new animation sequence.         */&lt;br /&gt;
typedef struct newAnimSeq {&lt;br /&gt;
        struct AnimOb  *nas_HeadOb; /* common Head of Object.               */&lt;br /&gt;
        WORD   *nas_Images;         /* array of Comp image data             */&lt;br /&gt;
        SHORT  *nas_Xt;             /* arrays of initial offsets.           */&lt;br /&gt;
        SHORT  *nas_Yt;             /* arrays of initial offsets.           */&lt;br /&gt;
        SHORT  *nas_Times;          /* array of Initial Timer value.        */&lt;br /&gt;
        WORD (**nas_Routines)();    /* Array of fns called when comp drawn  */&lt;br /&gt;
        SHORT   nas_CFlags;         /* Flags for the Component.             */&lt;br /&gt;
        SHORT   nas_Count;          /* Num Comps in seq (= arrays size)     */&lt;br /&gt;
        SHORT   nas_SingleImage;    /* one (or count) images.               */&lt;br /&gt;
        } NEWANIMSEQ;&lt;br /&gt;
&lt;br /&gt;
#define INTUITIONNAME &amp;amp;quot;intuition.library&amp;amp;quot; /* intuitionbase.h does not define a library name. */&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;quot;animtools_proto.h&amp;amp;quot;              /* Include Prototyping. */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* animtools_proto.h */&lt;br /&gt;
#include        &amp;amp;lt;clib/dos_protos.h&amp;amp;gt;&lt;br /&gt;
#include        &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include        &amp;amp;lt;clib/graphics_protos.h&amp;amp;gt;&lt;br /&gt;
#include        &amp;amp;lt;clib/intuition_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct GelsInfo *setupGelSys(struct RastPort *rPort, BYTE reserved);&lt;br /&gt;
VOID            cleanupGelSys(struct GelsInfo *gInfo, struct RastPort *rPort);&lt;br /&gt;
struct VSprite  *makeVSprite(NEWVSPRITE *nVSprite);&lt;br /&gt;
struct Bob      *makeBob(NEWBOB *nBob);&lt;br /&gt;
struct AnimComp *makeComp(NEWBOB *nBob, NEWANIMCOMP *nAnimComp);&lt;br /&gt;
struct AnimComp *makeSeq(NEWBOB *nBob, NEWANIMSEQ *nAnimSeq);&lt;br /&gt;
VOID            freeVSprite(struct VSprite *vsprite);&lt;br /&gt;
VOID            freeBob(struct Bob *bob, LONG rasdepth);&lt;br /&gt;
VOID            freeComp(struct AnimComp *myComp, LONG rasdepth);&lt;br /&gt;
VOID            freeSeq(struct AnimComp *headComp, LONG rasdepth);&lt;br /&gt;
VOID            freeOb(struct AnimOb *headOb, LONG rasdepth);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* animtools.c&lt;br /&gt;
**&lt;br /&gt;
** This file is a collection of tools which are used with the VSprite, Bob and Animation&lt;br /&gt;
** system software. It is intended as a useful EXAMPLE, and while it shows what must be&lt;br /&gt;
** done, it is not the only way to do it.  If Not Enough Memory, or error return, each&lt;br /&gt;
** cleans up after itself before returning.  NOTE that these routines assume a very specific&lt;br /&gt;
** structure to the GEL lists.  Make sure that you use the correct pairs together&lt;br /&gt;
** (i.e. makeOb()/freeOb(), etc.)&lt;br /&gt;
**&lt;br /&gt;
** Compile with SAS/C 5.10b: lc -b1 -cfist -v -y -oanimtools.o animtools.c&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfx.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gels.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/clip.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/rastport.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;quot;animtools.h&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* Setup the GELs system.  After this call is made you can use VSprites, Bobs, AnimComps&lt;br /&gt;
** and AnimObs.  Note that this links the GelsInfo structure into the RastPort, and calls&lt;br /&gt;
** InitGels().  It uses information in your RastPort structure to establish boundary collision&lt;br /&gt;
** defaults at the outer edges of the raster.  This routine sets up for everything - collision&lt;br /&gt;
** detection and all. You must already have run LoadView before ReadyGelSys is called.&lt;br /&gt;
*/&lt;br /&gt;
struct GelsInfo *setupGelSys(struct RastPort *rPort, BYTE reserved)&lt;br /&gt;
{&lt;br /&gt;
struct GelsInfo *gInfo;&lt;br /&gt;
struct VSprite  *vsHead;&lt;br /&gt;
struct VSprite  *vsTail;&lt;br /&gt;
&lt;br /&gt;
if (NULL != (gInfo = (struct GelsInfo *)AllocMem(sizeof(struct GelsInfo), MEMF_CLEAR)))&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL != (gInfo-&amp;amp;gt;nextLine = (WORD *)AllocMem(sizeof(WORD) * 8, MEMF_CLEAR)))&lt;br /&gt;
            {&lt;br /&gt;
            if (NULL != (gInfo-&amp;amp;gt;lastColor = (WORD **)AllocMem(sizeof(LONG) * 8, MEMF_CLEAR)))&lt;br /&gt;
                {&lt;br /&gt;
                if (NULL != (gInfo-&amp;amp;gt;collHandler = (struct collTable *)&lt;br /&gt;
                        AllocMem(sizeof(struct collTable),MEMF_CLEAR)))&lt;br /&gt;
                    {&lt;br /&gt;
                    if (NULL != (vsHead = (struct VSprite *)&lt;br /&gt;
                            AllocMem((LONG)sizeof(struct VSprite), MEMF_CLEAR)))&lt;br /&gt;
                        {&lt;br /&gt;
                        if (NULL != (vsTail = (struct VSprite *)&lt;br /&gt;
                                AllocMem(sizeof(struct VSprite), MEMF_CLEAR)))&lt;br /&gt;
                            {&lt;br /&gt;
                            gInfo-&amp;amp;gt;sprRsrvd   = reserved;&lt;br /&gt;
                            /* Set left- and top-most to 1 to better keep items */&lt;br /&gt;
                            /* inside the display boundaries.                   */&lt;br /&gt;
                            gInfo-&amp;amp;gt;leftmost   = gInfo-&amp;amp;gt;topmost    = 1;&lt;br /&gt;
                            gInfo-&amp;amp;gt;rightmost  = (rPort-&amp;amp;gt;BitMap-&amp;amp;gt;BytesPerRow &amp;amp;lt;&amp;amp;lt; 3) - 1;&lt;br /&gt;
                            gInfo-&amp;amp;gt;bottommost = rPort-&amp;amp;gt;BitMap-&amp;amp;gt;Rows - 1;&lt;br /&gt;
                            rPort-&amp;amp;gt;GelsInfo = gInfo;&lt;br /&gt;
                            InitGels(vsHead, vsTail, gInfo);&lt;br /&gt;
                            return(gInfo);&lt;br /&gt;
                            }&lt;br /&gt;
                        FreeMem(vsHead, (LONG)sizeof(*vsHead));&lt;br /&gt;
                        }&lt;br /&gt;
                    FreeMem(gInfo-&amp;amp;gt;collHandler, (LONG)sizeof(struct collTable));&lt;br /&gt;
                    }&lt;br /&gt;
                FreeMem(gInfo-&amp;amp;gt;lastColor, (LONG)sizeof(LONG) * 8);&lt;br /&gt;
                }&lt;br /&gt;
            FreeMem(gInfo-&amp;amp;gt;nextLine, (LONG)sizeof(WORD) * 8);&lt;br /&gt;
            }&lt;br /&gt;
        FreeMem(gInfo, (LONG)sizeof(*gInfo));&lt;br /&gt;
        }&lt;br /&gt;
return(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free all of the stuff allocated by setupGelSys().  Only call this routine if&lt;br /&gt;
** setupGelSys() returned successfully.  The GelsInfo structure is the one returned&lt;br /&gt;
** by setupGelSys().   It also unlinks the GelsInfo from the RastPort.&lt;br /&gt;
*/&lt;br /&gt;
VOID cleanupGelSys(struct GelsInfo *gInfo, struct RastPort *rPort)&lt;br /&gt;
{&lt;br /&gt;
rPort-&amp;amp;gt;GelsInfo = NULL;&lt;br /&gt;
FreeMem(gInfo-&amp;amp;gt;collHandler, (LONG)sizeof(struct collTable));&lt;br /&gt;
FreeMem(gInfo-&amp;amp;gt;lastColor, (LONG)sizeof(LONG) * 8);&lt;br /&gt;
FreeMem(gInfo-&amp;amp;gt;nextLine, (LONG)sizeof(WORD) * 8);&lt;br /&gt;
FreeMem(gInfo-&amp;amp;gt;gelHead, (LONG)sizeof(struct VSprite));&lt;br /&gt;
FreeMem(gInfo-&amp;amp;gt;gelTail, (LONG)sizeof(struct VSprite));&lt;br /&gt;
FreeMem(gInfo, (LONG)sizeof(*gInfo));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Create a VSprite from the information given in nVSprite.  Use freeVSprite()&lt;br /&gt;
** to free this GEL.&lt;br /&gt;
*/&lt;br /&gt;
struct VSprite *makeVSprite(NEWVSPRITE *nVSprite)&lt;br /&gt;
{&lt;br /&gt;
struct VSprite *vsprite;&lt;br /&gt;
LONG            line_size;&lt;br /&gt;
LONG            plane_size;&lt;br /&gt;
&lt;br /&gt;
line_size = sizeof(WORD) * nVSprite-&amp;amp;gt;nvs_WordWidth;&lt;br /&gt;
plane_size = line_size * nVSprite-&amp;amp;gt;nvs_LineHeight;&lt;br /&gt;
&lt;br /&gt;
if (NULL != (vsprite = (struct VSprite *)AllocMem((LONG)sizeof(struct VSprite), MEMF_CLEAR)))&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL != (vsprite-&amp;amp;gt;BorderLine = (WORD *)AllocMem(line_size, MEMF_CHIP)))&lt;br /&gt;
            {&lt;br /&gt;
            if (NULL != (vsprite-&amp;amp;gt;CollMask = (WORD *)AllocMem(plane_size, MEMF_CHIP)))&lt;br /&gt;
                {&lt;br /&gt;
                vsprite-&amp;amp;gt;Y          = nVSprite-&amp;amp;gt;nvs_Y;&lt;br /&gt;
                vsprite-&amp;amp;gt;X          = nVSprite-&amp;amp;gt;nvs_X;&lt;br /&gt;
                vsprite-&amp;amp;gt;Flags      = nVSprite-&amp;amp;gt;nvs_Flags;&lt;br /&gt;
                vsprite-&amp;amp;gt;Width      = nVSprite-&amp;amp;gt;nvs_WordWidth;&lt;br /&gt;
                vsprite-&amp;amp;gt;Depth      = nVSprite-&amp;amp;gt;nvs_ImageDepth;&lt;br /&gt;
                vsprite-&amp;amp;gt;Height     = nVSprite-&amp;amp;gt;nvs_LineHeight;&lt;br /&gt;
                vsprite-&amp;amp;gt;MeMask     = nVSprite-&amp;amp;gt;nvs_MeMask;&lt;br /&gt;
                vsprite-&amp;amp;gt;HitMask    = nVSprite-&amp;amp;gt;nvs_HitMask;&lt;br /&gt;
                vsprite-&amp;amp;gt;ImageData  = nVSprite-&amp;amp;gt;nvs_Image;&lt;br /&gt;
                vsprite-&amp;amp;gt;SprColors  = nVSprite-&amp;amp;gt;nvs_ColorSet;&lt;br /&gt;
                vsprite-&amp;amp;gt;PlanePick  = vsprite-&amp;amp;gt;PlaneOnOff = 0x00;&lt;br /&gt;
                InitMasks(vsprite);&lt;br /&gt;
                return(vsprite);&lt;br /&gt;
                }&lt;br /&gt;
            FreeMem(vsprite-&amp;amp;gt;BorderLine, line_size);&lt;br /&gt;
            }&lt;br /&gt;
        FreeMem(vsprite, (LONG)sizeof(*vsprite));&lt;br /&gt;
        }&lt;br /&gt;
return(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Create a Bob from the information given in nBob.  Use freeBob() to free this GEL.&lt;br /&gt;
** A VSprite is created for this bob.  This routine properly allocates all double&lt;br /&gt;
** buffered information if it is required.&lt;br /&gt;
*/&lt;br /&gt;
struct Bob *makeBob(NEWBOB *nBob)&lt;br /&gt;
{&lt;br /&gt;
struct Bob         *bob;&lt;br /&gt;
struct VSprite     *vsprite;&lt;br /&gt;
NEWVSPRITE          nVSprite ;&lt;br /&gt;
LONG                rassize;&lt;br /&gt;
&lt;br /&gt;
rassize = (LONG)sizeof(UWORD) * nBob-&amp;amp;gt;nb_WordWidth * nBob-&amp;amp;gt;nb_LineHeight * nBob-&amp;amp;gt;nb_RasDepth;&lt;br /&gt;
&lt;br /&gt;
if (NULL != (bob = (struct Bob *)AllocMem((LONG)sizeof(struct Bob), MEMF_CLEAR)))&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL != (bob-&amp;amp;gt;SaveBuffer = (WORD *)AllocMem(rassize, MEMF_CHIP)))&lt;br /&gt;
            {&lt;br /&gt;
            nVSprite.nvs_WordWidth  = nBob-&amp;amp;gt;nb_WordWidth;&lt;br /&gt;
            nVSprite.nvs_LineHeight = nBob-&amp;amp;gt;nb_LineHeight;&lt;br /&gt;
            nVSprite.nvs_ImageDepth = nBob-&amp;amp;gt;nb_ImageDepth;&lt;br /&gt;
            nVSprite.nvs_Image      = nBob-&amp;amp;gt;nb_Image;&lt;br /&gt;
            nVSprite.nvs_X          = nBob-&amp;amp;gt;nb_X;&lt;br /&gt;
            nVSprite.nvs_Y          = nBob-&amp;amp;gt;nb_Y;&lt;br /&gt;
            nVSprite.nvs_ColorSet   = NULL;&lt;br /&gt;
            nVSprite.nvs_Flags      = nBob-&amp;amp;gt;nb_BFlags;&lt;br /&gt;
            /* Push the values into the NEWVSPRITE structure for use in makeVSprite(). */&lt;br /&gt;
            nVSprite.nvs_MeMask     = nBob-&amp;amp;gt;nb_MeMask;&lt;br /&gt;
            nVSprite.nvs_HitMask    = nBob-&amp;amp;gt;nb_HitMask;&lt;br /&gt;
&lt;br /&gt;
            if ((vsprite = makeVSprite(&amp;amp;amp;nVSprite)) != NULL)&lt;br /&gt;
                {&lt;br /&gt;
                vsprite-&amp;amp;gt;PlanePick = nBob-&amp;amp;gt;nb_PlanePick;&lt;br /&gt;
                vsprite-&amp;amp;gt;PlaneOnOff = nBob-&amp;amp;gt;nb_PlaneOnOff;&lt;br /&gt;
                vsprite-&amp;amp;gt;VSBob   = bob;&lt;br /&gt;
                bob-&amp;amp;gt;BobVSprite  = vsprite;&lt;br /&gt;
                bob-&amp;amp;gt;ImageShadow = vsprite-&amp;amp;gt;CollMask;&lt;br /&gt;
                bob-&amp;amp;gt;Flags       = 0;&lt;br /&gt;
                bob-&amp;amp;gt;Before      = NULL;&lt;br /&gt;
                bob-&amp;amp;gt;After       = NULL;&lt;br /&gt;
                bob-&amp;amp;gt;BobComp     = NULL;&lt;br /&gt;
&lt;br /&gt;
                if (nBob-&amp;amp;gt;nb_DBuf)&lt;br /&gt;
                    {&lt;br /&gt;
                    if (NULL != (bob-&amp;amp;gt;DBuffer = (struct DBufPacket *)&lt;br /&gt;
                            AllocMem((LONG)sizeof(struct DBufPacket), MEMF_CLEAR)))&lt;br /&gt;
                        {&lt;br /&gt;
                        if (NULL != (bob-&amp;amp;gt;DBuffer-&amp;amp;gt;BufBuffer = (WORD *)AllocMem(rassize, MEMF_CHIP)))&lt;br /&gt;
                            return(bob);&lt;br /&gt;
                        FreeMem(bob-&amp;amp;gt;DBuffer, (LONG)sizeof(struct DBufPacket));&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                else&lt;br /&gt;
                    {&lt;br /&gt;
                    bob-&amp;amp;gt;DBuffer = NULL;&lt;br /&gt;
                    return(bob);&lt;br /&gt;
                    }&lt;br /&gt;
                freeVSprite(vsprite);&lt;br /&gt;
                }&lt;br /&gt;
            FreeMem(bob-&amp;amp;gt;SaveBuffer, rassize);&lt;br /&gt;
            }&lt;br /&gt;
        FreeMem(bob, (LONG)sizeof(*bob));&lt;br /&gt;
        }&lt;br /&gt;
return(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Create a Animation Component from the information given in nAnimComp and nBob.  Use&lt;br /&gt;
** freeComp() to free this GEL.  makeComp() calls makeBob(), and links the Bob into an AnimComp.&lt;br /&gt;
*/&lt;br /&gt;
struct AnimComp *makeComp(NEWBOB *nBob, NEWANIMCOMP *nAnimComp)&lt;br /&gt;
{&lt;br /&gt;
struct Bob      *compBob;&lt;br /&gt;
struct AnimComp *aComp;&lt;br /&gt;
&lt;br /&gt;
if ((aComp = AllocMem((LONG)sizeof(struct AnimComp),MEMF_CLEAR)) != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        if ((compBob = makeBob(nBob)) != NULL)&lt;br /&gt;
            {&lt;br /&gt;
            compBob-&amp;amp;gt;After   = compBob-&amp;amp;gt;Before  = NULL;&lt;br /&gt;
            compBob-&amp;amp;gt;BobComp = aComp;   /* Link &#039;em up. */&lt;br /&gt;
            aComp-&amp;amp;gt;AnimBob      = compBob;&lt;br /&gt;
            aComp-&amp;amp;gt;TimeSet      = nAnimComp-&amp;amp;gt;nac_Time; /* Num ticks active. */&lt;br /&gt;
            aComp-&amp;amp;gt;YTrans       = nAnimComp-&amp;amp;gt;nac_Yt; /* Offset rel to HeadOb */&lt;br /&gt;
            aComp-&amp;amp;gt;XTrans       = nAnimComp-&amp;amp;gt;nac_Xt;&lt;br /&gt;
            aComp-&amp;amp;gt;AnimCRoutine = nAnimComp-&amp;amp;gt;nac_Routine;&lt;br /&gt;
            aComp-&amp;amp;gt;Flags        = nAnimComp-&amp;amp;gt;nac_CFlags;&lt;br /&gt;
            aComp-&amp;amp;gt;Timer        = 0;&lt;br /&gt;
            aComp-&amp;amp;gt;NextSeq      = aComp-&amp;amp;gt;PrevSeq  = NULL;&lt;br /&gt;
            aComp-&amp;amp;gt;NextComp     = aComp-&amp;amp;gt;PrevComp = NULL;&lt;br /&gt;
            aComp-&amp;amp;gt;HeadOb       = NULL;&lt;br /&gt;
            return(aComp);&lt;br /&gt;
            }&lt;br /&gt;
        FreeMem(aComp, (LONG)sizeof(struct AnimComp));&lt;br /&gt;
        }&lt;br /&gt;
return(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Create an Animation Sequence from the information given in nAnimSeq and nBob.  Use&lt;br /&gt;
** freeSeq() to free this GEL.  This routine creates a linked list of animation components&lt;br /&gt;
** which make up the animation sequence.  It links them all up, making a circular list of&lt;br /&gt;
** the PrevSeq and NextSeq pointers. That is to say, the first component of the sequences&#039;&lt;br /&gt;
** PrevSeq points to the last component; the last component of * the sequences&#039; NextSeq&lt;br /&gt;
** points back to the first component.  If dbuf is on, the underlying Bobs will be set up&lt;br /&gt;
** for double buffering.  If singleImage is non-zero, the pImages pointer is assumed to&lt;br /&gt;
** point to an array of only one image, instead of an array of &#039;count&#039; images, and all&lt;br /&gt;
** Bobs will use the same image.&lt;br /&gt;
*/&lt;br /&gt;
struct AnimComp *makeSeq(NEWBOB *nBob, NEWANIMSEQ *nAnimSeq)&lt;br /&gt;
{&lt;br /&gt;
int seq;&lt;br /&gt;
struct AnimComp *firstCompInSeq = NULL;&lt;br /&gt;
struct AnimComp *seqComp = NULL;&lt;br /&gt;
struct AnimComp *lastCompMade = NULL;&lt;br /&gt;
LONG image_size;&lt;br /&gt;
NEWANIMCOMP nAnimComp;&lt;br /&gt;
&lt;br /&gt;
/* get the initial image.  this is the only image that is used&lt;br /&gt;
** if nAnimSeq-&amp;amp;gt;nas_SingleImage is non-zero.&lt;br /&gt;
*/&lt;br /&gt;
nBob-&amp;amp;gt;nb_Image = nAnimSeq-&amp;amp;gt;nas_Images;&lt;br /&gt;
image_size = nBob-&amp;amp;gt;nb_LineHeight * nBob-&amp;amp;gt;nb_ImageDepth * nBob-&amp;amp;gt;nb_WordWidth;&lt;br /&gt;
&lt;br /&gt;
/* for each comp in the sequence */&lt;br /&gt;
for (seq = 0; seq &amp;amp;lt; nAnimSeq-&amp;amp;gt;nas_Count; seq++)&lt;br /&gt;
        {&lt;br /&gt;
        nAnimComp.nac_Xt        = *(nAnimSeq-&amp;amp;gt;nas_Xt + seq);&lt;br /&gt;
        nAnimComp.nac_Yt        = *(nAnimSeq-&amp;amp;gt;nas_Yt + seq);&lt;br /&gt;
        nAnimComp.nac_Time      = *(nAnimSeq-&amp;amp;gt;nas_Times + seq);&lt;br /&gt;
        nAnimComp.nac_Routine   = nAnimSeq-&amp;amp;gt;nas_Routines[seq];&lt;br /&gt;
        nAnimComp.nac_CFlags    = nAnimSeq-&amp;amp;gt;nas_CFlags;&lt;br /&gt;
        if ((seqComp = makeComp(nBob, &amp;amp;amp;nAnimComp)) == NULL)&lt;br /&gt;
            {&lt;br /&gt;
            if (firstCompInSeq != NULL)&lt;br /&gt;
                freeSeq(firstCompInSeq, (LONG)nBob-&amp;amp;gt;nb_RasDepth);&lt;br /&gt;
            return(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        seqComp-&amp;amp;gt;HeadOb = nAnimSeq-&amp;amp;gt;nas_HeadOb;&lt;br /&gt;
        /* Make a note of where the first component is. */&lt;br /&gt;
        if (firstCompInSeq == NULL) firstCompInSeq = seqComp;&lt;br /&gt;
        /* link the component into the list */&lt;br /&gt;
        if (lastCompMade != NULL) lastCompMade-&amp;amp;gt;NextSeq = seqComp;&lt;br /&gt;
        seqComp-&amp;amp;gt;NextSeq = NULL;&lt;br /&gt;
        seqComp-&amp;amp;gt;PrevSeq = lastCompMade;&lt;br /&gt;
        lastCompMade = seqComp;&lt;br /&gt;
        /* If nAnimSeq-&amp;amp;gt;nas_SingleImage is zero, the image array has nAnimSeq-&amp;amp;gt;nas_Count images. */&lt;br /&gt;
        if (!nAnimSeq-&amp;amp;gt;nas_SingleImage)&lt;br /&gt;
            nBob-&amp;amp;gt;nb_Image += image_size;&lt;br /&gt;
        }&lt;br /&gt;
/* On The last component in the sequence, set Next/Prev to make */&lt;br /&gt;
/* the linked list a loop of components.                        */&lt;br /&gt;
lastCompMade-&amp;amp;gt;NextSeq = firstCompInSeq;&lt;br /&gt;
firstCompInSeq-&amp;amp;gt;PrevSeq = lastCompMade;&lt;br /&gt;
&lt;br /&gt;
return(firstCompInSeq);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free the data created by makeVSprite().  Assumes images deallocated elsewhere. */&lt;br /&gt;
VOID freeVSprite(struct VSprite *vsprite)&lt;br /&gt;
{&lt;br /&gt;
LONG    line_size;&lt;br /&gt;
LONG    plane_size;&lt;br /&gt;
&lt;br /&gt;
line_size = (LONG)sizeof(WORD) * vsprite-&amp;amp;gt;Width;&lt;br /&gt;
plane_size = line_size * vsprite-&amp;amp;gt;Height;&lt;br /&gt;
FreeMem(vsprite-&amp;amp;gt;BorderLine, line_size);&lt;br /&gt;
FreeMem(vsprite-&amp;amp;gt;CollMask, plane_size);&lt;br /&gt;
FreeMem(vsprite, (LONG)sizeof(*vsprite));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free the data created by makeBob().  It&#039;s important that rasdepth match the depth you */&lt;br /&gt;
/* passed to makeBob() when this GEL was made. Assumes images deallocated elsewhere.     */&lt;br /&gt;
VOID freeBob(struct Bob *bob, LONG rasdepth)&lt;br /&gt;
{&lt;br /&gt;
LONG    rassize =  sizeof(UWORD) * bob-&amp;amp;gt;BobVSprite-&amp;amp;gt;Width * bob-&amp;amp;gt;BobVSprite-&amp;amp;gt;Height * rasdepth;&lt;br /&gt;
&lt;br /&gt;
if (bob-&amp;amp;gt;DBuffer != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        FreeMem(bob-&amp;amp;gt;DBuffer-&amp;amp;gt;BufBuffer, rassize);&lt;br /&gt;
        FreeMem(bob-&amp;amp;gt;DBuffer, (LONG)sizeof(struct DBufPacket));&lt;br /&gt;
        }&lt;br /&gt;
FreeMem(bob-&amp;amp;gt;SaveBuffer, rassize);&lt;br /&gt;
freeVSprite(bob-&amp;amp;gt;BobVSprite);&lt;br /&gt;
FreeMem(bob, (LONG)sizeof(*bob));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free the data created by makeComp().  It&#039;s important that rasdepth match the depth you */&lt;br /&gt;
/* passed to makeComp() when this GEL was made. Assumes images deallocated elsewhere.    */&lt;br /&gt;
VOID freeComp(struct AnimComp *myComp, LONG rasdepth)&lt;br /&gt;
{&lt;br /&gt;
freeBob(myComp-&amp;amp;gt;AnimBob, rasdepth);&lt;br /&gt;
FreeMem(myComp, (LONG)sizeof(struct AnimComp));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free the data created by makeSeq().  Complimentary to makeSeq(), this routine goes through&lt;br /&gt;
** the NextSeq pointers and frees the Components.  This routine only goes forward through the&lt;br /&gt;
** list, and so it must be passed the first component in the sequence, or the sequence must&lt;br /&gt;
** be circular (which is guaranteed if you use makeSeq()).  It&#039;s important that rasdepth match&lt;br /&gt;
** the depth you passed to makeSeq() when this GEL was made.   Assumes images deallocated elsewhere!&lt;br /&gt;
*/&lt;br /&gt;
VOID freeSeq(struct AnimComp *headComp, LONG rasdepth)&lt;br /&gt;
{&lt;br /&gt;
struct AnimComp *curComp;&lt;br /&gt;
struct AnimComp *nextComp;&lt;br /&gt;
&lt;br /&gt;
/* Break the NextSeq loop, so we get a NULL at the end of the list. */&lt;br /&gt;
headComp-&amp;amp;gt;PrevSeq-&amp;amp;gt;NextSeq = NULL;&lt;br /&gt;
&lt;br /&gt;
curComp = headComp;         /* get the start of the list */&lt;br /&gt;
while (curComp != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        nextComp = curComp-&amp;amp;gt;NextSeq;&lt;br /&gt;
        freeComp(curComp, rasdepth);&lt;br /&gt;
        curComp = nextComp;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Free an animation object (list of sequences).  freeOb() goes through the NextComp&lt;br /&gt;
** pointers, starting at the AnimObs&#039; HeadComp, and frees every sequence.  It only&lt;br /&gt;
** goes forward. It then frees the Object itself.  Assumes images deallocated elsewhere!&lt;br /&gt;
*/&lt;br /&gt;
VOID freeOb(struct AnimOb *headOb, LONG rasdepth)&lt;br /&gt;
{&lt;br /&gt;
struct AnimComp *curSeq;&lt;br /&gt;
struct AnimComp *nextSeq;&lt;br /&gt;
&lt;br /&gt;
curSeq = headOb-&amp;amp;gt;HeadComp;          /* get the start of the list */&lt;br /&gt;
while (curSeq != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        nextSeq = curSeq-&amp;amp;gt;NextComp;&lt;br /&gt;
        freeSeq(curSeq, rasdepth);&lt;br /&gt;
        curSeq = nextSeq;&lt;br /&gt;
        }&lt;br /&gt;
FreeMem(headOb, sizeof(struct AnimOb));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Amiga&#039;s graphics animation functions. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Animation Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AddAnimOb()&lt;br /&gt;
| Add an AnimOb to the linked list of AnimObs.&lt;br /&gt;
|-&lt;br /&gt;
| AddBob()&lt;br /&gt;
| Add a Bob to the current GEL list.&lt;br /&gt;
|-&lt;br /&gt;
| AddVSprite()&lt;br /&gt;
| Add a VSprite to the current GEL list.&lt;br /&gt;
|-&lt;br /&gt;
| Animate()&lt;br /&gt;
| Process every AnimOb in the current animation list.&lt;br /&gt;
|-&lt;br /&gt;
| ChangeSprite()&lt;br /&gt;
| Change the sprite image pointer.&lt;br /&gt;
|-&lt;br /&gt;
| DoCollision()&lt;br /&gt;
| Test every GEL in a GEL list for collisions.&lt;br /&gt;
|-&lt;br /&gt;
| DrawGList()&lt;br /&gt;
| Process the GEL list, queueing VSprites, drawing Bobs.&lt;br /&gt;
|-&lt;br /&gt;
| FreeGBuffers()&lt;br /&gt;
| Deallocate memory obtained by GetGBuffers().&lt;br /&gt;
|-&lt;br /&gt;
| FreeSprite()&lt;br /&gt;
| Return sprite for use by others and virtual sprite machine.&lt;br /&gt;
|-&lt;br /&gt;
| GetGBuffers()&lt;br /&gt;
| Attempt to allocate all buffers of an entire AnimOb.&lt;br /&gt;
|-&lt;br /&gt;
| GetSprite()&lt;br /&gt;
| Attempt to get a sprite for the simple sprite manager.&lt;br /&gt;
|-&lt;br /&gt;
| InitGels()&lt;br /&gt;
| Initialize a GEL list; must be called before using GELs.&lt;br /&gt;
|-&lt;br /&gt;
| InitGMasks()&lt;br /&gt;
| Initialize all of the masks of an AnimOb.&lt;br /&gt;
|-&lt;br /&gt;
| InitMasks()&lt;br /&gt;
| Initialize the BorderLine and CollMask masks of a VSprite.&lt;br /&gt;
|-&lt;br /&gt;
| MoveSprite()&lt;br /&gt;
| Move sprite to a point relative to top of ViewPort.&lt;br /&gt;
|-&lt;br /&gt;
| RemBob()&lt;br /&gt;
| Remove a Bob from the GEL list.&lt;br /&gt;
|-&lt;br /&gt;
| RemIBob()&lt;br /&gt;
| Immediately remove a Bob from the GEL list and the RastPort.&lt;br /&gt;
|-&lt;br /&gt;
| RemVSprite()&lt;br /&gt;
| Remove a VSprite from the current GEL list.&lt;br /&gt;
|-&lt;br /&gt;
| SetCollision()&lt;br /&gt;
| Set a pointer to a user collision routine.&lt;br /&gt;
|-&lt;br /&gt;
| SortGList()&lt;br /&gt;
| Sort the current GEL list, ordering its &#039;&#039;x,y&#039;&#039; coordinates.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Cooperative_Record_Locking&amp;diff=12551</id>
		<title>Cooperative Record Locking</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Cooperative_Record_Locking&amp;diff=12551"/>
		<updated>2025-01-26T19:30:20Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
&lt;br /&gt;
The Amiga&#039;s cooperative record locking scheme allows an application to lock regions of a file rather than locking an entire file. These regions are known as records.&lt;br /&gt;
&lt;br /&gt;
The Amiga thinks of a record in terms of size and offset. The size refers to the length of the record in bytes. The offset refers to&lt;br /&gt;
the starting position of the record from the beginning of the file. The size and offset can vary from record to record. The only limit&lt;br /&gt;
on a record&#039;s size and offset are the limits the file system has on the size of the file.&lt;br /&gt;
&lt;br /&gt;
== File System or DOS? ==&lt;br /&gt;
&lt;br /&gt;
Starting with version 53.x of the DOS Library (dos.library), the record locking feature is handled entirely by AmigaDOS. DOS record locking is independent of the file system and is always available. Prior to this version, record locking was available only if each individual file system implemented the feature. If a file system does not record locking, the record locking functions described below will always fail.&lt;br /&gt;
&lt;br /&gt;
== Record Lock Types ==&lt;br /&gt;
&lt;br /&gt;
There are two basic types of record lock, exclusive and shared. An exclusive lock gives an application exclusive access to a record. While an application holds an exclusive record lock, no other process can obtain a record lock that overlaps the exclusively locked region. The file system can only grant an exclusive record lock on a region that is not part of an existing record lock.&lt;br /&gt;
&lt;br /&gt;
The other type of record lock, the shared lock, gives an application shared access to a record. While an application holds a shared&lt;br /&gt;
record lock, other applications can also obtain a shared record lock that overlaps the original shared record lock, but no other process can obtain an exclusive record lock that overlaps the shared record lock.&lt;br /&gt;
&lt;br /&gt;
== Locking and Unlocking Records ==&lt;br /&gt;
&lt;br /&gt;
To lock (or unlock) a record within a file, an application needs a valid file handle on the file. Two DOS Library functions handle&lt;br /&gt;
individual record locks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL LockRecord(BPTR myFH, ULONG my_offset, ULONG my_length, ULONG my_mode, ULONG my_timeout);&lt;br /&gt;
BOOL UnLockRecord(BPTR myFH, ULONG my_offset, ULONG my_length);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both functions, the myFH field refers to the valid file handle mentioned above, and the my_offset and my_length fields refer to the&lt;br /&gt;
record&#039;s offset and size.&lt;br /&gt;
&lt;br /&gt;
The LockRecord() function has two additional parameters: my_mode and my_timeout. The my_mode field refers to the type of record. DOS supports four record types:&lt;br /&gt;
&lt;br /&gt;
; REC_EXCLUSIVE&lt;br /&gt;
: Create an exclusive record lock. If the record is not immediately available, the file system will make a second record lock attempt when the timeout expires. The timeout (the my_timeout field from the RecordLock() prototype above) is in DOS ticks (1/50 of a second).&lt;br /&gt;
&lt;br /&gt;
; REC_EXCLUSIVE_IMMED&lt;br /&gt;
: This record type is like REC_EXCLUSIVE, but the attempt to lock a record will fail if the record is not immediately available. In this case the file system ignores the timeout.&lt;br /&gt;
&lt;br /&gt;
; REC_SHARED&lt;br /&gt;
: Create a shared record lock. If the record is not immediately available, the file system will make a second record lock attempt when the timeout expires. The timeout (the my_timeout field from the RecordLock() prototype above) is in DOS ticks (1/50th of a second).&lt;br /&gt;
&lt;br /&gt;
; REC_SHARED_IMMED&lt;br /&gt;
: This record type is like REC_SHARED, but the attempt to lock a record will fail if the record is not immediately available. In this case the file system ignores the timeout.&lt;br /&gt;
&lt;br /&gt;
The Amiga record locking scheme is cooperative--the file system does not prevent any process from accessing any part of a data&lt;br /&gt;
file. The record locking scheme has nothing to do with other actions of a file system. The file system will let other processes read and write a data file regardless of any existing record locks on the data file.&lt;br /&gt;
&lt;br /&gt;
Also, when an application attempts to create a record lock, the file system&#039;s record locking mechanism only makes sure the record won&#039;t&lt;br /&gt;
conflict with any existing record locks on the same data file. The file system doesn&#039;t check the validity of the locked region. This&lt;br /&gt;
makes it possible to lock records in an empty data file and also to lock records that are well beyond the end of a data file. This&lt;br /&gt;
feature may be useful to an application that needs to lock records that don&#039;t exist yet.&lt;br /&gt;
&lt;br /&gt;
When releasing a record lock, an application must provide exactly the same parameters it used when it locked the record. Attempting to&lt;br /&gt;
unlock a record using a different offset or length will fail. If the application does not successfully unlock a record, the record lock&lt;br /&gt;
will remain in effect. This means further requests to lock that record can fail, depending on the type of record lock. The record&lt;br /&gt;
lock will remain in effect until the data file is removed or the system restarted. As with most Amiga resources, an application&lt;br /&gt;
should release a record lock as quickly as possible. This helps out other applications that might be waiting to access the locked record.&lt;br /&gt;
&lt;br /&gt;
== Locking Multiple Records ==&lt;br /&gt;
&lt;br /&gt;
DOS Library offers a function to lock an array of record locks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL LockRecords(struct RecordLock *recordarray, ULONG multi_timeout);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
LockRecords() locks a group of records using one function call. LockRecords() accepts a pointer to an array of RecordLock structures&lt;br /&gt;
(as defined in &amp;lt;dos/record.h&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RecordLock {&lt;br /&gt;
        BPTR    rec_FH; /* The file handle of the data file */&lt;br /&gt;
        ULONG   rec_Offset;     /* The record offset in the data file */&lt;br /&gt;
        ULONG   rec_Length;     /* The length of the record */&lt;br /&gt;
        ULONG   rec_Mode;       /* The mode of the record lock */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fields in each RecordLock structure correspond to the parameters from the LockRecord() function. The records do not have to be in the same file. The array is terminated by a dummy RecordLock with a NULL file handle (rec_FH).&lt;br /&gt;
&lt;br /&gt;
The RecordLock structure does not include a timeout. Instead, LockRecords() applies the same timeout (multi_timeout from the LockRecords() prototype above) to each RecordLock in the array. If LockRecords() fails to lock any of the records in its array, LockRecords() releases any successful record locks from the array, and return DOSFALSE.&lt;br /&gt;
&lt;br /&gt;
To unlock records locked by LockRecords(), use the dos.library function UnLockRecords():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL UnLockRecords(struct RecordLock *recordarray);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function accepts the same array used to lock the records with LockRecords().&lt;br /&gt;
&lt;br /&gt;
Note that it is possible to use UnLockRecord() on a record locked by LockRecords(). However, if an application uses UnLockRecord() to&lt;br /&gt;
unlock one record in the RecordLock array, it should use UnLockRecord() to unlock all of the records in the array.&lt;br /&gt;
&lt;br /&gt;
== Locking Records Using DOS Packets ==&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t use DOS Packets|text=DOS Library now handles record locking as of version 53.?? so that file systems are no longer burdened with implementing this functionality. The information below is being provided for reference and for users of previous versions of the DOS Library.}}&lt;br /&gt;
&lt;br /&gt;
To lock a record using the DOS packet interface, send an ACTION_LOCK_RECORD (2008) packet to the file system. This packet uses the following arguments:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ARG1: BTPR   The file handle of the data file in which you wish to lock a record.&lt;br /&gt;
ARG2: ULONG  The offset (in bytes) in the file of the record.&lt;br /&gt;
ARG3: ULONG  The length (in bytes) of the record.&lt;br /&gt;
ARG4: ULONG  The mode with which you wish to lock record.&lt;br /&gt;
ARG5: ULONG  Time (in ticks) you are will to wait for the record to become available.&lt;br /&gt;
&lt;br /&gt;
RES1: BOOL   Upon return RES1 will contain the success/failure status:&lt;br /&gt;
             DOSTRUE if the record lock was successfully locked.&lt;br /&gt;
             DOSFALSE if the record lock failed.&lt;br /&gt;
RES2: CODE   Failure code if the record lock failed for a reason other than denied&lt;br /&gt;
             access (collision for example).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To unlock a record using the DOS packet interface, send an ACTION_FREE_RECORD (2009) packet. This packet uses the following arguments:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ARG1: BPTR   The file handle of the data file in which you locked the record.&lt;br /&gt;
ARG2: ULONG  The offset (in bytes) in the file of the record with which you locked&lt;br /&gt;
             the record.&lt;br /&gt;
ARG3: ULONG  The length (in bytes) of the record with which you locked the record.&lt;br /&gt;
&lt;br /&gt;
RES1: BOOL   Upon return RES1 will contain the success/failure status:&lt;br /&gt;
             DOSTRUE if the record lock was successfully unlocked.&lt;br /&gt;
             DOSFALSE if there was no lock to unlock.&lt;br /&gt;
RES2: CODE   Possible failure code if the record lock could not be unlocked.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== LockRecord.c ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// LockRecord.c&lt;br /&gt;
//&lt;br /&gt;
// LockRecord()/UnLockRecord() example.&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/lists.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/rdargs.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/record.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void GetCommandLine(BPTR, struct RDArgs *rdargs, UBYTE *cmdbuffer);&lt;br /&gt;
void DoLockRecord(BPTR, struct RDArgs *rdargs);&lt;br /&gt;
void DoUnLockRecord(BPTR fh, struct RDArgs *rdargs);&lt;br /&gt;
void ListRecordLocks(void);&lt;br /&gt;
struct LockNode *FindRecordLock(ULONG offset, ULONG length);&lt;br /&gt;
&lt;br /&gt;
/* List and node structures to keep track of record locks */&lt;br /&gt;
struct LockNode {&lt;br /&gt;
    struct LockNode *ln_Succ;&lt;br /&gt;
    struct LockNode *ln_Pred;&lt;br /&gt;
    ULONG ln_Counter;&lt;br /&gt;
    ULONG ln_Offset;&lt;br /&gt;
    ULONG ln_Length;&lt;br /&gt;
    ULONG ln_Mode;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct LockList {&lt;br /&gt;
    struct LockNode *lh_Head;&lt;br /&gt;
    struct LockNode *lh_Tail;&lt;br /&gt;
    struct LockNode *lh_TailPred;&lt;br /&gt;
    ULONG lh_Counter;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* Pseudo data file */&lt;br /&gt;
#define TESTFILE &amp;quot;t:locktest&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LOCK_TEMPLATE &amp;quot;OFFSET/K/N,LENGTH/K/N,EXCLUSIVE/S,IMMEDIATE/S,TIMEOUT/K/N&amp;quot;&lt;br /&gt;
#define UNLOCK_TEMPLATE &amp;quot;OFFSET/K/N,LENGTH/K/N&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_POS      0&lt;br /&gt;
#define LENGTH_POS      1&lt;br /&gt;
#define EXCLUSIVE_POS   2&lt;br /&gt;
#define IMMEDIATE_POS   3&lt;br /&gt;
#define TIMEOUT_POS     4&lt;br /&gt;
&lt;br /&gt;
struct LockList *locklist;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BPTR fh;&lt;br /&gt;
    struct RDArgs *rdargs;&lt;br /&gt;
    struct CSource *csource;&lt;br /&gt;
    UBYTE *cmdbuffer;&lt;br /&gt;
    struct LockNode *lnode, *nnode;&lt;br /&gt;
    LONG error = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
    if (locklist = AllocVecTags(sizeof(struct LockList), AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
        IExec-&amp;gt;NewList((struct List *)locklist);&lt;br /&gt;
&lt;br /&gt;
        /* Allocate RDArgs structure to parse command lines */&lt;br /&gt;
        if (rdargs = IDOS-&amp;gt;AllocDosObject(DOS_RDARGS, TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            csource = &amp;amp;rdargs-&amp;gt;RDA_Source;&lt;br /&gt;
&lt;br /&gt;
            /* Get buffer to read command lines in */&lt;br /&gt;
            if (csource-&amp;gt;CS_Buffer = IExec-&amp;gt;AllocVecTags(512, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
            {&lt;br /&gt;
                csource-&amp;gt;CS_Length = 512;&lt;br /&gt;
                csource-&amp;gt;CS_CurChr = 0;&lt;br /&gt;
&lt;br /&gt;
                /* Buffer to isolate command keyword */&lt;br /&gt;
                if (cmdbuffer = IExec-&amp;gt;AllocVecTags(80, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
                {&lt;br /&gt;
                    /* Open a testfile, create it if necessary */&lt;br /&gt;
                    if (fh = IDOS-&amp;gt;Open(TESTFILE, MODE_READWRITE))&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Process command lines */&lt;br /&gt;
                        GetCommandLine(fh, rdargs, cmdbuffer);&lt;br /&gt;
&lt;br /&gt;
                        /* Try to get rid of outstanding record locks */&lt;br /&gt;
                        lnode = locklist-&amp;gt;lh_Head;&lt;br /&gt;
                        while (nnode = lnode-&amp;gt;ln_Succ)&lt;br /&gt;
                        {&lt;br /&gt;
                            /* Try to unlock pending locks */&lt;br /&gt;
                            if ((IDOS-&amp;gt;UnLockRecord(fh,&lt;br /&gt;
                                              lnode-&amp;gt;ln_Offset,&lt;br /&gt;
                                              lnode-&amp;gt;ln_Length)) == DOSFALSE)&lt;br /&gt;
                            {&lt;br /&gt;
                                IDOS-&amp;gt;Printf(&amp;quot;Error unlocking record %ld with offset %ld length %ld\n&amp;quot;,&lt;br /&gt;
                                        lnode-&amp;gt;ln_Counter,&lt;br /&gt;
                                        lnode-&amp;gt;ln_Offset,&lt;br /&gt;
                                        lnode-&amp;gt;ln_Length);&lt;br /&gt;
                                if (IDOS-&amp;gt;IoErr())&lt;br /&gt;
                                    IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
                            }&lt;br /&gt;
                            /* Remove node no matter what */&lt;br /&gt;
                            IExec-&amp;gt;FreeVec(lnode);&lt;br /&gt;
                            lnode = nnode;&lt;br /&gt;
                        };&lt;br /&gt;
&lt;br /&gt;
                        IDOS-&amp;gt;Close(fh);&lt;br /&gt;
                    }&lt;br /&gt;
                    IExec-&amp;gt;FreeVec(cmdbuffer);&lt;br /&gt;
                } else&lt;br /&gt;
                    IDOS-&amp;gt;SetIoErr(ERROR_NO_FREE_STORE);&lt;br /&gt;
&lt;br /&gt;
                IExec-&amp;gt;FreeVec(csource-&amp;gt;CS_Buffer);&lt;br /&gt;
            } else&lt;br /&gt;
                IDOS-&amp;gt;SetIoErr(ERROR_NO_FREE_STORE);&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;FreeDosObject(DOS_RDARGS, rdargs);&lt;br /&gt;
        } else&lt;br /&gt;
            IDOS-&amp;gt;SetIoErr(ERROR_NO_FREE_STORE);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeVec(locklist);&lt;br /&gt;
    } else&lt;br /&gt;
        IDOS-&amp;gt;SetIoErr(ERROR_NO_FREE_STORE);&lt;br /&gt;
&lt;br /&gt;
    error = IDOS-&amp;gt;IoErr();&lt;br /&gt;
    if (error)&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
        error = RETURN_FAIL;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return(error);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void GetCommandLine(BPTR fh, struct RDArgs *rdargs, UBYTE *cmdbuffer)&lt;br /&gt;
{&lt;br /&gt;
    struct CSource *csource = &amp;amp;rdargs-&amp;gt;RDA_Source;&lt;br /&gt;
    UBYTE *cmdlinebuffer = csource-&amp;gt;CS_Buffer;&lt;br /&gt;
    LONG error;&lt;br /&gt;
&lt;br /&gt;
    /* Prompt for command line */&lt;br /&gt;
    IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;Cmd&amp;gt; &amp;quot;, 5);&lt;br /&gt;
&lt;br /&gt;
    /* Loop forever, waiting for commands */&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        /* Get command line */&lt;br /&gt;
        if ((IDOS-&amp;gt;FGets(IDOS-&amp;gt;Input(), cmdlinebuffer, 512)) != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            /* Use ReadItem() to isolate actual command */&lt;br /&gt;
            error = IDOS-&amp;gt;ReadItem(cmdbuffer, 80, csource);&lt;br /&gt;
&lt;br /&gt;
            /* Break on error */&lt;br /&gt;
            if (error == ITEM_ERROR)&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
            /* Make sure I&#039;ve got something */&lt;br /&gt;
            else if (error != ITEM_NOTHING)&lt;br /&gt;
            {&lt;br /&gt;
                /* cmdbuffer now contains the command:&lt;br /&gt;
                 *&lt;br /&gt;
                 * KNOWN COMMANDS:&lt;br /&gt;
                 * QUIT&lt;br /&gt;
                 * LIST&lt;br /&gt;
                 * LOCKRECORD&lt;br /&gt;
                 * UNLOCKRECORD&lt;br /&gt;
                 */&lt;br /&gt;
                if ((IUtility-&amp;gt;Stricmp(&amp;quot;QUIT&amp;quot;, cmdbuffer)) == 0)&lt;br /&gt;
                    break;&lt;br /&gt;
                else if ((IUtility-&amp;gt;Stricmp(&amp;quot;HELP&amp;quot;, cmdbuffer)) == 0)&lt;br /&gt;
                {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Available commands:\n&amp;quot;);&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;LOCKRECORD %s\n&amp;quot;, LOCK_TEMPLATE);&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;UNLOCKRECORD %s\n&amp;quot;, UNLOCK_TEMPLATE);&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;LIST\n&amp;quot;);&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;QUIT\n&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                else if ((IUtility-&amp;gt;Stricmp(&amp;quot;LIST&amp;quot;, cmdbuffer)) == 0)&lt;br /&gt;
                    ListRecordLocks();  /* Show all current locks */&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                    /* Note that I&#039;ve already isolated the command&lt;br /&gt;
                     * keyword, so I&#039;m using Source-&amp;gt;CS_CurChr to point&lt;br /&gt;
                     * after it.&lt;br /&gt;
                     */&lt;br /&gt;
                    csource-&amp;gt;CS_Buffer += csource-&amp;gt;CS_CurChr;&lt;br /&gt;
                    csource-&amp;gt;CS_CurChr = 0;&lt;br /&gt;
&lt;br /&gt;
                    if ((IUtility-&amp;gt;Stricmp(&amp;quot;LOCKRECORD&amp;quot;, cmdbuffer)) == 0)&lt;br /&gt;
                        DoLockRecord(fh, rdargs);&lt;br /&gt;
                    else if ((IUtility-&amp;gt;Stricmp(&amp;quot;UNLOCKRECORD&amp;quot;, cmdbuffer)) == 0)&lt;br /&gt;
                        DoUnLockRecord(fh, rdargs);&lt;br /&gt;
                    else&lt;br /&gt;
                        IDOS-&amp;gt;PrintFault(ERROR_NOT_IMPLEMENTED, cmdbuffer);&lt;br /&gt;
&lt;br /&gt;
                    /* Reset CSource */&lt;br /&gt;
                    csource-&amp;gt;CS_Buffer = cmdlinebuffer;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                /* Output new prompt. Make sure csource is OK. */&lt;br /&gt;
                IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;Cmd&amp;gt; &amp;quot;, 5);&lt;br /&gt;
                csource-&amp;gt;CS_CurChr = 0;&lt;br /&gt;
            }&lt;br /&gt;
        } else&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void DoLockRecord(BPTR fh, struct RDArgs *rdargs)&lt;br /&gt;
{&lt;br /&gt;
    struct RDArgs *readargs;&lt;br /&gt;
    LONG rargs[5];&lt;br /&gt;
    ULONG offset, length, timeout, mode;&lt;br /&gt;
    ULONG result;&lt;br /&gt;
    struct LockNode *lnode;&lt;br /&gt;
&lt;br /&gt;
    offset = length = timeout = mode = 0;&lt;br /&gt;
    rargs[0] = rargs[1] = rargs[2] = rargs[3] = rargs[4] = 0;&lt;br /&gt;
&lt;br /&gt;
    if (readargs = IDOS-&amp;gt;ReadArgs(LOCK_TEMPLATE, rargs, rdargs))&lt;br /&gt;
    {&lt;br /&gt;
        if (rargs[OFFSET_POS])&lt;br /&gt;
            offset = *((LONG *)rargs[OFFSET_POS]);&lt;br /&gt;
        if (rargs[LENGTH_POS])&lt;br /&gt;
            length = *((LONG *)rargs[LENGTH_POS]);&lt;br /&gt;
        if (rargs[TIMEOUT_POS])&lt;br /&gt;
            timeout = *((LONG *)rargs[TIMEOUT_POS]);&lt;br /&gt;
&lt;br /&gt;
        /* Type of locking */&lt;br /&gt;
        if (rargs[EXCLUSIVE_POS])&lt;br /&gt;
        {&lt;br /&gt;
            if (rargs[IMMEDIATE_POS])&lt;br /&gt;
                mode = REC_EXCLUSIVE_IMMED;&lt;br /&gt;
            else&lt;br /&gt;
                mode = REC_EXCLUSIVE;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            if (rargs[IMMEDIATE_POS])&lt;br /&gt;
                mode = REC_SHARED_IMMED;&lt;br /&gt;
            else&lt;br /&gt;
                mode = REC_SHARED;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        rargs[0] = offset;&lt;br /&gt;
        rargs[1] = length;&lt;br /&gt;
        switch (mode)&lt;br /&gt;
        {&lt;br /&gt;
            case REC_EXCLUSIVE_IMMED:&lt;br /&gt;
                rargs[2] = (LONG)&amp;quot;REC_EXCLUSIVE_IMMED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_EXCLUSIVE:&lt;br /&gt;
                rargs[2] = (LONG)&amp;quot;REC_EXCLUSIVE&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_SHARED_IMMED:&lt;br /&gt;
                rargs[2] = (LONG)&amp;quot;REC_SHARED_IMMED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_SHARED:&lt;br /&gt;
                rargs[2] = (LONG)&amp;quot;REC_SHARED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        rargs[3] = timeout;&lt;br /&gt;
&lt;br /&gt;
        /* Show what I&#039;m going to do */&lt;br /&gt;
        IUtility-&amp;gt;VFPrintf(Output(),&lt;br /&gt;
            &amp;quot;LockRecord: Offset %ld, Length %ld, Mode %s, Timeout %ld...&amp;quot;,&lt;br /&gt;
            rargs);&lt;br /&gt;
        IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
        /* Lock the record. Parameters are not checked. It is f.e. possible to&lt;br /&gt;
         * specify an offset larger than the size of the file. Possible since&lt;br /&gt;
         * Record Locks are not related to the file itself, only the means for&lt;br /&gt;
         * you to do arbitration.&lt;br /&gt;
         *&lt;br /&gt;
         * Note that the timeout value is in ticks...&lt;br /&gt;
         */&lt;br /&gt;
        result = IDOS-&amp;gt;LockRecord(fh, offset, length, mode, timeout);&lt;br /&gt;
&lt;br /&gt;
        if (result == DOSTRUE)&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;OK\n&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
            /* Add a node to track this record lock */&lt;br /&gt;
            if (lnode = IExec-&amp;gt;AllocVec(sizeof(struct LockNode), AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
            {&lt;br /&gt;
                lnode-&amp;gt;ln_Counter = locklist-&amp;gt;lh_Counter++;&lt;br /&gt;
                lnode-&amp;gt;ln_Offset = offset;&lt;br /&gt;
                lnode-&amp;gt;ln_Length = length;&lt;br /&gt;
                lnode-&amp;gt;ln_Mode = mode;&lt;br /&gt;
&lt;br /&gt;
                IExec-&amp;gt;AddTail((struct List *)locklist, (struct Node *)lnode);&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                /* Not enough memory for node. You&#039;re on your own... */&lt;br /&gt;
                IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;Not enough memory to track record lock.\n&amp;quot;, 40);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;FAILED\n&amp;quot;, 7);&lt;br /&gt;
            if (IDOS-&amp;gt;IoErr())&lt;br /&gt;
                IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Release memory associated with readargs */&lt;br /&gt;
        IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
    } else&lt;br /&gt;
        IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void DoUnLockRecord(BPTR fh, struct RDArgs *rdargs)&lt;br /&gt;
{&lt;br /&gt;
    struct RDArgs *readargs;&lt;br /&gt;
    LONG rargs[2];&lt;br /&gt;
    ULONG offset, length;&lt;br /&gt;
    ULONG result;&lt;br /&gt;
    struct LockNode *lnode;&lt;br /&gt;
&lt;br /&gt;
    offset = length = 0;&lt;br /&gt;
    rargs[0] = rargs[1] = 0;&lt;br /&gt;
&lt;br /&gt;
    if (readargs = IDOS-&amp;gt;ReadArgs(LOCK_TEMPLATE, rargs, rdargs))&lt;br /&gt;
    {&lt;br /&gt;
        if (rargs[OFFSET_POS])&lt;br /&gt;
            offset = *((LONG *)rargs[OFFSET_POS]);&lt;br /&gt;
        if (rargs[LENGTH_POS])&lt;br /&gt;
            length = *((LONG *)rargs[LENGTH_POS]);&lt;br /&gt;
&lt;br /&gt;
        rargs[0] = offset;&lt;br /&gt;
        rargs[1] = length;&lt;br /&gt;
&lt;br /&gt;
        /* Show what I&#039;m going to do */&lt;br /&gt;
        IUtility-&amp;gt;VFPrintf(Output(), &amp;quot;UnLockRecord: Offset %ld, Length %ld...&amp;quot;, rargs);&lt;br /&gt;
        IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
        /* Unlock indicated record with indicated offset and length.&lt;br /&gt;
         * If the same record (same offset/length) is locked multiple times,&lt;br /&gt;
         * only one, the first one in the list , will be unlocked.&lt;br /&gt;
         */&lt;br /&gt;
        result = IDOS-&amp;gt;UnLockRecord(fh, offset, length);&lt;br /&gt;
&lt;br /&gt;
        if (result == DOSTRUE) {&lt;br /&gt;
            IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;OK\n&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
            /* Remove node associated with this lock */&lt;br /&gt;
            if (lnode = FindRecordLock(offset, length))&lt;br /&gt;
            {&lt;br /&gt;
                IExec-&amp;gt;Remove((struct Node *)lnode);&lt;br /&gt;
                FreeVec(lnode);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(), &amp;quot;FAILED\n&amp;quot;, 7); /* Keep locknode */&lt;br /&gt;
            if (IDOS-&amp;gt;IoErr())&lt;br /&gt;
                IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
        }&lt;br /&gt;
        /* Release memory associated with readargs */&lt;br /&gt;
        IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
    } else&lt;br /&gt;
        IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ListRecordLocks(void)&lt;br /&gt;
{&lt;br /&gt;
    struct LockNode *lnode;&lt;br /&gt;
    LONG rargs[4];&lt;br /&gt;
&lt;br /&gt;
    for (lnode = locklist-&amp;gt;lh_Head; lnode-&amp;gt;ln_Succ; lnode = lnode-&amp;gt;ln_Succ)&lt;br /&gt;
    {&lt;br /&gt;
        rargs[0] = lnode-&amp;gt;ln_Counter;&lt;br /&gt;
        rargs[1] = lnode-&amp;gt;ln_Offset;&lt;br /&gt;
        rargs[2] = lnode-&amp;gt;ln_Length;&lt;br /&gt;
&lt;br /&gt;
        switch (lnode-&amp;gt;ln_Mode)&lt;br /&gt;
        {&lt;br /&gt;
            case REC_EXCLUSIVE_IMMED:&lt;br /&gt;
                rargs[3] = (LONG)&amp;quot;REC_EXCLUSIVE_IMMED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_EXCLUSIVE:&lt;br /&gt;
                rargs[3] = (LONG)&amp;quot;REC_EXCLUSIVE&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_SHARED_IMMED:&lt;br /&gt;
                rargs[3] = (LONG)&amp;quot;REC_SHARED_IMMED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
            case REC_SHARED:&lt;br /&gt;
                rargs[3] = (LONG)&amp;quot;REC_SHARED&amp;quot;;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IUtility-&amp;gt;VFPrintf(IDOS-&amp;gt;Output(), &amp;quot;RecordLock #%ld: Offset %ld Length %ld Mode %s\n&amp;quot;, rargs);&lt;br /&gt;
    }&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct LockNode *FindRecordLock(ULONG offset, ULONG length)&lt;br /&gt;
{&lt;br /&gt;
    struct LockNode *lnode;&lt;br /&gt;
&lt;br /&gt;
    for (lnode = locklist-&amp;gt;lh_Head; lnode-&amp;gt;ln_Succ; lnode = lnode-&amp;gt;ln_Succ)&lt;br /&gt;
    {&lt;br /&gt;
        if ((lnode-&amp;gt;ln_Offset == offset) &amp;amp;&amp;amp; lnode-&amp;gt;ln_Length == length)&lt;br /&gt;
            return(lnode);&lt;br /&gt;
    }&lt;br /&gt;
    return(NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ExRecLock1.c ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// ExRecLock1.c&lt;br /&gt;
//&lt;br /&gt;
// This is a simple example of using record locking to create an exclusive record&lt;br /&gt;
// lock on a file, and writing to that record.  The example ExRecLock2 is almost&lt;br /&gt;
// exactly the same as this example, except ExRecLock2 uses the record lock directly&lt;br /&gt;
// after ExRecLock1&#039;s record.  If you try to run ExRecLock1 (or ExRecLock2) while&lt;br /&gt;
// another instance of ExRecLock1 (or ExRecLock2) is running, the second record lock&lt;br /&gt;
// attempt will fail.&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;protob/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define RECORDSIZE   12&lt;br /&gt;
#define RECORDOFFSET 0&lt;br /&gt;
&lt;br /&gt;
UBYTE *string = &amp;quot;ExRecLock1\n&amp;quot;;                           /* This string will be the */&lt;br /&gt;
                                                          /* contents of the record. */&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BPTR fh;&lt;br /&gt;
&lt;br /&gt;
    if (fh = IDOS-&amp;gt;Open(&amp;quot;t:testRLock&amp;quot;, MODE_READWRITE))     /* Open the file, creating */&lt;br /&gt;
    {                                                 /*        it if necessary. */&lt;br /&gt;
        if (DOSTRUE == IDOS-&amp;gt;LockRecord(fh,           /* Lock the record as exclusive, */&lt;br /&gt;
                                  RECORDOFFSET, /* and do not wait if it is not  */&lt;br /&gt;
                                  RECORDSIZE,   /* available immediately.        */&lt;br /&gt;
                                  REC_EXCLUSIVE_IMMED, 0))&lt;br /&gt;
        {&lt;br /&gt;
            LONG error = RECORDOFFSET;&lt;br /&gt;
&lt;br /&gt;
                                 /* If the record is beyond the end of the file, */&lt;br /&gt;
            if (IDOS-&amp;gt;Seek(fh, 0, OFFSET_END) &amp;lt; RECORDOFFSET)    /* lengthen the file. */&lt;br /&gt;
                error = IDOS-&amp;gt;SetFileSize(fh, RECORDOFFSET, OFFSET_BEGINNING);&lt;br /&gt;
&lt;br /&gt;
            if (error == RECORDOFFSET)    /* If there was no error with the file */&lt;br /&gt;
            {                             /*                file size, continue. */&lt;br /&gt;
                if (IDOS-&amp;gt;Seek(fh, RECORDOFFSET, OFFSET_BEGINNING) &amp;lt; 0)&lt;br /&gt;
                    IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Seek() error&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                if (IDOS-&amp;gt;Write(fh, string, RECORDSIZE) &amp;lt; 0)&lt;br /&gt;
                    IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Write() error&amp;quot;);&lt;br /&gt;
                else&lt;br /&gt;
                    IDOS-&amp;gt;PutStr(&amp;quot;Write successful, &amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            IDOS-&amp;gt;PutStr(&amp;quot;Waiting 10 seconds...\n&amp;quot;);&lt;br /&gt;
            IDOS-&amp;gt;Delay(10 * 50);&lt;br /&gt;
            IDOS-&amp;gt;UnLockRecord(fh, RECORDOFFSET, RECORDSIZE);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Record Lock Failed&amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Close(fh);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Open Failed&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ExRecLock2.c ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// ExRecLock2.c&lt;br /&gt;
//&lt;br /&gt;
// This is a simple example of using record locking to create an exclusive record&lt;br /&gt;
// lock on a file, and writing to that record.  The example ExRecLock1 is almost&lt;br /&gt;
// exactly the same as this example, except ExRecLock1 uses the record lock directly&lt;br /&gt;
// before ExRecLock2&#039;s record.  If you try to run ExRecLock2 (or ExRecLock1) while&lt;br /&gt;
// another instance of ExRecLock2 (or ExRecLock1) is running, the second record lock&lt;br /&gt;
// attempt will fail.&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define RECORDSIZE   12&lt;br /&gt;
#define RECORDOFFSET 12&lt;br /&gt;
&lt;br /&gt;
UBYTE *string = &amp;quot;ExRecLock2\n&amp;quot;;                           /* This string will be the */&lt;br /&gt;
                                                          /* contents of the record. */&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BPTR fh;&lt;br /&gt;
&lt;br /&gt;
    if (fh = IDOS-&amp;gt;Open(&amp;quot;t:testRLock&amp;quot;, MODE_READWRITE))     /* Open the file, creating */&lt;br /&gt;
    {                                                 /*        it if necessary. */&lt;br /&gt;
        if (DOSTRUE == IDOS-&amp;gt;LockRecord(fh,           /* Lock the record as exclusive, */&lt;br /&gt;
                                  RECORDOFFSET, /* and do not wait if it is not  */&lt;br /&gt;
                                  RECORDSIZE,   /* available immediately.        */&lt;br /&gt;
                                  REC_EXCLUSIVE_IMMED, 0))&lt;br /&gt;
        {&lt;br /&gt;
            LONG error = RECORDOFFSET;&lt;br /&gt;
&lt;br /&gt;
                                 /* If the record is beyond the end of the file, */&lt;br /&gt;
            if (IDOS-&amp;gt;Seek(fh, 0, OFFSET_END) &amp;lt; RECORDOFFSET)    /* lengthen the file. */&lt;br /&gt;
                error = IDOS-&amp;gt;SetFileSize(fh, RECORDOFFSET, OFFSET_BEGINNING);&lt;br /&gt;
&lt;br /&gt;
            if (error == RECORDOFFSET)    /* If there was no error with the file */&lt;br /&gt;
            {                             /*                file size, continue. */&lt;br /&gt;
                if (IDOS-&amp;gt;Seek(fh, RECORDOFFSET, OFFSET_BEGINNING) &amp;lt; 0)&lt;br /&gt;
                    IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Seek() error&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                if (IDOS-&amp;gt;Write(fh, string, RECORDSIZE) &amp;lt; 0)&lt;br /&gt;
                    IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Write() error&amp;quot;);&lt;br /&gt;
                else&lt;br /&gt;
                    IDOS-&amp;gt;PutStr(&amp;quot;Write successful, &amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            IDOS-&amp;gt;PutStr(&amp;quot;Waiting 10 seconds...\n&amp;quot;);&lt;br /&gt;
            IDOS-&amp;gt;Delay(10 * 50);&lt;br /&gt;
            IDOS-&amp;gt;UnLockRecord(fh, RECORDOFFSET, RECORDSIZE);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Record Lock Failed&amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Close(fh);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), &amp;quot;Open Failed&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Interrupts&amp;diff=12550</id>
		<title>Exec Interrupts</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Interrupts&amp;diff=12550"/>
		<updated>2025-01-26T19:30:08Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{NeedUpdate}}{{CodeReview}}&lt;br /&gt;
== Exec Interrupts ==&lt;br /&gt;
&lt;br /&gt;
== Interrupts ==&lt;br /&gt;
&lt;br /&gt;
Exec manages the decoding, dispatching, and sharing of all system interrupts. This includes control of hardware interrupts, software interrupts, task-relative interrupts (see the discussion of exceptions in [[Exec_Tasks|Exec Tasks]]), and interrupt disabling and enabling. In addition, Exec supports a more extended prioritization of interrupts than that provided in the CPU.&lt;br /&gt;
&lt;br /&gt;
The proper operation of multitasking depends heavily on the consistent management of the interrupt system. Task activities are often driven by inter-system communication that is originated by various interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Sequence of Events During an Interrupt ===&lt;br /&gt;
&lt;br /&gt;
Before useful interrupt handling code can be executed, a considerable amount of hardware and software activity must occur. Each interrupt must propagate through several hardware and software interfaces before application code is finally dispatched:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;A hardware device decides to cause an interrupt and sends a signal to the interrupt control portions of the &#039;&#039;4703&#039;&#039; (Paula) custom chip.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The &#039;&#039;4703&#039;&#039; interrupt control logic notices this new signal and performs two primary operations. First, it records that the interrupt has been requested by setting a flag bit in the INTREQ register. Second, it examines the INTENA register to determine whether the corresponding interrupt and the interrupt master are enabled. If both are enabled, the &#039;&#039;4703&#039;&#039; generates an interrupt request by placing the priority level of the request onto the three &#039;&#039;68000&#039;&#039; interrupt control input lines (IPL0, IPL1, IPL2).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;These three signals correspond to seven interrupt priority levels in the 68000. If the priority of the new interrupt is &#039;&#039;greater&#039;&#039; than the current processor priority, an interrupt sequence is initiated. The priority level of the new interrupt is used to index into the top seven words of the processor address space. The odd byte (a vector number) of the indexed word is fetched and then shifted left by two to create an offset into the processor&#039;s auto-vector interrupt table. The vector offsets used are in the range of $064 to $07C. These are labeled as &#039;&#039;interrupt autovectors&#039;&#039; in the &#039;&#039;68000&#039;&#039; manual. The auto-vector table appears in low memory on a &#039;&#039;68000&#039;&#039; system, but its location for other &#039;&#039;68000&#039;&#039; family processors is determined by the processor&#039;s CPU &#039;&#039;Vector Base Register&#039;&#039; (VBR). VBR can be accessed from supervisor mode with the MOVEC instruction.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The processor then switches into &#039;&#039;supervisor&#039;&#039; mode (if it is not already in that mode), and saves copies of the status register and program counter (PC) onto the top of the &#039;&#039;system&#039;&#039; stack (additional information may be saved by processors other than the &#039;&#039;68000&#039;&#039;). The processor priority is then raised to the level of the active interrupt.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;From the low memory vector address (calculated in step three above), a 32-bit &#039;&#039;autovector&#039;&#039; address is fetched and loaded into the program counter. This is an entry point into Exec&#039;s interrupt dispatcher.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Exec must now further decode the interrupt by examining the INTREQ and INTENA &#039;&#039;4703&#039;&#039; chip registers. Once the active interrupt has been determined, Exec indexes into an ExecBase array to fetch the interrupt&#039;s handler entry point and handler data pointer addresses.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Exec now turns control over to the interrupt handler by calling it as if it were a subroutine. This handler may deal with the interrupt directly or may propagate control further by invoking interrupt server chain processing.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see from the above discussion that the interrupt autovectors &#039;&#039;should never be altered by the user&#039;&#039;. If you wish to provide your own system interrupt handler, you must use the Exec SetIntVector() function. You should not change the contents of any autovector location.&lt;br /&gt;
&lt;br /&gt;
Task multiplexing usually occurs as the result of an interrupt. When an interrupt has finished and the processor is about to return to user mode, Exec determines whether task-scheduling attention is required. If a task was signaled during interrupt processing, the task scheduler will be invoked. Because Exec uses preemptive task scheduling, it can be said that the interrupt subsystem is the heart of task multiplexing. If, for some reason, interrupts do not occur, a task might execute forever because it cannot be forced to relinquish the CPU.&lt;br /&gt;
&lt;br /&gt;
[[File:LibTable26-1.png|frame|center|Interrupts by Priority]]&lt;br /&gt;
&lt;br /&gt;
=== Interrupt Priorities ===&lt;br /&gt;
&lt;br /&gt;
Interrupts are prioritized in hardware and software. The 68000 CPU priority at which an interrupt executes is determined strictly by hardware. In addition to this, the software imposes a finer level of &#039;&#039;pseudo-priorities&#039;&#039; on interrupts with the same CPU priority. These pseudo-priorities determine the order in which simultaneous interrupts of the same CPU priority are processed. Multiple interrupts with the same CPU priority but a different pseudo-priority will not interrupt one another. Interrupts are serviced by either an exclusive handler or by server chains to which many servers may be attached, as shown in the Type field of the next table. The table above summarizes all interrupts by priority.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;8520&#039;&#039;s (also called &#039;&#039;CIA&#039;&#039;s) are Amiga peripheral interface adapter chips that generate the INT2 and INT6 interrupts. For more information about them, see the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
As described in the Motorola &#039;&#039;68000 Programmer&#039;s Manual&#039;&#039;, interrupts may nest only in the direction of higher priority. Because of the time-critical nature of many interrupts on the Amiga, the CPU priority level &#039;&#039;must never be changed&#039;&#039; by user or system code. When the system is running in user mode (multitasking), the CPU priority level must remain set at zero. When an interrupt occurs, the CPU priority is raised to the level appropriate for that interrupt. Lowering the CPU priority would permit unlimited interrupt recursion on the system stack and would &amp;quot;short-circuit&amp;quot; the interrupt-priority scheme.&lt;br /&gt;
&lt;br /&gt;
Because it is dangerous on the Amiga to hold off interrupts for any period of time, higher-level interrupt code must perform its business and exit promptly. If it is necessary to perform a time-consuming operation as the result of a high-priority interrupt, the operation should be deferred either by posting a &#039;&#039;software interrupt&#039;&#039; or by signalling a task. In this way, interrupt response time is kept to a minimum. Software interrupts are described in a later section.&lt;br /&gt;
&lt;br /&gt;
=== Nonmaskable Interrupt ===&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;68000&#039;&#039; provides a nonmaskable interrupt (NMI) of CPU priority 7. Although this interrupt cannot be generated by the Amiga hardware itself, it can be generated on the expansion bus by external hardware. Because this interrupt does not pass through the &#039;&#039;4703&#039;&#039; interrupt controller circuitry, it is capable of violating system code critical sections. In particular, it short-circuits the DISABLE mutual-exclusion mechanism. Code that uses NMI must not assume that it can access system data structures.&lt;br /&gt;
&lt;br /&gt;
Interrupts are serviced on the Amiga through the use of interrupt &#039;&#039;handlers&#039;&#039; and &#039;&#039;servers&#039;&#039;. An interrupt handler is a system routine that exclusively handles all processing related to a particular &#039;&#039;4703&#039;&#039; interrupt. An interrupt server is one of possibly many system routines that are invoked as the result of a single &#039;&#039;4703&#039;&#039; interrupt. Interrupt servers provide a means of interrupt sharing. This concept is useful for general-purpose interrupts such as vertical blanking.&lt;br /&gt;
&lt;br /&gt;
At system start, Exec designates certain interrupts as handlers and others as server chains. The PORTS, COPER, VERTB, EXTER, and NMI interrupts are initialized as server chains. Therefore, each of these may execute multiple interrupt routines per each interrupt. All other interrupts are designated as handlers and are always used exclusively.&lt;br /&gt;
&lt;br /&gt;
=== Interrupt Data Structure ===&lt;br /&gt;
&lt;br /&gt;
Interrupt handlers and servers are defined by the Exec Interrupt structure. This structure specifies an interrupt routine entry point and data pointer. The C definition of this structure is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Interrupt&lt;br /&gt;
{&lt;br /&gt;
    struct Node is_Node;&lt;br /&gt;
    APTR        is_Data;&lt;br /&gt;
    VOID      (*is_Code)();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once this structure has been properly initialized, it can be used for either a handler or a server.&lt;br /&gt;
&lt;br /&gt;
=== Environment ===&lt;br /&gt;
&lt;br /&gt;
Interrupts execute in an environment different from that of tasks. All interrupts execute in &#039;&#039;supervisor mode&#039;&#039; and utilize the single &#039;&#039;system stack&#039;&#039;. This stack is large enough to handle extreme cases of nested interrupts (of higher priorities). Interrupt processing has no effect on task stack usage.&lt;br /&gt;
&lt;br /&gt;
All interrupt processing code, both handlers and servers, is invoked as assembly code subroutines. Normal assembly code register conventions dictate that the D0, D1, A0 and A1 registers be free for scratch use. In the case of an interrupt handler, some of these registers also contain data that may be useful to the handler code. See the section on handlers below.&lt;br /&gt;
&lt;br /&gt;
Because interrupt processing executes outside the context of most system activities, certain data structures will not be self-consistent and must be considered off limits for all practical purposes. This happens because certain system operations are not atomic in nature and might be interrupted only after executing part of an important instruction sequence. For example, memory allocation and deallocation routines do not disable interrupts. This results in the possibility of interrupting a memory-related routine. In such a case, a memory linked list may be inconsistent during and interrupt. Therefore, interrupt routines must not use any memory allocation or deallocation functions.&lt;br /&gt;
&lt;br /&gt;
In addition, interrupts may not call any system function which might allocate memory, wait, manipulate unprotected lists, or modify ExecBase-&amp;amp;gt;ThisTask data (for example Forbid(), Permit(), and mathieee libraries). In practice, this means that very few system calls may be used within interrupt code. The following functions may generally be used safely within interrupts:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Alert()&lt;br /&gt;
|-&lt;br /&gt;
| Disable()&lt;br /&gt;
|-&lt;br /&gt;
| Enable()&lt;br /&gt;
|-&lt;br /&gt;
| Signal()&lt;br /&gt;
|-&lt;br /&gt;
| Cause()&lt;br /&gt;
|-&lt;br /&gt;
| GetMsg()&lt;br /&gt;
|-&lt;br /&gt;
| PutMsg()&lt;br /&gt;
|-&lt;br /&gt;
| ReplyMsg()&lt;br /&gt;
|-&lt;br /&gt;
| FindPort()&lt;br /&gt;
|-&lt;br /&gt;
| FindTask()&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
and if you are manipulating your &#039;&#039;own&#039;&#039; List structures while in an interrupt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| AddHead()&lt;br /&gt;
|-&lt;br /&gt;
| AddTail()&lt;br /&gt;
|-&lt;br /&gt;
| RemHead()&lt;br /&gt;
|-&lt;br /&gt;
| RemTail()&lt;br /&gt;
|-&lt;br /&gt;
| FindName()&lt;br /&gt;
|-&lt;br /&gt;
| FindIName()&lt;br /&gt;
|-&lt;br /&gt;
| GetHead()&lt;br /&gt;
|-&lt;br /&gt;
| GetTail()&lt;br /&gt;
|-&lt;br /&gt;
| GetSucc()&lt;br /&gt;
|-&lt;br /&gt;
| GetPred()&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In addition, certain devices (notably the timer device) specifically allow limited use of SendIO() and BeginIO() within interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Interrupt Handlers ===&lt;br /&gt;
&lt;br /&gt;
As described above, an interrupt handler is a system routine that exclusively handles all processing related to a particular &#039;&#039;4703&#039;&#039; interrupt. There can only be one handler per &#039;&#039;4703&#039;&#039; interrupt. Every interrupt handler consists of an Interrupt structure (as defined above) and a single assembly code routine. Optionally, a data structure pointer may also be provided. This is particularly useful for ROM-resident interrupt code.&lt;br /&gt;
&lt;br /&gt;
An interrupt handler is passed control as if it were a subroutine of Exec. Once the handler has finished its business, it must return to Exec by executing an RTS (return from subroutine) instruction rather than an RTE (return from exception) instruction. Interrupt handlers should be kept very short to minimize service-time overhead and thus minimize the possibilities of interrupt overruns. As described above, an interrupt handler has the normal scratch registers at its disposal. In addition, A5 and A6 are free for use. These registers are saved by Exec as part of the interrupt initiation cycle.&lt;br /&gt;
&lt;br /&gt;
For the sake of efficiency, Exec passes certain register parameters to the handler (see the list below). These register values may be utilized to trim a few microseconds off the execution time of a handler. All of the following registers (D0/D1/A0/A1/A5/A6) may be used as scratch registers by an interrupt handler, and need not be restored prior to returning.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t Make Assumptions About Registers|text=Interrupt servers have different register usage rules (see the &amp;quot;Interrupt Servers&amp;quot; section).}}&lt;br /&gt;
&lt;br /&gt;
==== Interrupt Handler Register Usage ====&lt;br /&gt;
&lt;br /&gt;
Here are the register conventions for interrupt handlers.&lt;br /&gt;
&lt;br /&gt;
; D0&lt;br /&gt;
: Contains no valid information.&lt;br /&gt;
&lt;br /&gt;
; D1&lt;br /&gt;
: Contains the &#039;&#039;4703&#039;&#039; INTENAR and INTREQR registers values &#039;&#039;&#039;AND&#039;&#039;&#039;&#039;ed together. This results in an indication of which interrupts are enabled &#039;&#039;and&#039;&#039; active.&lt;br /&gt;
&lt;br /&gt;
; A0&lt;br /&gt;
: Points to the base address of the Amiga custom chips. This information is useful for performing indexed instruction access to the chip registers.&lt;br /&gt;
&lt;br /&gt;
; A1&lt;br /&gt;
: Points to the data area specified by the is_Data field of the Interrupt structure. Because this pointer is always fetched (regardless of whether you use it), it is to your advantage to make some use of it.&lt;br /&gt;
&lt;br /&gt;
; A5&lt;br /&gt;
: Is used as a vector to your interrupt code.&lt;br /&gt;
&lt;br /&gt;
; A6&lt;br /&gt;
: Points to the Exec library base (SysBase). You may use this register to call Exec functions or set it up as a base register to access your own library or device.&lt;br /&gt;
&lt;br /&gt;
Interrupt handlers are established by passing the Exec function SetIntVector(), your initialized Interrupt structure, and the &#039;&#039;4703&#039;&#039; interrupt bit number of interest. The parameters for this function are as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
SetIntVector(ULONG intNumber, struct Interrupt *interrupt)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first argument is the bit number for which this interrupt server is to respond (example INTB_VERTB). The possible bits for interrupts are defined in &amp;amp;lt;hardware/intbits.h&amp;amp;gt;. The second argument is the address of an interrupt server node as described earlier in this article. Keep in mind that certain interrupts are established as server chains and should not be accessed as handlers.&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates initialization and installation of an assembler interrupt handler. See the [[Resources|Resources]] for more information on allocating resources, and [[Serial_Device|Serial Device]] for the more common method of serial communications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/* rbf.c - Execute me to compile me with SAS C 5.10&lt;br /&gt;
LC -d0 -b1 -cfistq -v -y -j73 rbf.c&lt;br /&gt;
Blink FROM LIB:c.o,rbf.o,rbfhandler.o TO rbf LIBRARY LIB:LC.lib,LIB:Amiga.lib&lt;br /&gt;
quit&lt;br /&gt;
&lt;br /&gt;
** rbf.c - serial receive buffer full interrupt handler example.&lt;br /&gt;
** Must be linked with assembler handler rbfhandler.o&lt;br /&gt;
**&lt;br /&gt;
** To receive characters, this example requires ASCII serial input&lt;br /&gt;
** at your Amiga&#039;s current serial hardware baud rate (ie. 9600 after&lt;br /&gt;
** reboot, else last baud rate used)&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/execbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/interrupts.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;resources/misc.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;hardware/custom.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;hardware/intbits.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/misc_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;string.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void)  { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
void chkabort(void) { return; }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BUFFERSIZE 256&lt;br /&gt;
&lt;br /&gt;
extern void RBFHandler();   /* proto for asm interrupt handler */&lt;br /&gt;
void main(void);&lt;br /&gt;
&lt;br /&gt;
struct MiscResource *MiscBase;&lt;br /&gt;
extern struct ExecBase *SysBase;&lt;br /&gt;
extern struct Custom far custom;    /* defined in amiga.lib */&lt;br /&gt;
&lt;br /&gt;
static UBYTE *allocname = &amp;amp;quot;rbf-example&amp;amp;quot;;&lt;br /&gt;
&lt;br /&gt;
struct RBFData {&lt;br /&gt;
    struct Task *rd_Task;&lt;br /&gt;
    ULONG rd_Signal;&lt;br /&gt;
    ULONG rd_BufferCount;&lt;br /&gt;
    UBYTE rd_CharBuffer[BUFFERSIZE + 2];&lt;br /&gt;
    UBYTE rd_FlagBuffer[BUFFERSIZE + 2];&lt;br /&gt;
    UBYTE rd_Name[32];&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
    struct RBFData *rbfdata;&lt;br /&gt;
    UBYTE *currentuser;&lt;br /&gt;
    BYTE signr;&lt;br /&gt;
    struct Device *serdevice;&lt;br /&gt;
    struct Interrupt *rbfint, *priorint;&lt;br /&gt;
    BOOL priorenable;&lt;br /&gt;
    ULONG signal;&lt;br /&gt;
&lt;br /&gt;
    if (MiscBase = OpenResource(&amp;amp;quot;misc.resource&amp;amp;quot;))&lt;br /&gt;
    {&lt;br /&gt;
        currentuser = AllocMiscResource(MR_SERIALPORT, allocname);        /* Allocate the serial */&lt;br /&gt;
        if (currentuser)                                                  /* port registers.     */&lt;br /&gt;
        {&lt;br /&gt;
            printf(&amp;amp;quot;serial hardware allocated by %s. Trying to remove it\n&amp;amp;quot;,&lt;br /&gt;
                   currentuser);                                         /* Hey! someone got it! */&lt;br /&gt;
            Forbid();&lt;br /&gt;
            if (serdevice = (struct Device *)FindName(&amp;amp;amp;SysBase-&amp;amp;gt;DeviceList, currentuser))&lt;br /&gt;
                RemDevice(serdevice);&lt;br /&gt;
            Permit();&lt;br /&gt;
&lt;br /&gt;
            currentuser = AllocMiscResource(MR_SERIALPORT, allocname);          /* and try again */&lt;br /&gt;
        }&lt;br /&gt;
        if (currentuser == NULL)&lt;br /&gt;
        {                                                                      /* Get the serial */&lt;br /&gt;
            currentuser = AllocMiscResource(MR_SERIALBITS, allocname);         /* control bits.  */&lt;br /&gt;
            if (currentuser)&lt;br /&gt;
            {&lt;br /&gt;
                printf(&amp;amp;quot;serial control allocated by %s\n&amp;amp;quot;, currentuser);            /* Give up. */&lt;br /&gt;
                FreeMiscResource(MR_SERIALPORT);&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {                                                                  /* Got them both. */&lt;br /&gt;
                printf(&amp;amp;quot;serial hardware allocated\n&amp;amp;quot;);&lt;br /&gt;
                if ((signr = AllocSignal(-1)) != -1)          /* Allocate a signal bit for the   */&lt;br /&gt;
                {                                             /* interrupt handler to signal us. */&lt;br /&gt;
                    if (rbfint = AllocMem(sizeof(struct Interrupt), MEMF_PUBLIC|MEMF_CLEAR))&lt;br /&gt;
                    {&lt;br /&gt;
                        if (rbfdata = AllocMem(sizeof(struct RBFData), MEMF_PUBLIC|MEMF_CLEAR))&lt;br /&gt;
                        {&lt;br /&gt;
                            rbfdata-&amp;amp;gt;rd_Task = FindTask(NULL);        /* Init rfbdata structure. */&lt;br /&gt;
                            rbfdata-&amp;amp;gt;rd_Signal = 1L &amp;amp;lt;&amp;amp;lt; signr;&lt;br /&gt;
&lt;br /&gt;
                            rbfint-&amp;amp;gt;is_Node.ln_Type = NT_INTERRUPT;      /* Init interrupt node. */&lt;br /&gt;
                            strcpy(rbfdata-&amp;amp;gt;rd_Name, allocname);&lt;br /&gt;
                            rbfint-&amp;amp;gt;is_Node.ln_Name = rbfdata-&amp;amp;gt;rd_Name;&lt;br /&gt;
                            rbfint-&amp;amp;gt;is_Data = (APTR)rbfdata;&lt;br /&gt;
                            rbfint-&amp;amp;gt;is_Code = RBFHandler;&lt;br /&gt;
                                                                        /* Save state of RBF and */&lt;br /&gt;
                            priorenable = custom.intenar &amp;amp;amp; INTF_RBF ? TRUE : FALSE; /* interrupt */&lt;br /&gt;
                            custom.intena = INTF_RBF;                             /* disable it. */&lt;br /&gt;
                            priorint = SetIntVector(INTB_RBF, rbfint);&lt;br /&gt;
&lt;br /&gt;
                            if (priorint) printf(&amp;amp;quot;replaced the %s RBF interrupt handler\n&amp;amp;quot;,&lt;br /&gt;
                                                 priorint-&amp;amp;gt;is_Node.ln_Name);&lt;br /&gt;
                            printf(&amp;amp;quot;enabling RBF interrupt\n&amp;amp;quot;);&lt;br /&gt;
                            custom.intena = INTF_SETCLR | INTF_RBF;&lt;br /&gt;
&lt;br /&gt;
                            printf(&amp;amp;quot;waiting for buffer to fill up. Use CTRL-C to break\n&amp;amp;quot;);&lt;br /&gt;
                            signal = Wait(1L &amp;amp;lt;&amp;amp;lt; signr | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                            if (signal &amp;amp;amp; SIGBREAKF_CTRL_C) printf(&amp;amp;quot;&amp;amp;gt;break&amp;amp;lt;\n&amp;amp;quot;);&lt;br /&gt;
                            printf(&amp;amp;quot;Character buffer contains:\n%s\n&amp;amp;quot;, rbfdata-&amp;amp;gt;rd_CharBuffer);&lt;br /&gt;
&lt;br /&gt;
                            custom.intena = INTF_RBF;               /* Restore previous handler. */&lt;br /&gt;
                            SetIntVector(INTB_RBF, priorint);&lt;br /&gt;
                                                                  /* Enable it if it was enabled */&lt;br /&gt;
                            if (priorenable) custom.intena = INTF_SETCLR|INTF_RBF;    /* before. */&lt;br /&gt;
&lt;br /&gt;
                            FreeMem(rbfdata, sizeof(struct RBFData));&lt;br /&gt;
                        }&lt;br /&gt;
                        else  printf(&amp;amp;quot;can&#039;t allocate memory for rbf data\n&amp;amp;quot;);&lt;br /&gt;
                        FreeMem(rbfint, sizeof(struct Interrupt));&lt;br /&gt;
                    }&lt;br /&gt;
                    else printf(&amp;amp;quot;can&#039;t allocate memory for interrupt structure\n&amp;amp;quot;);&lt;br /&gt;
                    FreeSignal(signr);&lt;br /&gt;
                }&lt;br /&gt;
                else printf(&amp;amp;quot;can&#039;t allocate signal\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                FreeMiscResource(MR_SERIALBITS);   /* release serial hardware */&lt;br /&gt;
                FreeMiscResource(MR_SERIALPORT);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } /* There is no &#039;CloseResource()&#039; function */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assembler interrupt handler code, RBFHandler, reads the complete word of serial input data from the serial hardware and then separates the character and flag bytes into separate buffers. When the buffers are full, the handler signals the main process causing main to print the character buffer contents, remove the handler, and exit.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=The data structure containing the signal to use, task address pointer, and buffers is allocated and initialized in main(), and passed to the handler (shown below) via the is_Data pointer of the Interrupt structure.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
* rbfhandler.asm. Example interrupt handler for rbf.&lt;br /&gt;
*&lt;br /&gt;
* Assembled with Howesoft Adapt 680x0 Macro Assembler Rel. 1.0&lt;br /&gt;
* hx68 from: rbfhandler.asm to rbfhandler.o INCDIR include:&lt;br /&gt;
* blink from lib:c.o rbf.o rbfhandler.o to rbf lib lib:lc.lib lib:amiga.lib&lt;br /&gt;
*&lt;br /&gt;
    INCLUDE &amp;amp;quot;exec/types.i&amp;amp;quot;&lt;br /&gt;
    INCLUDE &amp;amp;quot;hardware/custom.i&amp;amp;quot;&lt;br /&gt;
    INCLUDE &amp;amp;quot;hardware/intbits.i&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
        XDEF    _RBFHandler&lt;br /&gt;
&lt;br /&gt;
JSRLIB MACRO&lt;br /&gt;
       XREF _LVO\1&lt;br /&gt;
       JSR  _LVO\1(A6)&lt;br /&gt;
       ENDM&lt;br /&gt;
&lt;br /&gt;
BUFLEN    EQU    256&lt;br /&gt;
&lt;br /&gt;
       STRUCTURE RBFDATA,0&lt;br /&gt;
        APTR   rd_task&lt;br /&gt;
        ULONG  rd_signal&lt;br /&gt;
        UWORD  rd_buffercount&lt;br /&gt;
        STRUCT rd_charbuffer,BUFLEN+2&lt;br /&gt;
        STRUCT rd_flagbuffer,BUFLEN+2&lt;br /&gt;
        STRUCT rd_name,32&lt;br /&gt;
        LABEL RBFDATA_SIZEOF&lt;br /&gt;
&lt;br /&gt;
* Entered with:&lt;br /&gt;
*  D0 == scratch&lt;br /&gt;
*  D1 == INTENAT &amp;amp;amp; INTREQR (scratch)&lt;br /&gt;
*  A0 == custom chips (scratch)&lt;br /&gt;
*  A1 == is_Data which is RBFDATA structure (scratch)&lt;br /&gt;
*  A5 == vector to our code (scratch)&lt;br /&gt;
*  A6 == pointer to ExecBase (scratch)&lt;br /&gt;
*&lt;br /&gt;
* Note - This simple handler just receives one buffer full of serial&lt;br /&gt;
* input data, signals main, then ignores all subsequent serial data.&lt;br /&gt;
*&lt;br /&gt;
    section code&lt;br /&gt;
&lt;br /&gt;
_RBFHandler:                            ;entry to our interrupt handler&lt;br /&gt;
&lt;br /&gt;
        MOVE.W  serdatr(A0),D1          ;get the input word (flags and char)&lt;br /&gt;
&lt;br /&gt;
        MOVE.W  rd_buffercount(A1),D0   ;get our buffer index&lt;br /&gt;
        CMPI.W  #BUFLEN,D0              ;no more room in our buffer ?&lt;br /&gt;
        BEQ.S   ExitHandler             ;yes - just exit (ignore new char)&lt;br /&gt;
        LEA.L   rd_charbuffer(A1),A5    ;else get our character buffer address&lt;br /&gt;
        MOVE.B  D1,0(A5,D0.W)           ;store character in our character buffer&lt;br /&gt;
        LEA.L   rd_flagbuffer(A1),A5    ;get our flag buffer address&lt;br /&gt;
        LSR.W   #8,d1                   ;shift flags down&lt;br /&gt;
        MOVE.B  D1,0(A5,D0.W)           ;store flags in flagbuffer&lt;br /&gt;
&lt;br /&gt;
        ADDQ.W  #1,D0                   ;increment our buffer index&lt;br /&gt;
        MOVE.W  D0,rd_buffercount(A1)   ;   and replace it&lt;br /&gt;
        CMPI.W  #BUFLEN,D0              ;did our buffer just become full ?&lt;br /&gt;
        BNE.S   ExitHandler             ;no - we can exit&lt;br /&gt;
        MOVE.L  A0,-(SP)                ;yes - save custom&lt;br /&gt;
        MOVE.L  rd_signal(A1),D0        ;get signal allocated in main()&lt;br /&gt;
        MOVE.L  rd_task(A1),A1          ;and pointer to main task&lt;br /&gt;
        JSRLIB  Signal                  ;tell main we are full&lt;br /&gt;
        MOVE.L  (SP)+,A0                ;restore custom&lt;br /&gt;
                                        ;Note: system call trashed D0-D1/A0-A1&lt;br /&gt;
ExitHandler:&lt;br /&gt;
        MOVE.W  #INTF_RBF,intreq(A0)    ;clear the interrupt&lt;br /&gt;
        RTS                             ;return to exec&lt;br /&gt;
&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Interrupt Servers ===&lt;br /&gt;
&lt;br /&gt;
As mentioned above, an interrupt server is one of possibly many system interrupt routines that are invoked as the result of a single &#039;&#039;4703&#039;&#039; interrupt. Interrupt servers provide an essential mechanism for interrupt sharing.&lt;br /&gt;
&lt;br /&gt;
Interrupt servers must be used for PORTS, COPER, VERTB, EXTER, or NMI interrupts. For these interrupts, all servers are linked together in a chain. Every server in the chain will be called in turn as long as the previous server returned with the processor&#039;s Z (zero) flag set. If you determine that an interrupt was specifically for your server, you should return with the processor&#039;s Z flag cleared (non-zero condition) so that the remaining servers on the chain will be skipped.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Use The Z Flag|text=VERTB (vertical blank) servers should &#039;&#039;always&#039;&#039; return with the Z (zero) flag set. The processor Z flag is used rather than the normal function convention of returning a result in D0 because it may be tested more quickly by Exec upon the server&#039;s return.}}&lt;br /&gt;
&lt;br /&gt;
The easiest way to set the condition code register is to do an immediate move to the D0 register as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SetZflag_Calls_Next:&lt;br /&gt;
        MOVEQ   #0,D0&lt;br /&gt;
        RTS&lt;br /&gt;
&lt;br /&gt;
ClrZflag_Ends_Chain:&lt;br /&gt;
        MOVEQ   #1,D0&lt;br /&gt;
        RTS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The same Exec Interrupt structure used for handlers is also used for servers. Also, like interrupt handlers, servers must terminate their code with an RTS instruction.&lt;br /&gt;
&lt;br /&gt;
Interrupt servers are called in priority order. The priority of a server is specified in its is_Node.ln_Pri field. Higher-priority servers are called earlier than lower-priority servers. Adding and removing interrupt servers from a particular chain is accomplished with the Exec AddIntServer() and RemIntServer() functions. These functions require you to specify both the &#039;&#039;4703&#039;&#039; interrupt number and a properly initialized Interrupt structure.&lt;br /&gt;
&lt;br /&gt;
Servers have different register values passed than handlers do. A server cannot count on the D0, D1, A0, or A6 registers containing any useful information. However, the highest priority system vertical blank server currently expects to receive a pointer to the custom chips A0. Therefore, if you install a vertical blank server at priority 10 or greater, you must place custom ($DF¬†F000) in A0 before exiting. Other than that, a server is free to use D0-D1 and A0-A1/A5-A6 as scratch.&lt;br /&gt;
&lt;br /&gt;
==== Interrupt Server Register Usage ====&lt;br /&gt;
&lt;br /&gt;
; D0&lt;br /&gt;
: Scratch.&lt;br /&gt;
&lt;br /&gt;
; D1&lt;br /&gt;
: Scratch.&lt;br /&gt;
&lt;br /&gt;
; A0&lt;br /&gt;
: Scratch except in certain cases (see note above).&lt;br /&gt;
&lt;br /&gt;
; A1&lt;br /&gt;
: Points to the data area specified by the is_Data field of the Interrupt structure. Because this pointer is always fetched (regardless of whether you use it), it is to your advantage to make some use of it (scratch).&lt;br /&gt;
&lt;br /&gt;
; A5&lt;br /&gt;
: Points to your interrupt code (scratch).&lt;br /&gt;
&lt;br /&gt;
; A6&lt;br /&gt;
: Scratch.&lt;br /&gt;
&lt;br /&gt;
In a server chain, the interrupt is cleared automatically by the system. Having a server clear its interrupt is not recommended and not necessary (clearing could cause the loss of an interrupt on PORTS or EXTER).&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program to install and remove a low-priority vertical blank interrupt server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/* vertb.c - Execute me to compile me with SAS C 5.10&lt;br /&gt;
LC -d0 -b1 -cfistq -v -y -j73 vertb.c&lt;br /&gt;
Blink FROM LIB:c.o,vertb.o,vertbserver.o TO vertb LIBRARY LIB:LC.lib,LIB:Amiga.lib&lt;br /&gt;
quit ; */&lt;br /&gt;
/* vertb.c - Vertical blank interrupt server example.  Must be linked with vertbserver.o. */&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/interrupts.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;hardware/custom.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;hardware/intbits.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void)  { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
void chkabort(void) { return; }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
extern void VertBServer();  /* proto for asm interrupt server */&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
    struct Interrupt *vbint;&lt;br /&gt;
    ULONG counter = 0;&lt;br /&gt;
    ULONG endcount;&lt;br /&gt;
                                                       /* Allocate memory for  */&lt;br /&gt;
    if (vbint = AllocMem(sizeof(struct Interrupt),     /* interrupt node. */&lt;br /&gt;
                         MEMF_PUBLIC|MEMF_CLEAR))&lt;br /&gt;
    {&lt;br /&gt;
        vbint-&amp;amp;gt;is_Node.ln_Type = NT_INTERRUPT;         /* Initialize the node. */&lt;br /&gt;
        vbint-&amp;amp;gt;is_Node.ln_Pri = -60;&lt;br /&gt;
        vbint-&amp;amp;gt;is_Node.ln_Name = &amp;amp;quot;VertB-Example&amp;amp;quot;;&lt;br /&gt;
        vbint-&amp;amp;gt;is_Data = (APTR)&amp;amp;amp;counter;&lt;br /&gt;
        vbint-&amp;amp;gt;is_Code = VertBServer;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        AddIntServer(INTB_VERTB, vbint); /* Kick this interrupt server to life. */&lt;br /&gt;
&lt;br /&gt;
        printf(&amp;amp;quot;VBlank server will increment a counter every frame.\n&amp;amp;quot;);&lt;br /&gt;
        printf(&amp;amp;quot;counter started at zero, CTRL-C to remove server\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
        endcount = counter;&lt;br /&gt;
        printf(&amp;amp;quot;%ld vertical blanks occurred\nRemoving server\n&amp;amp;quot;, endcount);&lt;br /&gt;
&lt;br /&gt;
        RemIntServer(INTB_VERTB, vbint);&lt;br /&gt;
        FreeMem(vbint, sizeof(struct Interrupt));&lt;br /&gt;
    }&lt;br /&gt;
    else printf(&amp;amp;quot;Can&#039;t allocate memory for interrupt node\n&amp;amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the assembler VertBServer installed by the C example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
* vertbserver.asm. Example simple interrupt server for vertical blank&lt;br /&gt;
*&lt;br /&gt;
* Assembled with Howesoft Adapt 680x0 Macro Assembler Rel. 1.0&lt;br /&gt;
* hx68 from: vertbserver.asm to vertbserver.o INCDIR include:&lt;br /&gt;
* blink from lib:c.o vertb.o vertbserver.o to vertb lib lib:lc.lib lib:amiga.lib&lt;br /&gt;
*&lt;br /&gt;
    INCLUDE &amp;amp;quot;exec/types.i&amp;amp;quot;&lt;br /&gt;
    INCLUDE &amp;amp;quot;hardware/custom.i&amp;amp;quot;&lt;br /&gt;
    INCLUDE &amp;amp;quot;hardware/intbits.i&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
        XDEF    _VertBServer&lt;br /&gt;
&lt;br /&gt;
* Entered with:       A0 == scratch (execpt for highest pri vertb server)&lt;br /&gt;
*  D0 == scratch      A1 == is_Data&lt;br /&gt;
*  D1 == scratch      A5 == vector to interrupt code (scratch)&lt;br /&gt;
*                     A6 == scratch&lt;br /&gt;
*&lt;br /&gt;
    section code&lt;br /&gt;
&lt;br /&gt;
_VertBServer:&lt;br /&gt;
        ADDI.L  #1,(a1)           ; increments counter is_Data points to&lt;br /&gt;
        MOVEQ.L #0,d0             ; set Z flag to continue to process other vb-servers&lt;br /&gt;
        RTS                       ;return to exec&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software Interrupts ==&lt;br /&gt;
&lt;br /&gt;
Exec provides a means of generating &#039;&#039;software interrupts&#039;&#039;. Software interrupts execute at a priority higher than that of tasks but lower than that of hardware interrupts, so they are often used to defer hardware interrupt processing to a lower priority. Software interrupts use the same Interrupt data structure as hardware interrupts. As described above, this structure contains pointers to both interrupt code and data, and should be initialized as node type NT_INTERRUPT (not NT_SOFTINT which is an internal Exec flag).&lt;br /&gt;
&lt;br /&gt;
A software interrupt is usually activated with the Cause() function. If this function is called from a task, the task will be interrupted and the software interrupt will occur. If it is called from a hardware interrupt, the software interrupt will not be processed until the system exits from its last hardware interrupt. If a software interrupt occurs from within another software interrupt, it is not processed until the current one is completed. However, individual software interrupts do not nest, and will not be caused if already running as a software interrupt.&lt;br /&gt;
&lt;br /&gt;
Software interrupts are prioritized. Unlike interrupt servers, software interrupts have only five allowable priority levels: -32, -16, 0, +16, and +32. The priority should be put into the ln_Pri field prior to calling Cause().&lt;br /&gt;
&lt;br /&gt;
Software interrupts can also be generated by message arrival at a PA_SOFTINT message port. The applications of this technique are limited since it is not permissible, with most devices, to send IO requests from within interrupt code. However, the timer.device does allow such interactions, so a self-perpetuating PA_SOFTINT timer port can provide an application with quite consistent timing under varying multitasking loads. The following example demonstrates use of a software interrupt and a PA_SOFTINT port. See the [[Exec_Messages_and_Ports|Exec Messages and Ports]] for more information about messages and ports.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* timersoftint.c - Timer device software interrupt message port example. */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/interrupts.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MICRO_DELAY 1000&lt;br /&gt;
#define OFF     0&lt;br /&gt;
#define ON      1&lt;br /&gt;
#define STOPPED 2&lt;br /&gt;
&lt;br /&gt;
struct TSIData {&lt;br /&gt;
    uint32 tsi_Counter;&lt;br /&gt;
    uint32 tsi_Flag;&lt;br /&gt;
    struct MsgPort *tsi_Port;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct TSIData *tsidata;&lt;br /&gt;
&lt;br /&gt;
void tsoftcode(void);    /* Prototype for our software interrupt code */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    uint32 endcount;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate message port, data &amp;amp; interrupt structures. */&lt;br /&gt;
    tsidata = IExec-&amp;gt;AllocVecTags(sizeof(struct TSIData),&lt;br /&gt;
      AVT_Type, MEMF_SHARED,&lt;br /&gt;
      AVT_Lock, TRUE,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    /* Set up the (software)interrupt structure. Note that this task runs at  */&lt;br /&gt;
    /* priority 0. Software interrupts may only be priority -32, -16, 0, +16, */&lt;br /&gt;
    /* +32. Also note that the correct node type for a software interrupt is   */&lt;br /&gt;
    /* NT_INTERRUPT. (NT_SOFTINT is an internal Exec flag). This is the same  */&lt;br /&gt;
    /* setup as that for a software interrupt which you Cause(). */&lt;br /&gt;
    struct Interrupt *softint = IExec-&amp;gt;AllocSysObjectTags(ASOT_INTERRUPT,&lt;br /&gt;
      ASOINTR_Code, tsoftcode,&lt;br /&gt;
      ASOINTR_Data, tsidata,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
      ASOPORT_AllocSig, FALSE,&lt;br /&gt;
      ASOPORT_Action, PA_SOFTINT,&lt;br /&gt;
      ASOPORT_Target, softint,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if(tsidata != NULL &amp;amp;&amp;amp; softint != NULL &amp;amp;&amp;amp; port != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      softint-&amp;gt;is_Node.ln_Pri = 0;&lt;br /&gt;
&lt;br /&gt;
      /* Allocate timerequest */&lt;br /&gt;
      struct TimeRequest *tr = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
        ASOIOR_ReplyPort, port,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
      if (tr != NULL)&lt;br /&gt;
      {&lt;br /&gt;
        /* Open timer.device. NULL is success. */&lt;br /&gt;
        if (!(IExec-&amp;gt;OpenDevice(&amp;quot;timer.device&amp;quot;, UNIT_MICROHZ, (struct IORequest *)tr, 0)))&lt;br /&gt;
        {&lt;br /&gt;
          tsidata-&amp;gt;tsi_Flag = ON;        /* Init data structure to share globally. */&lt;br /&gt;
          tsidata-&amp;gt;tsi_Port = port;&lt;br /&gt;
&lt;br /&gt;
          /* Send of the first timerequest to start. IMPORTANT: Do NOT   */&lt;br /&gt;
          /* BeginIO() to any device other than audio or timer from      */&lt;br /&gt;
          /* within a software or hardware interrupt. The BeginIO() code */&lt;br /&gt;
          /* may allocate memory, wait or perform other functions which  */&lt;br /&gt;
          /* are illegal or dangerous during interrupts.                 */&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;starting softint. CTRL-C to break...\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          tr-&amp;gt;Request.io_Command = TR_ADDREQUEST;    /* Initial iorequest to start */&lt;br /&gt;
          tr-&amp;gt;Time.Microseconds = MICRO_DELAY;        /* software interrupt.        */&lt;br /&gt;
          IExec-&amp;gt;BeginIO((struct IORequest *)tr);&lt;br /&gt;
&lt;br /&gt;
          IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
          endcount = tsidata-&amp;gt;tsi_Counter;&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;timer softint counted %lu milliseconds.\n&amp;quot;, endcount);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Stopping timer...\n&amp;quot;);&lt;br /&gt;
          tsidata-&amp;gt;tsi_Flag = OFF;&lt;br /&gt;
&lt;br /&gt;
          while (tsidata-&amp;gt;tsi_Flag != STOPPED) IDOS-&amp;gt;Delay(10);&lt;br /&gt;
&lt;br /&gt;
          IExec-&amp;gt;CloseDevice((struct IORequest *)tr);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;couldn&#039;t open timer.device\n&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;couldn&#039;t create timerequest\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, tr);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_INTERRUPT, softint);&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, port);&lt;br /&gt;
    IExec-&amp;gt;FreeVec(tsidata);&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void tsoftcode(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Remove the message from the port. */&lt;br /&gt;
    struct TimeRequest *tr = (struct TimeRequest *)IExec-&amp;gt;GetMsg(tsidata-&amp;gt;tsi_Port);&lt;br /&gt;
&lt;br /&gt;
    /* Keep on going if main() hasn&#039;t set flag to OFF. */&lt;br /&gt;
    if ((tr) &amp;amp;&amp;amp; (tsidata-&amp;gt;tsi_Flag == ON))&lt;br /&gt;
    {&lt;br /&gt;
        /* increment counter and re-send timerequest--IMPORTANT: This         */&lt;br /&gt;
        /* self-perpetuating technique of calling BeginIO() during a software */&lt;br /&gt;
        /* interrupt may only be used with the audio and timer device.        */&lt;br /&gt;
        tsidata-&amp;gt;tsi_Counter++;&lt;br /&gt;
        tr-&amp;gt;Request.io_Command = TR_ADDREQUEST;&lt;br /&gt;
        tr-&amp;gt;Time.Microseconds = MICRO_DELAY;&lt;br /&gt;
        IExec-&amp;gt;BeginIO((struct IORequest *)tr);&lt;br /&gt;
    }&lt;br /&gt;
    /* Tell main() we&#039;re out of here. */&lt;br /&gt;
    else tsidata-&amp;gt;tsi_Flag = STOPPED;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Disabling Interrupts ==&lt;br /&gt;
&lt;br /&gt;
As mentioned in [[Exec_Tasks|Exec Tasks]], it is sometimes necessary to disable interrupts when examining or modifying certain shared system data structures. However, for proper system operation, interrupts should never be disabled unless absolutely necessary, and &#039;&#039;never for more than 250 microseconds&#039;&#039;. Interrupt disabling is controlled with the Disable() and Enable() functions.&lt;br /&gt;
&lt;br /&gt;
In some system code, there are nested disabled sections. Such code requires that interrupts be disabled with the first Disable() and not re-enabled until the &#039;&#039;last&#039;&#039; Enable(). The system Enable() and Disable() functions are designed to permit this sort of nesting.&lt;br /&gt;
&lt;br /&gt;
Disable() increments a counter to track how many levels of disable have been issued. Only 126 levels of nesting are permitted. Enable() decrements the counter, and reenables interrupts when the last disable level has been exited.&lt;br /&gt;
&lt;br /&gt;
== Quick Interrupts ==&lt;br /&gt;
&lt;br /&gt;
One of the features of the Zorro III bus is the Quick Interrupt, also known as the vectored interrupt. This feature allows Zorro III&lt;br /&gt;
hardware to supply a vector number to the system when an interrupt occurs. The system uses this vector number to go directly to an&lt;br /&gt;
interrupt routine.&lt;br /&gt;
&lt;br /&gt;
=== Conventional Amiga Interrupts ===&lt;br /&gt;
&lt;br /&gt;
The Amiga handles normal interrupts from Zorro II cards using an interrupt server chain. There are two interrupts available from the&lt;br /&gt;
Zorro II bus, the PORTS and EXTER interrupt server chains. If a driver for a Zorro II card needs to use an interrupt, it adds an interrupt routine to the appropriate chain. When the interrupt occurs, Exec calls each routine in the interrupt chain, which are sorted in priority order. Exec continues until it finds the routine that corresponds to the device that triggered the interrupt.&lt;br /&gt;
&lt;br /&gt;
The server chain allows several routines to share a single interrupt. This means that several devices trigger the same interrupt, so each interrupt routine must do some processing to determine if its card triggered the interrupt or if some other source caused the interrupt. For example, an interrupt routine might examine a register on its card to determine that the card triggered the interrupt.&lt;br /&gt;
&lt;br /&gt;
Although this scheme allows unrelated pieces of software to easily share an interrupt, it can make the interrupt overhead rather high.&lt;br /&gt;
These two interrupt server chains also handle interrupts from the CIA chips, which are used to trigger a variety of events. As a result, these server chains can contain a multitude of interrupt routines.&lt;br /&gt;
&lt;br /&gt;
Consider what happens when a Zorro II card generates a PORTS interrupt. Exec has to perform some set up and then step through the PORTS server chain. Exec calls each interrupt routine in priority order looking for the routine that services this interrupt. If there are 20 interrupt routines of higher priority than the card&#039;s interrupt routine in the server chain, Exec has to call 20 other routines before it gets to the correct routine.&lt;br /&gt;
&lt;br /&gt;
=== Zorro III Quick Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Quick interrupts avoid the overhead involved in Exec&#039;s interrupt server chains. Exec only helps set up the quick interrupt, which it does via the exec.library function ObtainQuickVector(). Once Exec has set up the quick interrupt routine, it does not intervene. Unlike conventional Amiga interrupt routines, which are called as subroutines from Exec&#039;s main interrupt code, the Amiga jumps directly to the quick interrupt routine using a private vector. This behavior requires quick interrupt routines to take some special precautions.&lt;br /&gt;
&lt;br /&gt;
There are two important differences between a conventional Amiga interrupt routine and a quick quick interrupt routine. A quick&lt;br /&gt;
interrupt routine must save and restore all of the registers it changes, including D0, D1, A0, and A1. It must do this because, unlike&lt;br /&gt;
regular interrupt routines, Exec doesn&#039;t do it for you. Also, a quick interrupt routine ends with a RTE (return from exception) instruction.&lt;br /&gt;
&lt;br /&gt;
If your quick interrupt routine is 100% self-contained and does not access any operating system structures or routines, then the work is rather simple. Just save the registers you use, perform your interrupt processing, restore the registers, and end with an RTE. If, however, the routine needs to call the OS or use an OS structure, it must check if the interrupt has been delayed. This is necessary in case the interrupt hit the CPU just after the CPU had told the hardware to hold off interrupts (see the Autodoc for ObtainQuickVector() to find out how to perform this test).&lt;br /&gt;
&lt;br /&gt;
As the Amiga OS is a dynamic operating system, quick interrupts are allocated by the OS. If your hardware/software wants to use a quick&lt;br /&gt;
interrupt, it must allocate a vector with ObtainQuickVector(). This routine accepts a pointer to the quick interrupt code (not a pointer to an Interrupt structure). If Exec installed the vector, ObtainQuickVector() returns the vector number. When the quick&lt;br /&gt;
interrupt occurs, the Zorro III card sends this vector number to the CPU, which tells the CPU where the interrupt code is.&lt;br /&gt;
&lt;br /&gt;
ObtainQuickVector() returns 0 if there are no more vectors. Since the number of vectors is limited, any Zorro III device that uses quick interrupts must be able to fall back to the Amiga&#039;s conventional interrupt scheme.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control interrupts. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Interrupt Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AddIntServer()&lt;br /&gt;
| Add an interrupt server to a system server chain.&lt;br /&gt;
|-&lt;br /&gt;
| Cause()&lt;br /&gt;
| Cause a software interrupt.&lt;br /&gt;
|-&lt;br /&gt;
| Disable()&lt;br /&gt;
| Disable interrupt processing.&lt;br /&gt;
|-&lt;br /&gt;
| Enable()&lt;br /&gt;
| Restart system interrupt processing.&lt;br /&gt;
|-&lt;br /&gt;
| RemIntServer()&lt;br /&gt;
| Remove an interrupt server from a system server chain.&lt;br /&gt;
|-&lt;br /&gt;
| SetIntVector()&lt;br /&gt;
| Set a new handler for a system interrupt vector.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Special_Functions&amp;diff=12549</id>
		<title>Intuition Special Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Special_Functions&amp;diff=12549"/>
		<updated>2025-01-26T19:29:48Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Intuition Special Functions ==&lt;br /&gt;
&lt;br /&gt;
There are several Intuition topics which, while not large enough to fill articles of their own, nonetheless deserve to be discussed. The subjects covered here include locking IntuitionBase, the Intuition memory functions AllocRemember() and FreeRemember(), using sprites with Intuition, and Intuition&#039;s special internal functions.&lt;br /&gt;
&lt;br /&gt;
== Locking IntuitionBase ==&lt;br /&gt;
&lt;br /&gt;
It is sometimes necessary to examine the IntuitionBase structure. Items such as the address of the active screen and window, current mouse coordinates and more can be found there. It is never a good idea to simply read these fields, as they are prone to sudden change. The IntuitionBase structure must always be locked before looking at its fields.&lt;br /&gt;
&lt;br /&gt;
It is necessary to inform Intuition that an application is about to examine IntuitionBase so that Intuition will not change any variables and IntuitionBase will remain static during the access. The call LockIBase() will lock the state of IntuitionBase so that it may be examined. During the time that the application has IntuitionBase locked, all Intuition input processing is frozen. Make every effort to examine IntuitionBase and release the lock as quickly as possible. The values in IntuitionBase are read-only. Applications should never write values to IntuitionBase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG LockIBase( ULONG dontknow );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
LockIBase() is passed a ULONG (dontknow in the prototype above) indicating the Intuition lock desired. For all foreseeable uses of the call this value should be 0. LockIBase() returns a ULONG, that must be passed to UnlockIBase() later to allow IntuitionBase to change once again.&lt;br /&gt;
&lt;br /&gt;
Every call to LockIBase() must be matched by a subsequent call to UnlockIBase():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID UnlockIBase( ULONG ibLock );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the ibLock argument to the value returned by the previous call to LockIBase().&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About LockIBase()|text=This function should not be called while holding any other system locks such as Layer and LayerInfo locks. Between calls to LockIBase() and UnlockIBase(), you may not call any Intuition or other high-level system functions so it is best to copy the information you need and release the lock as quickly as possible.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About IntuitionBase|text=Never, ever, modify any of the fields in IntuitionBase directly. Also, there are fields in IntuitionBase that are considered system private that should not be accessed, even for reading. (Refer to the &amp;lt;intuition/intuitionbase.h&amp;gt; include file.) Application programs cannot depend on (and should not use) the contents of these fields; their usage is subject to change in future revisions of Intuition.}}&lt;br /&gt;
&lt;br /&gt;
== Easy Memory Allocation and Deallocation ==&lt;br /&gt;
&lt;br /&gt;
Intuition has a pair of routines that enable applications to make multiple memory allocations which are easily deallocated with a single call. The Intuition routines for memory management are AllocRemember() and FreeRemember(). These routines rely upon the Remember structure to track allocations.&lt;br /&gt;
&lt;br /&gt;
=== Intuition Helps You Remember ===&lt;br /&gt;
&lt;br /&gt;
The AllocRemember() routine calls the Exec AllocMem() function to perform the memory allocation. (Of course, the application may directly call Exec memory functions, see [[Exec_Memory_Allocation|Exec Memory Allocation]] for details.)&lt;br /&gt;
&lt;br /&gt;
AllocRemember() performs two allocations each time it is called. The first allocation is the actual memory requested by the application. This memory is of the size and type specified in the call and is independent of the second block of memory. The second allocation is memory for a Remember structure which is used to save the specifics of the allocation in a linked list. When FreeRemember() is called it uses the information in this linked list to free all previous memory allocations at once. This is convenient since normally you would have to free each memory block one at a time which requires knowing the size and base address of each one.&lt;br /&gt;
&lt;br /&gt;
The AllocRemember() call takes three arguments:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
APTR AllocRemember( struct Remember **rememberKey, ULONG size, ULONG flags );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rememberKey is the address of a pointer to a Remember structure. Note that this is a double indirection, not just a simple pointer. The size is the size, in bytes, of the requested allocation. The flags argument is the specification for the memory allocation. These are the same as the specifications for the Exec AllocMem() function described in [[Exec_Memory_Allocation|Exec Memory Allocation]].&lt;br /&gt;
&lt;br /&gt;
If AllocRemember() succeeds, it returns the address of the allocated memory block. It returns a NULL if the allocation fails.&lt;br /&gt;
&lt;br /&gt;
The FreeRemember() function gives the option of freeing memory in either of two ways. The first (and most useful) option is to free both the link nodes that AllocRemember() created and the memory blocks to which they correspond. The second option is to free only the link nodes, leaving the memory blocks for further use (and later deallocation via Exec&#039;s FreeMem() function). But, as a general rule, the application should never free only the link nodes as this can greatly fragment memory. If the link nodes are not required, use the Exec memory allocation functions.&lt;br /&gt;
&lt;br /&gt;
The FreeRemember() call is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID FreeRemember( struct Remember **rememberKey, LONG reallyForget );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the rememberKey argument to the address of a pointer to a Remember structure. This is the same value that was passed to previous calls to AllocRemember(). The reallyForget argument is a boolean that should be set to TRUE. If TRUE, then both the link nodes and the memory blocks are freed. If FALSE, then only the link nodes are freed. Again, applications should avoid using the FALSE value since it can lead to highly fragmented memory.&lt;br /&gt;
&lt;br /&gt;
=== How to Remember ===&lt;br /&gt;
&lt;br /&gt;
To use Intuition&#039;s memory functions, first create an anchor for the memory to be allocated by declaring a variable that is a pointer to a Remember structure and initializing that pointer to NULL. This variable is called the &#039;&#039;remember key&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Remember *rememberKey = NULL;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Call AllocRemember() with the address of the remember key, along with the memory requirements for the specific allocation. Multiple allocations may be made before a call to FreeRemember().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
memBlockA = IIntuition-&amp;gt;AllocRemember(&amp;amp;rememberKey, SIZE_A, MEMF_CLEAR | MEMF_PRIVATE);&lt;br /&gt;
if (memBlockA == NULL)&lt;br /&gt;
    {&lt;br /&gt;
    /* error: allocation failed */&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Memory allocation failed.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    /* use the memory here */&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Memory allocation succeeded.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AllocRemember() actually performs two memory allocations per call, one for the memory requested and the other for a Remember structure. The Remember structure is filled in with data describing the allocation, and is linked into the list to which the &#039;&#039;remember key&#039;&#039; points.&lt;br /&gt;
&lt;br /&gt;
To free memory that has been allocated, simply call FreeRemember() with the correct remember key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IIntuition-&amp;gt;FreeRemember(&amp;amp;rememberKey, TRUE);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will free all the memory blocks previously allocated with AllocRemember() in a single call.&lt;br /&gt;
&lt;br /&gt;
=== The Remember Structure ===&lt;br /&gt;
&lt;br /&gt;
The Remember structure is defined in &amp;amp;lt;intuition/intuition.h&amp;amp;gt; as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Remember&lt;br /&gt;
    {&lt;br /&gt;
    struct Remember *NextRemember;&lt;br /&gt;
    ULONG RememberSize;&lt;br /&gt;
    UBYTE *Memory;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generally, the Remember structure is handled only by the system. Here are its fields:&lt;br /&gt;
&lt;br /&gt;
; NextRemember&lt;br /&gt;
: The link to the next Remember structure.&lt;br /&gt;
&lt;br /&gt;
; RememberSize&lt;br /&gt;
: The size of the memory tracked by this node.&lt;br /&gt;
&lt;br /&gt;
; Memory&lt;br /&gt;
: A pointer to the memory tracked by this node.&lt;br /&gt;
&lt;br /&gt;
=== An Example of Remembering ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** RememberTest - Illustrates the use of AllocRemember() and FreeRemember().&lt;br /&gt;
*/&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our function prototypes */&lt;br /&gt;
VOID methodOne(VOID);&lt;br /&gt;
VOID methodTwo(VOID);&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = 0;&lt;br /&gt;
&lt;br /&gt;
/* random sizes to demonstrate the Remember functions. */&lt;br /&gt;
#define SIZE_A 100L&lt;br /&gt;
#define SIZE_B 200L&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** main() - Initialize everything.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  LONG exitVal = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntutitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  if (IIntuition == NULL)&lt;br /&gt;
    exitVal = RETURN_FAIL;&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    methodOne();&lt;br /&gt;
    methodTwo();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
    &lt;br /&gt;
  return exitVal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** MethodOne&lt;br /&gt;
** Illustrates using AllocRemember() to allocate all memory and&lt;br /&gt;
** FreeRemember() to free it all.&lt;br /&gt;
*/&lt;br /&gt;
VOID methodOne(VOID)&lt;br /&gt;
{&lt;br /&gt;
  struct Remember *rememberKey = NULL;&lt;br /&gt;
&lt;br /&gt;
  APTR memBlockA = IIntuition-&amp;gt;AllocRemember(&amp;amp;rememberKey, SIZE_A, MEMF_CLEAR | MEMF_SHARED);&lt;br /&gt;
  if (memBlockA)&lt;br /&gt;
  {&lt;br /&gt;
    /*  The memBlockA allocation succeeded; try for memBlockB.  */&lt;br /&gt;
    APTR memBlockB = IIntuition-&amp;gt;AllocRemember(&amp;amp;rememberKey, SIZE_B, MEMF_CLEAR | MEMF_SHARED);&lt;br /&gt;
    if (memBlockB)&lt;br /&gt;
    {&lt;br /&gt;
      /*  Both memory allocations succeeded.&lt;br /&gt;
       **  The program may now use this memory.&lt;br /&gt;
       */&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* It is not necessary to keep track of the status of each allocation.&lt;br /&gt;
  ** Intuition has kept track of all successful allocations by updating its&lt;br /&gt;
  ** linked list of Remember nodes.  The following call to FreeRemember() will&lt;br /&gt;
  ** deallocate any and all of the memory that was successfully allocated.&lt;br /&gt;
  ** The memory blocks as well as the link nodes will be deallocated because&lt;br /&gt;
  ** the &amp;quot;ReallyForget&amp;quot; parameter is TRUE.&lt;br /&gt;
  **&lt;br /&gt;
  ** It is possible to have reached the call to FreeRemember()&lt;br /&gt;
  ** in one of three states.  Here they are, along with their results.&lt;br /&gt;
  **&lt;br /&gt;
  ** 1. Both memory allocations failed.&lt;br /&gt;
  **       RememberKey is still NULL.  FreeRemember() will do nothing.&lt;br /&gt;
  ** 2. The memBlockA allocation succeeded but the memBlockB allocation failed.&lt;br /&gt;
  **       FreeRemember() will free the memory block pointed to by memBlockA.&lt;br /&gt;
  ** 3. Both memory allocations were successful.&lt;br /&gt;
  **       FreeRemember() will free the memory blocks pointed to by&lt;br /&gt;
  **       memBlockA and memBlockB.&lt;br /&gt;
  */&lt;br /&gt;
  IIntuition-&amp;gt;FreeRemember(&amp;amp;rememberKey, TRUE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** MethodTwo&lt;br /&gt;
** Illustrates using AllocRemember() to allocate all memory,&lt;br /&gt;
** FreeRemember() to free the link nodes, and FreeMem() to&lt;br /&gt;
** free the actual memory blocks.&lt;br /&gt;
*/&lt;br /&gt;
VOID methodTwo(VOID)&lt;br /&gt;
{&lt;br /&gt;
  struct Remember *rememberKey = NULL;&lt;br /&gt;
&lt;br /&gt;
  APTR memBlockA = IIntuition-&amp;gt;AllocRemember(&amp;amp;rememberKey, SIZE_A, MEMF_CLEAR | MEMF_SHARED);&lt;br /&gt;
  if (memBlockA)&lt;br /&gt;
  {&lt;br /&gt;
    /*  The memBlockA allocation succeeded; try for memBlockB.  */&lt;br /&gt;
    APTR memBlockB = AllocRemember(&amp;amp;rememberKey, SIZE_B, MEMF_CLEAR | MEMF_SHARED);&lt;br /&gt;
    if (memBlockB)&lt;br /&gt;
    {&lt;br /&gt;
      /* Both memory allocations succeeded.&lt;br /&gt;
      ** For the purpose of illustration, FreeRemember() is called at&lt;br /&gt;
      ** this point, but only to free the link nodes.  The memory pointed&lt;br /&gt;
      ** to by memBlockA and memBlockB is retained.&lt;br /&gt;
      */&lt;br /&gt;
     IIntuition-&amp;gt;FreeRemember(&amp;amp;rememberKey, FALSE);&lt;br /&gt;
&lt;br /&gt;
      /* Individually free the two memory blocks. The Exec FreeMem()&lt;br /&gt;
      ** call must be used, as the link nodes are no longer available.&lt;br /&gt;
      */&lt;br /&gt;
      IExec-&amp;gt;FreeMem(memBlockA, SIZE_A);&lt;br /&gt;
      IExec-&amp;gt;FreeMem(memBlockB, SIZE_B);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* It is possible to have reached the call to FreeRemember()&lt;br /&gt;
  ** in one of three states.  Here they are, along with their results.&lt;br /&gt;
  **&lt;br /&gt;
  ** 1. Both memory allocations failed.&lt;br /&gt;
  **    RememberKey is still NULL.  FreeRemember() will do nothing.&lt;br /&gt;
  ** 2. The memBlockA allocation succeeded but the memBlockB allocation failed.&lt;br /&gt;
  **    FreeRemember() will free the memory block pointed to by memBlockA.&lt;br /&gt;
  ** 3. Both memory allocations were successful.&lt;br /&gt;
  **    If this is the case, the program has already freed the link nodes&lt;br /&gt;
  **    with FreeRemember() and the memory blocks with FreeMem().&lt;br /&gt;
  **    When FreeRemember() freed the link nodes, it reset RememberKey&lt;br /&gt;
  **    to NULL.  This (second) call to FreeRemember() will do nothing.&lt;br /&gt;
  */&lt;br /&gt;
  IIntuition-&amp;gt;FreeRemember(&amp;amp;rememberKey, TRUE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Current Time Values ==&lt;br /&gt;
&lt;br /&gt;
The function CurrentTime() gets the current time values. To use this function, first declare the variables Seconds and Micros. Then, when the application call the function, the current time is copied into the argument pointers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID CurrentTime( ULONG *seconds, ULONG *micros );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the DOS library Autodocs for more information on functions dealing with the date and time. The DOS library includes such functions as DateToStr(), StrToDate(), SetFileDate() and CompareDates().&lt;br /&gt;
&lt;br /&gt;
== Using Sprites in Intuition Windows and Screens ==&lt;br /&gt;
&lt;br /&gt;
Sprite functionality has limitations under Intuition. The hardware and graphics library sprite systems manage sprites independently of the Intuition display. In particular:&lt;br /&gt;
&lt;br /&gt;
* Sprites cannot be attached to any particular screen. Instead, they always appear in front of every screen.&lt;br /&gt;
&lt;br /&gt;
* When a screen is moved, the sprites do not automatically move with it. The sprites move to their correct locations only when the appropriate function is called (either DrawGList() or MoveSprite()).&lt;br /&gt;
&lt;br /&gt;
Hardware sprites are of limited use under the Intuition paradigm. They travel out of windows and out of screens, unlike all other Intuition mechanisms (except the Intuition pointer, which is meant to be global).&lt;br /&gt;
&lt;br /&gt;
Remember that sprite data must be in Chip memory to be accessible to the custom chips on classic Amiga hardware. This may be done with a compiler specific feature, such as the __chip keyword of SAS/C. Otherwise, Chip memory can be allocated with the Exec AllocMem() function or the Intuition AllocRemember() function, setting the memory requirement flag to MEMF_CHIP. The sprite data may then be copied to Chip memory using a function like CopyMem() in the Exec library. See [[Graphics_Sprites,_Bobs_and_Animation|Graphics Sprites, Bobs and Animation]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Intuition and Preferences ==&lt;br /&gt;
&lt;br /&gt;
The SetPrefs() function is used to configure Intuition&#039;s internal data states according to a given Preferences structure. This call relies on the Preferences system used in V34 and earlier versions of the OS. The old system has been superceded. See the [[Preferences|Preferences]] for details. This routine is called only by:&lt;br /&gt;
&lt;br /&gt;
* The Preferences program itself after the user changes Preferences settings (under V34 and earlier).&lt;br /&gt;
&lt;br /&gt;
* AmigaDOS when the system is being booted up. AmigaDOS opens the devs:system-configuration file and passes the information found there to the SetPrefs() routine. This way, the user can create an environment and have that environment restored every time the system is booted.&lt;br /&gt;
&lt;br /&gt;
The function takes three arguments:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct Preferences *SetPrefs( struct Preferences *prefbuf, LONG size,&lt;br /&gt;
                              LONG realThing );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The prefbuf argument is a pointer to a Preferences structure that will be used for Intuition&#039;s internal settings. The size is the number of bytes contained in your Preferences structure. Typically, you will use sizeof(struct Preferences) for this argument. The realThing argument is a boolean TRUE or FALSE designating whether or not this is an intermediate or final version of the Preferences. The difference is that final changes to Intuition&#039;s internal Preferences settings cause a global broadcast of NEWPREFS events to every application that is listening for this event. Intermediate changes may be used, for instance, to update the screen colors while the user is playing with the color gadgets.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About SetPrefs()|text=The intended use for the SetPrefs() call is entirely to serve the user. You should never use this routine to make your programming or design job easier at the cost of yanking the rug out from beneath the user.}}&lt;br /&gt;
&lt;br /&gt;
Refer to [[Preferences|Preferences]] for information about the Preferences structure and the preferred Preferences procedure calls.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions discussed in this article. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocRemember()&lt;br /&gt;
| Allocate memory and track the allocation.&lt;br /&gt;
|-&lt;br /&gt;
| FreeRemember()&lt;br /&gt;
| Free memory allocated with AllocRemember().&lt;br /&gt;
|-&lt;br /&gt;
| LockIBase()&lt;br /&gt;
| Lock IntuitionBase for reading.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockIBase()&lt;br /&gt;
| Unlock IntuitionBase when done reading.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentTime()&lt;br /&gt;
| Get the system time in seconds and micro-seconds.&lt;br /&gt;
|-&lt;br /&gt;
| SetPrefs()&lt;br /&gt;
| An Intuition internal function you should try to avoid.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Virtual_Sprites&amp;diff=12548</id>
		<title>Virtual Sprites</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Virtual_Sprites&amp;diff=12548"/>
		<updated>2025-01-26T19:29:08Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Using Virtual Sprites ==&lt;br /&gt;
&lt;br /&gt;
This section describes how to set up the VSprite structure so that it represents a true VSprite. True VSprites are managed by the GELs system which converts them to Simple Sprites and displays them. (Later sections describe how a VSprite structure can be set up for Bobs and AnimComps.)&lt;br /&gt;
&lt;br /&gt;
Before the system is told of a VSprite&#039;s existence, space for the VSprite data structure must be allocated and initialized to &amp;quot;correctly&amp;quot; represent a VSprite. Since the system does no validity checking on the VSprite structure, the result of using a bogus structure is usually a fireworks display, followed by a system failure.&lt;br /&gt;
&lt;br /&gt;
The system software provides a way to detect collisions between VSprites and other on-screen objects. There is also a method of extending the VSprite structure to incorporate user defined variables. These subjects are applicable to all GELs and are explained in [[Graphics_Sprites,_Bobs_and_Animation#Collisions_and_GEL_Structure_Extensions|Collisions and GEL Structure Extensions]].&lt;br /&gt;
&lt;br /&gt;
=== Specification of VSprite Structure ===&lt;br /&gt;
&lt;br /&gt;
The VSprite structure is defined in the include file &amp;amp;lt;graphics/gels.h&amp;amp;gt; as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* VSprite structure definition */&lt;br /&gt;
struct VSprite {&lt;br /&gt;
    struct VSprite *NextVSprite;&lt;br /&gt;
    struct VSprite *PrevVSprite;&lt;br /&gt;
    struct VSprite *DrawPath;&lt;br /&gt;
    struct VSprite *ClearPath;&lt;br /&gt;
    WORD            OldY, OldX;&lt;br /&gt;
    WORD            Flags;&lt;br /&gt;
    WORD            Y, X;&lt;br /&gt;
    WORD            Height;&lt;br /&gt;
    WORD            Width;&lt;br /&gt;
    WORD            Depth;&lt;br /&gt;
    WORD            MeMask;&lt;br /&gt;
    WORD            HitMask;&lt;br /&gt;
    WORD           *ImageData;&lt;br /&gt;
    WORD           *BorderLine;&lt;br /&gt;
    WORD           *CollMask;&lt;br /&gt;
    WORD           *SprColors;&lt;br /&gt;
    struct Bob     *VSBob;&lt;br /&gt;
    BYTE            PlanePick;&lt;br /&gt;
    BYTE            PlaneOnOff;&lt;br /&gt;
    VUserStuff      VUserExt;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are two primary ways to allocate and fill in space for VSprite data. They can be statically declared, or a memory allocation function can be called and they can be filled in programmatically. The declaration to statically set up a VSprite structure is listed below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* VSprite static data definition.&lt;br /&gt;
** must set the following for TRUE VSprites:&lt;br /&gt;
**    VSPRITE flag.&lt;br /&gt;
**    Width to 1.&lt;br /&gt;
**    Depth to 2.&lt;br /&gt;
**    VSBob to NULL.&lt;br /&gt;
*/&lt;br /&gt;
struct VSprite myVSprite =&lt;br /&gt;
    {&lt;br /&gt;
    NULL, NULL, NULL, NULL, 0, 0, VSPRITE, 0, 0, 5, 1, 2, 0, 0,&lt;br /&gt;
    &amp;amp;myImage, 0, 0, &amp;amp;mySpriteColors, NULL, 0x3, 0, 0&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This static allocation gives the required VSprite structure, but does not allocate or set up collision masks for the VSprite. Note that the VSprite structure itself does not need to reside in Chip memory.&lt;br /&gt;
&lt;br /&gt;
Refer to the makeVSprite() and freeVSprite() functions in the animtools.c listing at the end of the article for an example of dynamically allocating, initializing and freeing a VSprite structure.&lt;br /&gt;
&lt;br /&gt;
=== Reserved VSprite Members ===&lt;br /&gt;
&lt;br /&gt;
These VSprite structure members are reserved for system use (do not write to them):&lt;br /&gt;
&lt;br /&gt;
; NextVSprite and PrevVSprite&lt;br /&gt;
: These are used as links in the GelsInfo list.&lt;br /&gt;
&lt;br /&gt;
; DrawPath and ClearPath&lt;br /&gt;
: These are used for Bobs, not true VSprites.&lt;br /&gt;
&lt;br /&gt;
; OldY and OldX&lt;br /&gt;
: Previous position holder, the system uses these for double buffered Bobs, but application programs can read them too.&lt;br /&gt;
&lt;br /&gt;
The values can be set like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myVSprite.NextVSprite = NULL;&lt;br /&gt;
myVSprite.PrevVSprite = NULL;&lt;br /&gt;
myVSprite.DrawPath  = NULL;&lt;br /&gt;
myVSprite.ClearPath = NULL;&lt;br /&gt;
myVSprite.OldY = 0;&lt;br /&gt;
myVSprite.OldX = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using VSprite Flags ===&lt;br /&gt;
&lt;br /&gt;
The Flags member of the VSprite structure is both read and written by the system. Some bits are used by the application to inform the system; others are used by the system to indicate things to the application.&lt;br /&gt;
&lt;br /&gt;
The only Flags bits that are used by true VSprites are:&lt;br /&gt;
&lt;br /&gt;
; VSPRITE&lt;br /&gt;
: This may be set to indicate to the system that it should treat the structure as a true VSprite, not part of a Bob. This affects the interpretation of the data layout and the use of various system variables.&lt;br /&gt;
&lt;br /&gt;
; VSOVERFLOW&lt;br /&gt;
: The system sets this bit in the true VSprites that it is unable to display. This happens when there are too many in the same scan line, and the system has run out of Simple Sprites to assign. It indicates that this VSprite has not been displayed. If no sprites are reserved, this means that more than eight sprites touch one scan line. This bit will not be set for Bobs and should not be changed by the application.&lt;br /&gt;
&lt;br /&gt;
; GELGONE&lt;br /&gt;
: If the system has set GELGONE bit in the Flags member, then the GEL associated with this VSprite is not on the display at all, it is entirely outside the GEL boundaries. This area is defined by the GelsInfo members topmost, bottommost, leftmost and rightmost (see the &amp;amp;lt;graphics/rastport.h&amp;amp;gt; include file).&lt;br /&gt;
&lt;br /&gt;
On the basis of that information, the application may decide that the object need no longer be part of the GEL list and may decide to remove it to speed up the consideration of other objects. Use RemVSprite() (or RemBob(), if it&#039;s a Bob) to do this. This bit should not be changed by the application.&lt;br /&gt;
&lt;br /&gt;
The VSprite.Flags value should be initialized like this for a VSprite GEL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myVSprite.Flags = VSPRITE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== VSprite Position ===&lt;br /&gt;
&lt;br /&gt;
To control the position of a VSprite, the x and y variables in the VSprite structure are used. These specify where the upper left corner of the VSprite will be, relative to the upper left corner of the playfield area it appears over. So if VSprites are used under Intuition and within a screen, they will be positioned relative to the upper left-hand corner of the screen.&lt;br /&gt;
&lt;br /&gt;
In a 320x200 screen, a y value of 0 puts the VSprite at the top of that display, a y value of (200 - VSprite height) puts the VSprite at the bottom. And an x value of 0 puts the VSprite at the left edge of that display, while an x value of (320 - VSprite width) puts the VSprite at the far right. Values of less than (0,0) or greater than (320, 200) may be used to move the VSprite partially or entirely off the screen, if desired.&lt;br /&gt;
&lt;br /&gt;
See [[Graphics_Primitives|Graphics Primitives]] for more information on display coordinates and display size. See the &amp;quot;Amiga Hardware Reference Manual&amp;quot; for more information on hardware sprites.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Position VSprites Properly|text=It is important that the starting position of true VSprites is not less than -20 in the y direction, which is the start of the active display area for sprites. Also, if they are moved too far to the left, true VSprites may not have enough DMA time to be displayed.}}&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;x, y&#039;&#039; values may be set like this to put the VSprite in the upper-left:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myVSprite.Y = 0;&lt;br /&gt;
myVSprite.X = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== VSprite Image Size ===&lt;br /&gt;
&lt;br /&gt;
A true VSprite is always one word (16 pixels) wide and may be any number of lines high. It can be made to appear thinner by making some pixels transparent. Like Simple Sprites, VSprite pixels are always the size of a pixel in low-resolution mode (320x200); regardless of the resolution the display is set to. To specify how many lines make up the VSprite image, the VSprite structure member, Height, is used.&lt;br /&gt;
&lt;br /&gt;
VSprites always have a Depth of two, allowing for three colors. The values may be set like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myVSprite.Width  = 1;      /* ALWAYS 1 for true VSprites. */&lt;br /&gt;
myVSprite.Height = 5;      /* The example height. */&lt;br /&gt;
myVSprite.Depth  = 2;      /* ALWAYS 2 for true VSprites. */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== VSprites and Collision Detection ===&lt;br /&gt;
&lt;br /&gt;
Some members of the VSprite data structure are used for special purposes such as collision detection, user extensions or for system extensions (such as Bobs and AnimComps). For most applications these fields are set to zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
myVSprite.HitMask    = 0;  /* These are all used for collision detection */&lt;br /&gt;
myVSprite.MeMask     = 0;&lt;br /&gt;
myVSprite.BorderLine = 0;&lt;br /&gt;
myVSprite.CollMask   = 0;&lt;br /&gt;
&lt;br /&gt;
myVSprite.VUserExt = 0;    /* Only use this for user extensions to VSprite */&lt;br /&gt;
&lt;br /&gt;
myVSprite.VSBob = NULL;    /* Only Bobs and AnimComps need this */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The special uses of this fields are explained further in the sections that follow.&lt;br /&gt;
&lt;br /&gt;
=== VSprite Image Data ===&lt;br /&gt;
&lt;br /&gt;
The ImageData pointer of the VSprite structure must be initialized with the address of the first word of the image data array. The image data array must be in Chip memory. It takes two sequential 16-bit words to define each line of a VSprite. This means that the data area containing the VSprite image is always &amp;quot;Heightx2&amp;quot; (10 in the example case) words long.&lt;br /&gt;
&lt;br /&gt;
A VSprite image is defined just like a real hardware sprite. The combination of bits in corresponding locations in the two data words that define each line select the color for that pixel. The first of the pair of words supplies the low-order bit of the color selector for that pixel; the second word supplies the high-order bit.&lt;br /&gt;
&lt;br /&gt;
These binary values select colors as follows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| 00 || selects &amp;quot;transparent&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 01 || selects the first of three VSprite colors&lt;br /&gt;
|-&lt;br /&gt;
| 10 || selects the second VSprite color&lt;br /&gt;
|-&lt;br /&gt;
| 11 || selects the third VSprite color&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In those areas where the combination of bits yields a value of 0, the VSprite is transparent. This means that the playfield, and all Bobs and AnimComps, and any VSprite whose priority is lower than this VSprite will all show through in transparent sections. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(&amp;amp;VSprite-&amp;gt;ImageData)      1010 0000 0000 0000&lt;br /&gt;
(&amp;amp;VSprite-&amp;gt;ImageData + 1)  0110 0000 0000 0000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading from top to bottom, left to right, the combinations of these two sequential data words form the binary values of 01, 10, 11, and then all 00s. This VSprite&#039;s first pixel will be color 1, the next color 2, the third color 3. The rest will be transparent, making this VSprite appear to be three pixels wide. Thus, a three-color image, with some transparent areas, can be formed from a data set like the following sample:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Address    Binary Data            VSprite Image Data&lt;br /&gt;
-------    -----------            ------------------&lt;br /&gt;
mem        1111 1111 1111 1111    Defines top line&lt;br /&gt;
mem + 1    1111 1111 1111 1111    3333 3333 3333 3333&lt;br /&gt;
&lt;br /&gt;
mem + 2    0011 1100 0011 1100    Defines second line&lt;br /&gt;
mem + 3    0011 0000 0000 1100    0033 1100 0011 3300&lt;br /&gt;
&lt;br /&gt;
mem + 4    0000 1100 0011 0000    Defines third line&lt;br /&gt;
mem + 5    0000 1111 1111 0000    0000 3322 2233 0000&lt;br /&gt;
&lt;br /&gt;
mem + 6    0000 0010 0100 0000    Defines fourth line&lt;br /&gt;
mem + 7    0000 0011 1100 0000    0000 0032 2300 0000&lt;br /&gt;
&lt;br /&gt;
mem + 8    0000 0001 1000 0000    Defines fifth line&lt;br /&gt;
mem + 9    0000 0001 1000 0000    0000 0003 3000 0000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The VSprite.Height for this sample image is 5.&lt;br /&gt;
&lt;br /&gt;
=== Specifying the Colors of a VSprite ===&lt;br /&gt;
&lt;br /&gt;
The system software provides a great deal of versatility in the choice of colors for Virtual Sprites. Each VSprite has &amp;quot;its own set&amp;quot; of three colors, pointed to by SprColors, which the system jams into the display&#039;s Copper list as needed.&lt;br /&gt;
&lt;br /&gt;
SprColors points to the first of three 16-bit values. The first value represents the color used for the VSprite bits that select color 1, the second value is color 2, and the third value is color 3. When the system assigns a hardware sprite to carry the VSprite&#039;s image, it jams these color values into the Copper list (the intermediate Copper list, &amp;quot;not&amp;quot; the color table), so that the View&#039;s colors will be correct for this VSprite at the time the VSprite is displayed. It doesn&#039;t jam the original palette&#039;s colors back after the VSprite is done. If there is another VSprite later, that VSprite&#039;s colors will get jammed; if there is not another VSprite, the colors will remain the same until the next ViewPort&#039;s colors get loaded.&lt;br /&gt;
&lt;br /&gt;
If the SprColors pointer is set to NULL, that VSprite does not generate a color-change instruction stream for the Copper. Instead, the VSprite appears drawn in whatever color set that the hardware sprite happens to have in it already.&lt;br /&gt;
&lt;br /&gt;
Since the registers are initially loaded with the colors from the ViewPort&#039;s ColorMap, if all VSprites have NULL SprColors, they will appear in the ViewPort&#039;s colors.&lt;br /&gt;
&lt;br /&gt;
To continue our example, a set of colors can be declared and the VSprite colors set with the following statements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint16 mySpriteColors[] = { 0x0000, 0x00f0, 0x0f00 }; /* Declare colors statically */&lt;br /&gt;
&lt;br /&gt;
myVSprite.SprColors = mySpriteColors;                 /* Assign colors to VSprite */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding and Removing VSprites ===&lt;br /&gt;
&lt;br /&gt;
Once a true VSprite has been set up and initialized, the obvious next step is to give it to the system by adding it to the GEL list. The VSprite may then be manipulated as needed. Before the program ends, the VSprite should be removed from the GELs list by calling RemVSprite().&lt;br /&gt;
&lt;br /&gt;
A typical calling sequence could be performed like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct VSprite  myVSprite = {0};&lt;br /&gt;
struct RastPort myRastPort = {0};&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;AddVSprite(&amp;amp;myVSprite, &amp;amp;myRastPort);&lt;br /&gt;
&lt;br /&gt;
/* Manipulate the VSprite as needed here */&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;RemVSprite(&amp;amp;myVSprite);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;amp;myVSprite argument is a fully initialized VSprite structure and &amp;amp;amp;myRastPort is the RastPort with which this VSprite is to be associated. Note that you will probably not like the results if you try to RemVSprite() a VSprite that has not been added to the system with AddVSprite(). See the SDK for additional information on these functions.&lt;br /&gt;
&lt;br /&gt;
=== Changing VSprites ===&lt;br /&gt;
&lt;br /&gt;
Once the VSprite has been added to the GELs list and is in the display, some of its characteristics can be changed dynamically by:&lt;br /&gt;
&lt;br /&gt;
* Changing y, x to a new VSprite position&lt;br /&gt;
* Changing ImageData to point to a new VSprite image&lt;br /&gt;
* Changing SprColors to point to a new VSprite color set&lt;br /&gt;
&lt;br /&gt;
Study the next two sections to find out how to reserve hardware Sprites for use outside the VSprite system and how to assign the VSprites.&lt;br /&gt;
&lt;br /&gt;
=== Getting the VSprite List in Order ===&lt;br /&gt;
&lt;br /&gt;
When the system has displayed the last line of a VSprite, it is able to reassign the hardware sprite to another VSprite located at a lower position on the screen. The system allocates hardware sprites in the order in which it encounters the VSprites in the list. Therefore, the list of VSprites must be sorted before the system can assign the use of the hardware Sprites correctly.&lt;br /&gt;
&lt;br /&gt;
The function SortGList() must be used to get the GELs in the correct order before the system is asked to display them. &#039;&#039;This sorting step is essential!&#039;&#039; It should be done before calling DrawGList(), whenever a GEL has changed position. This function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RastPort myRastPort = {0};&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;SortGList(&amp;amp;myRastPort);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The only argument is a pointer to the RastPort structure containing the GelsInfo.&lt;br /&gt;
&lt;br /&gt;
=== Displaying the VSprites ===&lt;br /&gt;
&lt;br /&gt;
The next few sections explain how to display the VSprites. The following system functions are used:&lt;br /&gt;
&lt;br /&gt;
; DrawGList()&lt;br /&gt;
: Draws the VSprites into the current RastPort.&lt;br /&gt;
&lt;br /&gt;
; MrgCop()&lt;br /&gt;
: Installs the VSprites into the display.&lt;br /&gt;
&lt;br /&gt;
; LoadView()&lt;br /&gt;
: Asks the system to display the new View.&lt;br /&gt;
&lt;br /&gt;
; WaitTOF()&lt;br /&gt;
: Synchronizes the functions with the display.&lt;br /&gt;
&lt;br /&gt;
==== Drawing the Graphics Elements ====&lt;br /&gt;
&lt;br /&gt;
The system function called DrawGList() looks through the list of GELS and prepares the necessary Copper instructions and memory areas to display the data. This function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RastPort myRastPort = {0};&lt;br /&gt;
struct ViewPort myViewPort = {0};&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;DrawGList(&amp;amp;myRastPort, &amp;amp;myViewPort);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The myRastPort argument specifies the RastPort containing the GelsInfo list with the VSprites that you want to display. The &amp;amp;amp;myViewPort argument is a pointer to the ViewPort for which the VSprites will be created.&lt;br /&gt;
&lt;br /&gt;
==== Merging VSprite Instructions ====&lt;br /&gt;
&lt;br /&gt;
Once DrawGList() has prepared the necessary instructions and memory areas to display the data, the VSprites are installed into the display with MrgCop(). (DrawGList() does not actually draw the VSprites, it only prepares the Copper instructions.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct View *view;&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;MrgCop(view);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The view is a pointer to the View structure whose Copper instructions are to be merged.&lt;br /&gt;
&lt;br /&gt;
==== Loading the New View ====&lt;br /&gt;
&lt;br /&gt;
Now that the display instructions include the definition of the VSprites, the system can display this newly configured View with the LoadView() function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct View *view;&lt;br /&gt;
&lt;br /&gt;
IGraphics-&amp;gt;LoadView(view);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again, view is a pointer to the View that contains the the new Copper instruction list (if you are using GELs in an Intuition Screen, do not call LoadView().)&lt;br /&gt;
&lt;br /&gt;
The Copper instruction lists are double-buffered, so this instruction does not actually take effect until the next display field occurs. This avoids the possibility of some function trying to update the Copper instruction list while the Copper is trying to use it to create the display.&lt;br /&gt;
&lt;br /&gt;
==== Synchronizing with the Display ====&lt;br /&gt;
&lt;br /&gt;
To synchronize application functions with the display, call the system function WaitTOF().&lt;br /&gt;
&lt;br /&gt;
WaitTOF() holds your task until the vertical-blanking interval (blank area at the top of the screen) has begun. At that time, the system has retrieved the current Copper instruction list and is ready to allow generation of a new list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
WaitTOF();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
WaitTOF() takes no arguments and returns no values. It simply suspends your task until the video beam is at the top of field.&lt;br /&gt;
&lt;br /&gt;
== Complete VSprite Example ==&lt;br /&gt;
&lt;br /&gt;
The listing given here shows a complete VSprite example. This program requires the &amp;quot;animtools.c&amp;quot;, &amp;quot;animtools.h&amp;quot; and &amp;quot;animtools_proto.h&amp;quot; support files in order to compile and run. These files are listed at the end of this article.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* vsprite.c&lt;br /&gt;
**&lt;br /&gt;
** SAS/C V5.10a&lt;br /&gt;
** lc -b1 -cfist -v -y vsprite.c&lt;br /&gt;
** blink FROM LIB:c.o vsprite.o animtools.o LIB LIB:lc.lib LIB:amiga.lib TO vsprite&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/intuitionbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfx.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gels.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/collide.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;stdlib.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;quot;animtools.h&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
VOID borderCheck(struct VSprite *hitVSprite, LONG borderflags);&lt;br /&gt;
VOID process_window(struct Window *win, struct RastPort *myRPort, struct VSprite *MyVSprite);&lt;br /&gt;
VOID do_VSprite(struct Window *win, struct RastPort *myRPort);&lt;br /&gt;
VOID vspriteDrawGList(struct Window *win, struct RastPort *myRPort);&lt;br /&gt;
&lt;br /&gt;
struct GfxBase           *GfxBase;   /* pointer to Graphics library  */&lt;br /&gt;
struct IntuitionBase *IntuitionBase; /* pointer to Intuition library */&lt;br /&gt;
&lt;br /&gt;
int return_code;&lt;br /&gt;
#define GEL_SIZE        4 /* number of lines in the vsprite */&lt;br /&gt;
&lt;br /&gt;
/* VSprite data - there are two sets that are alternated between. */&lt;br /&gt;
/* note that this data is always displayed as low resolution.     */&lt;br /&gt;
WORD chip vsprite_data1[] = { 0x7ffe, 0x80ff,&lt;br /&gt;
                              0x7c3e, 0x803f,&lt;br /&gt;
                              0x7c3e, 0x803f,&lt;br /&gt;
                              0x7ffe, 0x80ff,&lt;br /&gt;
                              0, 0 };&lt;br /&gt;
&lt;br /&gt;
WORD chip vsprite_data2[] = { 0x7ffe, 0xff01,&lt;br /&gt;
                              0x7c3e, 0xfc01,&lt;br /&gt;
                              0x7c3e, 0xfc01,&lt;br /&gt;
                              0x7ffe, 0xff01,&lt;br /&gt;
                              0, 0 };&lt;br /&gt;
&lt;br /&gt;
WORD mySpriteColors[] =     { 0x0000, 0x00f0, 0x0f00 };&lt;br /&gt;
WORD mySpriteAltColors[] =  { 0x000f, 0x0f00, 0x0ff0 };&lt;br /&gt;
&lt;br /&gt;
NEWVSPRITE myNewVSprite = {              /* information for the new VSprite       */&lt;br /&gt;
        /* Image data, sprite color array word width (must be 1 for true VSprite) */&lt;br /&gt;
        vsprite_data1, mySpriteColors,1,&lt;br /&gt;
        /* Line height, image depth (must be 2 for true VSprite), x, y position   */&lt;br /&gt;
        GEL_SIZE, 2, 160, 100,&lt;br /&gt;
        /* Flags (VSPRITE == true VSprite), hit mask and me mask                  */&lt;br /&gt;
        VSPRITE, 1 &amp;amp;lt;&amp;amp;lt; BORDERHIT, 0&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
struct NewWindow myNewWindow = {        /* information for the new window */&lt;br /&gt;
    80, 20, 400, 150, -1, -1, CLOSEWINDOW | INTUITICKS,&lt;br /&gt;
    ACTIVATE | WINDOWCLOSE | WINDOWDEPTH | RMBTRAP | WINDOWDRAG,&lt;br /&gt;
    NULL, NULL, &amp;amp;quot;VSprite&amp;amp;quot;, NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
/* Basic VSprite display subroutine */&lt;br /&gt;
VOID vspriteDrawGList(struct Window *win, struct RastPort *myRPort)&lt;br /&gt;
{&lt;br /&gt;
SortGList(myRPort);&lt;br /&gt;
DrawGList(myRPort, ViewPortAddress(win));&lt;br /&gt;
RethinkDisplay();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Collision routine for vsprite hitting border.  Note that when the collision is VSprite to */&lt;br /&gt;
/* VSprite (or Bob to Bob, Bob to AnimOb, etc), then the parameters are both pointers to a VSprite. */&lt;br /&gt;
VOID borderCheck(struct VSprite *hitVSprite, LONG borderflags)&lt;br /&gt;
{&lt;br /&gt;
if (borderflags &amp;amp;amp; RIGHTHIT)&lt;br /&gt;
    {&lt;br /&gt;
    hitVSprite-&amp;amp;gt;SprColors = mySpriteAltColors;&lt;br /&gt;
    hitVSprite-&amp;amp;gt;VUserExt  = -40;&lt;br /&gt;
    }&lt;br /&gt;
if (borderflags &amp;amp;amp; LEFTHIT)&lt;br /&gt;
    {&lt;br /&gt;
    hitVSprite-&amp;amp;gt;SprColors = mySpriteColors;&lt;br /&gt;
    hitVSprite-&amp;amp;gt;VUserExt  = 20;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Process window and dynamically change vsprite. Get messages. Go away on           */&lt;br /&gt;
/* CLOSEWINDOW.  Update and redisplay vsprite on INTUITICKS. Wait for more messages. */&lt;br /&gt;
VOID process_window(struct Window *win, struct RastPort *myRPort, struct VSprite *myVSprite)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
&lt;br /&gt;
FOREVER&lt;br /&gt;
    {&lt;br /&gt;
    Wait(1L &amp;amp;lt;&amp;amp;lt; win-&amp;amp;gt;UserPort-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
    while (NULL != (msg = (struct IntuiMessage *)GetMsg(win-&amp;amp;gt;UserPort)))&lt;br /&gt;
        {&lt;br /&gt;
        /* Only CLOSEWINDOW and INTUITICKS are active */&lt;br /&gt;
        if (msg-&amp;amp;gt;Class == CLOSEWINDOW)&lt;br /&gt;
            {&lt;br /&gt;
            ReplyMsg((struct Message *)msg);&lt;br /&gt;
            return;&lt;br /&gt;
            }&lt;br /&gt;
        /* Must be an INTUITICKS:  change x and y values on the fly.  Note offset by&lt;br /&gt;
        ** window left and top edge--sprite relative to the screen, not window.  Divide&lt;br /&gt;
        ** the MouseY in half to adjust for Lores movement increments on a Hires screen.&lt;br /&gt;
        */&lt;br /&gt;
        myVSprite-&amp;amp;gt;X = win-&amp;amp;gt;LeftEdge + msg-&amp;amp;gt;MouseX + myVSprite-&amp;amp;gt;VUserExt;&lt;br /&gt;
        myVSprite-&amp;amp;gt;Y = win-&amp;amp;gt;TopEdge  + msg-&amp;amp;gt;MouseY/2 + 1;&lt;br /&gt;
        ReplyMsg((struct Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    /* Got a message, change image data on the fly */&lt;br /&gt;
    myVSprite-&amp;amp;gt;ImageData = (myVSprite-&amp;amp;gt;ImageData == vsprite_data1) ? vsprite_data2 : vsprite_data1;&lt;br /&gt;
    SortGList(myRPort);&lt;br /&gt;
    DoCollision(myRPort);&lt;br /&gt;
    vspriteDrawGList(win, myRPort);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Working with the VSprite.  Setup the GEL system and get a new VSprite (makeVSprite()).   */&lt;br /&gt;
/* Add VSprite to the system and display.  Use the vsprite.  When done, remove VSprite and  */&lt;br /&gt;
/* update the display without the VSprite.  Cleanup everything.                             */&lt;br /&gt;
VOID do_VSprite(struct Window *win, struct RastPort *myRPort)&lt;br /&gt;
{&lt;br /&gt;
struct VSprite       *myVSprite;&lt;br /&gt;
struct GelsInfo       *my_ginfo;&lt;br /&gt;
&lt;br /&gt;
if (NULL == (my_ginfo = setupGelSys(myRPort, 0xfc)))&lt;br /&gt;
    return_code = RETURN_WARN;&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    if (NULL == (myVSprite = makeVSprite(&amp;amp;amp;myNewVSprite)))&lt;br /&gt;
        return_code = RETURN_WARN;&lt;br /&gt;
    else&lt;br /&gt;
        {&lt;br /&gt;
        AddVSprite(myVSprite, myRPort);&lt;br /&gt;
        vspriteDrawGList(win, myRPort);&lt;br /&gt;
        myVSprite-&amp;amp;gt;VUserExt = 20;&lt;br /&gt;
        SetCollision(BORDERHIT, borderCheck, myRPort-&amp;amp;gt;GelsInfo);&lt;br /&gt;
        process_window(win, myRPort, myVSprite);&lt;br /&gt;
        RemVSprite(myVSprite);&lt;br /&gt;
        freeVSprite(myVSprite);&lt;br /&gt;
        }&lt;br /&gt;
    vspriteDrawGList(win, myRPort);&lt;br /&gt;
    cleanupGelSys(my_ginfo, myRPort);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Example VSprite program.  First open up the libraries and a window. */&lt;br /&gt;
VOID main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
struct Window       *win;&lt;br /&gt;
struct RastPort    myRPort = {0};&lt;br /&gt;
&lt;br /&gt;
return_code = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
if (NULL == (GfxBase = (struct GfxBase *)OpenLibrary(GRAPHICSNAME,37L)))&lt;br /&gt;
    return_code = RETURN_FAIL;&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    if (NULL == (IntuitionBase = (struct IntuitionBase *)OpenLibrary(INTUITIONNAME,37L)))&lt;br /&gt;
        return_code = RETURN_FAIL;&lt;br /&gt;
    else&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL == (win = OpenWindow(&amp;amp;amp;myNewWindow)))&lt;br /&gt;
            return_code = RETURN_WARN;&lt;br /&gt;
        else&lt;br /&gt;
            {&lt;br /&gt;
            InitRastPort(&amp;amp;amp;myRPort);&lt;br /&gt;
            myRPort = win-&amp;amp;gt;WScreen-&amp;amp;gt;RastPort;       /* Copy the structure. */&lt;br /&gt;
            do_VSprite(win, &amp;amp;amp;myRPort);&lt;br /&gt;
            CloseWindow(win);&lt;br /&gt;
            }&lt;br /&gt;
        CloseLibrary((struct Library *)IntuitionBase);&lt;br /&gt;
        }&lt;br /&gt;
    CloseLibrary((struct Library *)GfxBase);&lt;br /&gt;
    }&lt;br /&gt;
exit(return_code);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== VSprite Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section describes advanced topics pertaining to VSprites. It contains details about reserving hardware sprites for use outside of the GELs VSprite system, information about how VSprites are assigned, and more information about VSprite colors.&lt;br /&gt;
&lt;br /&gt;
=== Reserving Hardware Sprites ===&lt;br /&gt;
&lt;br /&gt;
To prevent the VSprite system from using specific hardware sprites, set the sprRsrvd member of the GelsInfo structure. The pointer to the GelsInfo structure is contained in the RastPort structure. If all of the bits of this 8-bit value are ones (0xFF), then all of the hardware sprites may be used by the VSprite system. If any of the bits is a 0, the sprite corresponding to that bit will not be utilized by VSprites.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Reserving Can Cause Problems|text=Reserving sprites increases the likelihood of the system not being able to display a VSprite (VSOVERFLOW). See the next section, &amp;quot;How VSprites are Assigned&amp;quot;, for further details on this topic.}}&lt;br /&gt;
&lt;br /&gt;
You reserve a sprite by setting its corresponding bit in sprRsrvd. For instance, to reserve sprite zero only, set sprRsrvd to 0x01. To reserve sprite three only, set sprRsrvd to 0x08.&lt;br /&gt;
&lt;br /&gt;
If a hardware sprite is reserved, the system will not consider it when it makes VSprite assignments. Remember, hardware sprite pairs share color register sets. If a hardware sprite is reserved, its mate should probably be reserved too, otherwise the reserved sprite&#039;s colors will change as the unreserved mate is assigned different VSprites. For example, it is common practice to reserve Sprites 0 and 1, so that the Intuition pointer (Sprite 0) is left alone. This could be accomplished with the following statements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RastPort myRastPort = {0};   /* the View structure is defined */&lt;br /&gt;
&lt;br /&gt;
myRastPort.GelsInfo-&amp;gt;sprRsrvd = 0x03;   /* reserve 0 and 1 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The GfxBase structure may be examined to find which sprites are already in use. This may, at your option, impact what sprites you reserve. If Intuition is running, sprite 0 will already be in use as its pointer.&lt;br /&gt;
&lt;br /&gt;
The reserved sprite status is accessible as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
currentreserved = GfxBase-&amp;gt;SpriteReserved;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The next section presents a few trouble-shooting techniques for VSprite assignment.&lt;br /&gt;
&lt;br /&gt;
=== How VSprites Are Assigned ===&lt;br /&gt;
&lt;br /&gt;
Although VSprites are managed for you by the GELs system there are some underlying limitations which could cause the system to run out of VSprites.&lt;br /&gt;
&lt;br /&gt;
As the system goes through the GEL list during DrawGList(), whenever it finds a true VSprite, it goes through the following procedure. If there is a Simple Sprite available (after the reserved sprites and preceding VSprites are accounted for), Copper instructions are added that will load the sprite hardware with this VSprite&#039;s data at the right point on the screen. It may need to add a Copper instruction sequence to load the display&#039;s colors associated with the sprite as well.&lt;br /&gt;
&lt;br /&gt;
There are only 8 &#039;&#039;real&#039;&#039; sprite DMA channels. The system will run out of hardware sprites if it is asked to display more than eight VSprites on one scan line. This limit goes down to four when the VSprites have different SprColor pointers. During the time that there is a conflict, the VSprites that could not be put into Simple Sprites will disappear. They will reappear when (as the VSprites are moved about the screen) circumstances permit.&lt;br /&gt;
&lt;br /&gt;
These problems can be alleviated by taking some precautions:&lt;br /&gt;
&lt;br /&gt;
* Minimize the number of VSprites to appear on a single horizontal line.&lt;br /&gt;
* If colors for some Virtual Sprites are the same, make sure that the pointer for each of the VSprite structures for these Virtual Sprites points to the same memory location, rather than to a duplicate set of colors elsewhere in memory. The system will know to map these into Sprite pairs.&lt;br /&gt;
&lt;br /&gt;
If a VSprite&#039;s SprColors are set to NULL, the VSprite will appear in the ViewPort&#039;s ColorMap colors. The system will display the VSprite in any one of a set of four different possible color groupings as indicated in the Simple Sprite section above.&lt;br /&gt;
&lt;br /&gt;
If SprColors points to a color set, the system will jam SprColors into the display hardware (via the Copper list), effectively overriding those ColorMap registers. The values in the ColorMap are &#039;&#039;not&#039;&#039; overwritten, but anything in the background display that used to appear in the ColorMap colors will appear in SprColors colors.&lt;br /&gt;
&lt;br /&gt;
=== How VSprite and Playfield Colors Interact ===&lt;br /&gt;
&lt;br /&gt;
At the start of each display, the system loads the colors from the ViewPort&#039;s color table into the display&#039;s hardware registers, so whatever is rendered into the BitMap is displayed correctly. But if the VSprite system is used, and the colors are specified (via SprColors) for each VSprite, the SprColors will be loaded by the system into the display hardware, as needed.&lt;br /&gt;
&lt;br /&gt;
The system does this by generating Copper instructions that will jam the colors into the hardware at specific moments in the display cycle. Any BitMap rendering, including Bobs, which share colors with VSprites, may change colors constantly as the video display beam progresses down the screen.&lt;br /&gt;
&lt;br /&gt;
This color changing can be avoided by taking one of the following precautions:&lt;br /&gt;
&lt;br /&gt;
* Use a four bitplane playfield, which only allows the lower 16 colors to be rendered into the BitMap (and allows Hires display mode).&lt;br /&gt;
* If a 32-color playfield display is being used, avoid rendering in colors 17-19, 21-23, 25-27, and 29-32, which are the colors affected by the VSprite system.&lt;br /&gt;
* Specify the VSprite SprColors pointer as a value of NULL to avoid changing the contents of any of the hardware sprite color registers. This may cause the VSprites to change colors depending on their positions relative to each other, as described in the previous section.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Device_I/O&amp;diff=12547</id>
		<title>Exec Device I/O</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Device_I/O&amp;diff=12547"/>
		<updated>2025-01-26T19:28:59Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Device I/O ==&lt;br /&gt;
&lt;br /&gt;
The Amiga system devices are software engines that provide access to the Amiga hardware. Through these devices, a programmer can operate a modem, spin a disk drive motor, time an event, and blast a trumpet sound in stereo. Yet, for all that variety, the programmer uses each device in the same manner.&lt;br /&gt;
&lt;br /&gt;
== What is a Device? ==&lt;br /&gt;
&lt;br /&gt;
An Amiga device is a software module that accepts commands and data and performs I/O operations based on the commands it receives. In most cases, it interacts with either internal or external hardware, (the exceptions are the clipboard device and ramdrive device which simply use memory). Generally, an Amiga device runs as a separate task which is capable of processing your commands while your application attends to other things.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot;|Amiga System Devices&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio Device|Audio]] || audio.device || Controls the use of the audio hardware&lt;br /&gt;
|-&lt;br /&gt;
| [[Clipboard Device|Clipboard]] || clipboard.device || Manages the cutting and pasting of common data blocks&lt;br /&gt;
|-&lt;br /&gt;
| [[Console Device|Console]] || console.device || Provides the text-oriented user interface.&lt;br /&gt;
|-&lt;br /&gt;
| [[Gameport Device|Gameport]] || gameport.device || Controls the two mouse/joystick ports.&lt;br /&gt;
|-&lt;br /&gt;
| [[Input Device|Input]] || input.device || Processes input from the gameport and keyboard devices.&lt;br /&gt;
|-&lt;br /&gt;
| [[Keyboard Device|Keyboard]] || keyboard.device || Controls the keyboard.&lt;br /&gt;
|-&lt;br /&gt;
| [[Narrator Device|Narrator]] || narrator.device || Produces the Amiga synthesized speech.&lt;br /&gt;
|-&lt;br /&gt;
| [[Parallel Device|Parallel]] || parallel.device || Controls the parallel port.&lt;br /&gt;
|-&lt;br /&gt;
| [[Printer Device|Printer]] || printer.device || Converts a standard set of printer control codes to printer specific codes.&lt;br /&gt;
|-&lt;br /&gt;
| [[SCSI Device|SCSI]] || scsi.device || Controls the Small Computer Standard Interface hardware.&lt;br /&gt;
|-&lt;br /&gt;
| [[Serial Device|Serial]] || serial.device || Controls the serial port.&lt;br /&gt;
|-&lt;br /&gt;
| [[Timer Device|Timer]] || timer.device || Provides timing functions to measure time intervals and send interrupts.&lt;br /&gt;
|-&lt;br /&gt;
| [[Trackdisk Device|Trackdisk]] || trackdisk.device || Controls the Amiga floppy disk drives.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The philosophy behind the devices is that I/O operations should be consistent and uniform. You print a file in the same manner as you play an audio sample, i.e., you send the device in question a WRITE command and the address of the buffer holding the data you wish to write.&lt;br /&gt;
&lt;br /&gt;
The result is that the interface presented to the programmer is essentially device independent and accessible from any computer language. This greatly expands the power the Amiga brings to the programmer and, ultimately, to the user.&lt;br /&gt;
&lt;br /&gt;
Devices support two types of commands: Exec standard commands like READ and WRITE, and device specific commands like the trackdisk device MOTOR command which controls the floppy drive motor, and the keyboard device READMATRIX command which returns the state of each key on the keyboard. You should keep in mind, however, that supporting standard commands does not mean that all devices execute them in &#039;&#039;exactly&#039;&#039; the same manner.&lt;br /&gt;
&lt;br /&gt;
Consult the [[Devices|Amiga devices]] and the commands they support.&lt;br /&gt;
&lt;br /&gt;
== Accessing a Device ==&lt;br /&gt;
&lt;br /&gt;
Accessing a device requires obtaining a message port, allocating memory for a specialized message packet called an I/O request, setting a pointer to the message port in the I/O request, and finally, establishing the link to the device itself by opening it. An example of how to do this will be provided later in this section.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message Port ===&lt;br /&gt;
&lt;br /&gt;
When a device completes the command in a message, it will return the message to the message port specifed as the &#039;&#039;reply&#039;&#039; port in the message. A message port is obtained by calling the AllocSysObject() function with an object type of ASOT_PORT. You must delete the message port when you are finished by calling the FreeSysObject() function.&lt;br /&gt;
&lt;br /&gt;
=== Creating an IO Request ===&lt;br /&gt;
&lt;br /&gt;
The I/O request is used to send commands and data from your application to the device. The I/O request consists of fields used to hold the command you wish to execute and any parameters it requires. You set up the fields with the appropriate information and send it to the device by using Exec I/O functions. Different Amiga devices often require different I/O request structures. These structures all start with a simple IORequest or IOStdReq structure (see &amp;lt;exec/io.h&amp;gt;) which may be followed by various device-specific fields. Consult the Autodoc and include file for each device to determine the type and size I/O request required to access the device.&lt;br /&gt;
&lt;br /&gt;
I/O request structures are commonly created and deleted with the AllocSysObject() and FreeSysObject() functions using an object type of ASOT_IOREQUEST. Any size and type of I/O request may be created with these functions.&lt;br /&gt;
&lt;br /&gt;
=== Opening a Device ===&lt;br /&gt;
&lt;br /&gt;
The device is opened by calling the OpenDevice() function. In addition to establishing the link to the device, OpenDevice() also initializes fields in the I/O request. OpenDevice() has this format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
result = IExec-&amp;gt;OpenDevice(device_name,unit_number,(struct IORequest *)IORequest,flags)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; device_name&lt;br /&gt;
: is the name of the device.&lt;br /&gt;
&lt;br /&gt;
; unit_number&lt;br /&gt;
: refers to one of the logical units of the device. Devices with one unit always use unit 0. Multiple unit devices like the trackdisk device and the timer device use the different units for specific purposes.&lt;br /&gt;
&lt;br /&gt;
; IORequest&lt;br /&gt;
: is the structure discussed above. Some of the devices have their own I/O requests defined in their include files and others use standard I/O requests, (IOStdReq). Refer to [[Devices|Devices]] for more information.&lt;br /&gt;
&lt;br /&gt;
; flags&lt;br /&gt;
: are bits set to indicate options for some of the devices. This field is set to zero for devices which don&#039;t accept options when they are opened. The flags for each device are explained in [[Devices|Devices]].&lt;br /&gt;
&lt;br /&gt;
; result&lt;br /&gt;
: is an indication of whether the OpenDevice() was successful with zero indicating success. Never assume that a device will successfully open. Check the return value and act accordingly.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=&#039;&#039;Zero Equals Success for OpenDevice().&#039;&#039;|Unlike most Amiga system functions, OpenDevice() returns zero for success and a device-specific error value for failure.}}&lt;br /&gt;
&lt;br /&gt;
=== Using A Device ===&lt;br /&gt;
&lt;br /&gt;
Once a device has been opened, you use it by passing the I/O request to it. When the device processes the I/O request, it acts on the information the I/O request contains and returns a reply message, i.e., the I/O request, to the reply port specified in the I/O request when it is finished. The I/O request is passed to a device using one of three functions, &#039;&#039;&#039;DoIO()&#039;&#039;&#039;, &#039;&#039;&#039;SendIO()&#039;&#039;&#039; and &#039;&#039;&#039;BeginIO()&#039;&#039;&#039;. They take only one argument: the I/O request you wish to pass to the device.&lt;br /&gt;
&lt;br /&gt;
;DoIO()&lt;br /&gt;
:DoIO() is a synchronous function. It will not return until the device has responded to the I/O request.&lt;br /&gt;
:This function calls the BeginIO() entry point in the device driver with IOF_QUICK set. If the device driver leaves IOF_QUICK set, it returns to the caller immediately. The return value is the extended value of the &amp;quot;io_Error&amp;quot; field in the I/O request. If the IOF_QUICK bit is cleared, it falls through to WaitIO().&lt;br /&gt;
&lt;br /&gt;
;SendIO()&lt;br /&gt;
:SendIO() is an asynchronous function. It can return immediately, but the I/O operation it initiates may take a short or long time. Using &#039;&#039;&#039;SendIO()&#039;&#039;&#039; requires you to monitor the message port for a return message from the device. In addition, some devices do not actually respond asynchronously even though you called &#039;&#039;&#039;SendIO()&#039;&#039;&#039;; they will return when the I/O operation is finished.&lt;br /&gt;
:This function calls the BeginIO() entry point in the device driver with IOF_QUICK clear. This means that the device driver should return the I/O request with ReplyMsg().&lt;br /&gt;
&lt;br /&gt;
;BeginIO()&lt;br /&gt;
:There is one operation which DoIO() and SendIO() cannot handle, and that is sending an I/O request with the IOF_QUICK flag set, but not waiting for it to complete. That is &amp;quot;run this as quickly as possible but if it&#039;s going to take a while, don&#039;t wait for it&amp;quot;. For this operation, the user must set IOF_QUICK manually, then call the device driver directly through its BeginIO() entry point.&lt;br /&gt;
:When the quick I/O flag (IOF_QUICK) is set in the I/O request, a device is allowed to take certain shortcuts in performing and completing a request. If the request can complete immediately, the device will not return a reply message and the quick I/O flag will remain set. If the request cannot be completed immediately, the QUICK_IO flag will be clear. &#039;&#039;&#039;DoIO()&#039;&#039;&#039; normally requests quick I/O; &#039;&#039;&#039;SendIO()&#039;&#039;&#039; does not.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DoIO()&#039;&#039;&#039; and &#039;&#039;&#039;SendIO()&#039;&#039;&#039; are most commonly used.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=BeginIO() Side Effects|text=BeginIO() will run on the context of the caller if quick I/O is used by the device driver. This means BeginIO() may allocate memory, wait or perform other functions which are illegal or dangerous during interrupts.}}&lt;br /&gt;
&lt;br /&gt;
An I/O request typically has three fields set for every command sent to a device. You set the command itself in the &#039;&#039;&#039;io_Command&#039;&#039;&#039; field, a pointer to the data for the command in the &#039;&#039;&#039;io_Data&#039;&#039;&#039; field, and the length of the data in the &#039;&#039;&#039;io_Length&#039;&#039;&#039; field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Length  = sizeof(ReadBuffer);&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Data    = ReadBuffer;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Command = CMD_READ;&lt;br /&gt;
IExec-&amp;gt;SendIO((struct IORequest *)SerialIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commands consist of two parts—a prefix and the command word separated by an underscore, all in upper case. The prefix indicates whether the command is an Exec or device specific command. All Exec commands have &#039;&#039;&#039;CMD&#039;&#039;&#039; as the prefix. They are defined in the include file exec/io.h.&lt;br /&gt;
&lt;br /&gt;
=== Standard Exec Commands ===&lt;br /&gt;
&lt;br /&gt;
This section lists the command and error numbers which are predefined in the Amiga system for all types of device drivers.&lt;br /&gt;
&lt;br /&gt;
The following commands are reserved for all devices:&lt;br /&gt;
* CMD_INVALID&lt;br /&gt;
* CMD_RESET&lt;br /&gt;
* CMD_READ&lt;br /&gt;
* CMD_WRITE&lt;br /&gt;
* CMD_UPDATE&lt;br /&gt;
* CMD_CLEAR&lt;br /&gt;
* CMD_STOP&lt;br /&gt;
* CMD_START&lt;br /&gt;
* CMD_FLUSH&lt;br /&gt;
* CMD_NONSTD&lt;br /&gt;
&lt;br /&gt;
It is seen that command number zero is invalid, and command numbers greater than 9 are custom defined.&lt;br /&gt;
&lt;br /&gt;
There are 65536 command values possible with io_Command:&lt;br /&gt;
&lt;br /&gt;
    $0000 - $3FFF       old style and 3rd party commands&lt;br /&gt;
    $4000 - $7FFF       RESERVED AREA!&lt;br /&gt;
    $8000 - $BFFF       old style and 3rd party commands&lt;br /&gt;
    $C000 - $FFFF       RESERVED AREA!&lt;br /&gt;
&lt;br /&gt;
Commands in the reserved areas may only be assigned and specified by the AmigaOS Development Team. Any &amp;quot;custom&amp;quot; implementation of these commands or other commands in these reserved areas is illegal and violates the [[NSD_Standard|New Style Device (NSD) Standard]].&lt;br /&gt;
&lt;br /&gt;
==== CMD_RESET ====&lt;br /&gt;
&lt;br /&gt;
This resets the device to a known initial state. Pending I/O requests not processed at the time of this command should be returned with an error.&lt;br /&gt;
&lt;br /&gt;
==== CMD_READ ====&lt;br /&gt;
&lt;br /&gt;
This requests that &amp;quot;io_Length&amp;quot; items of data be read from location &amp;quot;io_Offset&amp;quot; on the unit, and stored at &amp;quot;io_Data&amp;quot; in the caller&#039;s memory. The actual amount of data transferred is returned in &amp;quot;io_Actual&amp;quot;. The specifics depend on the device type.&lt;br /&gt;
&lt;br /&gt;
==== CMD_WRITE ====&lt;br /&gt;
&lt;br /&gt;
This requests that data be transferred from the caller&#039;s memory to the I/O unit. The arguments are the same as for CMD_READ.&lt;br /&gt;
&lt;br /&gt;
==== CMD_UPDATE ====&lt;br /&gt;
&lt;br /&gt;
This requests that all buffered, but unwritten data be forced out to the I/O unit. It might write out the track buffer in a disk device, for example.&lt;br /&gt;
&lt;br /&gt;
==== CMD_CLEAR ====&lt;br /&gt;
&lt;br /&gt;
This requests that all data buffered by the device for the given unit be invalidated. Thus, for example, it would throw away data waiting in a serial input buffer.&lt;br /&gt;
&lt;br /&gt;
==== CMD_STOP ====&lt;br /&gt;
&lt;br /&gt;
This requests that the unit stop processing commands. I/O requests not processed at the time of the CMD_STOP will wait until a CMD_START or CMD_RESET is received or they are aborted.&lt;br /&gt;
&lt;br /&gt;
==== CMD_START ====&lt;br /&gt;
&lt;br /&gt;
This requests that the unit clear a CMD_STOP condition and resume processing commands. Only one CMD_START is required, regardless of how many CMD_STOPs have been received.&lt;br /&gt;
&lt;br /&gt;
==== CMD_FLUSH ====&lt;br /&gt;
&lt;br /&gt;
This requests that the unit flush all pending commands. All I/O requests queued but not yet processed should be sent back with an error.&lt;br /&gt;
&lt;br /&gt;
==== Error Numbers ====&lt;br /&gt;
&lt;br /&gt;
The include file &amp;quot;exec/errors.h&amp;quot; lists the following standard error numbers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Constant !! Description&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_SUCCESS || not an error (success)&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_OPENFAIL || device/unit failed to open&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_ABORTED || request aborted&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_NOCMD || command not supported&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_BADLENGTH || not a valid length&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_BADADDRESS || invalid address (misaligned or bad range)&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_UNITBUSY || device opens ok, but requested unit is busy&lt;br /&gt;
|-&lt;br /&gt;
| IOERR_SELFTEST || device is currently performing a self-test&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Synchronous vs. Asynchronous Requests ===&lt;br /&gt;
&lt;br /&gt;
Each device maintains its own I/O request queue. When a device receives an I/O request, it either processes the request immediately or puts it in the queue because one is already being processed. After an I/O request is completely processed, the device checks its queue and if it finds another I/O request, begins to process that request.&lt;br /&gt;
&lt;br /&gt;
As stated above, you can send I/O requests to a device synchronously or asynchronously. The choice of which to use is largely a function of your application.&lt;br /&gt;
&lt;br /&gt;
Synchronous requests use the &#039;&#039;&#039;DoIO()&#039;&#039;&#039; function. &#039;&#039;&#039;DoIO()&#039;&#039;&#039; will not return control to your application until the I/O request has been satisfied by the device. The advantage of this is that you don’t have to monitor the message port for the device reply because &#039;&#039;&#039;DoIO()&#039;&#039;&#039; takes care of all the message handling. The disadvantage is that your application will be tied up while the I/O request is being processed, and should the request not complete for some reason, &#039;&#039;&#039;DoIO()&#039;&#039;&#039; will not return and your application will hang.&lt;br /&gt;
&lt;br /&gt;
Asynchronous requests use the &#039;&#039;&#039;SendIO()&#039;&#039;&#039; and &#039;&#039;&#039;BeginIO()&#039;&#039;&#039; functions. Both return to your application almost immediately after you call them. This allows you to do other operations, including sending more I/O requests to the device. Note that any additional I/O requests you send must use separate I/O request structures. Outstanding I/O requests are not available for re-use until the device is finished with them.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=&#039;&#039;Do Not Touch!&#039;&#039;|text=When you use &#039;&#039;&#039;SendIO()&#039;&#039;&#039; or &#039;&#039;&#039;BeginIO()&#039;&#039;&#039;, the I/O request you pass to the device and any associated data buffers should be considered read-only. Once you send it to the device, you must &#039;&#039;not&#039;&#039; modify it in any way until you receive the reply message from the device or abort the request (though you must still wait for a reply). Any exceptions to this rule are documented in the autodoc for the device.}}&lt;br /&gt;
&lt;br /&gt;
Sending multiple asynchronous I/O requests to a device can be tricky because devices require them to be unique and initialized. This means you can&#039;t use an I/O request that&#039;s still in the queue, but you need the fields which were initialized in it when you opened the device. The solution is to copy the initialized I/O request to another I/O request(s) before sending anything to the device.&lt;br /&gt;
&lt;br /&gt;
Regardless of what you do while you are waiting for an asynchronous I/O request to return, you need to have some mechanism for knowing when the request has been done. There are two basic methods for doing this.&lt;br /&gt;
&lt;br /&gt;
The first involves putting your application into a wait state until the device returns the I/O request to the message port of your application. You can use the &#039;&#039;&#039;WaitIO()&#039;&#039;&#039;, &#039;&#039;&#039;Wait()&#039;&#039;&#039; or &#039;&#039;&#039;WaitPort()&#039;&#039;&#039; function to wait for the return of the I/O request. It is important to note that all of the above functions and also &#039;&#039;&#039;DoIO()&#039;&#039;&#039; may &#039;&#039;&#039;Wait()&#039;&#039;&#039; on the message reply port&#039;s mp_SigBit. For this reason, the task that created the port must be the same task the waits for completion of the I/O. There are three ways to wait:&lt;br /&gt;
&lt;br /&gt;
; WaitIO()&lt;br /&gt;
: not only waits for the return of the I/O request, it also takes care of all the message handling functions. This is very convenient, but you can pay for this convenience: your application will hang in the unlikely event that the I/O request does not return.&lt;br /&gt;
: If the I/O request has the IOF_QUICK flag set, it cannot possibly be in progress, so it returns immediately.&lt;br /&gt;
&lt;br /&gt;
; Wait()&lt;br /&gt;
: waits for a signal to be sent to the message port. It will awaken your task when the signal arrives, but you are responsible for all of the message handling.&lt;br /&gt;
&lt;br /&gt;
; WaitPort()&lt;br /&gt;
: waits for the message port to be non-empty. It returns a pointer to the message in the port, but you are responsible for all of the message handling.&lt;br /&gt;
&lt;br /&gt;
The second method to detect when the request is complete involves using the &#039;&#039;&#039;CheckIO()&#039;&#039;&#039; function. &#039;&#039;&#039;CheckIO()&#039;&#039;&#039; takes an I/O request as its argument and returns an indication of whether or not it has been completed. When &#039;&#039;&#039;CheckIO()&#039;&#039;&#039; returns the completed indication, you will still have to remove the I/O request from the message port.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=WaitIO() and CheckIO() know about the IOF_QUICK flag and will behave accordingly.}}&lt;br /&gt;
&lt;br /&gt;
=== I/O Request Completion ===&lt;br /&gt;
&lt;br /&gt;
A device will set the &#039;&#039;&#039;io_Error&#039;&#039;&#039; field of the I/O request to indicate the success or failure of an operation. The indication will be either zero for success or a non-zero error code for failure. There are two types of error codes: Exec I/O and device specific. Exec I/O errors are defined in the include file &amp;amp;lt;exec/errors.h&amp;amp;gt;; device specific errors are defined in the include file for each device. You should always check that the operation you requested was successful.&lt;br /&gt;
&lt;br /&gt;
The exact method for checking &#039;&#039;&#039;io_Error&#039;&#039;&#039; can depend on whether you use &#039;&#039;&#039;DoIO()&#039;&#039;&#039; or &#039;&#039;&#039;SendIO()&#039;&#039;&#039;. In both cases, &#039;&#039;&#039;io_Error&#039;&#039;&#039; will be set when the I/O request is returned, but in the case of &#039;&#039;&#039;DoIO()&#039;&#039;&#039;, the &#039;&#039;&#039;DoIO()&#039;&#039;&#039; function itself returns the same value as &#039;&#039;&#039;io_Error&#039;&#039;&#039;. This gives you the option of checking the function return value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Length  = sizeof(ReadBuffer);&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Data    = ReadBuffer;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Command = CMD_READ;&lt;br /&gt;
if (IExec-&amp;gt;DoIO((struct IORequest *)SerialIO)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Read failed.  Error: %ld\n&amp;quot;, SerialIO-&amp;gt;IOSer.io_Error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can check io_Error directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Length  = sizeof(ReadBuffer);&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Data    = ReadBuffer;&lt;br /&gt;
SerialIO-&amp;gt;IOSer.io_Command = CMD_READ;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)SerialIO);&lt;br /&gt;
if (SerialIO-&amp;gt;IOSer.io_Error)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Read failed.  Error: %ld\n&amp;quot;, SerialIO-&amp;gt;IOSer.io_Error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keep in mind that checking &#039;&#039;&#039;io_Error&#039;&#039;&#039; is the &#039;&#039;only&#039;&#039; way that I/O requests sent by &#039;&#039;&#039;SendIO()&#039;&#039;&#039; can be checked.&lt;br /&gt;
&lt;br /&gt;
Testing for a failed I/O request is a minimum step, what you do beyond that depends on your application. In some instances, you may decide to resend the I/O request and in others, you may decide to stop your application. One thing you&#039;ll almost always want to do is to inform the user that an error has occurred.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Exiting The Correct Way|text=If you decide that you must prematurely end your application, you should deallocate, release, give back and let go of everything you took to run the application. In other words, you should exit gracefully.}}&lt;br /&gt;
&lt;br /&gt;
=== Ending Device Access ===&lt;br /&gt;
&lt;br /&gt;
You end device access by reversing the steps you did to access it. This means you close the device, deallocate the I/O request memory and delete the message port. In that order!&lt;br /&gt;
&lt;br /&gt;
Closing a device is how you tell Exec that you are finished using a device and any associated resources. This can result in housecleaning being performed by the device. However, before you close a device, you might have to do some housecleaning of your own.&lt;br /&gt;
&lt;br /&gt;
A device is closed by calling the &#039;&#039;&#039;CloseDevice()&#039;&#039;&#039; function. The &#039;&#039;&#039;CloseDevice()&#039;&#039;&#039; function does not return a value. It has this format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IExec-&amp;gt;CloseDevice(IORequest);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &#039;&#039;&#039;IORequest&#039;&#039;&#039; is the I/O request used to open the device.&lt;br /&gt;
&lt;br /&gt;
You should not close a device while there are outstanding I/O requests, otherwise you can cause major and minor problems. Let&#039;s begin with the minor problem: memory. If an I/O request is outstanding at the time you close a device, you won&#039;t be able to reclaim the memory you allocated for it.&lt;br /&gt;
&lt;br /&gt;
The major problem: the device will try to respond to the I/O request. If the device tries to respond to an I/O request, and you&#039;ve deleted the message port (which is covered below), you will probably crash the system.&lt;br /&gt;
&lt;br /&gt;
One solution would be to wait until all I/O requests you sent to the device return. This is not always practical if you&#039;ve sent a few requests and the user wants to exit the application immediately.&lt;br /&gt;
&lt;br /&gt;
In that case, the only solution is to abort and remove any outstanding I/O requests. You do this with the functions &#039;&#039;&#039;AbortIO()&#039;&#039;&#039; and &#039;&#039;&#039;WaitIO()&#039;&#039;&#039;. They must be used together for cleaning up. &#039;&#039;&#039;AbortIO()&#039;&#039;&#039; will abort an I/O request, but will not prevent a reply message from being sent to the application requesting the abort. &#039;&#039;&#039;WaitIO()&#039;&#039;&#039; will wait for an I/O request to complete and remove it from the message port. This is why they must be used together.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=&#039;&#039;Be Careful With AbortIO()!&#039;&#039;|Do not AbortIO() an I/O request which has &#039;&#039;not&#039;&#039; been sent to a device. If you do, you may crash the system.}}&lt;br /&gt;
&lt;br /&gt;
Here is the checklist for gracefully exiting:&lt;br /&gt;
&lt;br /&gt;
# Abort any outstanding I/O requests with &#039;&#039;&#039;AbortIO()&#039;&#039;&#039;.&lt;br /&gt;
# Wait for the completion of any outstanding or aborted I/O requests with &#039;&#039;&#039;WaitIO()&#039;&#039;&#039;.&lt;br /&gt;
# Close the device with &#039;&#039;&#039;CloseDevice()&#039;&#039;&#039;.&lt;br /&gt;
# Release the I/O request memory with &#039;&#039;&#039;FreeSysObject()&#039;&#039;&#039; using the &#039;&#039;&#039;ASOT_IOREQUEST&#039;&#039;&#039; object type.&lt;br /&gt;
# Delete the message port with &#039;&#039;&#039;FreeSysObject()&#039;&#039;&#039; using the &#039;&#039;&#039;ASOT_PORT&#039;&#039;&#039; object type.&lt;br /&gt;
&lt;br /&gt;
=== Expunging a Device ===&lt;br /&gt;
&lt;br /&gt;
A device can be deleted from the system using this function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
error = IExec-&amp;gt;RemDevice(device);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The system issues this function itself if it runs out of memory and tries to reclaim space. The device is called through its Expunge() entry point.&lt;br /&gt;
&lt;br /&gt;
The device should now shut down its activity, i.e. remove interrupt servers, deallocate buffers, and so on. Then it should unlink its device node from the device list, and deallocate the node, thus restoring things to the state they were in just before it was started up with InitResident(). Finally, it should return the &amp;quot;segList&amp;quot; parameter which was passed to it at initialization time. If the device came from disk, the system will use this to unload its code.&lt;br /&gt;
&lt;br /&gt;
If the device is not idle when the Expunge() call arrives, it can defer the operation. To do this, it sets the LIBF_DELEXP flag in the library structure, and returns zero. This indicates that it will delete itself at the earliest opportunity.&lt;br /&gt;
&lt;br /&gt;
== Devices With Functions ==&lt;br /&gt;
&lt;br /&gt;
Some devices, in addition to their commands, provide library-style functions which can be directly called by applications. These functions are documented by each device.&lt;br /&gt;
&lt;br /&gt;
Devices with functions behave much like Amiga libraries, i.e., you set up a base address pointer and call the functions as offsets from the pointer. See [[Exec_Libraries|Exec Libraries]] for more information.&lt;br /&gt;
&lt;br /&gt;
The procedure for accessing a device&#039;s functions is as follows:&lt;br /&gt;
&lt;br /&gt;
* Declare the device base address variable in the global data area. The correct name for the base address can be found in the device&#039;s proto file.&lt;br /&gt;
&lt;br /&gt;
* Create a message port.&lt;br /&gt;
&lt;br /&gt;
* Create an I/O request.&lt;br /&gt;
&lt;br /&gt;
* Call &#039;&#039;&#039;OpenDevice()&#039;&#039;&#039;, passing the I/O request. If &#039;&#039;&#039;OpenDevice()&#039;&#039;&#039; is successful (returns 0), the address of the device base may be found in the &#039;&#039;&#039;io_Device&#039;&#039;&#039; field of the I/O request structure. Consult the include file for the structure you are using to determine the full name of the &#039;&#039;&#039;io_Device&#039;&#039;&#039; field. The base address is only valid while the device is open.&lt;br /&gt;
&lt;br /&gt;
* Set the device base address variable to the pointer returned in the &#039;&#039;&#039;io_Device&#039;&#039;&#039; field.&lt;br /&gt;
&lt;br /&gt;
* Get the interface pointer for this device using the above device base address.&lt;br /&gt;
&lt;br /&gt;
We will use the timer device to illustrate the above method. The name of the timer device base address is listed in its proto file as TimerBase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *TimerBase;                   /* device base address pointer */&lt;br /&gt;
struct TimerIFace *ITimer;                   /* interface for timer device  */&lt;br /&gt;
struct MsgPort *TimerMP;                     /* message port pointer        */&lt;br /&gt;
struct TimeRequest *TimerIO;                 /* I/O request pointer         */&lt;br /&gt;
&lt;br /&gt;
/* Create the message port.    */&lt;br /&gt;
TimerMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
if (TimerMP != NULL)&lt;br /&gt;
{&lt;br /&gt;
    /* Create the I/O request.  */&lt;br /&gt;
    TimerIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
        ASOIOR_ReplyPort, TimerMP,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if ( TimerIO != NULL )&lt;br /&gt;
    {&lt;br /&gt;
        /* Open the timer device.  */&lt;br /&gt;
        if ( !(IExec-&amp;gt;OpenDevice(TIMERNAME,UNIT_MICROHZ,TimerIO,0)) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Set up pointer for timer functions.  */&lt;br /&gt;
            TimerBase = (struct Library *)TimerIO-&amp;gt;tr_node.io_Device;&lt;br /&gt;
&lt;br /&gt;
            ITimer = (struct TimerIFace *)IExec-&amp;gt;GetInterface(TimerBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
            /* Use timer device library-style functions such as CmpTime() through ITimer...*/&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;CloseDevice(TimerIO);   /* Close the timer device. */&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Error: Could not open %s\n&amp;quot;, TIMERNAME);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create I/O request\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create message port\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Using An Exec Device ==&lt;br /&gt;
&lt;br /&gt;
The following short example demonstrates use of an Amiga device. The example opens the serial.device and then demonstrates both synchronous (DoIO()) and asynchronous (SendIO()) use of the serial command SDCMD_QUERY. This command is used to determine the status of the serial device lines and registers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* DeviceUse.c - an example of using an Amiga device (here, serial device)    */&lt;br /&gt;
/*                                                                            */&lt;br /&gt;
/* If successful, use the serial command SDCMD_QUERY, then reverse our steps. */&lt;br /&gt;
/* If we encounter an error at any time, we will gracefully exit.             */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/serial.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct IOExtSer *reply;         /* for use with GetMsg             */&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *serialMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
    &lt;br /&gt;
    if (serialMP != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* Create the I/O request. Note that &amp;lt;devices/serial.h&amp;gt; defines the type */&lt;br /&gt;
        /* of IORequest required by the serial device--an IOExtSer. Many devices */&lt;br /&gt;
        /* require specialized extended IO requests which start with an embedded */&lt;br /&gt;
        /* struct IORequest. The generic Exec and amiga.lib device IO functions  */&lt;br /&gt;
        /* are prototyped for IORequest, so some pointer casting is necessary.   */&lt;br /&gt;
&lt;br /&gt;
        struct IOExtSer *serialIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
          ASOIOR_Size, sizeof(struct IOExtSer),&lt;br /&gt;
          ASOIOR_ReplyPort, serialMP,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (serialIO != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            /* Open the serial device (non-zero return value means failure here). */&lt;br /&gt;
            if (IExec-&amp;gt;OpenDevice( SERIALNAME, 0, (struct IORequest *)serialIO, 0))&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Error: %s did not open\n&amp;quot;, SERIALNAME);&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                /* Device is open */                         /* DoIO - demonstrates synchronous */&lt;br /&gt;
                serialIO-&amp;gt;IOSer.io_Command  = SDCMD_QUERY;   /* device use, returns error or 0. */&lt;br /&gt;
                if (IExec-&amp;gt;DoIO((struct IORequest *)serialIO))&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Query  failed. Error - %d\n&amp;quot;, serialIO-&amp;gt;IOSer.io_Error);&lt;br /&gt;
                else&lt;br /&gt;
                    /* Print serial device status - see include file for meaning */&lt;br /&gt;
                    /* Note that with DoIO, the Wait and GetMsg are done by Exec */&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Serial device status: $%x\n\n&amp;quot;, serialIO-&amp;gt;io_Status);&lt;br /&gt;
&lt;br /&gt;
                serialIO-&amp;gt;IOSer.io_Command = SDCMD_QUERY;    /* SendIO - demonstrates asynchronous */&lt;br /&gt;
                IExec-&amp;gt;SendIO((struct IORequest *)serialIO); /* device use (returns immediately).  */&lt;br /&gt;
&lt;br /&gt;
                /* We could do other things here while the query is being done.      */&lt;br /&gt;
                /* And to manage our asynchronous device IO:                         */&lt;br /&gt;
                /*   - we can CheckIO(serialIO) to check for completion              */&lt;br /&gt;
                /*   - we can AbortIO(serialIO) to abort the command                 */&lt;br /&gt;
                /*   - we can WaitPort(serialMP) to wait for any serial port reply   */&lt;br /&gt;
                /*  OR we can WaitIO(serialIO) to wait for this specific IO request  */&lt;br /&gt;
                /*  OR we can Wait(1L &amp;lt;&amp;lt; serialMP_&amp;gt;mp_SigBit) for reply port signal  */&lt;br /&gt;
&lt;br /&gt;
                IExec-&amp;gt;Wait(1U &amp;lt;&amp;lt; serialMP-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
                while(reply = (struct IOExtSer *)IExec-&amp;gt;GetMsg(serialMP))&lt;br /&gt;
                {    /* Since we sent out only one serialIO request the while loop is */&lt;br /&gt;
                     /* not really needed--we only expect one reply to our one query  */&lt;br /&gt;
                     /* command, and the reply message pointer returned by GetMsg()   */&lt;br /&gt;
                     /* will just be another pointer to our one serialIO request.     */&lt;br /&gt;
                     /* With Wait() or WaitPort(), you must GetMsg() the message.     */&lt;br /&gt;
                    if(reply-&amp;gt;IOSer.io_Error)&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Query  failed. Error - %d\n&amp;quot;, reply-&amp;gt;IOSer.io_Error);&lt;br /&gt;
                    else&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Serial device status: $%x\n\n&amp;quot;, reply-&amp;gt;io_Status);&lt;br /&gt;
                }&lt;br /&gt;
                IExec-&amp;gt;CloseDevice((struct IORequest *)serialIO);  /* Close the serial device.    */&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, serialIO);   /* Delete the I/O request.     */&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Error: Could create I/O request\n&amp;quot;); /* Inform user that the I/O    */&lt;br /&gt;
                                                                /* request could be created.   */&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, serialMP);          /* Delete the message port.    */&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create message port\n&amp;quot;);  /* Inform user that the message*/&lt;br /&gt;
                                                                  /* port could not be created.  */&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Creating An Exec Device ==&lt;br /&gt;
&lt;br /&gt;
=== The BeginIO Entry Point ===&lt;br /&gt;
&lt;br /&gt;
All I/O requests enter the device driver through the BeginIO() entry point. The device driver is entered in the context of the requesting task.&lt;br /&gt;
&lt;br /&gt;
Normally, the device driver will now use PutMsg() to enqueue the I/O request on a message port (in a Unit structure) for processing by an internal task. Then it can return from the BeginIO() function. When Exec checks to see if the I/O request is completed yet, it checks its type field, and if it is NT_MESSAGE (as results from the PutMsg() call) it knows that it is still in progress. Eventually, the internal task receives the I/O request, operates on it, and does a ReplyMsg(). This returns the I/O request to the caller&#039;s reply port, and also sets its type to NT_REPLYMSG, signaling that it is finished.&lt;br /&gt;
&lt;br /&gt;
It is clear that the device driver does not have to follow this procedure exactly. Short commands (such as checking if a disk is ready) can just be done immediately, in the caller&#039;s context. The I/O request must simply be returned with ReplyMsg() at the end, and its type field must be something other than NT_REPLYMSG if the BeginIO() function returns with&lt;br /&gt;
the I/O request not completed yet.&lt;br /&gt;
&lt;br /&gt;
A special case of I/O processing is signaled by the IOF_QUICK flag. When it is set, it means that the requester has used the DoIO() function, and thus will be doing nothing until the I/O is complete. In this case, the device driver can run the whole I/O operation in the caller&#039;s context and return immediately. Message passing and task switch overhead is eliminated. When the BeginIO() function returns with the IOF_QUICK bit still set, it means that the I/O operation is complete.&lt;br /&gt;
&lt;br /&gt;
If the device driver sees the IOF_QUICK flag set but cannot perform the I/O processing inline, it can simply clear the flag and return the I/O request with ReplyMsg() as usual.&lt;br /&gt;
&lt;br /&gt;
The BeginIO() function operates on the command and parameters in the I/O request, and sets the &amp;quot;io_Error&amp;quot; field to indicate the result. Exec I/O functions return the value of this field to the caller; BeginIO() itself does not return a value. &amp;quot;io_Error&amp;quot; set to zero means that no error has occurred.&lt;br /&gt;
&lt;br /&gt;
=== The AbortIO Entry Point ===&lt;br /&gt;
&lt;br /&gt;
Some device driver operations, such as waiting for a timeout or input on a serial port, may need to be aborted before they complete. The AbortIO() function is provided for this. The device driver is entered through its AbortIO() entry point with the address of the I/O request to be aborted and the device node pointer. If the device driver determines that the I/O request is indeed in progress and can successfully abort it, it returns zero, otherwise it returns a non-zero error code.&lt;br /&gt;
&lt;br /&gt;
A successfully aborted I/O request is returned by the normal method, i.e. ReplyMsg(). The &amp;quot;io_Error&amp;quot; field should indicate that it did not complete normally.&lt;br /&gt;
&lt;br /&gt;
=== Unit Structures ===&lt;br /&gt;
&lt;br /&gt;
Many device drivers manage more than one functional unit. For example, the trackdisk driver can handle up to four floppy drives. The preferred approach is to use a separate &amp;quot;Unit&amp;quot; structure for each functional unit (e.g. drive). Normally, a unit structure consists of at least the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Unit {&lt;br /&gt;
    struct  MsgPort unit_MsgPort;&lt;br /&gt;
    uint8   unit_flags;&lt;br /&gt;
            /*  UNITF_ACTIVE = 1&lt;br /&gt;
                UNITF_INTASK = 2 */&lt;br /&gt;
    uint8   unit_pad;&lt;br /&gt;
    uint16  unit_OpenCnt;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the device driver is opened, it uses the unit number to select the appropriate unit structure, and stores the pointer to this structure in the I/O request. Later, it can queue up pending I/O requests on the message port for processing by the unit task.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control device I/O. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Exec Device I/O Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_IOREQUEST)&lt;br /&gt;
| Create an IORequest structure.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_IOREQUEST)&lt;br /&gt;
| Delete an IORequest created by AllocSysObject().&lt;br /&gt;
|-&lt;br /&gt;
| OpenDevice()&lt;br /&gt;
| Gain access to an Exec device.&lt;br /&gt;
|-&lt;br /&gt;
| CloseDevice()&lt;br /&gt;
| Close Exec device opened with OpenDevice().&lt;br /&gt;
|-&lt;br /&gt;
| DoIO()&lt;br /&gt;
| Perform a device I/O command and wait for completion.&lt;br /&gt;
|-&lt;br /&gt;
| SendIO()&lt;br /&gt;
| Initiate an I/O command. Do not wait for it to complete.&lt;br /&gt;
|-&lt;br /&gt;
| CheckIO()&lt;br /&gt;
| Get the status of an IORequest.&lt;br /&gt;
|-&lt;br /&gt;
| WaitIO()&lt;br /&gt;
| Wait for completion of an I/O request.&lt;br /&gt;
|-&lt;br /&gt;
| AbortIO()&lt;br /&gt;
| Attempt to abort an I/O request that is in progress.&lt;br /&gt;
|-&lt;br /&gt;
| BeginIO()&lt;br /&gt;
| Initiate an asynchronous device I/O request.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Credits ==&lt;br /&gt;
&lt;br /&gt;
Special thanks to [http://wandel.ca Markus Wandel] for allowing us to use material from his [http://wandel.ca/homepage/execdis/devices_doc.txt Amiga Device Driver Guide].&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Xena_Resource&amp;diff=12546</id>
		<title>Xena Resource</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Xena_Resource&amp;diff=12546"/>
		<updated>2025-01-26T19:28:52Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Xena Resource ==&lt;br /&gt;
&lt;br /&gt;
The Xena Resource provides access to the Xena chip on the AmigaOne X1000. Two functions allow you to use the Xena chip.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Xena Resource Functions&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| AllocResource() || Obtain exclusive access to one of the Xena resources.&lt;br /&gt;
|-&lt;br /&gt;
| FreeResource() || Returns a resource to the system.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Xena Resource Information&lt;br /&gt;
|-&lt;br /&gt;
| Includes || resources/xena.h&lt;br /&gt;
|-&lt;br /&gt;
| AutoDocs || [[xenares.doc]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are four separate resources that may be requested.&lt;br /&gt;
The application must secure as many as it requires to access the Xena system.&lt;br /&gt;
XR_XTAGPORT is for access to the JTAG control lines that access the Xena chip and any additional devices on the Xorro board.&lt;br /&gt;
XR_DATABUS is for the fast Localbus access to Core0.&lt;br /&gt;
XR_INTERRUPT is used for Xena to interrupt the PA6T when it needs attention. This is likely to be used with XR_DATABUS&lt;br /&gt;
XR_LONELYBIT is the extra unassigned pin&lt;br /&gt;
&lt;br /&gt;
Example code to use the XTAG port:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/xena.h&amp;gt;&lt;br /&gt;
#include &amp;lt;resources/xena.h&amp;gt;&lt;br /&gt;
struct XenaIFace *IXena;&lt;br /&gt;
IXena = IExec-&amp;gt;OpenResource(&amp;quot;xena.resource&amp;quot;);&lt;br /&gt;
if(NULL == IXena)&lt;br /&gt;
{&lt;br /&gt;
    exit(-5);&lt;br /&gt;
}&lt;br /&gt;
STRPTR result = IXena-&amp;gt;AllocResource(XR_XTAGPORT, &amp;quot;MyAppName&amp;quot;);&lt;br /&gt;
if(NULL == result)&lt;br /&gt;
{&lt;br /&gt;
    // we have exclusive access, &lt;br /&gt;
    // code using the JTAG port goes here&lt;br /&gt;
&lt;br /&gt;
    //when finished, just&lt;br /&gt;
    IXena-&amp;gt;FreeResource(XR_XTAGPORT);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;We cannot access the port because %s got here first\n&amp;quot;, result);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmiWest_Lesson_1&amp;diff=12545</id>
		<title>AmiWest Lesson 1</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmiWest_Lesson_1&amp;diff=12545"/>
		<updated>2025-01-26T19:28:40Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= AmiWest Lesson 1: Coding Basics =&lt;br /&gt;
&lt;br /&gt;
== Compiler and Linker ==&lt;br /&gt;
&lt;br /&gt;
The [http://gcc.gnu.org/ GNU GCC toolset] is the officially supported standard compiler suite for Amiga operating system programming. An good alternative C compiler named [http://www.compilers.de/vbcc.html vbcc] is also supported and is popular with some programmers. We will focus on GCC which supports both C and C++.&lt;br /&gt;
&lt;br /&gt;
Some Amiga-specific extensions to the baseline GNU GCC have been made to support the new Exec Interfaces, switching C standard libraries, shared objects and more. The source code for GNU GCC is readily available from the [http://sourceforge.net/projects/adtools/ Amiga Development Tools web site] and you are encouraged to participate if you are able.&lt;br /&gt;
&lt;br /&gt;
There is a large wealth of detailed documentation and support for GNU GCC and programmers are encouraged to search around the internet. The Amiga-specific extensions are described on the [http://wiki.amigaos.net AmigaOS Documentation Wiki].&lt;br /&gt;
&lt;br /&gt;
== Standard C Library ==&lt;br /&gt;
&lt;br /&gt;
AmigaOS officially uses a variant of the [http://sourceware.org/newlib/ Newlib C standard library] implementation. For programmers, it is essential to know what your C standard library is and what its limits are. Functions may or may not be available. Behaviour may or may not be identical between C standard library implementations.&lt;br /&gt;
&lt;br /&gt;
A popular alternative C standard library implementation is called [http://sourceforge.net/projects/clib2/ clib2] and it is included as an option with the standard Amiga SDK. The clib2 library is particular well suited for porting software from the Unix world. The complete source code for clib2 is also available which makes is possible to debug complex problems involving the C library itself.&lt;br /&gt;
&lt;br /&gt;
Switching between C standard library is easy thank to an Amiga-specific extension to GCC. The -mcrt option is used for both compiling and linking:&lt;br /&gt;
 -mcrt=newlib (default, always thread-safe)&lt;br /&gt;
 -mcrt=clib2 (non-thread safe version)&lt;br /&gt;
 -mcrt=clib2-ts (thread-safe version)&lt;br /&gt;
&lt;br /&gt;
== AmigaOS Threading ==&lt;br /&gt;
&lt;br /&gt;
AmigaOS uses lightweight processes and a shared memory model. As such, it has never been a high priority to provide an Amiga-specific threading implementation.&lt;br /&gt;
&lt;br /&gt;
That said, the [http://en.wikipedia.org/wiki/POSIX_Threads POSIX threading] API (Pthreads) has been implemented and is available for programmers. This familiar API is quite popular so a lot of software has been written to use it. &lt;br /&gt;
&lt;br /&gt;
AmigaOS supports a subset of the pthreads standard. It is implemented as a standard shared library with both static and dynamic link interfaces. The dynamic link interface is an Amiga shared object named libpthread.so. The static link interface is available in both newlib and clib2 flavours. These link libraries are just thin wrappers for the underlying pthreads.library which is currently private.&lt;br /&gt;
&lt;br /&gt;
If your software is AmigaOS-specific then it is fine to launch and control processes using the existing API.&lt;br /&gt;
&lt;br /&gt;
== Threading in C and C++ ==&lt;br /&gt;
&lt;br /&gt;
It is very important to note that both C and C++ did not directly support threading until recently. This may come as a surprise to many programmers that have been developing threading applications in both C and C++ for many years.&lt;br /&gt;
&lt;br /&gt;
Since there was no official standard for so long, threading has always been system dependent and several threading standards have come and gone over the years.&lt;br /&gt;
&lt;br /&gt;
With C, the threading issue is relatively simple and GNU GCC supports a memory model which has always supported threading so there is little for an Amiga programmer to worry about. This is true for all platforms GCC supports.&lt;br /&gt;
&lt;br /&gt;
The C++ programming language is a more complicated issue. The current GCC compiler implementation does not support threading on AmigaOS. This can be verified with the g++ -v command. The output of this command will specify what threading model is supported. On AmigaOS it currently states:&lt;br /&gt;
&lt;br /&gt;
 Thread model: single&lt;br /&gt;
&lt;br /&gt;
This means threading is not supported. As a consequence, C++ exceptions and the RTTI feature will not function correctly in the presence of threads. Both features are optional in C++ but highly desirable. Amiga programmers may need to make some tough decisions in this case.&lt;br /&gt;
&lt;br /&gt;
== Program Startup ==&lt;br /&gt;
&lt;br /&gt;
There are two primary environments in which a user can start applications in AmigaOS - by Shell console or by Workbench.  Each method comes with its own way of sending the application its starting arguments, data or options.&lt;br /&gt;
&lt;br /&gt;
With the Workbench, the user can either double-click the application&#039;s icon, shift click the app and some other icon(s) or double-click an icon that has this application as its &amp;quot;default tool&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In the Shell console, the user usually enters the name of the program and ends the command line with arguments, options and/or a filename.&lt;br /&gt;
&lt;br /&gt;
In either case, the application typically uses and responds to the files, options and/or arguments that were involved in starting it.  A file is opened and/or the program runs as configured.&lt;br /&gt;
&lt;br /&gt;
=== How did your application start? ===&lt;br /&gt;
&lt;br /&gt;
Following standard ISO C, AmigaOS applications always start using the standard &#039;&#039;&#039;argc&#039;&#039;&#039; and &#039;&#039;&#039;argv&#039;&#039;&#039; parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;argc&#039;&#039;&#039; variable indicates the number of arguments received by the application. The first argument (&#039;&#039;&#039;argv[0]&#039;&#039;&#039;) contains the program name. Any following command line arguments are stored in following &#039;&#039;&#039;argv&#039;&#039;&#039; array elements (&#039;&#039;&#039;argv[1]&#039;&#039;&#039;, &#039;&#039;&#039;argv[2]&#039;&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
AmigaOS provides special startup code that fills in the argc and argv parameters for you. The reason for the special startup code is twofold: to setup the standard C library and to support launching programs from Workbench or the Shell.&lt;br /&gt;
&lt;br /&gt;
In AmigaOS, if &#039;&#039;argc&#039;&#039;&#039; is equal to zero that means the application was started from the Workbench. If it is non-zero then it means the application was started from the Shell console.&lt;br /&gt;
&lt;br /&gt;
While the ISO C method works for applications started from the Shell, it does not directly support an application started from the Workbench. AmigaOS provides ways for handling both Workbench start-up as well as a more sophisticated way of dealing with Shell start-up and arguments.&lt;br /&gt;
&lt;br /&gt;
=== Starting from the Console: the ISO C way ===&lt;br /&gt;
&lt;br /&gt;
For the sake of ISO C standard conformance, an AmigaOS application can use the standard &#039;&#039;&#039;argv&#039;&#039;&#039; array variables to read the arguments passed to the app.&lt;br /&gt;
&lt;br /&gt;
After determining the argument count (&#039;&#039;&#039;argc&#039;&#039;&#039;) is greater than zero, then that number of arguments can be accessed and printed out as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    for (int argNo=0; argNo &amp;lt; argc; ++argNo)&lt;br /&gt;
    {&lt;br /&gt;
        // print CLI arguments&lt;br /&gt;
        printf(&amp;quot;   argument #%d = &amp;gt;%s&amp;lt;\n&amp;quot;, argNo, argv[argNo]);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method then leaves the parsing and testing of the arguments to the application and its developer. Many 3rd party libraries are available to parse the arguments such as [http://www.gnu.org/software/libc/manual/html_node/Getopt.html Getopt]. Since we are focusing on AmigaOS-specific programming they won&#039;t be discussed any further.&lt;br /&gt;
&lt;br /&gt;
More information about this topic can be found in [[AmigaDOS_Programming#Running_a_Program_under_the_CLI|Running a Program under the CLI]].&lt;br /&gt;
&lt;br /&gt;
=== Starting from the Console: the AmigaOS way ===&lt;br /&gt;
&lt;br /&gt;
AmigaOS provides an alternate way for applications to parse arguments from a Shell command line using the &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function.  In its most common use, this function will parse command line arguments according to a &amp;quot;template&amp;quot; defined by the application.  If the user follows the command name with a question mark, this function will print the application&#039;s commands template and then allow the user to type in their arguments.  If the user fails to provide a valid command line, then &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; lets the application know and the application can then exit gracefully.&lt;br /&gt;
&lt;br /&gt;
More information about this topic can be found in [[AmigaDOS_Programming#Standard_Command_Line_Parsing|Standard Command Line Parsing]].&lt;br /&gt;
&lt;br /&gt;
==== Configuring ReadArgs ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function is called with the following syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RDArgs *result = IDOS-&amp;gt;ReadArgs(CONST_STRPTR template, int32 *array, struct RDArgs *rdargs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first &amp;quot;template&amp;quot; parameter is a pointer to a string that defines the application&#039;s keywords, settings and &amp;quot;toggles&amp;quot; along with the following &amp;quot;modifiers&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
:*	&#039;&#039;&#039;/S&#039;&#039;&#039; - Switch - indicates keyword string is a boolean setting&lt;br /&gt;
:*	&#039;&#039;&#039;/K&#039;&#039;&#039; - Keyword - indicates keyword string must appear before a setting&lt;br /&gt;
:*	&#039;&#039;&#039;/N&#039;&#039;&#039; - Number - indicates a setting must be an integer&lt;br /&gt;
:*	&#039;&#039;&#039;/T&#039;&#039;&#039; - Toggle - indicates keyword must be followed with a boolean setting (ON, OFF, etc)&lt;br /&gt;
:*	&#039;&#039;&#039;/A&#039;&#039;&#039; - Required - indicates keyword must be followed with a value&lt;br /&gt;
:*	&#039;&#039;&#039;/F&#039;&#039;&#039; - Rest of line - indicates the rest of the line isn&#039;t parsed and taken as one setting&lt;br /&gt;
:*	&#039;&#039;&#039;/M&#039;&#039;&#039; - Multiple strings - indicates following strings except for keywords are settings&lt;br /&gt;
&lt;br /&gt;
Examples of such templates can be found by typing most any AmigaOS command line command with a &amp;quot;?&amp;quot;.  Here is an code example of how such a &amp;quot;template&amp;quot; definition could look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
CONST_STRPTR argTemplate = &amp;quot;TEST/K,NUMBER/K/N,FILE&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[Note: Make sure there are no spaces in your template!]&lt;br /&gt;
&lt;br /&gt;
This template allows the user to enter the following optional settings for an application:&lt;br /&gt;
&lt;br /&gt;
:*	the TEST keyword followed with a value.&lt;br /&gt;
:*	the NUMBER keyword follwed with a numeric value.&lt;br /&gt;
:*	the FILE keyword and a filename.&lt;br /&gt;
&lt;br /&gt;
The second parameter in the &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function is an array of &#039;&#039;&#039;int32&#039;&#039;&#039; pointers.   If the &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function finds valid arguments, the respective array elements will point to any valid user input for those parameters (in template order).  To start with, those pointers can be defined and set to NULL values like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 argArray[] =&lt;br /&gt;
{&lt;br /&gt;
    0,&lt;br /&gt;
    0,&lt;br /&gt;
    0&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The final parameter of &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function is typically NULL unless the application wants to set some default values (see the DOS/ReadArgs autodocs for more information on this).&lt;br /&gt;
&lt;br /&gt;
==== Calling ReadArgs ====&lt;br /&gt;
&lt;br /&gt;
Once run, the &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function will return either a pointer to a &#039;&#039;&#039;RDArgs&#039;&#039;&#039; structure if it succesfully parsed valid arguments or NULL if inadequate input was provided by the user.   If the &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; function was successful, then each entry of the second parameter (the &#039;&#039;&#039;argArray&#039;&#039;&#039; defined above) will point to any valid input for that slot in the template.&lt;br /&gt;
&lt;br /&gt;
It is the developer&#039;s choice how best to deal with inadequate input from the user (&#039;&#039;&#039;ReadArgs&#039;&#039;&#039; returns a NULL value).  At the very lest the application should gracefully let the user know the command line arguments were not accepted.&lt;br /&gt;
&lt;br /&gt;
====Using ReadArgs Results====&lt;br /&gt;
For example, with the above code, the first entry (&#039;&#039;&#039;argArray[0]&#039;&#039;&#039;) would point to any valid input that followed the &amp;quot;TEST&amp;quot; keyword, if it was in the command line.  This value could be printed as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    if ((int32 *)argArray[0] != NULL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;     TEST = %s\n&amp;quot;, (STRPTR)argArray[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the second &amp;quot;NUMBER&amp;quot; keyword was used with a numeric value, its value could be obtained from the second arguments array entry as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    int32 intArg;&lt;br /&gt;
    if ((int32 *)argArray[1] != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        intArg = *(int32 *)argArray[1];&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;           NUMBER = %ld\n&amp;quot;, intArg);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Cleaning Up After ReadArgs ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; successfuly parsed input from the command line, the &#039;&#039;&#039;FreeArgs&#039;&#039;&#039; function must be called using the resulting pointer from &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; (&#039;&#039;&#039;rdargs&#039;&#039;&#039; in the above example).   This will release any memory used by &#039;&#039;&#039;ReadArgs&#039;&#039;&#039; to store the user&#039;s input, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IDOS-&amp;gt;FreeArgs(rdargs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Starting from the Workbench: Getting File Names ===&lt;br /&gt;
&lt;br /&gt;
As discussed above, if the user starts an application from the Workbench, AmigaOS indicates this by setting the &#039;&#039;&#039;argc&#039;&#039;&#039; variable to ZERO.  AmigaOS also uses the &#039;&#039;&#039;argv&#039;&#039;&#039; variable to send a pointer to a &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure that contains more information on the user&#039;s actions.&lt;br /&gt;
&lt;br /&gt;
There are three ways the user could have started an application that will be reflected by the contents of the &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure;&lt;br /&gt;
&lt;br /&gt;
:*&#039;&#039;&#039;The user double-clicked on the application&#039;s icon&#039;&#039;&#039; - In which case the &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure will indicate the program&#039;s name and &amp;quot;lock&amp;quot; its directory location on disk.&lt;br /&gt;
&lt;br /&gt;
:*&#039;&#039;&#039;The user double-clicked on the &amp;quot;project&amp;quot; file&#039;s icon that had this application set as its &amp;quot;tool&amp;quot;&#039;&#039;&#039; - In addition to the app&#039;s information, the &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure will indicate the project file&#039;s name and &amp;quot;lock&amp;quot; the project file&#039;s directory.&lt;br /&gt;
&lt;br /&gt;
:*&#039;&#039;&#039;The user selected one (or more) &amp;quot;project&amp;quot; files and shift-double-clicked on the application&#039;s icon&#039;&#039;&#039; - The &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure will have a file name and directory &amp;quot;lock&amp;quot; for each of the project files in addition to the application&#039;s information.&lt;br /&gt;
&lt;br /&gt;
In all these cases, when the application starts, it must goes though a few steps to determine if any files were selected with it.  If the application also wants to read any settings attached to those files - as &amp;quot;Tool Types&amp;quot; within their icons - the application has to go through a few more steps.  These steps start with reading the &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure passed to the app.&lt;br /&gt;
&lt;br /&gt;
More information about this topic can be found in [[Workbench_and_Icon_Library#Workbench_Environment|Workbench Environment]] and&lt;br /&gt;
[[Workbench_and_Icon_Library#Workbench_and_the_Startup_Code_Module|Workbench and Startup Code]].&lt;br /&gt;
&lt;br /&gt;
==== Accessing the WBStartup structure ====&lt;br /&gt;
&lt;br /&gt;
When started by the Workbench, an application is passed a pointer to the &#039;&#039;&#039;WBStartUp&#039;&#039;&#039; structure in the standard &#039;&#039;&#039;argv&#039;&#039;&#039; variable.  After checking the &#039;&#039;&#039;argc&#039;&#039;&#039; variable is equal to zero (to see that it was started by the Workbench), the &#039;&#039;&#039;WBStartUp&#039;&#039; structure can be obtained like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (argc == 0)&lt;br /&gt;
{&lt;br /&gt;
    struct WBStartup *wbs = (struct WBStartup *)argv;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;WBStartup&#039;&#039;&#039; structure is based on the AmigaOS &#039;&#039;&#039;Message&#039;&#039;&#039; structure and it has the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct WBStartup&lt;br /&gt;
{&lt;br /&gt;
    struct Message      sm_Message;     /* a standard message structure */&lt;br /&gt;
    struct MsgPort *    sm_Process;     /* the process descriptor for you */&lt;br /&gt;
    BPTR                sm_Segment;     /* a descriptor for your code */&lt;br /&gt;
    int32               sm_NumArgs;     /* the number of elements in ArgList */&lt;br /&gt;
    char *              sm_ToolWindow;  /* reserved for future use */&lt;br /&gt;
    struct WBArg *      sm_ArgList;     /* the arguments themselves */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Under typical circumstances the &#039;&#039;&#039;sm_NumArgs&#039;&#039;&#039; and &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; are the only elements of the structure that need to be accessed for program start up.  The number of arguments will always be at least one, with the first entry representing the application itself.  Any number of arguments above one reflects the number of &amp;quot;project&amp;quot; files or icons the user selected when the application was started.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; will be an array of &#039;&#039;&#039;WBArg&#039;&#039;&#039; structures where the number of elements corresponds to the number of application &amp;amp; data files selected by the user (reflected by the &#039;&#039;&#039;sm_NumArgs&#039;&#039;&#039; variable above).  The &#039;&#039;&#039;WBArg&#039;&#039;&#039; structure containing the arguments has the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct WBArg&lt;br /&gt;
{&lt;br /&gt;
    BPTR   wa_Lock; /* a lock descriptor */&lt;br /&gt;
    STRPTR * wa_Name; /* a string relative to that lock */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;wa_Lock&#039;&#039;&#039; contains a pointer to an AmigaOS &amp;quot;lock&amp;quot; on the directory containing that argument&#039;s application or data file.  For each of the files (the application or project files) passed to the application from the Workbench, a &amp;quot;lock&amp;quot; is applied to that directory.  This prevents the directory of the application or any of the selected data files (&amp;quot;projects&amp;quot;) from being disturbed or deleted while they are in use.  &lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;wa_Name&#039;&#039;&#039; contains a string pointer to the name of the application or data file.  This is a pointer to the name of the file itself and doesn&#039;t include the pathname for the directory is stored in.&lt;br /&gt;
&lt;br /&gt;
The names of the application and any selected files could be printed out with the following code excerpt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint8 NoArgs, argNo;&lt;br /&gt;
NoArgs = wbs-&amp;gt;sm_NumArgs;&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;     number of args received = %ld\n&amp;quot;, NoArgs);&lt;br /&gt;
for (argNo = 0; argNo &amp;lt; NoArgs; ++argNo)&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;     arg %ld name =  &amp;gt;%s&amp;lt;\n&amp;quot;, argNo, wbs-&amp;gt;sm_ArgList[argNo].wa_Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Below we will see how to obtain more information from the selected files and how to get full path names for each.  When the application finishes running, AmigaOS will release the &amp;quot;locks&amp;quot; on the directories referred to by the &#039;&#039;&#039;WBStartUp&#039;&#039;&#039; entries.&lt;br /&gt;
&lt;br /&gt;
=== WorkBench StartUp: Examining Icons &amp;amp; Tooltypes ===&lt;br /&gt;
&lt;br /&gt;
Whereas options might be given to a program in the Shell console in the form of argument in command line, Workbench programs frequently keep such settings within the application&#039;s icon, which is stored in a companion &#039;&#039;&#039;.info&#039;&#039;&#039; file.  Specifically, each icon has the ability to store &amp;quot;Tool Types&amp;quot; text strings within the icon&#039;s .info file.  While such settings can be almost any form of text, the standard format is to have a SETTING, equals sign and a value, like these: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    TEST=verbose&lt;br /&gt;
    NUMBER=23&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These &amp;quot;Tool Type&amp;quot; settings are usually stored within the application&#039;s icon, but such settings can be also stored in most any icon files in AmigaOS.  As such, an application can also look for such information within each project&#039;s icon file.&lt;br /&gt;
&lt;br /&gt;
For an application to access any information that might be stored in its icon&#039;s &#039;&#039;&#039;.info&#039;&#039;&#039; file or in those of the &amp;quot;Projects&amp;quot; passed to the application, the application needs to go through a few more steps after collecting the related file names (mentioned above).&lt;br /&gt;
&lt;br /&gt;
More information about this topic can be found in [[Workbench_and_Icon_Library#The_Tool_Types_Array|Tool Types Array]].&lt;br /&gt;
&lt;br /&gt;
==== Getting to Each Icon&#039;s Directory ====&lt;br /&gt;
&lt;br /&gt;
The easiest way to access any of the files sent by the Workbench is to use the &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; entry provided for each.  This is done by first confirming a valid directory was found and &amp;quot;locked&amp;quot; for us by the Workbench.  Then we call the &#039;&#039;&#039;CurrentDir&#039;&#039;&#039; function to switch the application to that directory, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (wbs-&amp;gt;sm_ArgList[argNo].wa_Lock != ZERO)&lt;br /&gt;
{&lt;br /&gt;
    BPTR oldDir = IDOS-&amp;gt;CurrentDir(wbs-&amp;gt;sm_ArgList[argNo].wa_Lock);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, this function returns a pointer to the old directory when we switch to the new directory location.  This is very important, since the Workbench also maintains a directory lock based on the application&#039;s current directory.  Before an application finishes, it needs to call the &#039;&#039;&#039;CurrentDir&#039;&#039;&#039; function again and switch back to the application&#039;s original location so it can be &amp;quot;unlocked&amp;quot; when the app quits - like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IDOS-&amp;gt;CurrentDir(oldDir);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Getting Icon Information ====&lt;br /&gt;
&lt;br /&gt;
Once the application has &amp;quot;moved&amp;quot; to the directory of the icon file to be examined, we can use the &#039;&#039;&#039;GetDiskObjectNew&#039;&#039;&#039; function to get information from the icon.  This function is simply called using the same &#039;&#039;&#039;wa_Name&#039;&#039;&#039; element from the &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; structure used above, as in this code excerpt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DiskObject *dobj;&lt;br /&gt;
dobj = IIcon-&amp;gt;GetDiskObjectNew(wbs-&amp;gt;sm_ArgList[argNo].wa_Name);&lt;br /&gt;
if(dobj != NULL)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;         Opened disk object.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;GetDiskObjectNew&#039;&#039;&#039; function automatically appends the &amp;quot;.info&amp;quot; necessary to open the selected file&#039;s icon.  If there is no .info file for the selected file, this function will automatically use the information from the &amp;quot;default icon&amp;quot; for the selected file type.  For example, if the file &amp;quot;test.jpg&amp;quot; has no &amp;quot;test.jpg.info&amp;quot; icon file, the &amp;quot;def_jpeg.info&amp;quot; will be used.&lt;br /&gt;
&lt;br /&gt;
If an application needs to explore icon information for a file not in the current directory of the app, the &#039;&#039;&#039;GetDiskObjectNew&#039;&#039;&#039; function will also accept the name of a file including a full path.  But as we will see below, this does involve the application going through the overhead of allocating a string sufficient to store the full file &amp;amp; path name.&lt;br /&gt;
&lt;br /&gt;
If successful, the &#039;&#039;&#039;GetDiskObjectNew&#039;&#039;&#039; function returns a pointer to a &#039;&#039;&#039;DiskObject&#039;&#039;&#039; structure that provides the following information for that &#039;&#039;&#039;.info&#039;&#039;&#039; file and icon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DiskObject&lt;br /&gt;
    {&lt;br /&gt;
    UWORD              do_Magic;       /* magic number at start of file */&lt;br /&gt;
    UWORD              do_Version;     /* so we can change structure    */&lt;br /&gt;
    struct Gadget      do_Gadget;      /* a copy of in core gadget      */&lt;br /&gt;
    UBYTE              do_Type;&lt;br /&gt;
    char              *do_DefaultTool;&lt;br /&gt;
    char             **do_ToolTypes;&lt;br /&gt;
    LONG               do_CurrentX;&lt;br /&gt;
    LONG               do_CurrentY;&lt;br /&gt;
    struct DrawerData *do_DrawerData;&lt;br /&gt;
    char              *do_ToolWindow;  /* only applies to tools */&lt;br /&gt;
    LONG               do_StackSize;   /* only applies to tools */&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, the &#039;&#039;&#039;DiskObject&#039;&#039;&#039; structure presents most of the information conveyed by an AmigaOS icon. The &#039;&#039;&#039;do_DefaultTool&#039;&#039;&#039; is the setting that is used to designate whichever application will be used when a &amp;quot;Project&amp;quot; icon is double-clicked. The &#039;&#039;&#039;do_ToolTypes&#039;&#039;&#039; element points to an array of strings containing the icon&#039;s &amp;quot;Tool Types&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== Checking for Our Tooltypes ====&lt;br /&gt;
&lt;br /&gt;
AmigaOS provides another function to simplify checking the Tool Types for values an application is interested in - the &#039;&#039;&#039;FindToolType&#039;&#039;&#039; function.  It is simply called with the pointer to the &#039;&#039;&#039;do_ToolTypes&#039;&#039;&#039; array and the desired setting string.  This function returns either a pointer to the string with that ToolType setting or NULL if the ToolType wasn&#039;t found.  The following code would check for our &amp;quot;TEST&amp;quot; ToolType and print out any results found.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
STRPTR TTarg = NULL;&lt;br /&gt;
TTarg = IIcon-&amp;gt;FindToolType(dobj-&amp;gt;do_ToolTypes, &amp;quot;TEST&amp;quot;);&lt;br /&gt;
if (TTarg)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;              TEST found set to = &amp;gt;%s&amp;lt;\n&amp;quot;, TTarg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With all the above code, an application can check each of the elements of the &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; sent by the Workbench to check for Tool Types in the app&#039;s icon as well as any of the icons of any Project files sent to the app.&lt;br /&gt;
&lt;br /&gt;
=== Workbench StartUp: Getting Path &amp;amp; File Names ===&lt;br /&gt;
&lt;br /&gt;
So far the above code has relied on using the &#039;&#039;&#039;WBStartUp&#039;&#039;&#039; structure and &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; sub-structure to obtain the necessary information on the application and any Project files selected.  This has avoided directly dealing with the full file and path names of those files.  If the full file &amp;amp; path name is required, the application can get the full path name by examining the directory &amp;quot;lock&amp;quot; of each &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; entry.  This is done by calling the DOS &#039;&#039;&#039;NameFromLock&#039;&#039;&#039; function with another multi-step process.&lt;br /&gt;
&lt;br /&gt;
First we must allocate a string in which to put the full name, then call &#039;&#039;&#039;NameFromLock&#039;&#039;&#039; to get the path name from the &amp;quot;Lock&amp;quot; and finally we must free the allocated memory after we&#039;re done with it. The following code excerpt would obtain the path to the application (always the first entry in &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    int32 StringLen = 2048;&lt;br /&gt;
    // Alloc memory for pathname string&lt;br /&gt;
    filename = (STRPTR) IExec-&amp;gt;AllocVecTags(StringLen, TAG_END);&lt;br /&gt;
    if (filename)&lt;br /&gt;
    {&lt;br /&gt;
        if (wbs-&amp;gt;sm_ArgList[0].wa_Lock != ZERO)&lt;br /&gt;
            if (IDOS-&amp;gt;NameFromLock(wbs-&amp;gt;sm_ArgList[0].wa_Lock,filename, StringLen) != 0)&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;    app path name = &amp;gt;%s&amp;lt;\n&amp;quot;, filename);&lt;br /&gt;
        // deallocate pathname string memory&lt;br /&gt;
        IExec-&amp;gt;FreeVec(filename);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we allocate a large string size for the path name - 2048 characters - to provide room for potentially long and/or heavily nested path names.  If that string is not long enough, the &#039;&#039;&#039;NameFromLock&#039;&#039;&#039; function will fail and return a NULL value.  The developer must use their judgment in deciding how likely this error might be and how much effort to put into handling it - whether to have the application politely handle and notify the user of the error or whether to wrap this function in some loop that keeps allocating larger string sizes until &#039;&#039;&#039;NameFromLock&#039;&#039;&#039; succeeds.&lt;br /&gt;
&lt;br /&gt;
Obviously, there are few levels of error trapping in this code: to make sure we were able to allocate our string (&#039;&#039;&#039;filename&#039;&#039;&#039;), to make sure there is a valid directory lock to use and finally to make sure the path name could be obtained from the lock.  Regardless of how unlikely failure might be, within any application these tests should occur and be &amp;quot;wrapped&amp;quot; in proper and graceful error handling and feedback.&lt;br /&gt;
&lt;br /&gt;
==== Getting The Full Name ====&lt;br /&gt;
&lt;br /&gt;
Once we have the path name, we can combine that with the name of the file (from the &#039;&#039;&#039;wa_Name&#039;&#039;&#039; element of the &#039;&#039;&#039;sm_ArgList&#039;&#039;&#039; structure). AmigaOS provides the &#039;&#039;&#039;AddPart&#039;&#039;&#039; function which also resolves AmigaOS separator characters (&amp;quot;/&amp;quot; or &amp;quot;:&amp;quot;) to create a valid and complete file pathname, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (IDOS-&amp;gt;AddPart(filename,wbs-&amp;gt;sm_ArgList[argNo].wa_Name,StringLen) != 0)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;         full path = &amp;gt;%s&amp;lt;\n&amp;quot;, filename);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting combined path and file name should be suitable for use by the application - to open or act upon the file selected by the user.  &lt;br /&gt;
&lt;br /&gt;
Bear in mind, we are still working within the fixed size of the &amp;quot;filename&amp;quot; string that must contain both the path name as well as the file name. As before, we use error trapping to check that the combined file and path names fit into the string length we&#039;ve defined. Again, should there be a failure, provide feedback and gracefully manage the situation.&lt;br /&gt;
&lt;br /&gt;
=== Example Program Source Code ===&lt;br /&gt;
&lt;br /&gt;
The following is an example program to demonstrate the above concepts in starting an application from the Shell and the Workbench.&lt;br /&gt;
&lt;br /&gt;
From the Shell, the user can enter arguments or enter the &amp;quot;?&amp;quot; to see the template prompt before entering arguments.  Commented out within the code&lt;br /&gt;
is a working example of using the simple ANSI method of reading command line arguments.&lt;br /&gt;
&lt;br /&gt;
From the Workbench, this example will print out all the above information found on all the arguments passed by the Workbench.  Besides the application&#039;s information, the printed information will reflected selected icons or Projects calling this app as a default Tool.&lt;br /&gt;
&lt;br /&gt;
An archive for use with CodeBench is available [[Media:ArgParser.lha|from here]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************&lt;br /&gt;
**&lt;br /&gt;
** Created by:	CodeBench 0.26 (04.01.2012)&lt;br /&gt;
**&lt;br /&gt;
** Project:		PJS-OS4ex-StartUp&lt;br /&gt;
**&lt;br /&gt;
** Version: 	4&lt;br /&gt;
**&lt;br /&gt;
** File:		This an example program of the various AmigaOS&lt;br /&gt;
**				ways of starting an application - from CLI or&lt;br /&gt;
**				Workbench - and dealing with the options.&lt;br /&gt;
**&lt;br /&gt;
** Date: 		25-08-2012 18:21:44&lt;br /&gt;
**&lt;br /&gt;
************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/execbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/icon.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// declare &amp;amp; initialize empty LONG words array to be filled with pointers to CLI arguments received&lt;br /&gt;
int32 argArray[] =&lt;br /&gt;
{&lt;br /&gt;
	0,&lt;br /&gt;
	0,&lt;br /&gt;
	0&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// declare the number of possible arguments (the size of the argArray above)&lt;br /&gt;
uint8 noArgs = 3;&lt;br /&gt;
&lt;br /&gt;
// declare CLI options template string with these modifiers:&lt;br /&gt;
//		/S - Switch - indicates keyword string is a boolean setting&lt;br /&gt;
//		/K - Keyword - indicates keyword string must appear before a setting&lt;br /&gt;
//		/N - Number - indicates a setting must be an integer&lt;br /&gt;
//		/T - Toggle - indicates keyword must be followed with a boolean setting (ON, OFF, etc)&lt;br /&gt;
//		/A - Required - indicates keyword must be followed with a value&lt;br /&gt;
//		/F - Rest of line - indicates the rest of the line isn&#039;t parsed and taken as one setting&lt;br /&gt;
//		/M - Multiple strings - indicates following strings except for keywords are settings&lt;br /&gt;
//&lt;br /&gt;
CONST_STRPTR argTemplate = &amp;quot;TEST/K,NUMBER/K/N,FILE&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
uint8 returnCode = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
// Starting program&lt;br /&gt;
int main(int argc,char **argv)&lt;br /&gt;
{&lt;br /&gt;
	uint8 argNo;&lt;br /&gt;
	&lt;br /&gt;
	IDOS-&amp;gt;Printf(&amp;quot;AmigaOS StartUp Example\n&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Check if program was started from CLI or Workbench based on value of &amp;quot;argc&amp;quot;&lt;br /&gt;
	if (argc &amp;gt; 0)&lt;br /&gt;
	{&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;Started from CLI.\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		// declare struct pointer to receive CLI options&lt;br /&gt;
		struct RDArgs *rdargs;&lt;br /&gt;
		&lt;br /&gt;
		// read CLI args (using AmigaOS method)&lt;br /&gt;
		rdargs=IDOS-&amp;gt;ReadArgs(argTemplate,argArray,NULL);&lt;br /&gt;
		&lt;br /&gt;
		// were CLI arguments sucessfully read?&lt;br /&gt;
		if (rdargs)&lt;br /&gt;
		{&lt;br /&gt;
			int32 intArg;&lt;br /&gt;
			&lt;br /&gt;
			// loop through possible options &amp;amp; settings and print them&lt;br /&gt;
			for (argNo=0; argNo&amp;lt;noArgs; ++argNo)&lt;br /&gt;
			{&lt;br /&gt;
				// print readarg arguments&lt;br /&gt;
				IDOS-&amp;gt;Printf(&amp;quot;     arg %ld = %s\n&amp;quot;, argNo, argArray[argNo]);&lt;br /&gt;
				if ((int32 *)argArray[argNo] != NULL)&lt;br /&gt;
				{&lt;br /&gt;
					intArg = *(int32 *)argArray[argNo];&lt;br /&gt;
					IDOS-&amp;gt;Printf(&amp;quot;           # = &amp;gt;%ld&amp;lt;\n&amp;quot;, intArg);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// free memory used by ReadArgs and data&lt;br /&gt;
	        IDOS-&amp;gt;FreeArgs(rdargs);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;    ERROR: Incorrect Values received by ReadArgs\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		//read args using ISO C method&lt;br /&gt;
		/*&lt;br /&gt;
		for (argNo=0; argNo&amp;lt;argc; ++argNo)&lt;br /&gt;
		{&lt;br /&gt;
			// print CLI arguments&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   argument #%ld = &amp;gt;%s&amp;lt;\n&amp;quot;, argNo, argv[argNo]);&lt;br /&gt;
		}&lt;br /&gt;
		*/&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;Started from Workbench.\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		// set pointer to WBStartUp structure using argv contents&lt;br /&gt;
		struct WBStartup *wbs = (struct WBStartup *)argv;&lt;br /&gt;
		&lt;br /&gt;
		// set a length for path names (to be added to file name length)&lt;br /&gt;
		int32 MaxPathLen = 1792;&lt;br /&gt;
		// set a length for file names (to be added to path name length)&lt;br /&gt;
		int32 MaxFileLen = 256;&lt;br /&gt;
		&lt;br /&gt;
		// working variable&lt;br /&gt;
		uint8 NoArgs;&lt;br /&gt;
		STRPTR filename = NULL;&lt;br /&gt;
		STRPTR TTarg = NULL;&lt;br /&gt;
		BPTR oldDir = ZERO;&lt;br /&gt;
		&lt;br /&gt;
		// print out number of arguments (first one is this program)&lt;br /&gt;
		NoArgs = wbs-&amp;gt;sm_NumArgs;&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;     number of args received = %ld\n&amp;quot;, NoArgs);&lt;br /&gt;
		&lt;br /&gt;
		// Alloc memory for pathname string&lt;br /&gt;
		filename = (STRPTR) IExec-&amp;gt;AllocVecTags(MaxPathLen+MaxFileLen, TAG_END);&lt;br /&gt;
		&lt;br /&gt;
		// Alloc disk object pointer for icon information&lt;br /&gt;
		struct DiskObject *dobj;&lt;br /&gt;
		&lt;br /&gt;
		if (filename)&lt;br /&gt;
		{&lt;br /&gt;
			// loop through WB arguments getting path names&lt;br /&gt;
			for (argNo=0; argNo&amp;lt;NoArgs; ++argNo)&lt;br /&gt;
			{&lt;br /&gt;
				IDOS-&amp;gt;Printf(&amp;quot;     arg %ld name =  &amp;gt;%s&amp;lt;\n&amp;quot;, argNo, wbs-&amp;gt;sm_ArgList[argNo].wa_Name);&lt;br /&gt;
				&lt;br /&gt;
				if (wbs-&amp;gt;sm_ArgList[argNo].wa_Lock != ZERO)&lt;br /&gt;
				{&lt;br /&gt;
					IDOS-&amp;gt;Printf(&amp;quot;         file locked.\n&amp;quot;);&lt;br /&gt;
					&lt;br /&gt;
					// CD to project file&#039;s dir&lt;br /&gt;
					oldDir = IDOS-&amp;gt;CurrentDir(wbs-&amp;gt;sm_ArgList[argNo].wa_Lock);&lt;br /&gt;
					&lt;br /&gt;
					// get disk object on argument&#039;s file&lt;br /&gt;
					dobj = IIcon-&amp;gt;GetDiskObjectNew(wbs-&amp;gt;sm_ArgList[argNo].wa_Name);&lt;br /&gt;
					if(dobj)&lt;br /&gt;
					{&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;         Opened disk object.\n&amp;quot;);&lt;br /&gt;
						&lt;br /&gt;
						// check for &amp;quot;TEST&amp;quot; keyword&lt;br /&gt;
						TTarg = IIcon-&amp;gt;FindToolType(dobj-&amp;gt;do_ToolTypes, &amp;quot;TEST&amp;quot;);&lt;br /&gt;
						if (TTarg)&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;              TEST found set to = &amp;gt;%s&amp;lt;\n&amp;quot;,TTarg);&lt;br /&gt;
						else&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;              TEST tooltype not found.\n&amp;quot;);&lt;br /&gt;
						&lt;br /&gt;
						// check for &amp;quot;NUMBER&amp;quot; keyword&lt;br /&gt;
						TTarg = IIcon-&amp;gt;FindToolType(dobj-&amp;gt;do_ToolTypes, &amp;quot;NUMBER&amp;quot;);&lt;br /&gt;
						if (TTarg)&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;              NUMBER found set to = &amp;gt;%s&amp;lt;\n&amp;quot;,TTarg);&lt;br /&gt;
						else&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;              NUMBER tooltype not found.\n&amp;quot;);&lt;br /&gt;
						&lt;br /&gt;
						// free disk object structure &amp;amp; memory&lt;br /&gt;
						IIcon-&amp;gt;FreeDiskObject(dobj);&lt;br /&gt;
					}&lt;br /&gt;
					else&lt;br /&gt;
					{&lt;br /&gt;
						// print why we couldn&#039;t get icon information&lt;br /&gt;
						int32 MYerror, MYerrLen;&lt;br /&gt;
						MYerror = IDOS-&amp;gt;IoErr();&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;     ERROR: Unable to open disk object.\n&amp;quot;);&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;              number = &amp;gt;%ld&amp;lt;\n&amp;quot;,MYerror);&lt;br /&gt;
						MYerrLen = IDOS-&amp;gt;Fault(MYerror,NULL,filename,80);&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;              text = &amp;gt;%s&amp;lt;\n&amp;quot;,filename);&lt;br /&gt;
						returnCode = RETURN_ERROR;&lt;br /&gt;
					}&lt;br /&gt;
					&lt;br /&gt;
					// CD back to previous dir&lt;br /&gt;
					IDOS-&amp;gt;CurrentDir(oldDir);&lt;br /&gt;
					&lt;br /&gt;
					// Indicate what type of file the WBArg is refering to&lt;br /&gt;
					if (argNo == 0)&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;         entry = application.\n&amp;quot;);&lt;br /&gt;
					else&lt;br /&gt;
					{&lt;br /&gt;
						// check item type&lt;br /&gt;
						if (strlen(wbs-&amp;gt;sm_ArgList[argNo].wa_Name) &amp;gt; 0)&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;         entry = file.\n&amp;quot;);&lt;br /&gt;
						else&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;         entry = device or directory.\n&amp;quot;);&lt;br /&gt;
					}&lt;br /&gt;
					&lt;br /&gt;
					// Get the path and full path/filename of the file&lt;br /&gt;
					if (IDOS-&amp;gt;NameFromLock(wbs-&amp;gt;sm_ArgList[argNo].wa_Lock,filename,MaxPathLen) != 0)&lt;br /&gt;
					{&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;         path name = &amp;gt;%s&amp;lt;\n&amp;quot;,filename);&lt;br /&gt;
						&lt;br /&gt;
						if (IDOS-&amp;gt;AddPart(filename,wbs-&amp;gt;sm_ArgList[argNo].wa_Name,MaxPathLen+MaxFileLen) != 0)&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;         full path = &amp;gt;%s&amp;lt;\n&amp;quot;,filename);&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							IDOS-&amp;gt;Printf(&amp;quot;     ERROR: full name is larger than %ld\n&amp;quot;,(MaxPathLen+MaxFileLen));&lt;br /&gt;
							returnCode = RETURN_WARN;&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					else&lt;br /&gt;
					{&lt;br /&gt;
						IDOS-&amp;gt;Printf(&amp;quot;     ERROR: path name is larger than %ld\n&amp;quot;,MaxPathLen);&lt;br /&gt;
						returnCode = RETURN_WARN;&lt;br /&gt;
					}&lt;br /&gt;
					&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					IDOS-&amp;gt;Printf(&amp;quot;     ERROR: file handle could not be locked.\n&amp;quot;);&lt;br /&gt;
					returnCode = RETURN_ERROR;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// deallocate pathname string memory&lt;br /&gt;
			IExec-&amp;gt;FreeVec(filename);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;     ERROR: unable to allocate memory for string!\n&amp;quot;);&lt;br /&gt;
			returnCode = RETURN_ERROR;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	IDOS-&amp;gt;Printf(&amp;quot;GOODBYE!\n&amp;quot;);&lt;br /&gt;
	return (returnCode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Requesters&amp;diff=12544</id>
		<title>Intuition Requesters</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Requesters&amp;diff=12544"/>
		<updated>2025-01-26T19:28:23Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Intuition Requesters ==&lt;br /&gt;
&lt;br /&gt;
This article explains how to create requesters, the information exchange boxes that both the system and applications can use for confirming actions, getting command options and similar operations. These boxes are called requesters because they generally request information from the user.&lt;br /&gt;
&lt;br /&gt;
== Types Of Requesters ==&lt;br /&gt;
&lt;br /&gt;
There are at least three kinds of display objects in Amiga terminology called requesters: &#039;&#039;true requesters&#039;&#039;, &#039;&#039;system requesters&#039;&#039; and &#039;&#039;ASL requesters&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
True requesters are general purpose display areas that can be thought of as temporary sub-windows. They display information to the user and allow the user to make a selection. True requesters always open within an existing window and are constrained to the boundaries of that window (often referred to as the &#039;&#039;parent&#039;&#039; window). If a requester extends beyond the edge of its parent window, either its position is adjusted or its graphics are clipped. True requesters always block input to their parent window as long as they are present.&lt;br /&gt;
&lt;br /&gt;
System requesters are typically used for warnings or to confirm an action the user has just initiated. System requesters differ from true requesters in that they cannot block input to the parent window. In fact, system requesters do not open in a parent window at all, but instead open their own separate window in the screen. Since these requesters are so different from true requesters, they will be discussed separately later in the article. See the sections on [[#Easy Requesters|Easy Requesters]] and [[#System Requesters|System Requesters]] for more information.&lt;br /&gt;
&lt;br /&gt;
The third type of requester, the ASL requester, is a special purpose requester. ASL requesters provide an easy, standard way to get a filename from the user for load and save operations. They can also be used to get a font selection or screen mode selection from the user. For the details about ASL file and font requesters, see [[ASL_Library|ASL Library]].&lt;br /&gt;
&lt;br /&gt;
== True Requesters ==&lt;br /&gt;
&lt;br /&gt;
The primary function of a requester is to display information to the user from which the user is to make a selection. Conceptually, requesters are similar to menus since both menus and requesters offer options to the user. Requesters, however, go beyond menus because they can have customized imagery, can be placed anywhere in a window, can be activated by the application and may have any type of gadget attached.&lt;br /&gt;
&lt;br /&gt;
For instance, to select a color for a given operation using a menu could be awkward, especially in an application that supports a large number of colors. In that case a requester could be used instead (see figure).&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig7-1.png|frame|center|Requester Deluxe]]&lt;br /&gt;
&lt;br /&gt;
The ability of a true requester to block input to its parent window is important in understanding how requesters are used. When input is blocked by a true requester (also known as a modal requester), the user must take some action before the program will proceed further, such as making a selection, correcting an error condition, or acknowledging a warning. These are situations where a true (modal) requester is appropriate, however, keep in mind that your application should try to be as user-responsive as possible. Putting up a requester merely because you are in a phase of the program where it would be difficult to deal with user input is bad style. Modal requesters should be used only when the program &#039;&#039;requires&#039;&#039; user interaction before proceeding.&lt;br /&gt;
&lt;br /&gt;
True requesters can be created in a window in two different ways.&lt;br /&gt;
&lt;br /&gt;
* An application can display a requester at any time by calling the Request() function.&lt;br /&gt;
&lt;br /&gt;
* The application can declare a requester as the window&#039;s &#039;&#039;double menu requester&#039;&#039;, which the user can bring up with a double-click of the menu button (this method is rarely used).&lt;br /&gt;
&lt;br /&gt;
=== Creating Application Requesters ===&lt;br /&gt;
&lt;br /&gt;
To create a requester, the application first allocates memory for or declares an instance of the Requester structure as defined in &amp;amp;lt;intuition/intuition.h&amp;amp;gt;. Once the Requester structure is set up, it is initialized with the InitRequester() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID InitRequester( struct Requester *requester);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function simply clears the Requester structure. The application should do further initialization depending on its needs. See the section on the &amp;quot;Requester Structure&amp;quot; below for an explanation of all the Requester fields and how to set them.&lt;br /&gt;
&lt;br /&gt;
A true (modal) requester is attached to its parent window and displayed with the Request() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL Request(struct Requester *requester, struct Window *window);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function returns TRUE if the requester opens successfully or FALSE if the requester cannot be opened. If the requester opens successfully, menu and gadget input in the parent window is blocked as long as the requester is displayed. The application should process input events from the requester, which are sent to the parent window&#039;s Window.UserPort, until the requester is satisfied.&lt;br /&gt;
&lt;br /&gt;
To remove a requester from its parent window and update the display, use EndRequest().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID EndRequest( struct Requester *requester, struct Window *window );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This removes only the one requester specified. It is possible to set up a requester with a special gadget that, if selected, will automatically close the requester. In that case, EndRequest() need not be called. If the program needs to cancel the request early, or cancel it only after some specific manipulation of the gadgets, EndRequest() should be used.&lt;br /&gt;
&lt;br /&gt;
The application should always provide a safe way for the user to back out of a requester without taking any action that affects the user&#039;s work. Providing an escape hatch is important, for instance, a requester with the message &amp;quot;Overwrite File?&amp;quot; should allow the user to cancel the operation without losing the old data.&lt;br /&gt;
&lt;br /&gt;
=== Requester I/O ===&lt;br /&gt;
&lt;br /&gt;
So long as a requester is active in a window, the only gadgets that can be used are those that are in the requester, plus all of the window&#039;s system gadgets except for the close gadget (i.e., the drag bar, size gadget, depth gadget, and zoom gadget). A requester also makes the menus of the parent window inaccessible. Additionally, mouse button and keyboard events will be blocked (unless the requester&#039;s NOISYREQ flag is set; see &amp;quot;Requester Structure&amp;quot; below). Mouse movement events, if enabled in the parent window (with WFLG_REPORTMOUSE), are not blocked.&lt;br /&gt;
&lt;br /&gt;
Requesters do not have their own IDCMP message ports. Instead, events for a requester are sent to the IDCMP port of the requester&#039;s parent window (Window.UserPort). Since the window&#039;s menus and application gadgets are inaccessible, no IDCMP events will be sent for them.&lt;br /&gt;
&lt;br /&gt;
Even though the window containing the requester is blocked for input, the user can work in another application or even in a different window of the same application without satisfying the requester. Only input to the parent window is blocked by a requester.&lt;br /&gt;
&lt;br /&gt;
Output is not blocked by a requester so nothing prevents the application from writing to the window. Be aware, however, that the requester obscures part of the display and cannot be moved within the window so this may limit the usefulness of any output you send to the parent window. There are several ways to monitor the comings and goings of requesters that allow the program to know if requesters are currently displayed in a given window. See &amp;quot;IDCMP Requester Features&amp;quot; below.&lt;br /&gt;
&lt;br /&gt;
The information displayed in a requester is placed in its own layer, so it does not overwrite the information in the window. Output to the window while the requester is displayed will not change the requester&#039;s display, it will go into the window&#039;s layer. The requester&#039;s layer is clipped to the window&#039;s boundaries, so the data in the requester is only visible if the window is large enough to allow for the complete display of that data.&lt;br /&gt;
&lt;br /&gt;
The requester will remain in the window and input will remain blocked until the user satisfies the request or the application removes the requester. Applications can set up some or all of the gadgets in the requester to automatically terminate the requester when the gadget is selected. This allows the requester to be removed from the window by user action. The application may also remove requesters from the window based on some event internal to the program.&lt;br /&gt;
&lt;br /&gt;
Multiple requesters may be nested in a single window. Such requesters must be satisfied in the reverse order in which they were posted; the last requester to be displayed must be satisfied first. Input will not be restored to a previous requester until all later requesters are satisfied.&lt;br /&gt;
&lt;br /&gt;
Note that the application may not bring up a limitless number of requesters in a window. Each requester creates a new layer for rendering in its window and the system currently has a limit of ten layers per window. Normal windows use one layer for the window rendering, GimmeZeroZero windows use a second layer for the border rendering. This leaves a maximum of eight or nine simultaneous requesters open in a window at any given time.&lt;br /&gt;
&lt;br /&gt;
FILE * If the requester is being brought up only to display an error message, the application may want to use a less intrusive method of bringing the error to the user&#039;s attention than a requester. Requesters interrupt the flow of the user&#039;s work, and force them to respond before continuing.&lt;br /&gt;
&lt;br /&gt;
As an alternative to bringing up an error requester, the application could flash the screen instead with Intuition&#039;s DisplayBeep() function. This allows the application to notify the user of an error that is not serious enough to warrant a requester and to which the user does not really need to respond. For more information, see the description of DisplayBeep() in [[Intuition_Screens|Intuition Screens]].&lt;br /&gt;
&lt;br /&gt;
=== Rendering Requesters ===&lt;br /&gt;
&lt;br /&gt;
The application may choose to use Intuition&#039;s rendering facilities to display the requester, or it may define its own custom bitmap. The Requester structure is initialized differently according to the rendering method chosen.&lt;br /&gt;
&lt;br /&gt;
To use Intuition&#039;s rendering facilities, you supply a list of one or more display objects with the Requester structure and submit the Requester to Intuition, allowing it to draw the objects. These objects can include independent lists of Borders, IntuiText, Images and Gadgets. Note that the ability to provide a list of Image structures requires the use of the USEREQIMAGE flag which must be set for them to be rendered. For more about Intuition rendering see [[Intuition_Images,_Line_Drawing_and_Text|Intuition Images, Line Drawing and Text]].&lt;br /&gt;
&lt;br /&gt;
The gadgets in a requester also have their own borders, images and text to add to the display imagery. Intuition will allocate the buffers, construct a bitmap that lasts for the duration of the display, and render the requester into the window. This rendering is all done over a solid color, filled background specified by the BackFill pen in the Requester structure. The backfill may be disabled by setting the NOREQBACKFILL flag.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a custom requester may be designed with predefined, bitmap imagery for the entire object. The image bitmap is submitted to Intuition through the ImageBMap field of the Requester structure. The bitmap should be designed to reduce user confusion; gadgets should line up with their images, and the designer should attempt to use glyphs (symbols) familiar to the user.&lt;br /&gt;
&lt;br /&gt;
To provide imagery for the requester, applications should always try to use data structures attached to the Requester structure as described above. Although, rendering directly into the requester layer&#039;s RastPort is tolerated, it must be done with great care.&lt;br /&gt;
&lt;br /&gt;
First, a requester is allowed to have gadgets that automatically close the requester when they are selected (GACT_ENDGADGET). If such a gadget is selected, the requester, its layer, and its layer&#039;s RastPort will be deleted asynchronously to your application. If your application is trying to render directly into the requester at that time, the results are unpredictable. Therefore, do not put GACT_ENDGADGET gadgets into a requester if you plan on rendering directly into its RastPort.&lt;br /&gt;
&lt;br /&gt;
Second, recall that requesters are clipped to the inside of the window (not including the borders). If the window can be sized smaller such that the requester would be entirely clipped, the requester&#039;s layer may be deleted by Intuition. If your window&#039;s minimum size and the requester size and position are such that the requester can be completely clipped, then reading Requester.ReqLayer is unsafe without additional protection. It would be correct to LockLayerInfo() the screen&#039;s Layer_Info, then check for the existence of the requester&#039;s ReqLayer, then render, then unlock.&lt;br /&gt;
&lt;br /&gt;
For reasons such as these, direct rendering is discouraged.&lt;br /&gt;
&lt;br /&gt;
=== Requester Refresh Type ===&lt;br /&gt;
&lt;br /&gt;
A requester appears in a Layer. By default, the requester layer is of type LAYERSMART, or, in window terminology, WFLG_SMART_REFRESH; so rendering is preserved in the requester when the window is moved or revealed.&lt;br /&gt;
&lt;br /&gt;
Requesters may also be simple refresh. This is the recommended type. If possible, make the requester a simple refresh layer requester by specifying the SIMPLEREQ flag.&lt;br /&gt;
&lt;br /&gt;
For all refresh types, Intuition will keep the gadget, border, image and bitmap imagery properly refreshed.&lt;br /&gt;
&lt;br /&gt;
=== Requester Display Position ===&lt;br /&gt;
&lt;br /&gt;
The location of true requesters may be specified in one of three ways. The requester may either be a constant location, which is an offset from the top left corner of the window; a location relative to the current location of the pointer; or a location relative to the center of the window.&lt;br /&gt;
&lt;br /&gt;
To display the requester as an offset from the upper left corner of the window, initialize the TopEdge and LeftEdge variables and clear the POINTREL flag. This will create a requester with a fixed position relative to the upper left corner for both normal requesters and double menu requesters.&lt;br /&gt;
&lt;br /&gt;
Displaying the requester relative to the pointer can get the user&#039;s attention immediately and closely associates the requester with whatever the user was doing just before the requester was displayed in the window. However, only double menu requesters may be positioned relative to the pointer position. See below for more information on double menu requesters.&lt;br /&gt;
&lt;br /&gt;
Requesters that are not double menu requesters may be positioned relative to the center of the window. This is done by setting the POINTREL flag and filling in the relative top and left of the gadget. Setting RelTop and RelLeft to zero will center the requester in the window. Positive values of RelTop and RelLeft will move the requester down and to the right, negative values will move it up and to the left.&lt;br /&gt;
&lt;br /&gt;
=== Gadgets in Requesters ===&lt;br /&gt;
&lt;br /&gt;
Each requester gadget must have the GTYP_REQGADGET flag set in the GadgetType field of its Gadget structure. This informs Intuition that this gadget is to be rendered in a requester rather than a window.&lt;br /&gt;
&lt;br /&gt;
Requesters can have gadgets in them that &#039;&#039;automatically&#039;&#039; satisfy the request and end the requester. When one of these gadgets is selected, Intuition will remove the requester from the window. This is equivalent to the application calling EndRequest(), and, if the request is terminated by selection of such a gadget, the application should not call EndRequest() for that requester.&lt;br /&gt;
&lt;br /&gt;
Set the GACT_ENDGADGET flag in the Activation field of the Gadget structure to create a gadget that automatically terminates the requester. Every time one of the requester&#039;s gadgets is selected, Intuition examines the GACT_ENDGADGET flag. If GACT_ENDGADGET is set, the requester is removed from the display and unlinked from the window&#039;s active requester list.&lt;br /&gt;
&lt;br /&gt;
Requesters rendered via Intuition and those that use a custom bitmap differ in how their gadgets are rendered. For requesters rendered via Intuition, the application supplies a regular gadget list just as it would for application gadgets in a window.&lt;br /&gt;
&lt;br /&gt;
In custom bitmap requesters, however, any gadget imagery is part of the bitmap supplied for the requester. Therefore the list of gadgets supplied for custom bitmap requesters should not provide gadget imagery but rather it should define only the select boxes, highlighting, and gadget types for the gadgets.&lt;br /&gt;
&lt;br /&gt;
The Gadget structures used with a custom bitmap requester should have their GadgetRender, SelectRender and GadgetText fields set to NULL as these will be ignored. Other gadget information-select box dimensions, highlighting, and gadget type-is still relevant. The select box information is especially important since the select box must have a well defined correspondence with the custom bitmap imagery supplied. The basic idea is to make sure that the user understands the requester imagery and gadgets.&lt;br /&gt;
&lt;br /&gt;
=== Using a Requester to Block Window Input ===&lt;br /&gt;
&lt;br /&gt;
There may be times when an application needs to block user input without a visible requester. In some cases, the application needs to be busy for a while. Other times, an application wants the blocking properties of a requester, but prefers to use a window instead of a true requester. In this case, the application can create a requester with no imagery, attaching it to the parent window to block input. A new window may then be opened to act as the requester.&lt;br /&gt;
&lt;br /&gt;
Some of the advantages of using a window as a requester instead of a real requester include:&lt;br /&gt;
&lt;br /&gt;
* A window can be resized, and moves independently of the parent window.&lt;br /&gt;
* It is legal to render directly into a window.&lt;br /&gt;
* The window can have its own menus since only the parent window&#039;s menus are disabled (this is only occasionally useful).&lt;br /&gt;
* Certain code or a library you are using may not work in requesters (GadTools library is an example of this).&lt;br /&gt;
&lt;br /&gt;
Of course, using a true requester instead of a window has the advantage that the requester automatically moves and depth-arranges along with the parent window.&lt;br /&gt;
&lt;br /&gt;
==== A Requester Example ====&lt;br /&gt;
&lt;br /&gt;
To use a window as a requester, first bring up a zero-sized requester attached to the main window (this provides the blocking feature). Then, bring up your second window, or bring up a busy pointer. The following example illustrates bringing up a busy pointer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** blockinput.c -- program to demonstrate how to block the input from a&lt;br /&gt;
** window using a minimal requester, and how to put up a busy pointer.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our function prototypes */&lt;br /&gt;
BOOL beginWait(struct Window *win, struct Requester *waitRequest);&lt;br /&gt;
VOID endWait(struct Window *win, struct Requester *waitRequest);&lt;br /&gt;
VOID processIDCMP(struct Window *win);&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
&lt;br /&gt;
/* data for a busy pointer. */&lt;br /&gt;
uint16 waitPointer[] =&lt;br /&gt;
    {&lt;br /&gt;
    0x0000, 0x0000,     /* reserved, must be NULL */&lt;br /&gt;
&lt;br /&gt;
    0x0400, 0x07C0,&lt;br /&gt;
    0x0000, 0x07C0,&lt;br /&gt;
    0x0100, 0x0380,&lt;br /&gt;
    0x0000, 0x07E0,&lt;br /&gt;
    0x07C0, 0x1FF8,&lt;br /&gt;
    0x1FF0, 0x3FEC,&lt;br /&gt;
    0x3FF8, 0x7FDE,&lt;br /&gt;
    0x3FF8, 0x7FBE,&lt;br /&gt;
    0x7FFC, 0xFF7F,&lt;br /&gt;
    0x7EFC, 0xFFFF,&lt;br /&gt;
    0x7FFC, 0xFFFF,&lt;br /&gt;
    0x3FF8, 0x7FFE,&lt;br /&gt;
    0x3FF8, 0x7FFE,&lt;br /&gt;
    0x1FF0, 0x3FFC,&lt;br /&gt;
    0x07C0, 0x1FF8,&lt;br /&gt;
    0x0000, 0x07E0,&lt;br /&gt;
&lt;br /&gt;
    0x0000, 0x0000,     /* reserved, must be NULL */&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** main()&lt;br /&gt;
**&lt;br /&gt;
** Open a window and display a busy-pointer for a short time then wait for&lt;br /&gt;
** the user to hit the close gadget (in processIDCMP()).  Normally, the&lt;br /&gt;
** application would bracket sections of code where it wishes to block window&lt;br /&gt;
** input with the beginWait() and endWait() functions.&lt;br /&gt;
*/&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct Window *win;&lt;br /&gt;
  &lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  &lt;br /&gt;
  if (IIntuition != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                        WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_INTUITICKS,&lt;br /&gt;
                        WA_Activate, TRUE,&lt;br /&gt;
                        WA_Width,  320,&lt;br /&gt;
                        WA_Height, 100,&lt;br /&gt;
                        WA_CloseGadget, TRUE,&lt;br /&gt;
                        WA_DragBar, TRUE,&lt;br /&gt;
                        WA_DepthGadget, TRUE,&lt;br /&gt;
                        WA_SizeGadget, TRUE,&lt;br /&gt;
                        WA_MaxWidth, ~0,&lt;br /&gt;
                        WA_MaxHeight, ~0,&lt;br /&gt;
                        TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
      processIDCMP(win);&lt;br /&gt;
      IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
   IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
   IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
   &lt;br /&gt;
   return 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** beginWait()&lt;br /&gt;
**&lt;br /&gt;
** Clear the requester with InitRequester.  This makes a requester of&lt;br /&gt;
** width = 0, height = 0, left = 0, top = 0; in fact, everything is zero.&lt;br /&gt;
** This requester will simply block input to the window until&lt;br /&gt;
** EndRequest is called.&lt;br /&gt;
**&lt;br /&gt;
** The pointer is set to a reasonable 4-color busy pointer, with proper offsets.&lt;br /&gt;
*/&lt;br /&gt;
BOOL beginWait(struct Window *win, struct Requester *waitRequest)&lt;br /&gt;
{&lt;br /&gt;
  IIntuition-&amp;gt;InitRequester(waitRequest);&lt;br /&gt;
  if (IIntuition-&amp;gt;Request(waitRequest, win))&lt;br /&gt;
  {&lt;br /&gt;
    IIntuition-&amp;gt;SetPointer(win, waitPointer, 16, 16, -6, 0);&lt;br /&gt;
    IIntuition-&amp;gt;SetWindowTitles(win, &amp;quot;Busy - Input Blocked&amp;quot;, (uint8 *)~0);&lt;br /&gt;
    return(TRUE);&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
    return(FALSE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** endWait()&lt;br /&gt;
**&lt;br /&gt;
** Routine to reset the pointer to the system default, and remove the&lt;br /&gt;
** requester installed with beginWait().&lt;br /&gt;
*/&lt;br /&gt;
VOID endWait(struct Window *win, struct Requester *waitRequest)&lt;br /&gt;
{&lt;br /&gt;
  IIntuition-&amp;gt;ClearPointer(win);&lt;br /&gt;
  IIntuition-&amp;gt;EndRequest(waitRequest, win);&lt;br /&gt;
  IIntuition-&amp;gt;SetWindowTitles(win, &amp;quot;Not Busy&amp;quot;, (uint8 *)~0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** processIDCMP()&lt;br /&gt;
**&lt;br /&gt;
** Wait for the user to close the window.&lt;br /&gt;
*/&lt;br /&gt;
VOID processIDCMP(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
  struct IntuiMessage *msg;&lt;br /&gt;
  struct Requester myreq;&lt;br /&gt;
  uint16 tick_count;&lt;br /&gt;
&lt;br /&gt;
  BOOL done = FALSE;&lt;br /&gt;
&lt;br /&gt;
  /* Put up a requester with no imagery (size zero). */&lt;br /&gt;
  if (beginWait(win, &amp;amp;myreq))&lt;br /&gt;
  {&lt;br /&gt;
    /*&lt;br /&gt;
    ** Insert code here for a window to act as the requester.&lt;br /&gt;
    */&lt;br /&gt;
&lt;br /&gt;
    /* We&#039;ll count down INTUITICKS, which come about ten times&lt;br /&gt;
    ** a second.  We&#039;ll keep the busy state for about three seconds.&lt;br /&gt;
    */&lt;br /&gt;
    tick_count = 30;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  while (!done)&lt;br /&gt;
  {&lt;br /&gt;
    IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
    while (NULL != (msg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort)))&lt;br /&gt;
    {&lt;br /&gt;
      uint32 class = msg-&amp;gt;Class;&lt;br /&gt;
      IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
      switch (class)&lt;br /&gt;
      {&lt;br /&gt;
        case IDCMP_CLOSEWINDOW:&lt;br /&gt;
          done = TRUE;&lt;br /&gt;
          break;&lt;br /&gt;
&lt;br /&gt;
        case IDCMP_INTUITICKS:&lt;br /&gt;
          if (tick_count &amp;gt; 0)&lt;br /&gt;
          {&lt;br /&gt;
            if (--tick_count == 0)&lt;br /&gt;
              endWait(win,&amp;amp;myreq);&lt;br /&gt;
          }&lt;br /&gt;
          break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Double Menu Requesters ===&lt;br /&gt;
&lt;br /&gt;
A double menu requester is exactly like other requesters with one exception: it is displayed only when the user double clicks the mouse menu button. Double menu requesters block input in exactly the same manner as other true requesters. A double menu requester is attached to a window by calling SetDMRequest().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BOOL SetDMRequest( struct Window *window, struct Requester *requester );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call does not display the requester, it simply prepares it for display. The requester will be brought up when the user double clicks the mouse menu button. The parent window will receive IDCMP_REQSET and IDCMP_REQCLEAR messages when the requester is added and removed.&lt;br /&gt;
&lt;br /&gt;
To prevent the user from bringing up a double menu requester, unlink it from the window by calling ClearDMRequest(). If a double menu request is set for a window, ClearDMRequest() should be called to remove the requester before that window is closed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BOOL ClearDMRequest( struct Window *window );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function unlinks the requester from the window and disables the ability of the user to bring it up. ClearDMRequest() will fail if the double menu request is currently being displayed.&lt;br /&gt;
&lt;br /&gt;
Double menu requesters can be positioned relative to the current mouse pointer position. For a mouse relative requester, specify POINTREL in the Flags field and initialize the RelLeft and RelTop variables. RelLeft and RelTop describe the offset of the upper, left corner of the requester from the pointer position at the time the requester is displayed. These values can be either negative or positive.&lt;br /&gt;
&lt;br /&gt;
The values of RelLeft and RelTop are only advisory; the actual position will be restricted such that the requester is entirely contained within the borders of its parent window, if possible. The actual top and left positions are stored in the TopEdge and LeftEdge variables.&lt;br /&gt;
&lt;br /&gt;
Positioning relative to the mouse pointer is possible only with double menu requesters. Setting POINTREL in a requester which is not a double menu requester will position the requester relative to the center of the window.&lt;br /&gt;
&lt;br /&gt;
=== IDCMP Requester Features ===&lt;br /&gt;
&lt;br /&gt;
Intuition can notify your application about user activity in the requester by sending a message to the parent window&#039;s IDCMP port (Window.UserPort). When using the IDCMP for input, the following IDCMP flags control how requester input events will be handled.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQSET&lt;br /&gt;
: With this flag set, the program will receive a message whenever a requester opens in its window. The application will receive one IDCMP_REQSET event for each requester opened in the window.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQCLEAR&lt;br /&gt;
: With this flag set, the program will receive a message whenever a requester is cleared from its window. The application will receive one IDCMP_REQCLEAR event for each requester closed in the window. By counting the number of IDCMP_REQSET and IDCMP_REQCLEAR events, the application may determine how many requesters are currently open in a window.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQVERIFY&lt;br /&gt;
: With this flag set, the application can ensure that it is ready to allow a requester to appear in the window before the requester is displayed.&lt;br /&gt;
&lt;br /&gt;
: When the program receives an IDCMP_REQVERIFY message, it must reply to that message before the requester is added to the window. If multiple requesters are opened in the window at the same time, only the first one will cause an IDCMP_REQVERIFY event. It is assumed that once a requester is in a window others may be added without the program&#039;s consent. After the requester count drops to zero and there are no open requesters in the window, the next requester to open will cause another IDCMP_REQVERIFY event.&lt;br /&gt;
&lt;br /&gt;
: IDCMP_REQVERIFY is ignored by the Request() function. Since Request() is controlled by the application, it is assumed that the program is prepared to handle the request when calling this function. Since the system does not render true requesters into an application&#039;s window (EasyRequest() and AutoRequest() come up in their own window, not in the application‚Äôs window), IDCMP_REQVERIFY will only control the timing of double menu requesters.&lt;br /&gt;
&lt;br /&gt;
These flags are set when the parent window is first opened by using either the WA_IDCMP tag or NewWindow.IDCMPFlags. They can also be set after the parent window is open by using the ModifyIDCMP() call. See [[Intuition_Input_and_Output_Methods|Intuition Input and Output Methods]], for further information about these IDCMP flags. See [[Intuition_Windows|Intuition Windows]] for details on setting IDCMP flags when a window is opened.&lt;br /&gt;
&lt;br /&gt;
== Requester Structure ==&lt;br /&gt;
&lt;br /&gt;
Unused fields in the Requester structure should be initialized to NULL or zero before using the structure. For global data that is pre-initialized, be sure to set all unused fields to zero. For dynamically allocated structures, allocate the storage with the MEMF_CLEAR flag, or call the InitRequester() function to clear the structure.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Requesters are Initialized According to Their Type|text=See &amp;quot;Rendering Requesters&amp;quot; and &amp;quot;Gadgets in Requesters&amp;quot; above for information about how the initialization of the structure differs according to how the requester is rendered.}}&lt;br /&gt;
&lt;br /&gt;
The specification for a Requester structure, defined in the &amp;amp;lt;intuition/intuition.h&amp;amp;gt; file, is as follows.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Requester&lt;br /&gt;
    {&lt;br /&gt;
    struct Requester *OlderRequest;&lt;br /&gt;
    WORD LeftEdge, TopEdge;&lt;br /&gt;
    WORD Width, Height;&lt;br /&gt;
    WORD RelLeft, RelTop;&lt;br /&gt;
    struct Gadget *ReqGadget;&lt;br /&gt;
    struct Border *ReqBorder;&lt;br /&gt;
    struct IntuiText *ReqText;&lt;br /&gt;
    UWORD Flags;&lt;br /&gt;
    UBYTE BackFill;&lt;br /&gt;
    struct Layer *ReqLayer;&lt;br /&gt;
    UBYTE ReqPad1[32];&lt;br /&gt;
    struct BitMap *ImageBMap;&lt;br /&gt;
    struct Window *RWindow;&lt;br /&gt;
    struct Image  *ReqImage;&lt;br /&gt;
    UBYTE ReqPad2[32];&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the meanings of the fields in the Requester structure:&lt;br /&gt;
&lt;br /&gt;
; OlderRequest&lt;br /&gt;
: For system use, initialize to NULL.&lt;br /&gt;
&lt;br /&gt;
; LeftEdge, TopEdge&lt;br /&gt;
: The location of the requester relative to the upper left corner of the window. These values must be set if the POINTREL flag is not set. Use RelLeft and RelTop for POINTREL requesters.&lt;br /&gt;
&lt;br /&gt;
; Width, Height&lt;br /&gt;
: These fields describe the size of the entire requester rectangle, containing all the text and gadgets.&lt;br /&gt;
&lt;br /&gt;
; RelLeft, RelTop&lt;br /&gt;
: These values are only used if the POINTREL flag in the requester&#039;s Flags field is set.&lt;br /&gt;
&lt;br /&gt;
: If the requester is a double menu requester and POINTREL is set then these values contain the relative offset of the requester&#039;s upper left corner from the current pointer position.&lt;br /&gt;
&lt;br /&gt;
: If the requester is not a double menu requester and POINTREL is set, then these values contain the relative offset of the requester&#039;s center from the center of the window that the requester is to be displayed in. For example, using POINTREL with a requester which is not a double menu requester with RelLeft and RelTop of zero will center the requester in the window. The requester is centered within the inner part of the window, that is, within the inside edge of the window&#039;s borders.&lt;br /&gt;
&lt;br /&gt;
: If the requester is POINTREL and part of the containing box will appear out of the window, Intuition will adjust the requester so that the upper left corner is visible and as much of the remaining box as possible is visible. The adjustment attempts to maintain the requester within the window&#039;s borders, not within the window&#039;s bounding box.&lt;br /&gt;
&lt;br /&gt;
; ReqGadget&lt;br /&gt;
: This field is a pointer to the first in a linked list of Gadget structures. GTYP_REQGADGET must be specified in the GadgetTypes field of all Gadget structures that are used in a requester. Take care not to specify gadgets that extend beyond the Requester rectangle specified by the Width and Height fields, as Intuition does no boundary checking.&lt;br /&gt;
&lt;br /&gt;
: For requesters with custom imagery, where PREDRAWN is set, ReqGadget points to a valid list of gadgets, which are real gadgets in every way except that the gadget text and imagery information are ignored (and can be NULL). The select box, highlighting, and gadget type data are still used. Try to maintain a close correspondence between the gadgets&#039; select boxes and the supplied imagery.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=String Gadgets and Pre-drawn Requesters|text=Intuition will not render string gadget text in a predrawn requester. The application must use other rendering means than the predrawn bitmap if it wishes to use string gadgets with a requester.}}&lt;br /&gt;
&lt;br /&gt;
; ReqBorder&lt;br /&gt;
: This field is a pointer to an optional linked list of Border structures for drawing lines around and within the requester. The lines specified in the border may go anywhere in the requester; they are not confined to the perimeter of the requester.&lt;br /&gt;
&lt;br /&gt;
: For requesters with custom imagery, where PREDRAWN is set, this variable is ignored and may be set to NULL.&lt;br /&gt;
&lt;br /&gt;
; ReqText&lt;br /&gt;
: This field is a pointer to an optional linked list of IntuiText structures containing text for the requester. This is for general text in the requester.&lt;br /&gt;
&lt;br /&gt;
: For requesters with custom imagery, where PREDRAWN is set, this variable is ignored and can be set to NULL.&lt;br /&gt;
&lt;br /&gt;
; Flags&lt;br /&gt;
: The following flags may be specified for the Requester:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| POINTREL&lt;br /&gt;
| Specify POINTREL to indicate that the requester is to appear in a relative rather than a fixed position.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
For double menu requesters, the position is relative to the pointer. Otherwise, the position of POINTREL requesters is relative to the center of the window.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
See the discussion of RelLeft and RelTop, above.&lt;br /&gt;
|-&lt;br /&gt;
| PREDRAWN&lt;br /&gt;
| Specify PREDRAWN if a custom BitMap structure is supplied for the requester and ImageBMap points to the structure.&lt;br /&gt;
|-&lt;br /&gt;
| NOISYREQ&lt;br /&gt;
| Normally, when a requester is active, any gadget, menu, mouse and keyboard events within the parent window are blocked. Specify the NOISYREQ requester flag to allow keyboard and mouse button IDCMP events to be posted, even though the requester is active in the parent window.&amp;lt;br/&amp;gt;&lt;br /&gt;
If the NOISYREQ requester flag is set, the application will receive IDCMP_RAWKEY, IDCMP_VANILLAKEY and IDCMP_MOUSEBUTTONS events. Note that with NOISYREQ set, IDCMP_MOUSEBUTTON events will also be sent when the user clicks on any of the blocked gadgets in the parent window.&amp;lt;br/&amp;gt;&lt;br /&gt;
Although the reporting of mouse button events depends on NOISYREQ, mouse movement events do not. IDCMP_MOUSEMOVE events are reported if the window flag WFLG_REPORTMOUSE is set in the parent window, or if one of the requester gadgets is down and has the GACT_FOLLOWMOUSE flag set. This is true even if the requester is a double-menu requester.&lt;br /&gt;
|-&lt;br /&gt;
| USEREQIMAGE&lt;br /&gt;
| Render the linked list of images pointed to by ReqImage after rendering the BackFill color but before gadgets and text.&lt;br /&gt;
|-&lt;br /&gt;
| NOREQBACKFILL&lt;br /&gt;
| Do not backfill the requester with the BackFill pen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
: In addition, Intuition uses these flags in the Requester:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| REQOFFWINDOW&lt;br /&gt;
| Set by Intuition if the requester is currently active but is positioned off window.&lt;br /&gt;
|-&lt;br /&gt;
| REQACTIVE&lt;br /&gt;
| This flag is set or cleared by Intuition as the requester is posted and removed. The active requester is indicated by the value of Window.FirstRequest.&lt;br /&gt;
|-&lt;br /&gt;
| SYSREQUEST&lt;br /&gt;
| This flag is set by Intuition if this is a system generated requester. Since the system will never create a true requester in an application window, the application should not be concerned with this flag.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; BackFill&lt;br /&gt;
: BackFill is the pen number to be used to fill the rectangle of the requester before any drawing takes place. For requesters with custom imagery, where PREDRAWN is set, or for requesters with NOREQBACKFILL set, this variable is ignored.&lt;br /&gt;
&lt;br /&gt;
; ReqLayer&lt;br /&gt;
: While the requester is active, this contains the address of the Layer structure used in rendering the requester.&lt;br /&gt;
&lt;br /&gt;
; ImageBMap&lt;br /&gt;
: A pointer to the custom bitmap for this requester. If this requester is not PREDRAWN, Intuition ignores this variable.&lt;br /&gt;
: When a custom bitmap is supplied, the PREDRAWN flag in the requester&#039;s Flags field must be set.&lt;br /&gt;
&lt;br /&gt;
; RWindow&lt;br /&gt;
: Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
; ReqImage&lt;br /&gt;
: A pointer to a list of Image structures used to create imagery within the requester. Intuition ignores this field if the flag USEREQIMAGE is not set. This imagery is automatically redrawn by Intuition each time the requester needs refreshing. The images are drawn after filling with the BackFill pen, but before the gadgets and text.&lt;br /&gt;
&lt;br /&gt;
; ReqPad1, ReqPad2&lt;br /&gt;
: Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
== Easy Requesters ==&lt;br /&gt;
&lt;br /&gt;
EasyRequest() provides a simple way to make a requester that allows the user to select one of a limited number of choices. (A similar function, AutoRequest(), is also available but is not as flexible or as powerful. See the SDK for more information.)&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig7-2.png|frame|center|A Simple Requester Made with EasyRequest()]]&lt;br /&gt;
&lt;br /&gt;
The program supplies the text for the body of the requester, text for each of the possible options, an optional title for the window, and other arguments. The body text can consist of one or more lines with lines separated by the linefeed character.&lt;br /&gt;
&lt;br /&gt;
Each option for an easy requester is displayed as a simple button gadget positioned beneath the body text you specify. The layout of the requester, its text and buttons, is done automatically and is font sensitive. The screen font (Screen.Font) is used for all text in the requester.&lt;br /&gt;
&lt;br /&gt;
Typically, easy requesters have one selection indicating a positive action and one selection indicating a negative action. The text used for the positive action might be &amp;quot;OK&amp;quot;, &amp;quot;Yes,&amp;quot; &amp;quot;True,&amp;quot; &amp;quot;Retry,&amp;quot; or similar responses. Likewise, the text used for the negative action might be &amp;quot;No,&amp;quot; &amp;quot;False,&amp;quot; &amp;quot;Cancel&amp;quot; and so on. The negative choice should always be the rightmost or final choice and will return a zero if selected.&lt;br /&gt;
&lt;br /&gt;
When EasyRequest() is called, Intuition will build the requester, display it, and wait for user response.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LONG EasyRequest( struct Window *window, struct EasyStruct *easyStruct,&lt;br /&gt;
                  ULONG *idcmpPtr, APTR arg1, ... );&lt;br /&gt;
&lt;br /&gt;
LONG EasyRequestArgs( struct Window *window, struct EasyStruct *easyStruct,&lt;br /&gt;
                      ULONG *idcmpPtr, APTR args );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The window argument is a pointer to the reference window. The requester will be displayed on the same screen that the reference window is on and also takes its title from the reference window, if not otherwise specified. This argument can be NULL, which means the requester is to appear on the Workbench screen, or the default public screen, if defined.&lt;br /&gt;
&lt;br /&gt;
The easyStruct argument is a pointer to an EasyStruct structure which defines the setup and the text of this easy requester (described below).&lt;br /&gt;
&lt;br /&gt;
The idcmpPtr argument is a pointer to a ULONG containing the IDCMP flags for the event that you want to terminate this requester. If such an event occurs the requester is terminated (with a result of -1) and the ULONG that idcmpPtr points to will contain the actual class of the event message. This feature allows external events to satisfy the request, such as the user inserting a disk in the disk drive. This argument can be set to NULL for no automatic termination.&lt;br /&gt;
&lt;br /&gt;
The gadget and body text for an easy requester is specified in an EasyStruct structure (see below). Body text can be specified using a printf()-style format string that also accepts variables as part of the text. If variables are specified in the requester text, their value is taken from the args (or arg1,...) parameters shown in the prototypes above. EasyRequestArgs() takes a pointer to an array of pointers to arguments, while EasyRequest() has a &#039;&#039;varargs&#039;&#039; interface and takes individual arguments as part of the function call. The types of these arguments are specified in the format strings of the EasyStruct structure. Arguments for es_GadgetFormat follow arguments for es_TextFormat.&lt;br /&gt;
&lt;br /&gt;
The EasyRequest() functions return an integer from 0 to &#039;&#039;n - 1&#039;&#039;, where &#039;&#039;n&#039;&#039; is the number of choices specified for the requester. The numbering from left-to-right is: 1, 2, ..., &#039;&#039;n - 1&#039;&#039;, 0. This is for compatibility with AutoRequest() which returns FALSE for the rightmost gadget.&lt;br /&gt;
&lt;br /&gt;
The function will return -1 if it receives an IDCMP event that matches one of the termination events specified in the idcmpPtr argument.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Turn Off the Verify Messages|text=Use ModifyIDCMP() to turn off all verify messages (such as MENUVERIFY) before calling EasyRequest() or AutoRequest(). Neglecting to do so can cause situations where Intuition is waiting for the return of a message that the application program cannot receive because its input is shut off while the requester is up. If Intuition finds itself in a deadlock state, the verify function will timeout and will be automatically replied.}}&lt;br /&gt;
&lt;br /&gt;
=== The EasyStruct Structure ===&lt;br /&gt;
&lt;br /&gt;
The text and setup of an easy requester is specified in an EasyStruct structure, defined in &amp;amp;lt;intuition/intuition.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct EasyStruct&lt;br /&gt;
    {&lt;br /&gt;
    ULONG       es_StructSize;&lt;br /&gt;
    ULONG       es_Flags;&lt;br /&gt;
    UBYTE       *es_Title;&lt;br /&gt;
    UBYTE       *es_TextFormat;&lt;br /&gt;
    UBYTE       *es_GadgetFormat;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; es_StructSize&lt;br /&gt;
: Contains the size of the EasyStruct structure, sizeof(struct EasyStruct).&lt;br /&gt;
&lt;br /&gt;
; es_Flags&lt;br /&gt;
: Set to zero.&lt;br /&gt;
&lt;br /&gt;
; es_Title&lt;br /&gt;
: Title of easy requester window. If this is NULL, the title will be taken to be the same as the title of the reference window, if one is specified in the EasyRequest() call, else the title will be &amp;quot;System Request&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
; es_TextFormat&lt;br /&gt;
: Format string for the text in the requester body, with printf()-style variable substitution as described in the Exec library function RawDoFmt(). Multiple lines are separated by the linefeed character (hex 0x0A or \n in C). Formatting &amp;quot;%&amp;quot; functions are supported exactly as in RawDoFmt(). The variables that get substituted in the format string come from the last argument passed to EasyRequest() (see prototype above).&lt;br /&gt;
&lt;br /&gt;
; es_GadgetFormat&lt;br /&gt;
: Format string for gadgets, where the text for separate gadgets is separated by &amp;quot;|&amp;quot; (vertical bar). As with the body text, printf()-style formatting with variable substitution is supported, but multi-line text in the gadgets is not supported. At least one gadget &#039;&#039;must&#039;&#039; be specified.&lt;br /&gt;
&lt;br /&gt;
Requesters generated with EasyRequest() and BuildEasyRequest() (including system requesters, which use SysReqHandler() to handle input) can be satisfied by the user via the keyboard. The key strokes left Amiga V and left Amiga B correspond to selecting the requester&#039;s leftmost or rightmost gadgets with the mouse, respectively.&lt;br /&gt;
&lt;br /&gt;
An easy request must have at least one choice. Multiple choices are specified through the &amp;quot;|&amp;quot; (vertical bar) separator character in the es_GadgetFormat string. The buttons are displayed evenly spaced, from left-to-right in the order in which they appear in the string.&lt;br /&gt;
&lt;br /&gt;
The requesters generated by EasyRequest() appear in the visible portion of the specified screen. They do not cause the screen to scroll. Under the current implementation, the window for an easy requester will appear in the upper left corner of the display clip for the specified screen.&lt;br /&gt;
&lt;br /&gt;
When a request is posted using EasyRequest() or BuildEasyRequest(), it will move the screen it appears on to the front, if that screen is not already the front-most. This brings the request to the attention of the user. The request also comes up as the active window and could potentially steal the input focus from the current window.&lt;br /&gt;
&lt;br /&gt;
When the request is satisfied the screen will be moved to back if the request caused the screen to move to the front when it was displayed. Note that the final screen position may not be the same as the original screen position.&lt;br /&gt;
&lt;br /&gt;
==== Example Using EasyRequest() ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
** easyrequest.c - show the use of an easy requester.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* declare the easy request structure.&lt;br /&gt;
** this uses many features of EasyRequest(), including:&lt;br /&gt;
**     multiple lines of body text separated by &#039;\n&#039;.&lt;br /&gt;
**     variable substitution of a string (%s) in the body text.&lt;br /&gt;
**     multiple button gadgets separated by &#039;|&#039;.&lt;br /&gt;
**     variable substitution in a gadget (long decimal &#039;%ld&#039;).&lt;br /&gt;
*/&lt;br /&gt;
struct EasyStruct myES =&lt;br /&gt;
    {&lt;br /&gt;
    sizeof(struct EasyStruct),&lt;br /&gt;
    0,&lt;br /&gt;
    &amp;quot;Request Window Name&amp;quot;,&lt;br /&gt;
    &amp;quot;Text for the request\nSecond line of %s text\nThird line of text for the request&amp;quot;,&lt;br /&gt;
    &amp;quot;Yes|%ld|No&amp;quot;,&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Main routine to show the use of EasyRequest()&lt;br /&gt;
*/&lt;br /&gt;
int main ()&lt;br /&gt;
{&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*) IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuitionBase != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    int32 number = 3125794;  /* for use in the middle button */&lt;br /&gt;
&lt;br /&gt;
    /* note in the variable substitution:&lt;br /&gt;
    **     the string goes in the first open variable (in body text).&lt;br /&gt;
    **     the number goes in the second open (gadget text).&lt;br /&gt;
    */&lt;br /&gt;
    int32 answer = IIntuition-&amp;gt;EasyRequest(NULL, &amp;amp;myES, NULL, &amp;quot;(Variable)&amp;quot;, number);&lt;br /&gt;
&lt;br /&gt;
    /* Process the answer.  Note that the buttons are numbered in&lt;br /&gt;
    ** a strange order.  This is because the rightmost button is&lt;br /&gt;
    ** always a negative reply.  The code can use this if it chooses,&lt;br /&gt;
    ** with a construct like:&lt;br /&gt;
    **&lt;br /&gt;
    **     if (EasyRequest())&lt;br /&gt;
    **          positive_response();&lt;br /&gt;
    */&lt;br /&gt;
    switch (answer)&lt;br /&gt;
        {&lt;br /&gt;
        case 1:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;selected &#039;Yes&#039;\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;selected &#039;%ld&#039;\n&amp;quot;, number);&lt;br /&gt;
            break;&lt;br /&gt;
        case 0:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;selected &#039;No&#039;\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Low Level Access to Easy Requesters ===&lt;br /&gt;
&lt;br /&gt;
The EasyRequest() function calls a lower level Intuition function named BuildEasyRequest() to construct the requester. An application can call BuildEasyRequest() directly if it needs to use an easy requester but requires custom handling of the events sent to the requester. Handling of the events should be done using the SysReqHandler() function as described below.&lt;br /&gt;
&lt;br /&gt;
The BuildEasyRequest() functions take the same arguments as EasyRequest():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Window *BuildEasyRequestArgs( struct Window *window,&lt;br /&gt;
                                     struct EasyStruct *easyStruct,&lt;br /&gt;
                                     ULONG idcmp, APTR args );&lt;br /&gt;
&lt;br /&gt;
struct Window *BuildEasyRequest( struct Window *window,&lt;br /&gt;
                                 struct EasyStruct *easyStruct,&lt;br /&gt;
                                 ULONG idcmp, APTR arg1, ... );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To process input event information directly while an easy requester is displayed, first call BuildEasyRequest() then call SysReqHandler() periodically to process user input.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
LONG SysReqHandler( struct Window *window, ULONG *idcmpPtr, LONG waitInput );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will provide standard handling of events but allow the application to control the timing of checking the events. This handling includes checks for left Amiga keys.&lt;br /&gt;
&lt;br /&gt;
The FreeSysRequest() function must be called after an application has finished with a requester (if it was created with BuildEasyRequest() call).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID FreeSysRequest( struct Window *window );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function ends the requester and frees any resources allocated with the BuildEasyRequest() call.&lt;br /&gt;
&lt;br /&gt;
== System Requesters ==&lt;br /&gt;
&lt;br /&gt;
System requesters, such as DOS requests to &amp;quot;Insert volume foo in any drive&amp;quot;, are created by the system using EasyRequest(). Unless otherwise specified, these requests appear on the default public screen.&lt;br /&gt;
&lt;br /&gt;
System requests may appear at any time the system requires a resource that is not available. The user may be in the middle of an action, the program may be in any state.&lt;br /&gt;
&lt;br /&gt;
Use the function ModifyIDCMP() to turn off all verify messages before calling any function that might generate a system requester. Neglecting to do so can cause situations where Intuition is waiting for the return of a message which the application program is unable to receive because its input is shut off while the requester is up. If Intuition finds itself in a deadlock state, the verify function will timeout and be automatically replied.&lt;br /&gt;
&lt;br /&gt;
=== Redirecting System Requesters ===&lt;br /&gt;
&lt;br /&gt;
A process can force the system requests which are caused by its actions to appear on a custom screen by changing the pr_WindowPtr field of its Process structure. This field may be set to three values: zero, negative one or a valid pointer to the Window structure of an open window. If pr_WindowPtr is set to zero, the request will appear on the default public screen. If pr_WindowPtr is set to negative one, the system request will never appear and the return code will be as if the user had selected the rightmost button (negative response). If pr_WindowPtr is set to a valid window pointer, then the request will appear on the same screen as the window.&lt;br /&gt;
&lt;br /&gt;
The original value of pr_WindowPtr &#039;&#039;must&#039;&#039; be cached and restored before the window is closed.&lt;br /&gt;
&lt;br /&gt;
=== Function Reference ===&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions that relate to the use of Intuition requesters. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| Request()&lt;br /&gt;
| Open a requester in an open window.&lt;br /&gt;
|-&lt;br /&gt;
| EndRequest()&lt;br /&gt;
| Close an open requester in a window.&lt;br /&gt;
|-&lt;br /&gt;
| InitRequester()&lt;br /&gt;
| Clear a requester structure before use.&lt;br /&gt;
|-&lt;br /&gt;
| EasyRequestArgs()&lt;br /&gt;
| Open a system requester.&lt;br /&gt;
|-&lt;br /&gt;
| EasyRequest()&lt;br /&gt;
| Alternate calling sequence for EasyRequestArgs().&lt;br /&gt;
|-&lt;br /&gt;
| BuildEasyRequestArgs()&lt;br /&gt;
| Low level function to open EasyRequestArgs().&lt;br /&gt;
|-&lt;br /&gt;
| BuildEasyRequest()&lt;br /&gt;
| Low level function to close EasyRequestArgs().&lt;br /&gt;
|-&lt;br /&gt;
| SysReqHandler()&lt;br /&gt;
| Event handler function for EasyRequestArgs().&lt;br /&gt;
|-&lt;br /&gt;
| AutoRequest()&lt;br /&gt;
| Open a pre-V36 system requester.&lt;br /&gt;
|-&lt;br /&gt;
| BuildSysRequest()&lt;br /&gt;
| Low level function to open an AutoRequest().&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysRequest()&lt;br /&gt;
| Low level function to close an AutoRequest().&lt;br /&gt;
|-&lt;br /&gt;
| SetDMRequest()&lt;br /&gt;
| Set a double menu requester for an open window.&lt;br /&gt;
|-&lt;br /&gt;
| ClearDMRequest()&lt;br /&gt;
| Clear a double menu requester from an open window.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Trackdisk_Device&amp;diff=12543</id>
		<title>Trackdisk Device</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Trackdisk_Device&amp;diff=12543"/>
		<updated>2025-01-26T19:27:56Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Trackdisk Device ==&lt;br /&gt;
&lt;br /&gt;
The Amiga trackdisk device directly drives the disk, controls the disk motors, reads raw data from the tracks, and writes raw data to the tracks. Normally, you use the AmigaDOS functions to write or read data from the disk. The trackdisk device is the lowest-level software access to the disk data and is used by AmigaDOS to access the disks. The trackdisk device supports the usual commands such as CMD_WRITE and CMD_READ. In addition, it supports an extended form of these commands to allow additional control over the trackdisk device.&lt;br /&gt;
&lt;br /&gt;
== Trackdisk Device Commands and Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command&lt;br /&gt;
! Command Operation&lt;br /&gt;
|-&lt;br /&gt;
| CMD_CLEAR || Mark track buffer as invalid. Forces the track to be re-read.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_CLEAR || ETD_CLEAR also checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_READ || Read one or more sectors from a disk.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_READ || ETD_READ also reads the sector label area and checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_UPDATE || Write out track buffer if it has been changed.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_UPDATE || ETD_UPDATE also checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_WRITE || Write one or more sectors to a disk.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_WRITE || ETD_WRITE also writes the sector label area and checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| TD_ADDCHANGEINT || Add an interrupt handler to be activated on a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| TD_CHANGENUM || Return the current value of the diskchange counter used by the ETD commands to determine if a diskchange has occurred.&lt;br /&gt;
|-&lt;br /&gt;
| TD_CHANGESTATE || Return the disk present/not-present status of a drive.&lt;br /&gt;
|-&lt;br /&gt;
| TD_EJECT || Eject a disk from a drive. This command will only work on drives that support an eject command.&lt;br /&gt;
|-&lt;br /&gt;
| TD_FORMAT || Initialize one or more tracks with a data buffer.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_FORMAT || ETD_FORMAT also initializes the sector label area.&lt;br /&gt;
|-&lt;br /&gt;
| TD_GETDRIVETYPE || Return the type of disk drive in use by the unit.&lt;br /&gt;
|-&lt;br /&gt;
| TD_GETGEOMETRY || Return the disk geometry table.&lt;br /&gt;
|-&lt;br /&gt;
| TD_GETNUMTRACKS || Return the number of tracks usable with the unit.&lt;br /&gt;
|-&lt;br /&gt;
| TD_MOTOR || Turn the motor on or off.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_MOTOR || ETD_MOTOR also checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| TD_PROTSTATUS || Return the write-protect status of a disk.&lt;br /&gt;
|-&lt;br /&gt;
| TD_RAWREAD || Read RAW sector data from disk (unencoded MFM).&lt;br /&gt;
|-&lt;br /&gt;
| ETD_RAWREAD || ETD_RAWREAD also checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| TD_RAWWRITE || Write RAW sector data to disk.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_RAWWRITE || ETD_RAWWRITE also checks for a diskchange.&lt;br /&gt;
|-&lt;br /&gt;
| TD_REMCHANGEINT || Remove a diskchange interrupt handler.&lt;br /&gt;
|-&lt;br /&gt;
| TD_SEEK || Move the head to a specific track.&lt;br /&gt;
|-&lt;br /&gt;
| ETD_SEEK || ETD_SEEK also checks for a diskchange.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device Interface ==&lt;br /&gt;
&lt;br /&gt;
The trackdisk device operates like other Amiga devices. To use it, you must first open the device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.&lt;br /&gt;
&lt;br /&gt;
The trackdisk device uses two different types of I/O request blocks, IOStdReq and IOExtTD and two types of commands, standard and extended. An IOExtTD is required for the extended trackdisk commands (those beginning with “ETD_”), but can be used for both types of commands. Thus, the IOExtTD is the type of I/O request that will be used in this article.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IOExtTD&lt;br /&gt;
{&lt;br /&gt;
    struct  IOStdReq iotd_Req;&lt;br /&gt;
    ULONG   iotd_Count;         /* Diskchange counter */&lt;br /&gt;
    ULONG   iotd_SecLabel;      /* Sector label data */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file devices/trackdisk.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
The enhanced commands listed above—those beginning with “ETD_”— are similar to their standard counterparts but have additional features: they allow you to control whether a command will be executed if the disk has been changed and they allow you to read or write to the sector label portion of a sector.&lt;br /&gt;
&lt;br /&gt;
Enhanced commands require a larger I/O request, IOExtTD, than the IOStdReq request used by the standard commands. IOExtTD contains extra information needed by the enhanced command; since the standard form of a command ignores the extra fields, IOExtTD requests can be used for both types. The extra information takes the form of two extra longwords at the end of the data structure. These commands are performed only if the change count is less than or equal to the value in the iotd_Count field of the command’s request block.&lt;br /&gt;
&lt;br /&gt;
The iotd_Count field keeps old I/O requests from being performed when the disk is changed. Any request found with an iotd_Count less than the current change counter value will be returned with a characteristic error (TDERR_DiskChange) in the io_Error field. This allows stale I/O requests to be returned to the user after a disk has been changed. The current disk-change counter value can be obtained by TD_CHANGENUM. If the user wants enhanced disk I/O but does not care about disk removal, then iotd_Count may be set to the maximum unsigned long integer value (0xFFFFFFFF).&lt;br /&gt;
&lt;br /&gt;
The iotd_SecLabel field allows access to the sector identification section of the sector header. Each sector has 16 bytes of descriptive data space available to it; the trackdisk device does not interpret this data. If iotd_SecLabel is NULL, then this descriptive data is ignored. If it is not NULL, then iotd_SecLabel should point to a series of contiguous 16-byte chunks (one for each sector that is to be read or written). These chunks will be written out to the sector’s label region on a write or filled with the sector’s label area on a read. If a CMD_WRITE (the standard write call) is done, then the sector label area is left unchanged.&lt;br /&gt;
&lt;br /&gt;
=== About Amiga Floppy Disks ===&lt;br /&gt;
&lt;br /&gt;
The standard 3.5 inch Amiga floppy disk consists of a number of tracks that are NUMSECS (11) sectors of TD_SECTOR (512) usable data bytes plus TD_LABELSIZE (16) bytes of label area. There are usually 2 tracks per cylinder (2 heads) and 80 cylinders per disk. The number of tracks can be found using the TD_GETNUMTRACKS command.&lt;br /&gt;
&lt;br /&gt;
The NUMSECS in some drives may be variable and may change when a disk is inserted. Use TD_GETGEOMETRY to determine the current number of sectors.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Think Tracks not Cylinders|text=The result is given in tracks and not cylinders. On a standard 3.5&amp;amp;quot; drive, this gives useful space of 880K bytes plus 28K bytes of sector label area per floppy disk.}}&lt;br /&gt;
&lt;br /&gt;
Although the disk is logically divided up into sectors, all I/O to the disk is done a track at a time. This allows access to the drive with no interleaving and increases the useful storage capacity by about 20 percent. Each disk drive on the system has its own buffer which holds the track data going to and from the drive.&lt;br /&gt;
&lt;br /&gt;
Normally, a read of a sector will only have to copy the data from the track buffer. If the track buffer contains another track’s data, then the buffer will first be written back to the disk (if it is “dirty”) and the new track will be read in. All track boundaries are transparent to the programmer (except for FORMAT, SEEK, and RAWREAD/RAWWRITE commands) because you give the device an offset into the disk in the number of bytes from the start of the disk. The device ensures that the correct track is brought into memory.&lt;br /&gt;
&lt;br /&gt;
The performance of the disk is greatly enhanced if you make effective use of the track buffer. The performance of sequential reads will be up to an order of magnitude greater than reads scattered across the disk. In addition, only full-sector writes on sector boundaries are supported.&lt;br /&gt;
&lt;br /&gt;
The trackdisk device is based upon a standard device structure. It has the following restrictions:&lt;br /&gt;
&lt;br /&gt;
* All reads and writes must use an io_Length that is an integer multiple of TD_SECTOR bytes (the sector size in bytes).&lt;br /&gt;
* The offset field must be an integer multiple of TD_SECTOR.&lt;br /&gt;
* The data buffer must be word-aligned.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Trackdisk Device ===&lt;br /&gt;
&lt;br /&gt;
Three primary steps are required to open the trackdisk device:&lt;br /&gt;
&lt;br /&gt;
* Create a message port by calling CreatePort(). Reply messages from the device must be directed to a message port.&lt;br /&gt;
* Create an extended I/O request structure of type IOExtTD. The IOExtTD structure is created by the CreateExtIO() function.&lt;br /&gt;
* Open the trackdisk device. Call OpenDevice(), passing it the extended I/O request.&lt;br /&gt;
&lt;br /&gt;
For the trackdisk device, the flags parameter of the OpenDevice() function specifies whether you are opening a 3.5’’ drive (flags=0) or a 5.25’’ drive (flags=1). With flags set to 0 trackdisk will only open a 3.5’’ drive. To tell the device to open any drive it understands, set the flags parameter to TDF_ALLOW_NON_3_5. (See the include file devices/trackdisk.h for more information.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/trackdisk.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *TrackMP;         /* Pointer for message port */&lt;br /&gt;
struct IOExtTD *TrackIO;         /* Pointer for IORequest */&lt;br /&gt;
&lt;br /&gt;
if (TrackMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END))&lt;br /&gt;
{&lt;br /&gt;
    if (TrackIO = (struct IOExtTD *)&lt;br /&gt;
           IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
               ASOIOR_ReplyPort, TrackMP,&lt;br /&gt;
               ASOIOR_Size, sizeof(struct IOExtTD),&lt;br /&gt;
               TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
        if (IExec-&amp;gt;OpenDevice(TD_NAME,0L,(struct IORequest *)TrackIO,Flags))&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;%s did not open\n&amp;quot;, TD_NAME);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Disk Drive Unit Numbers|text=The unit number—second parameter of the OpenDevice() call—can be any value from 0 to 3. Unit 0 is the built-in 3.5’’ disk drive. Units 1 through 3 represent additional disk drives that may be connected to an Amiga system.}}&lt;br /&gt;
&lt;br /&gt;
=== Reading from the Trackdisk Device ===&lt;br /&gt;
&lt;br /&gt;
You read from the trackdisk device by passing an IOExtTD to the device with CMD_READ set in io_Command, the number of bytes to be read set in io_Length, the address of the read buffer set in io_Data and the track you want to read—specified as a byte offset from the start of the disk—set in io_Offset.&lt;br /&gt;
&lt;br /&gt;
The byte offset of a particular track is calculated by multiplying the number of the track you want to read by the number of bytes in a track. The number of bytes in a track is obtained by multiplying the number of sectors (NUMSECS) by the number of bytes per sector (TD_SECTOR). Thus you would multiply 11 by 512 to get 5632 bytes per track. To read track 15, you would multiply 15 by 5632 giving 84,480 bytes offset from the beginning of the disk.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define TRACK_SIZE ((int32) (NUMSECS * TD_SECTOR))&lt;br /&gt;
uint8 *Readbuffer;&lt;br /&gt;
&lt;br /&gt;
if (Readbuffer = IExec-&amp;gt;AllocVecTags(TRACK_SIZE, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
{&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Readbuffer;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * track);&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Command = CMD_READ;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For reads using the enhanced read command ETD_READ, the IOExtTD is set the same as above with the addition of setting iotd_Count to the current diskchange number. The diskchange number is returned by the TD_CHANGENUM command (see below). If you wish to also read the sector label area, you must set iotd_SecLabel to a non-NULL value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Readbuffer;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * track);&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count          = change_count;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_READ;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ETD_READ and CMD_READ obey all of the trackdisk device restrictions noted above. They transfer data from the track buffer to the user’s buffer. If the desired sector is already in the track buffer, no disk activity is initiated. If the desired sector is not in the buffer, the track containing that sector is automatically read in. If the data in the current track buffer has been modified, it is written out to the disk before a new track is read.&lt;br /&gt;
&lt;br /&gt;
=== Writing to the Trackdisk Device ===&lt;br /&gt;
&lt;br /&gt;
You write to the trackdisk device by passing an IOExtTD to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length, the address of the write buffer set in io_Data and the track you want to write—specified as a byte offset from the start of the disk—set in io_Offset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define TRACK_SIZE ((int32) (NUMSECS * TD_SECTOR))&lt;br /&gt;
uint8 *Writebuffer;&lt;br /&gt;
&lt;br /&gt;
if (Writebuffer = IExec-&amp;gt;AllocVecTags(TRACK_SIZE, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
{&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Writebuffer;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * tracknum);&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Command = CMD_WRITE;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writes using the enhanced write command ETD_WRITE, the IOExtTD is set the same as above with the addition of setting iotd_Count to the current diskchange number. The diskchange number is returned by the TD_CHANGENUM command (see below). If you wish to also write the sector label area, you must set iotd_SecLabel to a non-NULL value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Writebuffer;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * tracknum);&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count          = change_count;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_WRITE;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ETD_WRITE and CMD_WRITE obey all of the trackdisk device restrictions noted above. They transfer data from the user’s buffer to the track buffer. If the track that contains this sector is already in the track buffer, no disk activity is initiated. If the desired sector is not in the buffer, the track containing that sector is automatically read in. If the data in the current track buffer has been modified, it is written out to the disk before a new track is read in for modification.&lt;br /&gt;
&lt;br /&gt;
=== Closing the Trackdisk Device ===&lt;br /&gt;
&lt;br /&gt;
As with all devices, you &#039;&#039;must&#039;&#039; close the trackdisk device when you have finished using it. To release the device, a CloseDevice() call is executed with the same IOExtTD used when the device was opened. This only closes the device and makes it available to the rest of the system. It does &#039;&#039;&#039;not&#039;&#039;&#039; deallocate the IOExtTD structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IExec-&amp;gt;CloseDevice((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced Commands ==&lt;br /&gt;
&lt;br /&gt;
=== Determining the Drive Geometry Table ===&lt;br /&gt;
&lt;br /&gt;
The layout geometry of a disk drive can be determined by using the TD_GETGEOMETRY command. The layout can be defined three ways:&lt;br /&gt;
&lt;br /&gt;
* TotalSectors&lt;br /&gt;
* Cylinders and CylSectors&lt;br /&gt;
* Cylinders, Heads, and TrackSectors&lt;br /&gt;
&lt;br /&gt;
Of the three, TotalSectors is the most accurate, Cylinders and CylSectors is less so, and Cylinders, Heads and TrackSectors is the least accurate. All are usable, though the last two may waste some portion of the available space on some drives.&lt;br /&gt;
&lt;br /&gt;
The TD_GETGEOMETRY commands returns the disk layout geometry in a DriveGeometry structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DriveGeometry&lt;br /&gt;
{&lt;br /&gt;
    ULONG dg_SectorSize;        /* in bytes */&lt;br /&gt;
    ULONG dg_TotalSectors;      /* total # of sectors on drive */&lt;br /&gt;
    ULONG dg_Cylinders;         /* number of cylinders */&lt;br /&gt;
    ULONG dg_CylSectors;        /* number of sectors/cylinder */&lt;br /&gt;
    ULONG dg_Heads;             /* number of surfaces */&lt;br /&gt;
    ULONG dg_TrackSectors;      /* number of sectors/track */&lt;br /&gt;
    ULONG dg_BufMemType;        /* preferred buffer memory type */&lt;br /&gt;
                                /* (usually MEMF_PUBLIC) */&lt;br /&gt;
    UBYTE dg_DeviceType;        /* codes as defined in the SCSI-2 spec*/&lt;br /&gt;
    UBYTE dg_Flags;             /* flags, including removable */&lt;br /&gt;
    UWORD dg_Reserved;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file devices/trackdisk.h for the complete structure definition and values for the dg_DeviceType and dg_Flags fields.&lt;br /&gt;
&lt;br /&gt;
You determine the drive layout geometry by passing an IOExtTD with TD_GETGEOMETRY set in io_Command and a pointer to a DriveGeometry structure set in io_Data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DriveGeometry *Euclid =&lt;br /&gt;
    IExec-&amp;gt;AllocVecTags(sizeof(struct DriveGeometry),&lt;br /&gt;
        AVT_Type, MEMF_SHARED,&lt;br /&gt;
        AVT_ClearWithValue, 0,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data = Euclid;     /* put layout geometry here */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_GETGEOMETRY;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_GETGEOMETRY is preferred over TD_GETNUMTRACKS for determining the number of tracks on a disk. This is because new drive types may have more sectors or different sector sizes, etc., than standard Amiga drives.&lt;br /&gt;
&lt;br /&gt;
=== Clearing the Track Buffer ===&lt;br /&gt;
&lt;br /&gt;
ETD_CLEAR and CMD_CLEAR mark the track buffer as invalid, forcing a reread of the disk on the next operation. ETD_UPDATE or CMD_UPDATE would be used to force data out to the disk before turning the motor off. ETD_CLEAR or CMD_CLEAR is usually used after having locked out the trackdisk device via the use of the disk resource, when you wish to prevent the track from being updated, or when you wish to force the track to be re-read. ETD_CLEAR or CMD_CLEAR will not do an update, nor will an update command do a clear.&lt;br /&gt;
&lt;br /&gt;
You clear the track buffer by passing an IOExtTD to the device with CMD_CLEAR or ETD_CLEAR set in io_Command. For ETD_CLEAR, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_CLEAR;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Controlling the Drive Motor ===&lt;br /&gt;
&lt;br /&gt;
ETD_MOTOR and TD_MOTOR give you control of the motor. When the trackdisk device executes this command, the old state of the motor is returned in io_Actual. If io_Actual is zero, then the motor was off. Any other value implies that the motor was on. If the motor is just being turned on, the device will delay the proper amount of time to allow the drive to come up to speed. Normally, turning the drive on is not necessary—the device does this automatically if it receives a request when the motor is off.&lt;br /&gt;
&lt;br /&gt;
However, turning the motor off &#039;&#039;is&#039;&#039; the programmer’s responsibility. In addition, the standard instructions to the user are that it is safe to remove a disk if, and only if, the motor is off (that is, if the disk light is off).&lt;br /&gt;
&lt;br /&gt;
You control the drive motor by passing an IOExtTD to the device with CMD_MOTOR or ETD_MOTOR set in io_Command and the state you want to put the motor in set in io_Length. If io_Length is set to 1, the trackdisk device will turn on the motor; a 0 will turn it off. For ETD_MOTOR, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length = 1;          /* Turn on the drive motor */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_MOTOR;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Updating a Track Sector ===&lt;br /&gt;
&lt;br /&gt;
The Amiga trackdisk device does not write data sectors unless it is necessary (you request that a different track be used) or until the user requests that an update be performed. This improves system speed by caching disk operations. The update commands ensure that any buffered data is flushed out to the disk. If the track buffer has not been changed since the track was read in, the update commands do nothing.&lt;br /&gt;
&lt;br /&gt;
You update a data sector by passing an IOExtTD to the device with CMD_UPDATE or ETD_UPDATE set in io_Command. For ETD_UPDATE, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_UPDATE;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting a Track ===&lt;br /&gt;
&lt;br /&gt;
ETD_FORMAT and TD_FORMAT are used to write data to a track that either has not yet been formatted or has had a hard error on a standard write command. TD_FORMAT completely ignores all data currently on a track and does not check for disk change before performing the command. The device will format the requested tracks, filling each sector with the contents of the buffer pointed to by io_Data field. You should do a read pass to verify the data.&lt;br /&gt;
&lt;br /&gt;
If you have a hard write error during a normal write, you may find it possible to use the TD_FORMAT command to reformat the track as part of your error recovery process. ETD_FORMAT will write the sector label area if the iotd_SecLabel is non-NULL.&lt;br /&gt;
&lt;br /&gt;
You format a track by passing an IOExtTD to the device with CMD_FORMAT or ETD_FORMAT set in io_Command, io_Data set to at least track worth of data, io_Offset field set to the byte offset of the track you want to write and the io_Length set to the length of a track. For ETD_FORMAT, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define TRACK_SIZE (NUMSECS * TD_SECTOR)&lt;br /&gt;
uint8 *Writebuffer;&lt;br /&gt;
&lt;br /&gt;
if (WriteBuffer = AllocVecTags(TRACK_SIZE, AVT_Type, MEMF_SHARED, AVT_ClearWithValue, 0, TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Data    =(APTR)Writebuffer;&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Offset  =(uint32)(TRACK_SIZE * track);&lt;br /&gt;
    DiskIO-&amp;gt;iotd_Req.io_Command = TD_FORMAT;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ejecting a Disk ===&lt;br /&gt;
&lt;br /&gt;
Certain disk drive manufacturers allow software control of disk ejection. The trackdisk device provides the TD_EJECT command to tell such drives to eject a disk.&lt;br /&gt;
&lt;br /&gt;
You eject a disk by passing an IOExtTD to the device with TD_EJECT set in io_Command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_EJECT;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Read the Instruction Manual|text=The 3.5’’ drives for the Amiga and most other Amiga drive manufacturers do not support software disk ejects. Attempting this command on those drives will result in an error condition. Consult the instruction manual for your disk drive to determine whether this is supported.}}&lt;br /&gt;
&lt;br /&gt;
== Disk Status Commands ==&lt;br /&gt;
&lt;br /&gt;
Disk status commands return status on the current disk in the opened unit. These commands may be done with quick I/O and thus may be called within interrupt handlers (such as the trackdisk disk change handler). See [[Exec_Device_I/O|Exec Device I/O]] for more detailed information on quick I/O.&lt;br /&gt;
&lt;br /&gt;
=== Determining the Presence of a Disk ===&lt;br /&gt;
&lt;br /&gt;
You determine the presence of a disk in a drive by passing an IOExtTD to the device with TD_CHANGESTATE set in io_Command. For quick I/O, you must set io_Flags to IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_CHANGESTATE;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_CHANGESTATE returns the presence indicator of a disk in io_Actual. The value returned will be zero if a disk is currently in the drive and nonzero if the drive has no disk.&lt;br /&gt;
&lt;br /&gt;
=== Determining the Write-Protect Status of a Disk ===&lt;br /&gt;
&lt;br /&gt;
You determine the write-protect status of a disk by passing an IOExtTD to the device with TD_PROTSTATUS set in io_Command. For quick I/O, you must set io_Flags to IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_PROTSTATUS;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_PROTSTATUS returns the write-protect status in io_Actual. The value will be zero if the disk is not write-protected and nonzero if the disk is write-protected.&lt;br /&gt;
&lt;br /&gt;
=== Determining the Drive Type ===&lt;br /&gt;
&lt;br /&gt;
You determine the drive type of a unit by passing an IOExtTD to the device with TD_GETDRIVETYPE set in io_Command. For quick I/O, you must set io_Flags to IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_GETDRIVETYPE;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_GETDRIVETYPE returns the drive type for the unit that was opened in io_Actual. The value will be DRIVE3_5 for 3.5’’ drives and DRIVE5_25 for 5.25’’ drives. The unit can be opened only if the device understands the drive type it is connected to.&lt;br /&gt;
&lt;br /&gt;
=== Determining the Number of Tracks of a Drive ===&lt;br /&gt;
&lt;br /&gt;
You determine the number of a tracks of a drive by passing an IOExtTD to the device with TD_GETNUMTRACKS set in io_Command. For quick I/O, you must set io_Flags to IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_GETNUMTRACKS;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_GETNUMTRACKS returns the number of tracks on that device in io_Actual. This is the number of tracks of TD_SECTOR * NUMSECS size. It is not the number of cylinders. With two heads, the number of cylinders is half of the number of tracks. The number of cylinders is equal to the number of tracks divided by the number of heads (surfaces). The standard 3.5’’ Amiga drive has two heads&lt;br /&gt;
&lt;br /&gt;
TD_GETGEOMETRY is the preferred over TD_GETNUMTRACKS especially since new drive types may have more sectors or different sector sizes, etc., than standard Amiga drives.&lt;br /&gt;
&lt;br /&gt;
=== Determining the Current Diskchange Number ===&lt;br /&gt;
&lt;br /&gt;
You determine the current diskchange number of a disk by passing an IOExtTD to the device with TD_CHANGENUM set in io_Command. For quick I/O, you must set io_Flags to IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_CHANGENUM;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TD_CHANGENUM returns the current value of the diskchange counter (as used by the enhanced commands) in io_Actual. The disk change counter is incremented each time the disk is inserted or removed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags = IOF_QUICK;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_CHANGENUM;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)DiskIO);&lt;br /&gt;
uint32 change_count = DiskIO-&amp;gt;iotd_Req.io_Actual;   /* store current diskchange value */&lt;br /&gt;
&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length = 1;          /* Turn on the drive motor */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count = change_count;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_MOTOR;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commands for Diagnostics and Repair ==&lt;br /&gt;
&lt;br /&gt;
The trackdisk device provides commands to move the drive heads to a specific track. These commands are provided for internal diagnostics, disk repair, and head cleaning only.&lt;br /&gt;
&lt;br /&gt;
=== Moving the Drive Head to a Specific Track ===&lt;br /&gt;
&lt;br /&gt;
You move the drive head to a specific track by passing an IOExtTD to the device with TD_SEEK or ETD_SEEK set in io_Command, and io_Offset set to the byte offset of the track to which the seek is to occur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset = (uint32)(TRACK_SIZE * track);&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_SEEK;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Seeking is not Reading|text=TD_SEEK and ETD_SEEK do not verify their position until the next read. That is, they only move the heads; they do not actually read any data.}}&lt;br /&gt;
&lt;br /&gt;
== Notification of Disk Changes ==&lt;br /&gt;
&lt;br /&gt;
Many programs will wish to be notified if the user has changed the disk in the active drive. While this can be done via the Intuition DISKREMOVED and DISKINSERTED messages, sometimes more tightly controlled testing is required. The trackdisk device provides commands to initiate interrupt processing when disks change.&lt;br /&gt;
&lt;br /&gt;
Those implementing their own disk change notification support in a file system or device driver should review [[Supporting_Disk_Change_Events|Supporting Disk Change Events]].&lt;br /&gt;
&lt;br /&gt;
=== Adding a Diskchange Software Interrupt Handler ===&lt;br /&gt;
&lt;br /&gt;
The trackdisk device lets you add a software interrupt handler that will be Cause()’ed when a disk insert or remove occurs. Within the handler, you may only call the status commands that can use IOF_QUICK.&lt;br /&gt;
&lt;br /&gt;
You add a software interrupt handler by passing an IOExtTD to the device with a pointer to an Interrupt structure set in io_Data, the length of the structure set in io_Length and TD_ADDCHANGEINT set in io_Command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = sizeof(struct Interrupt)&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Disk_Interrupt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_ADDCHANGEINT;&lt;br /&gt;
IExec-&amp;gt;SendIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Going, going, gone|text=This command does &#039;&#039;not&#039;&#039; return when executed. It holds onto the IORequest until the TD_REMCHANGEINT command is executed with that same IORequest. Hence, you must use SendIO() with this command.}}&lt;br /&gt;
&lt;br /&gt;
=== Removing a Diskchange Software Interrupt Handler ===&lt;br /&gt;
&lt;br /&gt;
You remove a software interrupt handler by passing the same IOExtTD to the device you used with TD_ADDCHANGEINT but with the io_Command set to TD_REMCHANGEINT. You &#039;&#039;must&#039;&#039; pass it the same Interrupt structure used to add the handler.&lt;br /&gt;
&lt;br /&gt;
For maximum compatibility, when you remove the change interrupt handler with the TD_REMCHANGEINT command you must make sure that the TD_ADDCHANGEINT command was successful, and if not, then you will have to remove the IOExtTD as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = TD_REMCHANGEINT;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&lt;br /&gt;
/* Was the TD_ADDCHANGEINT command successful? */&lt;br /&gt;
if (IExec-&amp;gt;CheckIO((struct IORequest *)DiskIO) == NULL)&lt;br /&gt;
{&lt;br /&gt;
   DiskIO-&amp;gt;io_Command = TD_REMCHANGEINT;&lt;br /&gt;
   IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
   /* The TD_ADDCHANGEINT command was not successful and has been rejected. */&lt;br /&gt;
   IExec-&amp;gt;WaitIO((struct IORequest *)DiskIO);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commands for Low-Level Access ==&lt;br /&gt;
&lt;br /&gt;
The trackdisk device provides commands to read and write raw flux changes on the disk. The data returned from a low-level read or sent via a low-level write should be encoded into some form of legal flux patterns. See the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; and books on magnetic media recording and reading.&lt;br /&gt;
&lt;br /&gt;
=== Reading Raw Data from a Disk ===&lt;br /&gt;
&lt;br /&gt;
ETD_RAWREAD and TD_RAWREAD perform a raw read from a track on the disk. They seek to the specified track and read it into the user’s buffer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;No processing of the track is done.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will appear exactly as the bits come off the disk – typically in some legal flux format (such as MFM, FM, GCR, etc; if you don’t know what these are, you shouldn’t be using this call). Caveat programmer.&lt;br /&gt;
&lt;br /&gt;
This interface is intended for sophisticated programming only. You must fully understand digital magnetic recording to be able to utilize this call. It is also important that you understand that the MFM encoding scheme used by the higher level trackdisk routines may change without notice. Thus, this routine is only really useful for reading and decoding other disks such as MS-DOS formatted disks.&lt;br /&gt;
&lt;br /&gt;
You read raw data from a disk by passing an IOExtTD to the device with TD_RAWREAD or ETD_RAWREAD set in io_Command, the number of bytes to be read set in io_Length (maximum 32K), a pointer to the read buffer set in io_Data, and io_Offset set to the byte offset of the track where you want to the read to begin. For ETD_RAWREAD, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = 1024;             /* number of bytes to read */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Readbuffer; /* pointer to buffer */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * track); /* track number */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags   = IOTDF_INDEX       /* Set for index sync */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count          = change_count;     /* diskchange number */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_RAWREAD;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A raw read may be synched with the index pulse by setting the IOTDF_INDEXSYNC flag or synched with a $4489 sync pattern by setting the IOTDF_WORDSYNC flag. See the [http://wiki.amigaos.net/amiga/autodocs/trackdisk.doc.txt trackdisk.doc] in the SDK for more information about these flags.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Forewarned is Forearmed|text=The AmigaOS development team may make enhancements to the disk format in the future. The AmigaOS development team intends to provide compatibility within the trackdisk device. Anyone who uses these raw routines is bypassing this upward-compatibility and does so at her own risk.}}&lt;br /&gt;
&lt;br /&gt;
=== Writing Raw Data to a Disk ===&lt;br /&gt;
&lt;br /&gt;
ETD_RAWWRITE and TD_RAWWRITE perform a raw write to a track on the disk. They seek to the specified track and write it from the user’s buffer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;No processing of the track is done.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will be written exactly as the bits come out of the buffer – typically in some legal flux format (such as MFM, FM, GCR; if you don’t know what these are, you shouldn’t be using this call). Caveat Programmer.&lt;br /&gt;
&lt;br /&gt;
This interface is intended for sophisticated programming only. You must fully understand digital magnetic recording to be able to utilize this call. It is also important that you understand that the MFM encoding scheme used by the higher level trackdisk routines may change without notice. Thus, this routine is only really useful for encoding and writing other disk formats such as MS-DOS disks.&lt;br /&gt;
&lt;br /&gt;
You write raw data to a disk by passing an IOExtTD to the device with TD_RAWRITE or ETD_RAWRITE set in io_Command, the number of bytes to be written set in io_Length (maximum 32K), a pointer to the write buffer set in io_Data, and io_Offset set to the byte offset of the track where you want to the write to begin. For ETD_RAWWRITE, you must also set iotd_Count to the current diskchange number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = 1024;              /* number of bytes to write */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Writebuffer; /* pointer to buffer */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * track); /* track number */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Flags   = IOTDF_INDEX        /* Set for index sync */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count          = change_count;      /* diskchange number */&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_RAWWRITE;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)DiskIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A raw write may be synched with the index pulse by setting the IOTDF_INDEXSYNC flag or synched with a $4489 sync pattern by setting the IOTDF_WORDSYNC flag. See the [http://wiki.amigaos.net/amiga/autodocs/trackdisk.doc.txt trackdisk.doc] in the SDK for more information about these flags.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Beware The Synch On Write|text=Synch on write will most likely destroy some of the bits following the synch before reliable writing begins. This is due to how the &#039;&#039;Paula&#039;&#039; chip implements this feature. Use synch on write very carefully if at all.}}&lt;br /&gt;
&lt;br /&gt;
=== Limitations for Sync’ed Reads and Writes ===&lt;br /&gt;
&lt;br /&gt;
There is a delay between the index pulse and the start of bits coming in from the drive (e.g. DMA started). It is in the range of 135-200 microseconds. This delay breaks down as follows: 55 microseconds for software interrupt overhead (this is the time from interrupt to the write of the DSKLEN register); 66 microseconds for one horizontal line delay (remember that disk I/O is synchronized with &#039;&#039;Agnus&#039;&#039;’ display fetches). The last variable (0-65 microseconds) is an additional scan line since DSKLEN is poked anywhere in the horizontal line. This leaves 15 microseconds unaccounted for. In short, you will almost never get bits within the first 135 microseconds of the index pulse, and may not get it until 200 microseconds. At 4 microsecs/bit, this works out to be between 4 and 7 bytes of user data delay.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Forewarned is Forearmed|text=The AmigaOS development team may make enhancements to the disk format in the future. The AmigaOS development team intends to provide compatibility within the trackdisk device. Anyone who uses these raw routines is bypassing this upward-compatibility and does so at her own risk.}}&lt;br /&gt;
&lt;br /&gt;
== Trackdisk Device Errors ==&lt;br /&gt;
&lt;br /&gt;
The trackdisk device returns error codes whenever an operation is attempted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Length  = TRACK_SIZE;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Data    = (APTR)Writebuffer;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Offset  = (uint32)(TRACK_SIZE * tracknum);&lt;br /&gt;
DiskIO-&amp;gt;iotd_Count          = change_count;&lt;br /&gt;
DiskIO-&amp;gt;iotd_Req.io_Command = ETD_WRITE;&lt;br /&gt;
if (IExec-&amp;gt;DoIO((struct IORequest *)DiskIO))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;ETD_WRITE failed.  Error: %ld\n&amp;quot;, DiskIO-iotd.io_Error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When an error occurs, these error numbers will be returned in the io_Error field of your IOExtTD block.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Trackdisk Device Error Codes&lt;br /&gt;
|-&lt;br /&gt;
! Error&lt;br /&gt;
! Value&lt;br /&gt;
! Explanation&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_NotSpecified || 20 || Error could not be determined&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_NoSecHdr || 21 || Could not find sector header&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadSecPreamble || 22 || Error in sector preamble&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadSecID || 23 || Error in sector identifier&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadHdrSum || 24 || Header field has bad checksum&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadSecSum || 25 || Sector data field has bad checksum&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_TooFewSecs || 26 || Incorrect number of sectors on track&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadSecHdr || 27 || Unable to read sector header&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_WriteProt || 28 || Disk is write-protected&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_DiskChanged || 29 || Disk has been changed or is not currently present&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_SeekError || 30 || While verifying seek position, found seek error&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_NoMem || 31 || Not enough memory to do this operation&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadUnitNum || 32 || Bad unit number (unit # not attached)&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_BadDriveType || 33 || Bad drive type (not an Amiga 3.5’’ disk)&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_DriveInUse || 34 || Drive already in use (only one task exclusive)&lt;br /&gt;
|-&lt;br /&gt;
| TDERR_PostReset || 35 || User hit reset; awaiting doom&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Example Trackdisk Program ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Track_Copy.c&lt;br /&gt;
 *&lt;br /&gt;
 * This program does a track by track copy from one drive to another&lt;br /&gt;
 *&lt;br /&gt;
 * This program will only run from the CLI.  If started from&lt;br /&gt;
 * the workbench, it will just exit...&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:  trackcopy  dfx dfy&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/trackdisk.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define TRACK_SIZE      ((int32)(NUMSECS * TD_SECTOR))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Turn the BUSY flag off/on for the drive&lt;br /&gt;
 * If onflag is TRUE, the disk will be marked as busy...&lt;br /&gt;
 *&lt;br /&gt;
 * This is to stop the validator from executing while&lt;br /&gt;
 * we are playing with the disks.&lt;br /&gt;
 */&lt;br /&gt;
VOID disk_busy(CONST_STRPTR drive, int32 onflag)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Inhibit(drive, onflag);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This turns the motor off&lt;br /&gt;
 */&lt;br /&gt;
VOID Motor_Off(struct IOExtTD *disk)&lt;br /&gt;
{&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Length=0;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Command=TD_MOTOR;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)disk);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This turns the motor on&lt;br /&gt;
 */&lt;br /&gt;
VOID    Motor_On(struct IOExtTD *disk)&lt;br /&gt;
{&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Length=1;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Command=TD_MOTOR;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)disk);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This reads a track, reporting any errors...&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
int16 Read_Track(struct IOExtTD *disk,UBYTE *buffer, int16 track)&lt;br /&gt;
{&lt;br /&gt;
int16 All_OK=TRUE;&lt;br /&gt;
&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Length=TRACK_SIZE;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Data=(APTR)buffer;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Command=CMD_READ;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Offset=(uint32)(TRACK_SIZE * track);&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)disk);&lt;br /&gt;
    if (disk-&amp;gt;iotd_Req.io_Error)&lt;br /&gt;
    {&lt;br /&gt;
        All_OK=FALSE;&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Error %lu when reading track %ld&amp;quot;,disk-&amp;gt;iotd_Req.io_Error,track);&lt;br /&gt;
    }&lt;br /&gt;
    return(All_OK);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This writes a track, reporting any errors...&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
int16 Write_Track(struct IOExtTD *disk,UBYTE *buffer,int16 track)&lt;br /&gt;
{&lt;br /&gt;
int16 All_OK=TRUE;&lt;br /&gt;
&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Length=TRACK_SIZE;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Data=(APTR)buffer;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Command=TD_FORMAT;&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Offset=(uint32)(TRACK_SIZE * track);&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)disk);&lt;br /&gt;
    if (disk-&amp;gt;iotd_Req.io_Error)&lt;br /&gt;
    {&lt;br /&gt;
        All_OK=FALSE;&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Error %ld when writing track %ld&amp;quot;,disk-&amp;gt;iotd_Req.io_Error,track);&lt;br /&gt;
    }&lt;br /&gt;
    return(All_OK);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This function finds the number of TRACKS on the device.&lt;br /&gt;
 * NOTE That this is TRACKS and not cylinders.  On a Two-Head&lt;br /&gt;
 * drive (such as the standard 3.5&amp;quot; drives) the number of tracks&lt;br /&gt;
 * is 160, 80 cylinders, 2-heads.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
int16 FindNumTracks(struct IOExtTD *disk)&lt;br /&gt;
{&lt;br /&gt;
    disk-&amp;gt;iotd_Req.io_Command=TD_GETNUMTRACKS;&lt;br /&gt;
    IExec-&amp;gt;DoIO((struct IORequest *)disk);&lt;br /&gt;
    return((int16)disk-&amp;gt;iotd_Req.io_Actual);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This routine allocates the memory for one track and does&lt;br /&gt;
 * the copy loop.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
VOID Do_Copy(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1)&lt;br /&gt;
{&lt;br /&gt;
UBYTE *buffer;&lt;br /&gt;
int16 track;&lt;br /&gt;
int16 All_OK;&lt;br /&gt;
int16 NumTracks;&lt;br /&gt;
&lt;br /&gt;
    if (buffer=IExe-&amp;gt;AllocVecTags(TRACK_SIZE, AVT_Type, MEMF_SHARED, TAG_END))&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot; Starting Motors\r&amp;quot;);&lt;br /&gt;
        Motor_On(diskreq0);&lt;br /&gt;
        Motor_On(diskreq1);&lt;br /&gt;
        All_OK=TRUE;&lt;br /&gt;
&lt;br /&gt;
        NumTracks=FindNumTracks(diskreq0);&lt;br /&gt;
&lt;br /&gt;
        for (track=0;(track&amp;lt;NumTracks) &amp;amp;&amp;amp; All_OK;track++)&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot; Reading track %ld\r&amp;quot;,track);&lt;br /&gt;
&lt;br /&gt;
            if (All_OK=Read_Track(diskreq0,buffer,track))&lt;br /&gt;
            {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot; Writing track %ld\r&amp;quot;,track);&lt;br /&gt;
&lt;br /&gt;
                All_OK=Write_Track(diskreq1,buffer,track);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (All_OK) IDOS-&amp;gt;Printf(&amp;quot; * Copy complete *&amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
        Motor_Off(diskreq0);&lt;br /&gt;
        Motor_Off(diskreq1);&lt;br /&gt;
        IExec-&amp;gt;FreeVec(buffer);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;No memory for track buffer...\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Prompts the user to remove one of the disks.&lt;br /&gt;
 * Since this program makes an EXACT copy of the disks&lt;br /&gt;
 * AmigaDOS would get confused by them so one must be removed&lt;br /&gt;
 * before the validator is let loose.  Also, note that the&lt;br /&gt;
 * disks may NEVER be in drives on the SAME computer at the&lt;br /&gt;
 * SAME time unless one of the disks is renamed.  This is due&lt;br /&gt;
 * to a bug in the system.  It would normally be prevented&lt;br /&gt;
 * by a diskcopy program that knew the disk format and modified&lt;br /&gt;
 * the creation date by one clock-tick such that the disks would&lt;br /&gt;
 * be different.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
VOID Remove_Disks(VOID)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nYou *MUST* remove at least one of the disks now.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nPress RETURN when ready\n&amp;quot;);&lt;br /&gt;
    while(getchar()!=&#039;\n&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Prompts the user to insert the disks.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
VOID Insert_Disks(char drive1[], char drive2[])&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nPlease insert source disk in %s\n&amp;quot;,drive1);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n          and destination in %s\n&amp;quot;,drive2);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nPress RETURN when ready\n&amp;quot;);&lt;br /&gt;
    while(getchar()!=&#039;\n&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Open the devices and mark them as busy&lt;br /&gt;
 */&lt;br /&gt;
VOID Do_OpenDevice(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1, int32 unit[])&lt;br /&gt;
{&lt;br /&gt;
char drive1[] = &amp;quot;DFx:&amp;quot;;  /* String for source drive */&lt;br /&gt;
char drive2[] = &amp;quot;DFx:&amp;quot;;  /* String for destination drive */&lt;br /&gt;
&lt;br /&gt;
    drive1[2] = unit[0]+ &#039;0&#039;;  /* Set drive number for source */&lt;br /&gt;
&lt;br /&gt;
    if (!IExec-&amp;gt;OpenDevice(TD_NAME,unit[0],(struct IORequest *)diskreq0,0L))&lt;br /&gt;
    {&lt;br /&gt;
          disk_busy(drive1,TRUE);&lt;br /&gt;
          drive2[2] = unit[1]+ &#039;0&#039;;  /* Set drive number for destination */&lt;br /&gt;
&lt;br /&gt;
        if (!IExec-&amp;gt;OpenDevice(TD_NAME,unit[1],(struct IORequest *)diskreq1,0L))&lt;br /&gt;
        {&lt;br /&gt;
            disk_busy(drive2,TRUE);&lt;br /&gt;
&lt;br /&gt;
            Insert_Disks(drive1,drive2);&lt;br /&gt;
            Do_Copy(diskreq0,diskreq1);&lt;br /&gt;
            Remove_Disks();&lt;br /&gt;
&lt;br /&gt;
            disk_busy(drive2,FALSE);&lt;br /&gt;
            IExec-&amp;gt;CloseDevice((struct IORequest *)diskreq1);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Could not open %s\n&amp;quot;,drive2);&lt;br /&gt;
&lt;br /&gt;
        disk_busy(drive1,FALSE);&lt;br /&gt;
        IExec-&amp;gt;CloseDevice((struct IORequest *)diskreq0);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Could not open %s\n&amp;quot;,drive1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int16 ParseArgs(int argc, char **argv, int32 Unit[])&lt;br /&gt;
#define OKAY 1&lt;br /&gt;
{&lt;br /&gt;
int j=1, params = OKAY;&lt;br /&gt;
char *position[]={&amp;quot;First&amp;quot;,&amp;quot;Second&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
if (argc != 3)&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nYou must specify a source and destination disk\n&amp;quot;);&lt;br /&gt;
    return(!OKAY);&lt;br /&gt;
    }&lt;br /&gt;
else if (strcmp(argv[1],argv[2]) == 0)&lt;br /&gt;
           {&lt;br /&gt;
           IDOS-&amp;gt;Printf(&amp;quot;\nYou must specify different disks for source and destination\n&amp;quot;);&lt;br /&gt;
           return(!OKAY);&lt;br /&gt;
           }&lt;br /&gt;
     else while (params == OKAY &amp;amp;&amp;amp; j&amp;lt;3)&lt;br /&gt;
           {&lt;br /&gt;
           if (strnicmp(argv[j],&amp;quot;df&amp;quot;,2)==0)&lt;br /&gt;
             {&lt;br /&gt;
             if (argv[j][2] &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; argv[j][2] &amp;lt;= &#039;3&#039; &amp;amp;&amp;amp; argv[j][3] == &#039;\0&#039;)&lt;br /&gt;
               {&lt;br /&gt;
               Unit[j-1] = argv[j][2] - 0x30;&lt;br /&gt;
               }&lt;br /&gt;
             else&lt;br /&gt;
               {&lt;br /&gt;
               IDOS-&amp;gt;Printf(&amp;quot;\n%s parameter is wrong, unit number must be 0-3\n&amp;quot;,position[j-1]);&lt;br /&gt;
               params = !OKAY;&lt;br /&gt;
               return(!OKAY);&lt;br /&gt;
               }&lt;br /&gt;
             }&lt;br /&gt;
           else&lt;br /&gt;
             {&lt;br /&gt;
             IDOS-&amp;gt;Printf(&amp;quot;\n%s parameter is wrong, you must specify a floppy device df0 - df3\n&amp;quot;,&lt;br /&gt;
                     position[j-1]);&lt;br /&gt;
             params=!OKAY;&lt;br /&gt;
             return(!OKAY);&lt;br /&gt;
             }&lt;br /&gt;
           j++;&lt;br /&gt;
           }&lt;br /&gt;
return(OKAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    int32 unit[2];&lt;br /&gt;
&lt;br /&gt;
    if (ParseArgs(argc, argv, unit))       /* Check inputs */&lt;br /&gt;
    {&lt;br /&gt;
        struct MsgPort *diskPort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
        if (diskPort != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            struct IOExtTD *diskreq0 = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
                ASOIOR_Size, sizeof(struct IOExtTD),&lt;br /&gt;
                ASOIOR_ReplyPort, diskPort,&lt;br /&gt;
                TAG_END);&lt;br /&gt;
        &lt;br /&gt;
            if (diskreq0 != NULL)&lt;br /&gt;
            {&lt;br /&gt;
                struct IOExtTD *diskreq1 = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
                    ASOIOR_Size, sizeof(struct IOExtTD),&lt;br /&gt;
                    ASOIOR_ReplyPort, diskPort,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
            &lt;br /&gt;
                if (diskreq1 != NULL)&lt;br /&gt;
                {&lt;br /&gt;
                    Do_OpenDevice(diskreq0,diskreq1, unit);&lt;br /&gt;
                    IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, diskreq1);&lt;br /&gt;
                }&lt;br /&gt;
                else IDOS-&amp;gt;Printf(&amp;quot;Out of memory\n&amp;quot;);&lt;br /&gt;
                IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, diskreq0);&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Out of memory\n&amp;quot;);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, diskPort);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Could not create diskReq port\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Only one per customer|text=Since this example program makes an exact track-for-track duplicate, AmigaDOS will get confused if both disks are in drives on the system at the same time. While the disks are inhibited, this does not cause a problem, but during normal operation, this will cause a system hang. To prevent this, you can relabel one of the disks. A commercial diskcopy program would have to understand the disk format and either relabel the disk or modify the volume creation date/time by a bit in order to make the disks look different to the system.}}&lt;br /&gt;
&lt;br /&gt;
== Additional Information on the Trackdisk Device ==&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the trackdisk device can be found in the include files and the autodocs for the trackdisk device. Both are contained in the [[Autodocs:Main|Autodocs]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!colspan=2|Trackdisk Device Information&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Includes||devices/trackdisk.h&lt;br /&gt;
|-&lt;br /&gt;
|devices/trackdisk.i&lt;br /&gt;
|-&lt;br /&gt;
|[[Autodocs:Main|Autodocs]]||[http://wiki.amigaos.net/amiga/autodocs/trackdisk.doc.txt trackdisk.doc]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Revision_4&amp;diff=12542</id>
		<title>Revision 4</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Revision_4&amp;diff=12542"/>
		<updated>2025-01-26T19:27:39Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= SANA-II Revision 4 =&lt;br /&gt;
&lt;br /&gt;
Extending the SANA-II network driver specification&amp;lt;br /&amp;gt;&lt;br /&gt;
by Olaf Barthel&lt;br /&gt;
&lt;br /&gt;
(Last updated 02-Mar-2003)&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The years have gone by without any substantial changes to the SANA-II standard being made. With good reason, since there was no apparent need to push the envelope and extend the driver specification. The last proposed change came 1997 from Holger Kruse, who suggested that commands should be added to control multicast packet reception for ranges of addresses rather than individual addresses.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve been working on a TCP/IP stack and PPP drivers to go with them for a while and found that there were some things that SANA-II did specifically not address, and which ought to be covered by it. In other areas clarification was needed. Also, discussions I had with Harald Frank suggested that the extensions made by Heinz Wrobel and Stefan Sticht in the SANA-IIR3 specification could need extending.&lt;br /&gt;
&lt;br /&gt;
The following text tries to tie all issues together and will conclude by listing problems which I think still need attention (or in other words, I didn&#039;t find a solution myself). Wherever possible, I have tried to provide a rationale for the changes I propose.&lt;br /&gt;
&lt;br /&gt;
Please feel free to comment; you can contact me through the [http://www.amigaos.net/contact AmigaOS contact form]. Note that the issues discussed in this document are just a list of proposed changes. No such features are part of any SANA-II specifications, although I&#039;d like to claim that I have tried to implement the majority of the changes listed below in my own software.&lt;br /&gt;
&lt;br /&gt;
This is a revised version of the original document I put together around October 14, 2001. For a list of changes that have been made since, please see [[#10_changes|section 10]] (&#039;Changes&#039;). This document has been updated and enhanced several times, in response to discussions with Heinz Wrobel.&lt;br /&gt;
&lt;br /&gt;
== New commands ==&lt;br /&gt;
&lt;br /&gt;
=== S2_GETPEERADDRESS ===&lt;br /&gt;
&lt;br /&gt;
As part of the negotiation process, the PPP protocol can return the addresses used by the peer (the other side of the point-to-point connection PPP establishes; typically a dial-in server) and assigned to the client establishing the connection. The SANA-II standard does not provide for a mechanism to return such information. Existing drivers therefore had to resort to other means, such as by setting global environment variables containing this information.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETPEERADDRESS -- Obtain the addresses used by the peer&lt;br /&gt;
           (server) and the client of a point-to-point connection.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Obtain the address used by the peer of a point-to-point connection and&lt;br /&gt;
        the address assigned to the local driver.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETPEERADDRESS&lt;br /&gt;
        ios2_Flags            - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
        ios2_SrcAddr      - Address assigned to the local driver&lt;br /&gt;
        ios2_DstAddr      - Address used by the peer&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The size of the address returned by S2_GETPEERADDRESS must not be&lt;br /&gt;
        different from the size returned by the S2_DEVICEQUERY command. For&lt;br /&gt;
        example, if a 32 bit IPv4 address was advertized, the driver must not&lt;br /&gt;
        return a 128 bit IPv6 address instead.&lt;br /&gt;
&lt;br /&gt;
        If the driver is unable to return the local driver address&lt;br /&gt;
        (ios2_SrcAddr) or the peer&#039;s address (ios2_DstAddr) it must fill the&lt;br /&gt;
        respective address fields with zeroes. It is legal for a driver to&lt;br /&gt;
        respond to the S2_GETPEERADDRESS command with two zero addresses (both&lt;br /&gt;
        ios2_SrcAddr and ios2_DstAddr filled with zeroes).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETPEERADDRESS 0xC002&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This command may be useful beyond the typical application (PPP) described above.&lt;br /&gt;
&lt;br /&gt;
=== S2_GETDNSADDRESS ===&lt;br /&gt;
&lt;br /&gt;
The PPP negotiation process may produce information on which domain name and NetBIOS name servers are available to the client. I think that it is doubtful that the availability of NetBIOS name servers will be useful for Amiga software (let alone whether NetBIOS name resolution has a future), which is why I suggest that a SANA-II command for returning only the domain name servers is introduced.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETDNSADDRESS -- Obtain the addresses of the primary&lt;br /&gt;
           and secondary domain name servers.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        Obtain the addresses of the domain name servers available to the&lt;br /&gt;
        client using this driver&#039;s address.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETDNSADDRESS&lt;br /&gt;
        ios2_Flags            - Supported flags are:&lt;br /&gt;
                                SANA2IOB_QUICK&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
        ios2_SrcAddr      - Address of primary domain name server&lt;br /&gt;
        ios2_DstAddr      - Address of secondary domain name server&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The size of the address returned by S2_GETPEERADDRESS must not be&lt;br /&gt;
        different from the size as returned by the S2_DEVICEQUERY command. For&lt;br /&gt;
        example, if a 32 bit IPv4 address was advertized, the driver must not&lt;br /&gt;
        return a 128 bit IPv6 address instead.&lt;br /&gt;
&lt;br /&gt;
        If the driver is unable to return the primary domain name server&lt;br /&gt;
        address (ios2_SrcAddr) or the secondary domain name server address&lt;br /&gt;
        (ios2_DstAddr) it must fill the respective address fields with zeroes.&lt;br /&gt;
        It is legal for a driver to respond to the S2_GETDNSADDRESS command&lt;br /&gt;
        with two zero addresses (both ios2_SrcAddr and ios2_DstAddr filled&lt;br /&gt;
        with zeroes).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETDNSADDRESS 0xC003&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This command may be useful beyond the typical application (PPP) described above.&lt;br /&gt;
&lt;br /&gt;
=== S2_GETEXTENDEDGLOBALSTATS ===&lt;br /&gt;
&lt;br /&gt;
There already exists a SANA-II command for querying global device statistics (&amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt;) which should be common to all kinds of networking devices. Statistics that are particular to a certain device type are intended to be returned through the &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt; command. I feel that these mechanisms both fail to cater well enough for dial-up or session-oriented networking applications such as PPP or PPPoE. Since the data structure used by &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt; is of a fixed size and not intended to accomodate for new fields, I propose to introduce a new command which uses a different data structure, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ExtDeviceStats&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2xds_Length;&lt;br /&gt;
   ULONG          s2xds_Actual;&lt;br /&gt;
&lt;br /&gt;
   S2QUAD         s2xds_PacketsReceived;&lt;br /&gt;
   S2QUAD         s2xds_PacketsSent;&lt;br /&gt;
   S2QUAD         s2xds_BadData;&lt;br /&gt;
   S2QUAD         s2xds_Overruns;&lt;br /&gt;
   S2QUAD         s2xds_UnknownTypesReceived;&lt;br /&gt;
   S2QUAD         s2xds_Reconfigurations;&lt;br /&gt;
   struct timeval s2xds_LastStart;&lt;br /&gt;
&lt;br /&gt;
   struct timeval s2xds_LastConnected;&lt;br /&gt;
   struct timeval s2xds_LastDisconnected;&lt;br /&gt;
   struct timeval s2xds_TimeConnected;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before I proceed to explain what purposes the individual members serve, a few words on the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type, which is defined as follows:&lt;br /&gt;
&amp;lt;tt&amp;gt;typedef struct { ULONG s2q_High; ULONG s2q_Low; } S2QUAD;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type stands for an unsigned 64 bit big endian integer, as expressed in ISO &#039;C&#039; terms.&lt;br /&gt;
&lt;br /&gt;
The structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the size of the data structure to be filled in and should be initialized by the caller to &amp;lt;tt&amp;gt;sizeof(struct Sana2ExtDeviceStats)&amp;lt;/tt&amp;gt;. Smaller values are permitted, but these &#039;&#039;&#039;must not&#039;&#039;&#039; be smaller than 8 (which covers the &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_Actual&amp;lt;/tt&amp;gt; members). A driver which finds an &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt; &amp;amp;lt; 8 &#039;&#039;&#039;must&#039;&#039;&#039; treat this as an error and reject the command with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Actual&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the data structure filled with information. This member is initialized by the driver and &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;amp;lt;= &amp;lt;tt&amp;gt;s2xds_Length&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_PacketsReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets that this unit has received. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_PacketsSent&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets that this unit has sent. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_BadData&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of bad packets received (i.e., hardware CRC failed). This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Overruns&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets dropped due to insufficient resources available in the network interface. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_UnknownTypesReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of packets received that had no pending read command with the appropriate packet type. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_Reconfigurations&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of network reconfigurations since this unit was last configured. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastStart&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last went on-line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last established a connection. For dial-up connections, this should be the time when the underlying serial line started to accumulate costs. &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero if the unit never managed to make a connection.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when this unit last shut down a connection. For dial-up connections, this should be the time when the underlying serial line stopped accumulating costs, e.g. when the modem&#039;s carrier signal was lost. &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero if the unit never disconnected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time this unit has been connected. For dial-up connections this should be the time between now and when the underlying serial line started accumulating costs.&lt;br /&gt;
&lt;br /&gt;
If this unit is not currently connected, then &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be set to zero. This means in particular that when the connection is lost, &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be immediately set to zero and &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must&#039;&#039;&#039; be filled in so that client software can query how long the unit was connected by subtracting &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; from &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If this unit is currently connected, &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; &#039;&#039;&#039;must never&#039;&#039;&#039; be zero; if necessary, set &amp;lt;tt&amp;gt;s2xds_TimeConnected.tv_secs=0&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_TimeConnected.tv_micros=1&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; is zero, check &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;; if the latter two are not zero, you can calculate the previous connection time by subtracting &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt; from &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The layout and semantics used by the &amp;lt;tt&amp;gt;Sana2ExtDeviceStats&amp;lt;/tt&amp;gt; data structure suggest that there is a difference between the underlying networking media (the link layer) and the state of the protocol that is running on top of it. With drivers for networking hardware such as Ethernet there was no difference between these two, but for protocols like SLIP, PPP or PPPoE there is a difference. The difference is in that a session or connection may exist for a certain time whereas the protocol running inside that session may be switched &#039;online&#039; later. The primary purpose of the &amp;lt;tt&amp;gt;s2xds_LastConnected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_LastDisconnected&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_TimeConnected&amp;lt;/tt&amp;gt; fields is to allow for cost accounting and traffic monitoring (so that, for example, a driver may be disconnected after it has been idle for a while) to be written.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_GETEXTENDEDGLOBALSTATS -- Get interface accumulated statistics;&lt;br /&gt;
           updated version.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command causes the device driver to retrieve various global&lt;br /&gt;
        runtime statistics for this network interface. The format of the data&lt;br /&gt;
        returned is as follows:&lt;br /&gt;
&lt;br /&gt;
           struct Sana2ExtDeviceStats&lt;br /&gt;
           {&lt;br /&gt;
              ULONG s2xds_Length;&lt;br /&gt;
              ULONG s2xds_Actual;&lt;br /&gt;
&lt;br /&gt;
              S2QUAD s2xds_PacketsReceived;&lt;br /&gt;
              S2QUAD s2xds_PacketsSent;&lt;br /&gt;
              S2QUAD s2xds_BadData;&lt;br /&gt;
              S2QUAD s2xds_Overruns;&lt;br /&gt;
              S2QUAD s2xds_UnknownTypesReceived;&lt;br /&gt;
              S2QUAD s2xds_Reconfigurations;&lt;br /&gt;
              struct timeval s2xds_LastStart;&lt;br /&gt;
&lt;br /&gt;
              struct timeval s2xds_LastConnected;&lt;br /&gt;
              struct timeval s2xds_LastDisconnected;&lt;br /&gt;
              struct timeval s2xds_TimeConnected;&lt;br /&gt;
           };&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_GETEXTENDEDGLOBALSTATS&lt;br /&gt;
        ios2_StatData         - Pointer to Sana2ExtDeviceStats structure&lt;br /&gt;
                                to fill in&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_GETEXTENDEDGLOBALSTATS 0xC004&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2_CONNECT ===&lt;br /&gt;
&lt;br /&gt;
The driver model specified by the SANA-II standard really only covers networking hardware well. Software-only drivers, such as for dial-up networking, are, well, somehow mentioned in the standard, but they don&#039;t receive much attention. In particular, this means that a networking driver is assumed to be practically always attached to its link layer and no provisions exist to specify what kind of link layer that might be and how it might be accessed. This small oversight can probably be explained by the fact that at the time the SANA-II standard was adopted, dial-up networking had not yet gained the prominence it has today.&lt;br /&gt;
&lt;br /&gt;
To bridge this gap, I propose a new command which will make a driver connect to its link layer and go online, which uses the following data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2Connection&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2c_Size;&lt;br /&gt;
   struct MinList s2c_Options;&lt;br /&gt;
   struct Hook    s2c_ErrorHook;&lt;br /&gt;
   struct Hook    s2c_ConnectHook;&lt;br /&gt;
   struct Hook    s2c_DisconnectHook;&lt;br /&gt;
   STRPTR         s2c_Login;&lt;br /&gt;
   STRPTR         s2c_Password;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The individual structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the entire data structure is stored here. This value &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;amp;gt;= 84. Smaller values &#039;&#039;&#039;must&#039;&#039;&#039; be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;. The purpose of &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt; is to allow for future expansion during which the structure may grow in size.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This list contains options, to be used during the connection process. Each node has the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ConnectionOption&lt;br /&gt;
{&lt;br /&gt;
   struct MinNode s2co_MinNode;&lt;br /&gt;
   STRPTR         s2co_Name;&lt;br /&gt;
   STRPTR         s2co_Value;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;s2co_Name&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2co_Value&amp;lt;/tt&amp;gt; entries point to &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated strings, which contain the name and the value of a parameter. &#039;&#039;Note that for numeric values, the respective number will be encoded in a text string.&#039;&#039; A number of parameters are reserved, which are [[#s2c_options|listed later]] in this text.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called whenever an error message is to be reported during the connection/disconnection process. The hook function is invoked using the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;error_hook_func(hook,reserved,message);&lt;br /&gt;
&lt;br /&gt;
VOID error_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                     STRPTR message);&amp;lt;/pre&amp;gt;&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;message&amp;lt;/tt&amp;gt; parameter points to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string. It &#039;&#039;&#039;must not&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Because the hook function may have to allocate memory, it &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called when the link level device has been set up, but further initializations are necessary, such as telling a modem to dial out. The hook function is invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
success = connect_hook_func(hook,reserved,s2cm);&lt;br /&gt;
&lt;br /&gt;
BOOL connect_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                       struct Sana2ConnectionMessage *s2cm);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;s2cm&amp;lt;/tt&amp;gt; parameter points to a data structure, as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ConnectionMessage&lt;br /&gt;
{&lt;br /&gt;
   ULONG                    s2cm_Size;&lt;br /&gt;
   struct Sana2Connection * s2cm_Connection;&lt;br /&gt;
   struct IORequest *       s2cm_Request[2];&lt;br /&gt;
   LONG                     s2cm_RequestType;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this structure, the members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Size&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of this data structure; it &#039;&#039;&#039;must&#039;&#039;&#039; be at least 20 bytes in size. The purpose of &amp;lt;tt&amp;gt;s2cm_Size&amp;lt;/tt&amp;gt; is to allow for future expansion, which may cause the size of this structure to grow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Connection&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This points back to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure which the hook that was invoked with the &amp;lt;tt&amp;gt;Sana2ConnectionMessage&amp;lt;/tt&amp;gt; is embedded in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_Request&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here you will find two I/O requests which can be used for reading and writing data to the link layer. These pointers &#039;&#039;&#039;must not&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; and they &#039;&#039;&#039;must&#039;&#039;&#039; refer to different I/O requests, it is not permitted to pass the same request twice.&lt;br /&gt;
&lt;br /&gt;
The dialer can use these requests for communicating with the modem, but it is also permitted to clone these requests by creating new I/O requests of the same size, copying the original contents and filling in different reply ports.&lt;br /&gt;
&lt;br /&gt;
There is a danger in that the hook code may not receive the right kind of I/O request, which is why the &amp;lt;tt&amp;gt;s2cm_RequestType&amp;lt;/tt&amp;gt; field identifies the kind of device the requests were created for.&lt;br /&gt;
&lt;br /&gt;
When the hook function returns, it &#039;&#039;&#039;must&#039;&#039;&#039; make sure that none of the I/O requests are still pending, i.e. asynchronous I/O &#039;&#039;&#039;must&#039;&#039;&#039; have been stopped.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2cm_RequestType&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This identifies the type of device the I/O requests passed in &amp;lt;tt&amp;gt;s2cm_Request&amp;lt;/tt&amp;gt; were created for. Possible values for this entry come from the New Style Device specification, e.g. &amp;lt;tt&amp;gt;NSDEVTYPE_SERIAL&amp;lt;/tt&amp;gt; for a &amp;lt;tt&amp;gt;serial.device&amp;lt;/tt&amp;gt;-like device or &amp;lt;tt&amp;gt;NSDEVTYPE_SANA2&amp;lt;/tt&amp;gt; for a networking driver.&lt;br /&gt;
&lt;br /&gt;
The hook function &#039;&#039;&#039;must&#039;&#039;&#039; return &amp;lt;tt&amp;gt;TRUE&amp;lt;/tt&amp;gt; if the connection could be established, and &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; otherwise. Note that it is &#039;&#039;not&#039;&#039; sufficient to just return &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; in case of failure. Your code &#039;&#039;&#039;must&#039;&#039;&#039; have called the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an explanation why things went wrong first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This hook is called by &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; when the connection could not be established (the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; returned &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt;), or by &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt;, shortly before the link level device is to be closed. The hook function is invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disconnect_hook_func(hook,reserved,s2cm);&lt;br /&gt;
&lt;br /&gt;
VOID disconnect_hook_func(struct Hook *hook,APTR reserved,&lt;br /&gt;
                          struct Sana2ConnectionMessage *s2cm);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;s2cm&amp;lt;/tt&amp;gt; parameter points to a data structure, as was described for the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The purpose of these entries is to transport the authentication information the protocol may require. These entries are either &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; or contain pointers to &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated strings. If &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;, then both login and password &#039;&#039;&#039;must&#039;&#039;&#039; be assumed to be empty. If &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; is not &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;, then the password &#039;&#039;&#039;must&#039;&#039;&#039; be assumed to be empty.&lt;br /&gt;
&lt;br /&gt;
The list of options in &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; supplies the necessary information on how the driver is to connect to the link layer. Each node contains an option, which bears a name and contains a value. This pair is what I call a &#039;&#039;parameter&#039;&#039;. A number of parameter names are reserved, as will be listed below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.device&lt;br /&gt;
&lt;br /&gt;
Name of device driver to use for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;serial.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.unit&lt;br /&gt;
&lt;br /&gt;
Device unit number to use for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp&amp;lt;/tt&amp;gt;.async.speed&lt;br /&gt;
&lt;br /&gt;
Transmission speed to use for asynchronous PPP in bits per second.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;115200&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.buffersize&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Receive buffer size for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;50000&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.checkcarrier&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether the carrier signal of the link layer should be tested or not. This can be either 0 (do not test) or 1 (test the carrier signal).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.rtscts&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not hardware handshaking should be used by the link layer. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.shared&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the link layer device should be opened in shared mode. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.nullmodem&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the link layer is a direct connection, such as a nullmodem. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.eof&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the underlying serial device driver&#039;s &#039;EOF mode&#039; should be enabled. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.readrequests&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of read requests to be used for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.writerequests&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of write requests to be used for asynchronous PPP.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.accm&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Asynchronous control character map, expressed as a hexadecimal value.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;$000A0000&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.pfc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not protocol field compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.aacfc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not address and control field compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.vjhc&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not Van Jacobson header compression should be used. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.ignorefcs&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not frame check sequences should be ignored upon reception. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.initialize&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem initialization command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;AT\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.dial&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem dial command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;ATD12345\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.dialtimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The dial timeout, i.e. the number of seconds to wait after the dial command has been sent for the modem to establish a connection.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;60&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.async.hangup&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The modem hangup command, with embedded control sequences, if possible.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;ATH0\r&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.idletimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of seconds the local host may remain idle, i.e. send no data to the peer, before a watchdog timeout elapses and proceeds to verify that the line is still operational.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;30&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.localaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to assign to the local host, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.remoteaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to assume for the peer, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dns1address&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to be used by the primary domain name server, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dns2address&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IP address to be used by the secondary domain name server, as part of the PPP negotiation process. This must be given in dotted decimal notation (RFC1700).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1.2.3.4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxfail&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of negative configuration acknowledgements to be sent before the PPP negotiation process switches to reject those options.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;5&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxterm&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of termination requests to be sent before the respective PPP network or link protocol gives up.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.maxconfig&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum number of configuration requests to be sent before the respective PPP network or link protocol gives up.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;10&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.timeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of seconds that have to pass before the respective PPP network or link protocol will retry to do whatever didn&#039;t work during the last attempt.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.mtu&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The maximum transmission unit to use.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1500&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.peeridletimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of seconds the peer may remain idle, i.e. send no data to the local host, before a watchdog timeout elapses and proceeds to verify that the line is still operational.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;30&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.rejectpap&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the Password Authentication Protocol should be accepted, if offered by the peer. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (accept) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (reject).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.sendid&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A flag which controls whether the local host should send LCP identification packets or not. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.pap.timeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Password Authentication Protocol requires that the server answers to the client&#039;s request to log in. The server may be unable to respond immediately, which means that the client will have to repeat its request. A short delay should separate each request sent, such as three seconds.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.pap.retry&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the client does not manage to authenticate with the server immediately, it may resend the authentication request several times. But the attempts have to stop eventually, such as after having resent the message ten times.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;10&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.dummyremoteaddress&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the PPP driver should make up an IP address if the peer refuses to state its own IP address.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;logfile&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of a log file to create. If the file already exists, then new data should be appended to it.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;t:logfile&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;logoptions&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A list of options which control what exactly should be logged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name of device driver to use for PPPoE (PPP over Ethernet).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;a2065.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.unit&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Device unit number to use for PPPoE.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.raw&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not raw link layer frames should be constructed for transmission or not. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.bypass&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whether or not the IP packet transmission and reception should bypass several copying steps. This can be either &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (off) or &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; (on).&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.readpackets&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of read requests to queue for the link layer.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.writepackets&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of write requests to queue for the link layer.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;16&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.service&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of the PPPoE service to request.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.ac&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name of the PPPoE access concentrator to request.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ppp.ethernet.connecttimeout&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of seconds to wait for the PPPoE server to allow a session to be opened.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this list of parameters may suggest that the command described above can be used solely with the PPP protocol, do not let that put you off. This list is merely the starting point, but it is not set in stone that it cannot be extended.&lt;br /&gt;
&lt;br /&gt;
The names of the parameters are not case sensitive. As the names suggest, the name space itself is hierarchic in construction, i.e. everything related to the PPP protocol bears a name starting with the letters &#039;ppp&#039; with the dot &#039;.&#039; separating the individual items. By this rule, ppp.async refers to options that concern asynchronous PPP and ppp.ethernet to options that concern PPP over Ethernet wire.&lt;br /&gt;
&lt;br /&gt;
To add your own parameter, register it with the maintainer of the SANA-II standard or prefix its name with the letters &#039;x-&#039;. For example, to use your own kind of &#039;ppp.ethernet.connecttimeout&#039; parameter, change the name of the last component like this: ppp.ethernet.x-connecttimeout. No officially-registered parameter will ever begin with the prefix &#039;x-&#039;.&lt;br /&gt;
&lt;br /&gt;
The command should work as follows:&lt;br /&gt;
&lt;br /&gt;
# The client must set up the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure, initialize the &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields.&lt;br /&gt;
# The connection options must be filled in, which means that nodes containing the respective information must be stored in the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list. The client must make sure that the syntax of the parameters conforms to the specifications described above.&lt;br /&gt;
# A pointer to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure is placed in the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of an &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;, the command is set to &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and the request is sent via &amp;lt;tt&amp;gt;DoIO()&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SendIO()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver receives the request and begins to examine the contents of the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list, as provided in the data structure pointed to by the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of the request. Unknown options are ignored, options whose values do not conform to the syntax specification are rejected; this is done by calling the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an error message referring to the option in question and by returning the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an error code of &amp;lt;tt&amp;gt;S2ERR_BAD_ARGUMENT&amp;lt;/tt&amp;gt; and wire error code of &amp;lt;tt&amp;gt;S2WERR_INVALID_OPTION&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# If the options are all in good order, the driver proceeds to verify that all mandatory options are provided. If this is not the case, the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; is called with an error message referring to the option in question and the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an error code of &amp;lt;tt&amp;gt;S2ERR_BAD_ARGUMENT&amp;lt;/tt&amp;gt; and wire error code of &amp;lt;tt&amp;gt;S2WERR_MISSING_OPTION&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver proceeds to do its local initialization, which involves opening the link layer device, etc. If this initialization fails, the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; is called with an error message referring to the problem and the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an appropriate error code.&lt;br /&gt;
# When the initialization has finished, the driver may invoke the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; callback. Some drivers may require this, such as asynchronous PPP, some may not, such as PPPoE. The purpose of the hook function is to give the client a chance to perform modem initializations and connect to the peer. If the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; cannot perform its duties, it has to invoke the &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt; with an error message and eventually return &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt;. If &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; is returned, the driver must invoke the &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, reverse any initializations it had made and eventually returned the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an appropriate error code. If the &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt; returned &amp;lt;tt&amp;gt;TRUE&amp;lt;/tt&amp;gt;, then the driver must proceed with the actions that require that the link layer is operational. A protocol negotiation may follow, which, if successful, will make the driver return the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; with an error code of zero, indicating success. If successful, the SANA-II events &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; must be sent.&lt;br /&gt;
# The command will eventually return, but the client &#039;&#039;&#039;must not&#039;&#039;&#039; release the memory allocated for the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure and the option nodes in the &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt; list. This is because the driver may have to invoke the &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt; hook due to the connection shutting down on its own accord.&lt;br /&gt;
&lt;br /&gt;
The connect and disconnect hook functions &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code. For each hook only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority must be assumed. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_CONNECT -- Establish a link layer connection and go&lt;br /&gt;
            online.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command is for use by networking devices which require&lt;br /&gt;
        a special link layer device to transmit their data, such as&lt;br /&gt;
        an asynchronous serial line and need to know about the&lt;br /&gt;
        configuration parameters necessary to open the connection.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_CONNECT&lt;br /&gt;
        ios2_Data             - Pointer to Sana2Connection structure&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If successful, this command implies S2_ONLINE, i.e. the&lt;br /&gt;
        link layer is allocated and used by the driver.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Connection structure must be valid&lt;br /&gt;
        until the connection is eventually shut down. The driver will&lt;br /&gt;
        need to cache it, so it must not be deallocated or otherwise&lt;br /&gt;
        modified.&lt;br /&gt;
&lt;br /&gt;
        Note that S2_ONLINE does not necessarily imply S2_CONNECT, if&lt;br /&gt;
        the S2_CONNECT command is listed as supported by the driver via&lt;br /&gt;
        NSCMD_DEVICEQUERY. If S2_CONNECT is not listed as supported,&lt;br /&gt;
        S2_ONLINE obviously implies connect functionality.&lt;br /&gt;
&lt;br /&gt;
        S2_CONNECT/S2_DISCONNECT do not nest.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_DISCONNECT&amp;lt;/pre&amp;gt;&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_CONNECT 0xC005&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2_DISCONNECT ===&lt;br /&gt;
&lt;br /&gt;
This command complements &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; in that it tears down a connection. It uses the same &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; structure and hooks, but most of these members are ignored.&lt;br /&gt;
&lt;br /&gt;
The command should work as follows:&lt;br /&gt;
&lt;br /&gt;
# The client must set up the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure, initialize the &amp;lt;tt&amp;gt;s2c_Size&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ErrorHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields. The &amp;lt;tt&amp;gt;s2c_Options&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_ConnectHook&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2c_Login&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2c_Password&amp;lt;/tt&amp;gt; fields will be ignored, but the client should play things safe.&lt;br /&gt;
# A pointer to the &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure is placed in the &amp;lt;tt&amp;gt;ios2_Data&amp;lt;/tt&amp;gt; member of an &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;, the command is set to &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and the request is sent via &amp;lt;tt&amp;gt;DoIO()&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SendIO()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# The driver receives the request and proceeds to reverse the steps that previously allowed it to establish a connection. This includes telling the peer to shut down the link, but it does not include cleaning up the link layer device access, i.e. no I/O requests used for accessing a modem may be shut down yet.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;s2c_DisconnectHook&amp;lt;/tt&amp;gt; may be invoked with the proper parameters. Some drivers, such as for asynchronous PPP, will need the hook to tell the modem to hang up the line. Some drivers, such as for PPPoE, may not need this hook and thus ignore it.&lt;br /&gt;
# The initialization is reversed completely, all resources allocated when the connection was previously opened are released. The &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt; is returned with an error code of zero, indicating success. The SANA-II event &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; must be sent, and, if necessary, &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The connect and disconnect hook functions must not be called from interrupt code. For each hook only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority must be assumed. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_DISCONNECT -- Go offline and close a link layer connection&lt;br /&gt;
            previously established with S2_CONNECT.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command complements the S2_CONNECT command in that it&lt;br /&gt;
        reverses the steps taken to establish a link layer connection.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_DISCONNECT&lt;br /&gt;
        ios2_Data             - Pointer to Sana2Connection structure&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
        ios2_WireError    - More specific error number&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If successful, this command implies S2_OFFLINE, i.e. the&lt;br /&gt;
        link layer is deallocated.&lt;br /&gt;
&lt;br /&gt;
        The driver must ignore the S2_DISCONNECT command and&lt;br /&gt;
        recover gracefully if the S2_CONNECT was never sent or&lt;br /&gt;
        returned with an error.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Connection structure are valid only&lt;br /&gt;
        until the device driver has processed the command and returned&lt;br /&gt;
        the IOSana2Req. Any data the driver may need to retain beyond&lt;br /&gt;
        that point of time must be copied.&lt;br /&gt;
&lt;br /&gt;
        Once the S2_DISCONNECT command has returned, it is safe to dispose&lt;br /&gt;
        of the Sana2Connection structure provided at S2_CONNECT time.&lt;br /&gt;
&lt;br /&gt;
        Note that S2_OFFLINE does not necessarily imply S2_DISCONNECT, if&lt;br /&gt;
        the S2_DISCONNECT command is listed as supported by the driver via&lt;br /&gt;
        NSCMD_DEVICEQUERY. If S2_DISCONNECT is not listed as supported,&lt;br /&gt;
        S2_OFFLINE obviously implies disconnect functionality.&lt;br /&gt;
&lt;br /&gt;
        S2_CONNECT/S2_DISCONNECT do not nest.&lt;br /&gt;
&lt;br /&gt;
   SEE ALSO&lt;br /&gt;
        S2_CONNECT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_DISCONNECT 0xC006&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2_SAMPLE_THROUGHPUT ===&lt;br /&gt;
&lt;br /&gt;
The SANA-II standard already allows for statistics to be returned on the amount of data that has passed through a driver. Unfortunately, that information is not very accurate in that no information is provided on the time span in which the data was accumulated. Such information would be helpful in trying to determine as accurately as possible how large the data throughput actually is. I therefore propose a new command which can be used to obtain that information, which uses the following data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2ThroughputStats&lt;br /&gt;
{&lt;br /&gt;
   ULONG          s2ts_Length;&lt;br /&gt;
   ULONG          s2ts_Actual;&lt;br /&gt;
&lt;br /&gt;
   struct Task *  s2ts_NotifyTask;&lt;br /&gt;
   ULONG          s2ts_NotifyMask;&lt;br /&gt;
&lt;br /&gt;
   struct timeval s2ts_StartTime;&lt;br /&gt;
   struct timeval s2ts_EndTime;&lt;br /&gt;
   S2QUAD         s2ts_BytesSent;&lt;br /&gt;
   S2QUAD         s2ts_BytesReceived;&lt;br /&gt;
   S2QUAD         s2ts_Updates;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before I proceed to explain what purposes the individual members serve, a few words on the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type, which is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;typedef struct { ULONG s2q_High; ULONG s2q_Low; } S2QUAD;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type stands for an unsigned 64 bit big endian integer, as expressed in ISO &#039;C&#039; terms.&lt;br /&gt;
&lt;br /&gt;
The structure members have the following purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the size of the data structure to be filled in and should be initialized by the caller to &amp;lt;tt&amp;gt;sizeof(struct Sana2ThroughputStats)&amp;lt;/tt&amp;gt;. Smaller values are permitted, but these must not be smaller than 8 (which covers the &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2ts_Actual&amp;lt;/tt&amp;gt; members). A driver which finds an &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt; &amp;amp;lt; 8 must treat this as an error and reject the command with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;=&amp;lt;tt&amp;gt;IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Actual&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The size of the data structure filled with information. This member is initialized by the driver and must be &amp;amp;lt;= &amp;lt;tt&amp;gt;s2ts_Length&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_NotifyTask&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; to notify whenever the contents of this data structure change. This must be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; if no notification is desired.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; This feature should be used carefully, as so much data may arrive that the driver will almost be constantly signalling this &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; that a change has taken place.&lt;br /&gt;
&lt;br /&gt;
It is recommend that periodic polling be used, such as to update displays of a link monitoring program.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_NotifyMask&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The signal mask to use for notifying the &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; whose address is found in &amp;lt;tt&amp;gt;s2ts_NotifyTask&amp;lt;/tt&amp;gt; (via &amp;lt;tt&amp;gt;Signal(s2ts-&amp;amp;gt;s2ts_NotifyTask,s2ts-&amp;amp;gt;s2ts_NotifyMask);&amp;lt;/tt&amp;gt;). This must be zero if no notification is desired.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_StartTime&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when the driver started to fill in this data structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_EndTime&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The time when the driver last updated the contents of this data structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_BytesSent&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total number of bytes sent since the driver started to fill in this data structure. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_BytesReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total number of bytes received since the driver started to fill in this data structure. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2ts_Updates&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Number of times the driver has updated this data structure. This value will increase with every change. This is a 64 bit integer.&lt;br /&gt;
&lt;br /&gt;
A driver implementing this command should take care to update the members &amp;lt;tt&amp;gt;s2ts_EndTime&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2ts_BytesSent&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2ts_BytesReceived&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2ts_Updates&amp;lt;/tt&amp;gt; atomically each time changes are made.&lt;br /&gt;
&lt;br /&gt;
I propose a command with the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_SAMPLE_THROUGHPUT -- Obtain accurate information on&lt;br /&gt;
           driver data throughput.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This command installs a data structure which is updated every time&lt;br /&gt;
        data is sent or received by the driver.&lt;br /&gt;
&lt;br /&gt;
        This command must be sent via SendIO() or BeginIO(); until&lt;br /&gt;
        the associated I/O request is recalled using AbortIO(), the&lt;br /&gt;
        device unit will continue to update the Sana2ThroughputStats&lt;br /&gt;
        structure in real time.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command          - S2_SAMPLE_THROUGHPUT&lt;br /&gt;
        ios2_StatData         - Pointer to Sana2ThroughputStats structure&lt;br /&gt;
                                to fill in&lt;br /&gt;
        ios2_BufferManagement - Magic cookie as returned when opening the&lt;br /&gt;
                                driver with a struct IOSana2Req&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Req.io_Error - Zero if successful; non-zero otherwise&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        If this device driver does not understand this command,&lt;br /&gt;
        it will immediately return the IOSana2Req with&lt;br /&gt;
        ios2_Req.io_Error set to IOERR_NOCMD. Otherwise, the request will&lt;br /&gt;
        remain queued until it is removed with AbortIO() later.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I propose that the following command number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_SAMPLE_THROUGHPUT 0xC007&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Annotations for existing commands ==&lt;br /&gt;
&lt;br /&gt;
=== S2_ONLINE and S2_OFFLINE ===&lt;br /&gt;
&lt;br /&gt;
This proposal introduces two new commands (&amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt;) which are somewhat related to the &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; commands. How this relation works out shall be explained below. Note that the following text assumes that both the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; command pairs are implemented.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; implies &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and, if successful, may report &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; events. If the unit is currently disconnected, but still online, only the &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; event shall be sent. Invoking the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command on a driver which is already connected must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_CONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; implies &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; and, if successful, may report &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; events. If the unit is curently connected and offline, then only the &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; event shall be sent. Invoking the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command on a driver which is already disconnected must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_DISCONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; may be used after the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; has successfully connected the unit. In this case the driver will release control over the link layer and report the &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt; event. The connection established using the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; will, however, persist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; may be used after &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; has successfully connected the unit and the &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; was used. In this case the driver will again try to obtain control over the link layer and report the &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt; event if successful.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command was never successfully executed, then the commands &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=S2ERR_BAD_STATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ios2_WireError=S2WERR_UNIT_DISCONNECTED&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Extensions for existing commands ==&lt;br /&gt;
&lt;br /&gt;
=== S2_DEVICEQUERY ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure filled in by the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt; command provides for information on the device&#039;s properties, including the maximum transmission unit (MTU) that may be used. What is not specifically covered is the &#039;raw&#039; MTU a device may offer. In this context &#039;raw&#039; means the number of bytes that are available for reading and writing when using the &amp;lt;tt&amp;gt;SANA2IOB_RAW&amp;lt;/tt&amp;gt; flag with a &amp;lt;tt&amp;gt;CMD_READ&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CMD_WRITE&amp;lt;/tt&amp;gt; request on a device that supports these access methods. Currently, software developers can only make assumptions on how many bytes might comprise the &#039;raw&#039; MTU by checking the &amp;lt;tt&amp;gt;Sana2DeviceQuery.HardwareType&amp;lt;/tt&amp;gt; member and hoping that the driver supports raw &amp;lt;tt&amp;gt;CMD_READ&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CMD_WRITE&amp;lt;/tt&amp;gt; access.&lt;br /&gt;
&lt;br /&gt;
I propose that the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt; command and the associated &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure are extended to allow for the raw MTU to be queried. The new &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Sana2DeviceQuery&lt;br /&gt;
{&lt;br /&gt;
    /*&lt;br /&gt;
    ** Standard information&lt;br /&gt;
    */&lt;br /&gt;
    ULONG SizeAvailable;    /* bytes available */&lt;br /&gt;
    ULONG SizeSupplied;     /* bytes supplied */&lt;br /&gt;
    LONG  DevQueryFormat;   /* this is type 0 */&lt;br /&gt;
    LONG  DeviceLevel;      /* this document is level 0 */&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
    ** Common information&lt;br /&gt;
    */&lt;br /&gt;
    UWORD AddrFieldSize;    /* address size in bits */&lt;br /&gt;
    ULONG MTU;              /* maximum packet data size */&lt;br /&gt;
    LONG  bps;              /* line rate (bits/sec) */&lt;br /&gt;
    LONG  HardwareType;     /* what the wire is */&lt;br /&gt;
    ULONG RawMTU;           /* maximum raw packet data size */&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
    ** Format specific information&lt;br /&gt;
    */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is new. Devices which do not know and support this structure member may fill in the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure only up to and including the &amp;lt;tt&amp;gt;HardwareType&amp;lt;/tt&amp;gt; member.&lt;br /&gt;
&lt;br /&gt;
Devices which know and support the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member must fill it with a well-defined value. For Amiga Ethernet drivers, that value would be 1514, which is the standard MTU value of 1500 bytes plus the size of the Ethernet frame header, as per RFC894 (six bytes for the destination address, six bytes for the source address and two bytes for the frame type; the eight byte preamble and the terminating four byte CRC value are typically not under the control of the driver). Drivers which do not support raw read or write access must set the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member to zero.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is not provided, all bets are off and the application software must fall back to making estimates based upon the hardware type and the raw frame types it wishes to read and write. Ultimatively, the driver itself must decide whether it can accept raw read and write commands (or has to reject them with &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt;) and whether the raw packet size is still covered by the underlying hardware MTU (or must be rejected with &amp;lt;tt&amp;gt;S2ERR_MTU_EXCEEDED&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
A word of warning: a little testing with various Ethernet hardware drivers has revealed that the A2065 driver &amp;lt;tt&amp;gt;a2065.device&amp;lt;/tt&amp;gt; does not handle the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt; command properly if the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure provided is larger than 30 bytes. In other words, the command will fail if the proposed &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt; member is present in the query data structure to be filled in.&lt;br /&gt;
&lt;br /&gt;
== New events ==&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_CONFIGCHANGED ===&lt;br /&gt;
&lt;br /&gt;
The SANA-II standard does not cover protocols or devices which can change their properties during operation, such as the hardware address of the underlying driver. For drivers such as those which implement the proposed &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt; commands it is vital that such changes can take place and be noticed by the client software. For this purpose I propose that a new event type is introduced, to be used with the SANA-II &amp;lt;tt&amp;gt;S2_ONEVENT&amp;lt;/tt&amp;gt; command, using the following definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_CONFIGCHANGED (1L&amp;amp;lt;&amp;amp;lt;8)&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This event should be triggered whenever client-visible configuration information changes, as can be queried via the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSTATIONADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt; commands. Here is a short list of what could change:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;AddrFieldSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;MTU&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;BPS&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_GETSTATIONADDRESS&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;Reconfigurations&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;LastStart&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ios2_DstAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ios2_SrcAddr&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ios2_DstAddr&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The purpose of this event &#039;&#039;&#039;is not&#039;&#039;&#039; to post a notification whenever another byte or event counter has changed so that a monitoring program may update its display. The purpose &#039;&#039;&#039;is&#039;&#039;&#039; to convey to the client software that an important device configuration option has changed and that it is supposed to react and adapt to it. For example, a TCP/IP stack may, upon learning that a device&#039;s IP address has changed, rebuild its routing table.&lt;br /&gt;
&lt;br /&gt;
Since the &amp;lt;tt&amp;gt;S2EVENT_CONFIGCHANGED&amp;lt;/tt&amp;gt; event may arrive at any time and does not indicate what exactly has changed, application software should query the information it expects to change during its life time, and keep a copy of it around for later reference. When the &amp;lt;tt&amp;gt;S2EVENT_CONFIGCHANGED&amp;lt;/tt&amp;gt; event arrives, it can compare the contents of the copy against the current state of affairs and act according to the differences it finds.&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_CONNECT ===&lt;br /&gt;
&lt;br /&gt;
This event is a counterpart to &amp;lt;tt&amp;gt;S2EVENT_ONLINE&amp;lt;/tt&amp;gt;, and is associated with the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command. I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_CONNECT (1L&amp;amp;lt;&amp;amp;lt;9) /* Driver has opened session */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The event is to be sent when the driver has successfully established a link layer connection.&lt;br /&gt;
&lt;br /&gt;
=== S2EVENT_DISCONNECT ===&lt;br /&gt;
&lt;br /&gt;
This event is a counterpart to &amp;lt;tt&amp;gt;S2EVENT_OFFLINE&amp;lt;/tt&amp;gt;, and is associated with the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command. I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2EVENT_DISCONNECT (1L&amp;amp;lt;&amp;amp;lt;10) /* Driver has closed session */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The event is to be sent when the driver has closed the link layer connection previously established by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command.&lt;br /&gt;
&lt;br /&gt;
== New wire error codes ==&lt;br /&gt;
&lt;br /&gt;
=== S2WERR_UNIT_DISCONNECTED ===&lt;br /&gt;
&lt;br /&gt;
This error code is a counterpart to &amp;lt;tt&amp;gt;S2WERR_UNIT_OFFLINE&amp;lt;/tt&amp;gt;. It indicates that the associated command could not be executed because the link layer is not connected.&lt;br /&gt;
&lt;br /&gt;
I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2WERR_UNIT_DISCONNECTED 19 /* unit is currently not connected */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2WERR_UNIT_CONNECTED ===&lt;br /&gt;
&lt;br /&gt;
This error code is a counterpart to &amp;lt;tt&amp;gt;S2WERR_UNIT_ONLINE&amp;lt;/tt&amp;gt;. It indicates that the associated command could not be executed because the link layer is already connected.&lt;br /&gt;
&lt;br /&gt;
I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2WERR_UNIT_CONNECTED 20 /* unit is currently connected */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2WERR_INVALID_OPTION ===&lt;br /&gt;
&lt;br /&gt;
This error code indicates that an option, such as passed by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command, is not acceptable. The option&#039;s value may be out of range or may not match the syntax specifications. To indicate which option that may be, a different mechanism &#039;&#039;&#039;must&#039;&#039;&#039; be used; a simple indication that something was wrong is &#039;&#039;&#039;not sufficient&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2WERR_INVALID_OPTION 21 /* invalid option rejected */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2WERR_MISSING_OPTION ===&lt;br /&gt;
&lt;br /&gt;
This error code indicates that a mandatory option, such as passed by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command, is not present. To indicate which option that may be, a different mechanism &#039;&#039;&#039;must&#039;&#039;&#039; be used; a simple indication that something was wrong is &#039;&#039;&#039;not sufficient&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2WERR_MISSING_OPTION 22 /* a mandatory option is missing */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== S2WERR_AUTHENTICATION_FAILED ===&lt;br /&gt;
&lt;br /&gt;
Some drivers run protocols that require them to authenticate to a server. That process may fail. This wire error code is to indicate this fact.&lt;br /&gt;
&lt;br /&gt;
I propose the following semantics:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2WERR_AUTHENTICATION_FAILED 23 /* could not log in */&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Callbacks ==&lt;br /&gt;
&lt;br /&gt;
=== S2_DMACopyToBuff64 and S2_DMACopyFromBuff64 ===&lt;br /&gt;
&lt;br /&gt;
These two callbacks are identical in operation to the &amp;lt;tt&amp;gt;S2_DMACopyToBuff32&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DMACopyFromBuff32&amp;lt;/tt&amp;gt; callbacks, as specified in the SANA-IIR3 standard. The difference is in that the memory region DMA is to take place in must be aligned to a 64 bit boundary and must be large enough to hold data that is a multiple of 64 bits in size. The purpose of these hooks is to allow for 64 bit aligned PCI DMA accesses to take place.&lt;br /&gt;
&lt;br /&gt;
I propose that the following numbers should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_DMACopyToBuff64   (S2_Dummy + 10)&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define S2_DMACopyFromBuff64 (S2_Dummy + 11)&lt;br /&gt;
&lt;br /&gt;
=== S2_Log ===&lt;br /&gt;
&lt;br /&gt;
A driver may want to report an important event for the user to see. Adding a log message to a file or opening a window to display a message in may not be the optimum approach as the user may be unaware of the context into which the message belongs. It may be advisable for the driver to use the message reporting and logging facilities used by the client software that uses its services, such as a TCP/IP stack. The &amp;lt;tt&amp;gt;S2_Log&amp;lt;/tt&amp;gt; callback hook is intended to provide for such a link. If present, the driver must use this callback hook rather than whatever logging methods it implements itself. Note that unlike the other SANA-II callbacks, this is a regular hook, as to be invoked using &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;CallHookPkt()&amp;lt;/tt&amp;gt;. The hook function is invoked using the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log_hook_function(hook,reserved,message)&lt;br /&gt;
&lt;br /&gt;
void log_hook_function(struct hook * hook,APTR reserved,&lt;br /&gt;
                       struct S2LogMessage * message);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;reserved&amp;lt;/tt&amp;gt; parameter &#039;&#039;&#039;must&#039;&#039;&#039; be set to &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;S2LogMessage&amp;lt;/tt&amp;gt; structure passed as the third parameter looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct S2LogMessage&lt;br /&gt;
{&lt;br /&gt;
   LONG   s2lm_Size;&lt;br /&gt;
   ULONG  s2lm_Priority;&lt;br /&gt;
   STRPTR s2lm_Name;&lt;br /&gt;
   STRPTR s2lm_Message;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The individual structure members serve the following functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2lm_Size&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Size of the &amp;lt;tt&amp;gt;S2LogMessage&amp;lt;/tt&amp;gt; structure, in bytes. The idea is to extend this data structure in the future, and the size stored in here tells you how long the structure is. The size &#039;&#039;&#039;must&#039;&#039;&#039; always be &amp;amp;gt;= 16.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2lm_Priority&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The smaller this value, the more important the message to be logged or displayed. The following priority levels are defined (similar to the Unix &amp;lt;tt&amp;gt;syslog()&amp;lt;/tt&amp;gt; mechanism):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Emergency 0&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A panic condition.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Alert 1&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A condition that should be corrected immediately.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Critical 2&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Critical conditions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Error 3&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A plain error.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Warning 4&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A warning message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Notice 5&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Conditions that are not error conditions, but should possibly be handled specially.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Information 6&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An informational message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2LOG_Debug 7&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Messages that contain information normally of use only when debugging.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2lm_Name&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which identifies the source of this message. This can be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; in which case the name is treated as being unknown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;s2lm_Message&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which contains the log message. The text should not contain any formatting characters such as line feeds or carriage returns. The &amp;lt;tt&amp;gt;s2lm_Message&amp;lt;/tt&amp;gt; member &#039;&#039;&#039;must never&#039;&#039;&#039; be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All error messages issued by the device driver should use the current system locale wherever this is possible. The purpose of an error message is, after all, to assist the user in recovering from the error. Which may be difficult if the user does not even know the language in which the message is written.&lt;br /&gt;
&lt;br /&gt;
The log message string is valid until the log hook function returns. If the driver needs to retain the message any longer, it must make a copy of it.&lt;br /&gt;
&lt;br /&gt;
Since the client software into which the log hook calls may have to allocate memory to hold and display the log message, the log hook &#039;&#039;&#039;must not&#039;&#039;&#039; be called from interrupt code. The log hook shall not &amp;lt;tt&amp;gt;Wait()&amp;lt;/tt&amp;gt; and it shall assume only a &amp;lt;tt&amp;gt;Task&amp;lt;/tt&amp;gt; calling context of unknown priority. &amp;lt;tt&amp;gt;dos.library&amp;lt;/tt&amp;gt; functions may not be called. Also, stack space is provided only to call &amp;lt;tt&amp;gt;exec.library&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;utility.library&amp;lt;/tt&amp;gt; functions. The callback shall not place excessive data on the stack. Stack space should be considered limited and the callback should be designed to be fast and short.&lt;br /&gt;
&lt;br /&gt;
This hook is installed at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time, which means that the hook is used for the unit that was opened, and not just for the I/O request it was opened with. The hook must remain installed until the I/O request that installed it is eventually used to close the device. When this happens, the device should fall back to use no log hook at all. No nesting is permitted or required.&lt;br /&gt;
&lt;br /&gt;
I propose that the following number should be assigned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;#define S2_Log (S2_Dummy + 12)&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Proposed driver requirements ==&lt;br /&gt;
&lt;br /&gt;
This is an attempt to clarify part of the specification and to lay down a few rules that every SANA-II driver should follow in addition to the requirements described in the existing specifications. Here is what I propose:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;A driver that does not use a broadcast medium, such as Ethernet, must not implement the &amp;lt;tt&amp;gt;S2_BROADCAST&amp;lt;/tt&amp;gt; command. Likewise, if no multicast mechanism is supported, the &amp;lt;tt&amp;gt;S2_MULTICAST&amp;lt;/tt&amp;gt; must not be implemented either.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;SANA-II standard commands which the driver does not implement must be rejected with the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; error code. Commands that are implemented, but which cannot perform the requested services, must be rejected with the &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; error code. The difference between the two cases is in when the decision is made whether a command can be handled or not. Which commands should return &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; is decided upon at the time the driver is designed and implemented. At this stage the implementor knows for sure which capabilities the driver will have and which it will not have. Commands which the driver will never be able to execute will be made to return the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt; error code. If the decision whether a command can be executed is made only at run time, by evaluating the conditions under which a command can be executed, then the error code &amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; should be returned in case of failure.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;It must be possible to open the driver with a plain &amp;lt;tt&amp;gt;struct IOStdReq&amp;lt;/tt&amp;gt;. This is necessary for the NewStyleDevices query command to work. In the command dispatcher, the driver must verify that all SANA-II commands are invoked with a proper size &amp;lt;tt&amp;gt;struct IOSana2Req&amp;lt;/tt&amp;gt; I/O request. If the I/O request is shorter (as can be verified by looking at the embedded Message&#039;s &amp;lt;tt&amp;gt;mn_Length&amp;lt;/tt&amp;gt; member), the command must be rejected with &amp;lt;tt&amp;gt;ios2_Req.io_Error=IOERR_BADLENGTH&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;A driver must implement the NewStyleDevices &amp;lt;tt&amp;gt;NSCMD_DEVICEQUERY&amp;lt;/tt&amp;gt; command, in conformance with the NSD specification 1.6 or newer. The motivation for this is to have a mechanism available for probing the capabilities of the driver, and the supported command set can provide for vital clues. In this context, the absence of the &amp;lt;tt&amp;gt;S2_BROADCAST&amp;lt;/tt&amp;gt; command would suggest that the driver cannot send or receive broadcast messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;A driver that does not allow its station address to be set with the &amp;lt;tt&amp;gt;S2_CONFIGINTERFACE&amp;lt;/tt&amp;gt; command may silently ignore the command (returning it without setting an error condition) and even pretend that it can be configured more than once.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In response to the &amp;lt;tt&amp;gt;S2_DEVICEQUERY&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETSPECIALSTATS&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_GETGLOBALSTATS&amp;lt;/tt&amp;gt; commands a driver may return information that is not entirely correct if it has to go online before it can provide for the correct data. For example, the maximum transmission unit for PPP is a number in the range of [1..1500] which is negotiated during the protocol configuration process. It is unlikely that numbers greater than 1500 will be used, yet it is still not impossible. Since the actual number will be known only after the driver has configured the protocol, the MTU value returned before the session was opened can differ from the MTU value valid after it has been opened. A driver should therefore attempt to return &#039;safe&#039; defaults in place of information that is unavailable at the time it is queried. The &#039;safe&#039; values shall be set up to allow the driver to work even if the protocol stack is not aware of later changes to those values. Beware of zero-length buffer sizes or time intervals that may cause client software to perform zero-length memory allocations or divisions by zero.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;SANA-II assigns packet type numbers according to the underlying transport media. For example, Ethernet uses packet type 2048 for IP frames. No such packet type definition exists for PPP yet, which is why I propose to assign packet type 31 for IP packets transmitted via PPP.&lt;br /&gt;
To simplify client software configuration, drivers may treat packet type 2048 as equivalent to the packet number associated with IP frames. This association is permitted only if it does not introduce ambiguity. For example, this association would not be permitted if the driver would receive and transmit IP packets in two different frame types or if the driver already associates packet type 2048 with non-IP packets.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unsolved problems ==&lt;br /&gt;
&lt;br /&gt;
* Protocols like PPP can provide both for IPv4 and IPv6 addresses that should be used by the local client, the peer or any of the domain name servers. While the two addressing families are interoperable, there is a problem in how the driver should report them. Can you assume 128 bit addresses and encapsulate 32 bit addresses in them? If so, how do you make sure that the address format is unambiguous?&lt;br /&gt;
* Currently, only the device&#039;s hardware type provides a clue as to what packet type responds to which protocol transported via the link. For Ethernet, IP packets are encapsulated in type 2048 frames, PPP encapsulates IP packets in type 31 frames, Arcnet can use type 240 or 212. Matching a protocol with a frame type is not an easy process which could be handled more elegantly.&lt;br /&gt;
* How to extend the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure in the future? The current layout separates standard, common and format specific information, but there is no hint as to where which each section starts and where the next begins. Now that there is a proposal to add a new field to the common section, how would you add fields to the format specific section?&lt;br /&gt;
&lt;br /&gt;
== Changes ==&lt;br /&gt;
&lt;br /&gt;
Changes since 02-May-2012:&lt;br /&gt;
&lt;br /&gt;
* Converted to MediaWiki format.&lt;br /&gt;
&lt;br /&gt;
Changes since 24-Dec-2002:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;tt&amp;gt;Sana2Connection&amp;lt;/tt&amp;gt; data structure used by the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; command now must remain valid until the &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; command is sent (see [[#2.4|section 2.4]]).&lt;br /&gt;
&lt;br /&gt;
Changes since 01-May-2002:&lt;br /&gt;
&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;ppp.async.readrequests&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ppp.async.eof&amp;lt;/tt&amp;gt; configuration keywords.&lt;br /&gt;
&lt;br /&gt;
Changes since 04-Jan-2002:&lt;br /&gt;
&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;ppp.dummyremoteaddress&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ppp.ethernet.ac&amp;lt;/tt&amp;gt; configuration keywords.&lt;br /&gt;
&lt;br /&gt;
Changes since 10-Dec-2001:&lt;br /&gt;
&lt;br /&gt;
* Converted to HTML format.&lt;br /&gt;
* Added the &amp;lt;tt&amp;gt;&amp;amp;lt;devices/sana2.h&amp;amp;gt;&amp;lt;/tt&amp;gt; header file to the appendix.&lt;br /&gt;
* The memory alignment for the &amp;lt;tt&amp;gt;S2_DMACopyToBuff64&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DMACopyFromBuff64&amp;lt;/tt&amp;gt; hooks refers to bits and not to bytes.&lt;br /&gt;
&lt;br /&gt;
Changes since 19-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Added to the list of reserved configuration keywords (&amp;lt;tt&amp;gt;ppp.idletimeout&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ppp.peeridletimeout&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ppp.sendid&amp;lt;/tt&amp;gt;).&lt;br /&gt;
* Renamed the fields of the &amp;lt;tt&amp;gt;Sana2ExtDeviceStats&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
* Clarified that the &amp;lt;tt&amp;gt;S2QUAD&amp;lt;/tt&amp;gt; type is a big endian integer.&lt;br /&gt;
* More clarifications for the &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_ONLINE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_OFFLINE&amp;lt;/tt&amp;gt; commands.&lt;br /&gt;
* Updated the discussion of the &amp;lt;tt&amp;gt;Sana2DeviceQuery.RawMTU&amp;lt;/tt&amp;gt; field, clarifying what is included in the the Ethernet &amp;lt;tt&amp;gt;RawMTU&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Updated the &amp;lt;tt&amp;gt;&amp;amp;lt;devices/sana2.h&amp;amp;gt;&amp;lt;/tt&amp;gt; header file. Note that there is no equivalent &amp;lt;tt&amp;gt;&amp;amp;quot;devices/sana2.i&amp;amp;quot;&amp;lt;/tt&amp;gt; header file yet.&lt;br /&gt;
&lt;br /&gt;
Changes since 12-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Changed the command numbers of &amp;lt;tt&amp;gt;S2_GETPEERADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETDNSADDRESS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT&amp;lt;/tt&amp;gt; to be NSD-compliant. Also assigned a new number to the &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT &amp;lt;/tt&amp;gt; command.&lt;br /&gt;
* Added section 4 (&amp;amp;quot;Extensions for existing commands&amp;amp;quot;).&lt;br /&gt;
* Added the last paragraph to section 9, relating to the future extension of the &amp;lt;tt&amp;gt;Sana2DeviceQuery&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
&lt;br /&gt;
Changes since 03-Nov-2001:&lt;br /&gt;
&lt;br /&gt;
* Renamed &amp;lt;tt&amp;gt;S2_GETNEWGLOBALSTATS&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt; (see [[#2.3|section 2.3]]).&lt;br /&gt;
* The &amp;lt;tt&amp;gt;S2_GETEXTENDEDGLOBALSTATS&amp;lt;/tt&amp;gt; now uses 64 bit quantities for the &amp;lt;tt&amp;gt;s2xds_PacketsReceived&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_PacketsSent&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_BadData&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_Overruns&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;s2xds_UnknownTypesReceived&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2xds_Reconfigurations&amp;lt;/tt&amp;gt; counters.&lt;br /&gt;
* In section 2.6 the &amp;lt;tt&amp;gt;S2_SAMPLE_THROUGHPUT&amp;lt;/tt&amp;gt; command was modified to use 64 bit integers for all members of the &amp;lt;tt&amp;gt;Sana2ThroughputStats&amp;lt;/tt&amp;gt; structure.&lt;br /&gt;
* All proposed commands are now listed with their numeric IDs.&lt;br /&gt;
* The command autodocs specifically mention the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field.&lt;br /&gt;
* All references to &amp;lt;tt&amp;gt;ios2_Error&amp;lt;/tt&amp;gt; have been replaced with &amp;lt;tt&amp;gt;ios2_Req.io_Error&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* In section 7 the use of the &amp;lt;tt&amp;gt;IOERR_NOCMD&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2ERR_NOT_SUPPORTED&amp;lt;/tt&amp;gt; error codes is clarified.&lt;br /&gt;
* Section 7 takes a more detailed look at safe default values returned by the query commands.&lt;br /&gt;
* Inserted section 3 (&amp;amp;quot;Annotations for existing commands&amp;amp;quot;).&lt;br /&gt;
* The &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; commands now specifically mention the life time of the data they have to deal with.&lt;br /&gt;
&lt;br /&gt;
Changes since 14-Oct-2001:&lt;br /&gt;
&lt;br /&gt;
* Added &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; commands.&lt;br /&gt;
* Added &amp;lt;tt&amp;gt;S2EVENT_CONNECT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2EVENT_DISCONNECT&amp;lt;/tt&amp;gt; events.&lt;br /&gt;
* Added section 4 (&amp;amp;quot;New wire error codes&amp;amp;quot;).&lt;br /&gt;
* In section 5.2 the originally proposed log callback function has been wrapped into a standard Hook structure.&lt;br /&gt;
* Removed item on &amp;lt;tt&amp;gt;S2_CONNECT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;S2_DISCONNECT&amp;lt;/tt&amp;gt; from section 7 (&amp;amp;quot;Unsolved problems&amp;amp;quot;).&lt;br /&gt;
* Added section 8 (&amp;amp;quot;Changes&amp;amp;quot;).&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Classic_Graphics_Primitives&amp;diff=12541</id>
		<title>Classic Graphics Primitives</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Classic_Graphics_Primitives&amp;diff=12541"/>
		<updated>2025-01-26T19:27:24Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Graphics Primitives ==&lt;br /&gt;
&lt;br /&gt;
This section defines the display routines for use on Classic Amiga hardware. These routines show you how to form and manipulate a display, including the following aspects of display use:&lt;br /&gt;
&lt;br /&gt;
* How to query the graphics system to find out what type of video monitor is attached and which graphics modes can be displayed on it.&lt;br /&gt;
* How to identify the memory area that you wish to have displayed.&lt;br /&gt;
* How to position the display area window to show only a certain portion of a larger drawing area.&lt;br /&gt;
* How to split the screen into as many vertically stacked slices as you wish.&lt;br /&gt;
* How to determine which horizontal and vertical resolution modes to use.&lt;br /&gt;
* How to determine the current correct number of pixels across and lines down for a particular section of the display.&lt;br /&gt;
* How to specify how many color choices per pixel are to be available in a specific section of the display.&lt;br /&gt;
&lt;br /&gt;
== Components of a Display ==&lt;br /&gt;
&lt;br /&gt;
In producing a display, you are concerned with two primary components: sprites and the playfield. Sprites are the easily movable parts of the display. The playfield is the static part of the display and forms a backdrop against which the sprites can move and with which the sprites can interact.&lt;br /&gt;
&lt;br /&gt;
This article covers the creation of the background. Sprites are described in [[Graphics Sprites, Bobs and Animation]].&lt;br /&gt;
&lt;br /&gt;
=== Introduction to Raster Displays ===&lt;br /&gt;
&lt;br /&gt;
There are three major television standards in common use around the world: NTSC, PAL, and SECAM. NTSC is used primarily in the United States and Japan; PAL and SECAM are used primarily in Europe. The Amiga currently supports both NTSC and PAL. The major differences between the two systems are refresh frequency and the number of scan lines produced. Where necessary, the differences will be described and any special considerations will be mentioned.&lt;br /&gt;
&lt;br /&gt;
The Amiga produces its video displays on standard television or video monitors by using raster display techniques. The picture you see on the video display screen is made up of a series of horizontal video lines stacked one on top of another, as illustrated in the following figure. Each line represents one sweep of an electronic video beam, which “paints” the picture as it moves along. The beam sweeps from left to right, producing the full screen one line at a time. After producing the full screen, the beam returns to the top of the display screen.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-1.png|frame|center|How the Video Display Picture Is Produced]]&lt;br /&gt;
&lt;br /&gt;
The diagonal lines in the figure show how the video beam returns to the start of each horizontal line.&lt;br /&gt;
&lt;br /&gt;
==== Effect of Display Overscan on the Viewing Area ====&lt;br /&gt;
&lt;br /&gt;
To assure that the picture entirely fills the monitor (or television) screen, the manufacturer of the video display device usually creates a deliberate &#039;&#039;overscan&#039;&#039;. That is, the video beam is swept across an area that is larger than the viewable region of the monitor.&lt;br /&gt;
&lt;br /&gt;
The video beam actually covers 262 vertical lines (312 for PAL). The user, however, sees only the portion of the picture that is within the center region of the display, typically surrounded by a border as illustrated in the figure below. The center region is nominally about 200 lines high on an NTSC machine (256 lines for PAL). Overscan also limits the amount of video data that can appear on each display line. The width of the center region is nominally, about 320 pixels for both PAL and NTSC.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-2.png|frame|center|Display Overscan Restricts Usable Picture Area]]&lt;br /&gt;
&lt;br /&gt;
The flexibility of the Amiga graphics subsystem allows the overscan region, which normally forms the border of the display, to be used for application graphics instead. So the nominal dimensions given above can be enlarged.&lt;br /&gt;
&lt;br /&gt;
The time during which the video beam is below the bottom line of the viewable region and above the first line is called the &#039;&#039;vertical blanking interval&#039;&#039;. The recommended minimum to allow for this interval is 21 lines for NTSC (29 lines for PAL). So, for applications that take full advantage of the overscan area, a maximum of 241 usable lines in NTSC (283 in PAL) can be achieved. The display resolution can also be changed by changing the Amiga display mode as discussed in the sections below.&lt;br /&gt;
&lt;br /&gt;
==== Color Information for the Video Lines ====&lt;br /&gt;
&lt;br /&gt;
The hardware reads the system display memory to obtain the color information for each line. As the video display beam sweeps across the screen producing the display line, it changes color, producing the images you have defined. On the current generation of Amiga hardware, there are 4,096 possible colors.&lt;br /&gt;
&lt;br /&gt;
=== Interlaced and Non-Interlaced Modes ===&lt;br /&gt;
&lt;br /&gt;
In producing the complete display (262 lines in NTSC, 312 in PAL), the video display device produces the top line, then the next lower line, then the next, until it reaches the bottom of the screen. When it reaches the bottom, it returns to the top to start a new scan of the screen. Each complete set of lines is called a &#039;&#039;display field&#039;&#039;. It takes about 1/60th of a second to produce a complete NTSC display field (1/50th of a second for PAL).&lt;br /&gt;
&lt;br /&gt;
The Amiga has two vertical display modes: &#039;&#039;interlaced&#039;&#039; and &#039;&#039;non-interlaced&#039;&#039;. In non-interlaced mode, the video display produces the same picture for each successive display field. A non-interlaced NTSC display normally has about 200 lines in the viewable area (up to a maximum of 241 lines with overscan) while a PAL display will normally show 256 lines (up to a maximum of 283 with overscan).&lt;br /&gt;
&lt;br /&gt;
With interlaced mode, the amount of information in the viewable area can be doubled. On an NTSC display this amounts to 400 lines (482 with overscan), while on a PAL display it amounts to 512 lines (566 with overscan).&lt;br /&gt;
&lt;br /&gt;
For interlaced mode, the video beam scans the screen at the same rate (1/60th of a second per complete NTSC video display field); however, it takes two display fields to form a complete video display picture and twice as much display memory to store line data. During the first of each pair of display fields, the system hardware shows the odd-numbered lines of an interlaced display (1, 3, 5, and so on). During the second display field, it shows the even-numbered lines (2, 4, 6 and so on). The second field is positioned slightly lower so that the lines in the second field are “interlaced” with those of the first field, giving the higher vertical resolution of this mode.&lt;br /&gt;
&lt;br /&gt;
Interlaced Mode – Display Fields and Data in Memory&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;odd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;&#039;&#039;&#039;Data as Displayed&#039;&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;&#039;&#039;&#039;Data In Memory&#039;&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;even&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Odd field - Line 1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;odd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Even field - Line 1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;even&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Odd field - Line 2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 3&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;odd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Even field - Line 2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;even&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;. . .&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;. . .&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;odd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Odd field - Line 200&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 399&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;even&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Even field - Line 200&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;left&amp;quot;&amp;gt;Line 400&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following figure shows a display formed as display lines 1, 2, 3, 4, ... 400. The 400-line interlaced display uses the same physical display area as a 200-line non-interlaced display.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-4.png|frame|center|Interlaced Mode Doubles Vertical Resolution]]&lt;br /&gt;
&lt;br /&gt;
During an interlaced display, it appears that both display fields are present on the screen at the same time and form one complete picture. However, interlaced displays will appear to flicker if adjacent (odd and even) scan lines have contrasting brightness. Choosing appropriate colors for your display will reduce this flicker considerably. This phenomenon can also be reduced by using a long-persistence monitor, or alleviated completely with a hardware de-interlacer.&lt;br /&gt;
&lt;br /&gt;
=== Low, High and Super-High Resolution Modes ===&lt;br /&gt;
&lt;br /&gt;
The Amiga also has three horizontal display modes: low-resolution (or &#039;&#039;Lores&#039;&#039;), high-resolution (&#039;&#039;Hires&#039;&#039;) and super-high-resolution (&#039;&#039;SuperHires&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Normally, these three horizontal display modes have a width of 320 for Lores, 640 for Hires or 1,280 for SuperHires on both PAL and NTSC machines. However, by taking full advantage of the overscan region, it is possible to create dispays up to 362 pixels wide in Lores mode, 724 pixels wide in Hires or 1,448 pixels wide in SuperHires. Usually, however, you should use the standard values (320, 640 or 1,280) for most applications.&lt;br /&gt;
&lt;br /&gt;
In general, the number of colors available in each display mode decreases as the resolution increases. The Amiga has two special display modes that can be used to increase the number of colors available. HAM is &#039;&#039;Hold-And-Modify&#039;&#039; mode, EHB is &#039;&#039;Extra-Half-Brite&#039;&#039; mode.&lt;br /&gt;
&lt;br /&gt;
Hold-And-Modify (HAM) allows you to display the entire palette of 4,096 colors on-screen at once with certain restrictions, explained later.&lt;br /&gt;
&lt;br /&gt;
Extra-Half-Brite allows for 64 colors on-screen at once; 32 colors plus 32 additional colors that are half the intensity of the first 32. For example, if color 1 is defined as 0xFFF (white), then color 33 is 0x777 (grey).&lt;br /&gt;
&lt;br /&gt;
==== Display Modes, Colors, and Requirements ====&lt;br /&gt;
&lt;br /&gt;
The following chart lists all of the display modes that are available.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! 15 kHz Amiga Display Modes&lt;br /&gt;
! NTSC&lt;br /&gt;
! PAL&lt;br /&gt;
! Maximum Colors&lt;br /&gt;
! Supports HAM/EHB&lt;br /&gt;
|-&lt;br /&gt;
| Lores || 320x200 || 320x256 || 32 of 4,096 || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Lores-Interlaced || 320x400 || 320x512 || 32 of 4,096 || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Hires || 640x200 || 640x256 || 16 of 4,096 || No&lt;br /&gt;
|-&lt;br /&gt;
| Hires-Interlaced || 640x400 || 640x512 || 16 of 4,096 || No&lt;br /&gt;
|-&lt;br /&gt;
| SuperHires* || 1,280x200 || 1,280x256 || 4 out of 64 || No&lt;br /&gt;
|-&lt;br /&gt;
| SuperHires-Interlaced* || 1,280x400 || 1,280x512 || 4 out of 4,096 || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; Requires ECS&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! 31 kHz Amiga Display Modes*&lt;br /&gt;
! Default Resolution&lt;br /&gt;
! Maximum Colors&lt;br /&gt;
! Supports HAM/EHB&lt;br /&gt;
|-&lt;br /&gt;
| VGA-ExtraLores&lt;br /&gt;
| 160x480&lt;br /&gt;
| 32 out of 4,096&lt;br /&gt;
| Yes&lt;br /&gt;
|-&lt;br /&gt;
| VGA-ExtraLores-Interlace&lt;br /&gt;
| 160x960&lt;br /&gt;
| 32 out of 4,096&lt;br /&gt;
| Yes&lt;br /&gt;
|-&lt;br /&gt;
| VGA-Lores&lt;br /&gt;
| 320x480&lt;br /&gt;
| 16 out of 4,096&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| VGA-Lores-Interlace&lt;br /&gt;
| 320x960&lt;br /&gt;
| 16 out of 4,096&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| Productivity&lt;br /&gt;
| 640x480&lt;br /&gt;
| 4 out of 64&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| Productivity-Interlace&lt;br /&gt;
| 640x960&lt;br /&gt;
| 4 out of 64&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; 31 kHz modes require ECS and either a bi-scan or multi-scan monitor&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! A2024* Display Modes&lt;br /&gt;
! NTSC&lt;br /&gt;
! PAL&lt;br /&gt;
! Maximum Colors&lt;br /&gt;
|-&lt;br /&gt;
| A2024-10Hz || 1,008x800 || 1,008x1,024 || 4 out of 4 grey levels&lt;br /&gt;
|-&lt;br /&gt;
| A2024-15Hz || 1,008x800 || 1,008x1,024 || 4 out of 4 grey levels&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; A2024 modes require special hardware&lt;br /&gt;
&lt;br /&gt;
=== About ECS ===&lt;br /&gt;
&lt;br /&gt;
ECS stands for &#039;&#039;Enhanced Chip Set&#039;&#039;, the latest version of the Amiga’s custom chips that provides for improved graphics capabilities. Some of the special features of the Amiga’s graphics sub-system such as the VGA, Productivity and SuperHires display modes require the ECS.&lt;br /&gt;
&lt;br /&gt;
==== SuperHires (35 nanosecond) Pixel Resolutions ====&lt;br /&gt;
&lt;br /&gt;
The enhanced version of the Denise chip can generate SuperHires pixels that are twice as fine as Hires pixels. It is convenient to refer to pixels here by their speed, rather than width, for reasons that will be explained below. They are approximately 35nS long, while Hires are 70nS, and Lores 140nS. In the absence of any other features, this can bring a new mode with nominal dimensions of 1,280x200 (NTSC) or 1,280x256 (PAL). This mode requires the ECS Agnus chip as well.&lt;br /&gt;
&lt;br /&gt;
When Denise is generating these new fast pixels, simple bandwidth arithmetic indicates that at most two bitplanes can be supported. Also note that with two bitplanes, DMA bandwidth is saturated. The palette for SuperHires pixels is also restricted to 64 colors.&lt;br /&gt;
&lt;br /&gt;
==== Productivity Mode ====&lt;br /&gt;
&lt;br /&gt;
The enhanced version of the Denise chip can support monitor horizontal scan frequencies of 31kHz, twice the old 15.75kHz rate. This provides over 400 non-interlaced horizontal lines in a frame, but requires the use of a multiple scan rate, or multi-sync monitor.&lt;br /&gt;
&lt;br /&gt;
This effect speeds up the video beam roughly by a factor of two, which has the side effect of doubling the width of a pixel emitted at a given speed. Thus, for a given Denise mode, pixels are twice as fat, and there are half as many on a given line.&lt;br /&gt;
&lt;br /&gt;
The increased scan rate interacts with all of the Denise modes. So with both SuperHires (35nS) pixels and the double scan rate the display generated would be 640 pixels wide by more than 400 rows, non-interlaced, with up to four colors from a palette of 64. This combination is termed Productivity mode, and the default international height is 480 rows.&lt;br /&gt;
&lt;br /&gt;
This conforms, in a general way, to the VGA Mode 3 Standard &#039;&#039;8514/A&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The support in Agnus is actually more flexible, and gives the ability to conform to special-purpose modes, such as displays synchronized to motion picture cameras.&lt;br /&gt;
&lt;br /&gt;
==== Selectable PAL/NTSC ====&lt;br /&gt;
&lt;br /&gt;
The Enhanced Chip Set can be set to NTSC or PAL modes under software control. Its initial default behavior is determined by a jumper or trace on the system motherboard. This has no bearing on Productivity mode and other programmable scan operations, but the new system software can support displays in either mode.&lt;br /&gt;
&lt;br /&gt;
==== Determining Chip Versions ====&lt;br /&gt;
&lt;br /&gt;
It is possible to ascertain whether the ECS chips are in the machine at run time by looking in the ChipRevBits0 field of the GfxBase structure. If this field contains the flag for the chip you are interested in (as defined in the &amp;amp;lt;gfxbase.h&amp;amp;gt; include file), then that chip is present.&lt;br /&gt;
&lt;br /&gt;
For example, if the C statement (GfxBase-&amp;amp;gt;ChipRevBits0 &amp;amp;amp; GFXF_HR_AGNUS) evaluates to non-zero, then the machine contains the ECS version of the Agnus chip and has advanced features such as the ability to handle larger rasters. Older Agnus chips were capable of handling rasters up to 1,024 by 1,024 pixels. The ECS Agnus can handle rasters up to 16,384 by 16,384 pixels.&lt;br /&gt;
&lt;br /&gt;
If (GfxBase-&amp;amp;gt;ChipRevBits0 &amp;amp;amp; GFXF_HR_DENISE) is non-zero, then the ECS version of the Denise chip is present. Having both the ECS Agnus and ECS Denise present allows for the special SuperHires, VGA and Productivity display modes. For more information on ECS and the custom chips, refer to the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Forming an Image ===&lt;br /&gt;
&lt;br /&gt;
To create an image, you write data (that is, you “draw”) into a memory area in the computer. From this memory area, the system can retrieve the image for display. You tell the system exactly how the memory area is organized, so that the display is correctly produced. You use a block of memory words at sequentially increasing addresses to represent a rectangular region of data bits. The following figure shows the contents of three example memory words: 0 bits are shown as blank rectangles, and 1 bits as filled-in rectangles.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-5.png|frame|center|Sample Memory Words]]&lt;br /&gt;
&lt;br /&gt;
The system software lets you define linear memory as rectangular regions, called &#039;&#039;bitplanes&#039;&#039;. The figure below shows how the system would organize three sequential words in memory into a rectangular bitplane with dimensions of 16x3 pixels.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-6.png|frame|center|A Rectangular Bitplane Made from 3 Memory Words]]&lt;br /&gt;
&lt;br /&gt;
The following figure shows how 4,000 words (8,000 bytes) of memory can be organized to provide enough bits to define a single bitplane of a full-screen, low-resolution video display (320x200).&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-7.png|frame|center|Bitplane for a Full-screen, Low-resolution Display]]&lt;br /&gt;
&lt;br /&gt;
Each memory data word contains 16 data bits. The color of each pixel on a video display line is directly related to the value of one or more data bits in memory, as follows:&lt;br /&gt;
&lt;br /&gt;
* If you create a display in which each pixel is related to only one data bit, you can select from only two possible colors, because each bit can have a value of only 0 or 1.&lt;br /&gt;
* If you use two bits per pixel, there is a choice of four different colors because there are four possible combinations of the values of 0 and 1 from each of the two bits.&lt;br /&gt;
* If you specify three, four, or five bits per pixel, you will have eight, sixteen, or thirty-two possible choices of a color for a pixel.&lt;br /&gt;
* If you use six bits per pixel, then depending on the video mode (EHB or HAM), you will have sixty-four or 4,096 possible choices for a pixel.&lt;br /&gt;
&lt;br /&gt;
To create multicolored images, you must tell the system how many bits are to be used per pixel. The number of bits per pixel is the same as the &#039;&#039;number of bitplanes&#039;&#039; used to define the image.&lt;br /&gt;
&lt;br /&gt;
As the video beam sweeps across the screen, the system retrieves one data bit from each bitplane. Each of the data bits is taken from a different bitplane, and one or more bitplanes are used to fully define the video display screen. For each pixel, data-bits in the same x,y position in each bitplane are combined by the system hardware to create a binary value. This value determines the color that appears on the video display for that pixel.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-8.png|frame|center|Bits from Each Bitplane Select Pixel Color]]&lt;br /&gt;
&lt;br /&gt;
You will find more information showing how the data bits actually select the color of the displayed pixel in the section called “ViewPort Color Selection”.&lt;br /&gt;
&lt;br /&gt;
=== Role of the Copper (Coprocessor) ===&lt;br /&gt;
&lt;br /&gt;
The Amiga has a special-purpose coprocessor, called the &#039;&#039;Copper&#039;&#039;, that can control nearly the entire graphics system. The Copper can control register updates, reposition sprites, change the color palette, and update the blitter. The graphics and animation routines use the Copper to set up lists of instructions for handling displays, and advanced programmers can create their own custom Copper lists.&lt;br /&gt;
&lt;br /&gt;
== Display Routines and Structures ==&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Caution|text=This section describes the lowest-level graphics interface to the system hardware. If you use any of the routines and the data structures described in these sections, your program will essentially take over the entire display. In general, this is not compatible with Intuition’s multiwindow operating environment since Intuition calls these low-level routines for you.}}&lt;br /&gt;
&lt;br /&gt;
The descriptions of the display routines, as well as those of the drawing routines, occasionally use the same terminology as that in the Intuition articles. These routines and data structures are the same ones that Intuition software uses to produce its displays.&lt;br /&gt;
&lt;br /&gt;
The computer produces a display from a set of instructions you define. You organize the instructions as a set of parameters known as the View structure (see the &amp;amp;lt;graphics/view.h&amp;amp;gt; include file for more information).&lt;br /&gt;
&lt;br /&gt;
The following figure shows how the system interprets the contents of a View structure. This drawing shows a complete display composed of two different component parts, which could (for example) be a low-resolution, multicolored part and a high-resolution, two-colored part.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-9.png|frame|center|The Display Is Composed of ViewPorts]]&lt;br /&gt;
&lt;br /&gt;
A complete display consists of one or more ViewPorts, whose display sections are vertically separated from each other by at least one blank scan line (non-interlaced). (If the system must make many changes to the display during the transition from one ViewPort to the next, there may be two or more blank scanlines between the ViewPorts.)&lt;br /&gt;
&lt;br /&gt;
The viewable area defined by each ViewPort is rectangular. It may be only a portion of the full ViewPort, it may be the full ViewPort, or it may be &#039;&#039;larger&#039;&#039; than the full ViewPort, allowing it to be moved within the limits of its DisplayClip (discussed later). You are essentially defining a display consisting of a number of stacked rectangular areas in which separate sections of graphics rasters can be shown.&lt;br /&gt;
&lt;br /&gt;
=== Limitations on the Use of Viewports ===&lt;br /&gt;
&lt;br /&gt;
The system software for defining ViewPorts allows only vertically stacked fields to be defined.&lt;br /&gt;
&lt;br /&gt;
The following figure shows acceptable and unacceptable display configurations.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-10.png|frame|center|Correct and Incorrect Uses of ViewPorts]]&lt;br /&gt;
&lt;br /&gt;
A ViewPort is related to the custom screen option of Intuition. In a custom screen, you can split the screen into slices as shown in the “correct” illustration of the above figure. Each custom screen can have its own set of colors, use its own resolution, and show its own display area.&lt;br /&gt;
&lt;br /&gt;
=== Characteristics of a ViewPort ===&lt;br /&gt;
&lt;br /&gt;
To describe a ViewPort fully, you need to set the following parameters: height, width, depth and display mode.&lt;br /&gt;
&lt;br /&gt;
In addition to these parameters, you must tell the system the location in memory from which the data for the ViewPort display should be retrieved (by associating with it a BitMap structure) and how to position the final ViewPort display on the screen. The ViewPort will take on the user’s default Workbench colors unless otherwise instructed with a ColorMap. See the section called “Preparing the ColorMap Structure” for more information.&lt;br /&gt;
&lt;br /&gt;
=== ViewPort Size Specifications ===&lt;br /&gt;
&lt;br /&gt;
The following figure illustrates that the variables DHeight, and DWidth specify the size of a ViewPort.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-11.png|frame|center|Size Definition for a ViewPort]]&lt;br /&gt;
&lt;br /&gt;
==== ViewPort Height ====&lt;br /&gt;
&lt;br /&gt;
The DHeight field of the ViewPort structure determines how many video lines will be reserved to show the height of this display segment. The size of the actual segment depends on whether you define a non-interlaced or an interlaced display. An interlaced ViewPort displays twice as many lines as does a non-interlaced ViewPort in the same physical height.&lt;br /&gt;
&lt;br /&gt;
For example, a complete View consisting of two ViewPorts might be defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;ViewPort&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;1 is 150 lines, high-resolution mode (uses the top three-quarters of the display).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;ViewPort&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;2 is 49 lines of low-resolution mode (uses the bottom quarter of the display and allows the space for the required blank line between ViewPorts).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Initialize the height directly in DHeight. Nominal height for a non-interlaced display is 200 lines for NTSC, 256 for PAL. Nominal height for an interlaced display is 400 lines for NTSC, 512 for PAL.&lt;br /&gt;
&lt;br /&gt;
To set your ViewPort to the maximum supported (displayable) height, use the following code fragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct DimensionInfo querydims;&lt;br /&gt;
struct Rectangle *oscan;&lt;br /&gt;
struct ViewPort viewport;&lt;br /&gt;
&lt;br /&gt;
if (GetDisplayInfoData( NULL,(UBYTE *)&amp;amp;amp;querydims, sizeof(struct DimensionInfo),&lt;br /&gt;
                        DTAG_DIMS, modeID ))&lt;br /&gt;
{&lt;br /&gt;
    /* Use StdOScan instead of MaxOScan to get standard overscan */&lt;br /&gt;
    /* dimensions as set by the user in Overscan Preferences     */&lt;br /&gt;
    oscan = &amp;amp;amp;querydims.MaxOScan;&lt;br /&gt;
    viewPort-&amp;amp;gt;DHeight = oscan-&amp;amp;gt;MaxY - oscan-&amp;amp;gt;MinY + 1;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ViewPort Width ====&lt;br /&gt;
&lt;br /&gt;
The DWidth variable in the ViewPort structure determines how wide, in pixels, the display segment will be. To set your ViewPort to the maximum supported (displayable) NTSC high-resolution width, use the following fragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct DimensionInfo querydims;&lt;br /&gt;
struct Rectangle *oscan;&lt;br /&gt;
struct ViewPort viewport;&lt;br /&gt;
&lt;br /&gt;
/* Use PAL_MONITOR_ID instead of NTSC_MONITOR_ID to get PAL dimensions */&lt;br /&gt;
if (GetDisplayInfoData( NULL,(UBYTE *)&amp;amp;amp;querydims, sizeof(querydims),&lt;br /&gt;
    DTAG_DIMS, NTSC_MONITOR_ID|HIRES_KEY ))&lt;br /&gt;
{&lt;br /&gt;
    /* Use StdOScan instead of MaxOScan to get standard overscan */&lt;br /&gt;
    /* dimensions as set by the user in Overscan Preferences     */&lt;br /&gt;
    oscan = &amp;amp;amp;querydims.MaxOScan;&lt;br /&gt;
    viewPort-&amp;amp;gt;DWidth = oscan-&amp;amp;gt;MaxX - oscan-&amp;amp;gt;MinX + 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may specify a smaller value of pixels per line to produce a narrower display segment or simply set ViewPort.DWidth to the nominal value for this resolution.&lt;br /&gt;
&lt;br /&gt;
Although the system software allows you define low-resolution displays as wide as 362 pixels and high-resolution displays as wide as 724 pixels, you should use caution in exceeding the normal values of 320 or 640, respectively. Because display overscan varies from one monitor to another, many video displays will not be able to show all of a wider display, and sprite display may also be affected. However, if you use the standard overscan values (DimensionInfo.StdOScan) provided by the function GetDisplayInfoData() as shown above, the user’s preference for the size of the display will be satisfied.&lt;br /&gt;
&lt;br /&gt;
If you are using hardware sprites or VSprites with your display, and you specify ViewPort widths exceeding 320 or 640 pixels (for low or high-resolution, respectively), it is likely that some hardware sprites will not be properly rendered on the screen.&lt;br /&gt;
&lt;br /&gt;
These sprites may not be rendered because playfield DMA (direct memory access) takes precedence over sprite DMA when an extra-wide display is produced. See the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; for a more complete description of this phenomenon.&lt;br /&gt;
&lt;br /&gt;
=== ViewPort Color Selection ===&lt;br /&gt;
&lt;br /&gt;
The maximum number of colors that a ViewPort can display is determined by the depth of the BitMap that the ViewPort displays. The depth is specified when the BitMap is initialized.&lt;br /&gt;
&lt;br /&gt;
See the section below called “Preparing the BitMap Structure.”&lt;br /&gt;
&lt;br /&gt;
Depth determines the number of bitplanes used to define the colors of the rectangular image you are trying to build (the raster image) and the number of different colors that can be displayed at the same time within a ViewPort.&lt;br /&gt;
&lt;br /&gt;
For any single pixel, the system can display any one of 4,096 possible colors.&lt;br /&gt;
&lt;br /&gt;
The following table shows depth values and the corresponding number of possible colors for each value.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Depth Values and Number of Colors in the ViewPort&lt;br /&gt;
! Colors !! Depth Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 3 || 1&lt;br /&gt;
|-&lt;br /&gt;
| 16 || 4 || 1, 2&lt;br /&gt;
|-&lt;br /&gt;
| 32 || 5 || 1, 2, 3&lt;br /&gt;
|-&lt;br /&gt;
| 16 || 6 || 1, 4&lt;br /&gt;
|-&lt;br /&gt;
| 64 || 6 || 1, 2, 3, 5&lt;br /&gt;
|-&lt;br /&gt;
| 4,096 || 6 || 1, 2, 3, 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
# Not available for SUPERHIRES.&lt;br /&gt;
# Single-playfield mode only - DUALPF &#039;&#039;not&#039;&#039; one of the ViewPort’s attributes.&lt;br /&gt;
# Low-resolution mode only - neither HIRES nor SUPERHIRES one of the ViewPort attributes.&lt;br /&gt;
# Dual Playfield mode - DUALPF is an attribute of this ViewPort. Up to eight colors (in three planes) for each playfield.&lt;br /&gt;
# Extra-Half-Brite mode - EXTRA_HALFBRITE is an attribute of this ViewPort.&lt;br /&gt;
# Hold-And-Modify mode only - HAM is an attribute of this ViewPort.&lt;br /&gt;
&lt;br /&gt;
The color palette used by a ViewPort is specified in a ColorMap. See the section called “Preparing the ColorMap” for more information.&lt;br /&gt;
&lt;br /&gt;
Depending on whether single- or dual-playfield mode is used, the system will use different color register groupings for interpreting the on-screen colors. The table below details how the depth and the different ViewPort modes affect the registers the system uses.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Color Registers Used in Single-playfield Mode&lt;br /&gt;
! Color !! Depth !! Registers Used&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0,1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0-3&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 0-7&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 0-15&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 0-31 || (if EXTRA_HALFBRITE is an attribute of this ViewPort.)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 0-31 || (if HAM is an attribute of this ViewPort.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The following table shows the five possible combinations when DUALPF &#039;&#039;is&#039;&#039; an attribute of the ViewPort.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Color Registers Used in Dual-playfield Mode&lt;br /&gt;
! Depth (PF-1} || Color Registers !! Depth (PF-2) || Color Registers&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0,1 || 1 || 8,9&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0-3 || 1 || 8,9&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0-3 || 2 || 8-11&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 0-7 || 2 || 8-11&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 0-7 || 3 || 8-15&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== ViewPort Display Modes ===&lt;br /&gt;
&lt;br /&gt;
The system has many different display modes that you can specify for each ViewPort. Under 1.3, the eight constants that control the modes are DUALPF, PFBA, HIRES, SUPERHIRES, LACE, HAM, SPRITES, and EXTRA_HALFBRITE. Some, but not all of the modes can be combined in a ViewPort. HIRES and LACE combine to make a high-resolution, interlaced ViewPort, but HIRES and SUPERHIRES conflict, and cannot be combined.&lt;br /&gt;
&lt;br /&gt;
Set the display mode for a ViewPort by using the VideoControl() function as described in [[Display Database]].&lt;br /&gt;
&lt;br /&gt;
The DUALPF and PFBA modes are related. DUALPF tells the system to treat the raster specified by this ViewPort as the first of two independent and separately controllable playfields. It also modifies the manner in which the pixel colors are selected for this raster (see the above table).&lt;br /&gt;
&lt;br /&gt;
When PFBA is specified, it indicates that the second playfield has video priority over the first one. Playfield relative priorities can be controlled when the playfield is split into two overlapping regions. Single-playfield and dual-playfield modes are discussed in “Advanced Topics” below.&lt;br /&gt;
&lt;br /&gt;
HIRES tells the system that the raster specified by this ViewPort is to be displayed with (nominally) 640 horizontal pixels, rather than the 320 horizontal pixels of Lores mode.&lt;br /&gt;
&lt;br /&gt;
SUPERHIRES tells the system that the raster specified by this ViewPort is to be displayed with (nominally) 1,280 horizontal pixels. This can be used with 31 kHz scan rates to provide the VGA and Productivity modes. SUPERHIRES modes require ECS. See [[#Determining Chip Versions|Determining Chip Versions]] for an explanation of how to find out if the ECS is present.&lt;br /&gt;
&lt;br /&gt;
LACE tells the system that the raster specified by this ViewPort is to be displayed in interlaced mode. If the ViewPort is non-interlaced and the View is interlaced, the ViewPort will be displayed at its specified height and will look only slightly different than it would look when displayed in a non-interlaced View (this is handled by the system automatically). See “Interlaced Mode vs. Non-interlaced Mode” below for more information.&lt;br /&gt;
&lt;br /&gt;
HAM tells the system to use “hold-and-modify” mode, a special mode that lets you display up to 4,096 colors on screen at the same time. It is described in the “Advanced Topics” section.&lt;br /&gt;
&lt;br /&gt;
SPRITES tells the system that you are using sprites in this display (either VSprites or Simple Sprites). The system will load color registers for the sprites. Note that since the mouse pointer is a sprite, omitting this mode will prevent the mouse pointer from being displayed when this ViewPort is frontmost.&lt;br /&gt;
&lt;br /&gt;
See [[Graphics Sprites, Bobs and Animation]] for more information about sprites.&lt;br /&gt;
&lt;br /&gt;
EXTRA_HALFBRITE tells the system to use the Extra-Half-Brite mode, a special mode that allows you to display up to 64 colors on screen at the same time. It is described in the “Advanced Topics” section.&lt;br /&gt;
&lt;br /&gt;
If you peruse the &amp;amp;lt;graphics/view.h&amp;amp;gt; include file you will see another flag, EXTENDED_MODE. Never set this flag yourself; it is used by the system to control more advanced mode features.&lt;br /&gt;
&lt;br /&gt;
Be sure to read the section the [[Display Database]] for additional information about the ViewPort mode.&lt;br /&gt;
&lt;br /&gt;
==== Single-playfield Mode vs. Dual-playfield Mode ====&lt;br /&gt;
&lt;br /&gt;
When you specify single-playfield mode you are asking that the system treat all bitplanes as part of the definition of a single playfield image. Each of the bitplanes defined as part of this ViewPort contributes data bits that determine the color of the pixels in a single playfield.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-12.png|frame|center|A Single-playfield Display]]&lt;br /&gt;
&lt;br /&gt;
If you use dual-playfield mode, you can define two independent, separately controllable playfield areas as shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-13.png|frame|center|A Dual-playfield Display]]&lt;br /&gt;
&lt;br /&gt;
In the previous figure, PFBA was included in the display mode. If PFBA had not been included, the relative priorities would have been reversed; playfield 2 would have appeared to be behind playfield 1.&lt;br /&gt;
&lt;br /&gt;
=== Low-resolution Mode vs. High-resolution Mode ===&lt;br /&gt;
&lt;br /&gt;
In LORES mode, horizontal lines of 320 pixels fill most of the ordinary viewing area. The system software lets you define a screen segment width up to 362 pixels in this mode, or you can define a screen segment as narrow as you desire (minimum of 16 pixels). In HIRES mode, 640 pixels fill a horizontal line. In this mode you can specify any width from 16 to 724 pixels. In SUPERHIRES mode, 1,280 pixels fill a horizontal line. In this mode you can specify any width from 16 to 1,448 pixels. The fact that many monitor manufacturers set their monitors to overscan the video display normally limits you to showing only 16 to 320 pixels per line in LORES, 16 to 640 pixels per line in HIRES, or 16 to 1,280 pixels per line in SUPERHIRES. The user can set the monitor’s viewable screen size with the Preferences Overscan editor.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-14.png|frame|center|How HIRES and SUPERHIRES Affect the Width of Pixels]]&lt;br /&gt;
&lt;br /&gt;
==== Interlaced Mode vs. Non-interlaced Mode ====&lt;br /&gt;
&lt;br /&gt;
In interlaced mode, there are twice as many lines available as in non-interlaced mode, providing better vertical resolution in the same display area.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-15.png|frame|center|How LACE Affects Vertical Resolution]]&lt;br /&gt;
&lt;br /&gt;
If the View structure does not specify LACE, and the ViewPort specifies LACE, only the top half of the ViewPort data will be displayed.&lt;br /&gt;
&lt;br /&gt;
If the View structure specifies LACE and the ViewPort is non-interlaced, the same ViewPort data will be repeated in both fields. The height of the ViewPort display is the height specified in the ViewPort structure.&lt;br /&gt;
&lt;br /&gt;
If both the View and the ViewPort are interlaced, the ViewPort will be built with double the normal vertical resolution. That means it will need twice as much data space in memory as a non-interlaced picture to fill the display.&lt;br /&gt;
&lt;br /&gt;
==== ViewPort Display Memory ====&lt;br /&gt;
&lt;br /&gt;
The picture you create in memory can be larger than the screen image that can be displayed within your ViewPort. This big picture (called a raster and represented by the BitMap structure) can have a maximum size dependent upon the version of the Agnus chip in the Amiga. The ECS Agnus can handle rasters up to 16,384x16,384 pixels. Older Agnus chips are limited to rasters up to 1,024x1,024 pixels. The section on [[#Determining Chip Versions|Determining Chip Versions]] explains how to find out which Agnus is installed.&lt;br /&gt;
&lt;br /&gt;
The example in the following figure introduces terms that tell the system how to find the display data and how to display it in the ViewPort. These terms are RHeight, RWidth, RyOffset, RxOffset, DHeight, DWidth, DyOffset and DxOffset.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig27-16.png|frame|center|ViewPort Data Area Parameters]]&lt;br /&gt;
&lt;br /&gt;
The terms RHeight and RWidth do not appear in actual system data structures. They refer to the dimensions of the raster and are used here to relate the size of the raster to the size of the display area.&lt;br /&gt;
&lt;br /&gt;
RHeight is the number of rows in the raster and RWidth is the number of bytes per row times 8. The raster shown in the figure is too big to fit entirely in the display area, so you tell the system which pixel of the raster should appear in the upper left corner of the display segment specified by your ViewPort. The variables that control that placement are RyOffset and RxOffset.&lt;br /&gt;
&lt;br /&gt;
To compute RyOffset and RxOffset, you need RHeight, RWidth, DHeight, and DWidth. The DHeight and DWidth variables define the height and width in pixels of the portion of the display that you want to appear in the ViewPort. The example shows a full-screen, low-resolution mode (320-pixel), non-interlaced (200-line) display formed from the larger overall picture.&lt;br /&gt;
&lt;br /&gt;
Normal values for RyOffset and RxOffset are defined by the formulas:&lt;br /&gt;
&lt;br /&gt;
 0 &amp;lt; = RyOffset &amp;lt; = (RHeight - DHeight)&lt;br /&gt;
 0 &amp;lt; = RxOffset &amp;lt; = (RWidth - DWidth)&lt;br /&gt;
&lt;br /&gt;
Once you have defined the size of the raster and the section of that raster that you wish to display, you need only specify where to put this ViewPort on the screen. This is controlled by the ViewPort variables DyOffset and DxOffset. These are offsets relative to the View.DxOffset and DyOffset.&lt;br /&gt;
&lt;br /&gt;
Possible NTSC values for DyOffset range from -23 to +217 (-46 to +434 if the ViewPort is interlaced), PAL values range from -15 to +267 (-30 to +534 for interlaced ViewPorts). Possible values for DxOffset range from -18 to +362 (-36 to +724 if the ViewPort is Hires, -72 to +1,448 if SuperHires), when the View is in its default, initialized position.&lt;br /&gt;
&lt;br /&gt;
The parameters shown in the figure above are distributed in the following data structures:&lt;br /&gt;
&lt;br /&gt;
* View (information about the whole display) includes the variables that you use to position the whole display on the screen. The View structure contains a Modes field used to determine if the whole display is to be interlaced or non-interlaced. It also contains pointers to its list of ViewPorts and pointers to the Copper instructions produced by the system to create the display you have defined.&lt;br /&gt;
&lt;br /&gt;
* ViewPort (information about this segment of the display) includes the values DxOffset and DyOffset that are used to position this portion relative to the overall View. The ViewPort also contains the variables DHeight and DWidth, which define the size of this display segment; a Modes variable; and a pointer to the local ColorMap. The VideoControl() function and its various tags are used to manipulate the ColorMap and ViewPort.Modes. Each ViewPort also contains a pointer to the next ViewPort. You create a linked list of ViewPorts to define the complete display.&lt;br /&gt;
&lt;br /&gt;
* RasInfo (information about the raster) contains the variables RxOffset and RyOffset. It also contains pointers to the BitMap structure and to a companion RasInfo structure if this is a dual playfield.&lt;br /&gt;
&lt;br /&gt;
* BitMap (information about memory usage) tells the system where to find the display and drawing area memory and shows how this memory space is organized, including the display’s depth.&lt;br /&gt;
&lt;br /&gt;
You must allocate enough memory for the display you define. The memory you use for the display may be shared with the area control structures used for drawing. This allows you to draw into the same areas that you are currently displaying on the screen.&lt;br /&gt;
&lt;br /&gt;
As an alternative, you can define two BitMaps. One of them can be the active structure (that being displayed) and the other can be the inactive structure. If you draw into one BitMap while displaying another, the user cannot see the drawing taking place. This is called &#039;&#039;double-buffering&#039;&#039; of the display. See “Advanced Topics” below for an explanation of the steps required for double-buffering. Double-buffering takes twice as much memory as single-buffering because two full displays are produced.&lt;br /&gt;
&lt;br /&gt;
To determine the amount of required memory for each ViewPort for single-buffering, you can use the following formula.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfx.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Depth, Width, and Height get set to something reasonable. */&lt;br /&gt;
UBYTE Depth, Width, Height;&lt;br /&gt;
&lt;br /&gt;
/* Calculate resulting VP size. */&lt;br /&gt;
bytes_per_ViewPort = Depth * RASSIZE(Width, Height);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RASSIZE() is a system macro attuned to the current design of the system memory allocation for display rasters. See the &amp;amp;lt;graphics/gfx.h&amp;amp;gt; include file for the formula with which RASSIZE() is calculated.&lt;br /&gt;
&lt;br /&gt;
For example, a 32-color ViewPort (depth = 5), 320 pixels wide by 200 lines high currently uses 40,000 bytes. A 16-color ViewPort (depth = 4), 640 pixels wide by 400 lines high currently uses 128,000 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Forming a Basic Display ===&lt;br /&gt;
&lt;br /&gt;
Here are the data structures that you need to define to create a basic display:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct View view;               /* These get used in all versions of the OS */&lt;br /&gt;
struct ViewPort viewPort;&lt;br /&gt;
struct BitMap bitMap;&lt;br /&gt;
struct RasInfo rasInfo;&lt;br /&gt;
struct ColorMap *cm;&lt;br /&gt;
&lt;br /&gt;
struct ViewExtra *vextra;       /* Extra View data */&lt;br /&gt;
struct ViewPortExtra *vpextra;  /* Extra ViewPort data */&lt;br /&gt;
struct MonitorSpec *monspec;    /* Monitor data */&lt;br /&gt;
struct DimensionInfo dimquery;  /* Display dimension data */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewExtra and ViewPortExtra are data structures used to hold extended data about their corresponding parent structure. ViewExtra contains information about the video monitor being used to render the View. ViewPortExtra contains information required for clipping of the ViewPort.&lt;br /&gt;
&lt;br /&gt;
GfxNew() is used to create these extended data structures and GfxAssociate() is used to associate the extended data structure with an appropriate parent structure. Although GfxAssociate() can associate a ViewPortExtra structure with a ViewPort, it is better to use VideoControl() with the VTAG_VIEWPORTEXTRA_SET tag instead. Keep in mind that GfxNew() allocates memory for the resulting data structure which must be returned using GfxFree() before the application exits. The function GfxLookUp() will find the address of an extended data structure from the address of its parent.&lt;br /&gt;
&lt;br /&gt;
==== Preparing the View Structure ====&lt;br /&gt;
&lt;br /&gt;
The following code prepares the View structure for further use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InitView(&amp;amp;amp;view);      /*  Initialize the View.  */&lt;br /&gt;
view.Modes |= LACE;   /*  Only interlaced, 1.3 displays require this */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A ViewExtra structure must also be created with GfxNew() and associated with this View with GfxAssociate() as shown in the example programs RGBBoxes.c and WBClone.c.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* Form the ModeID from values in &amp;amp;lt;displayinfo.h&amp;amp;gt; */&lt;br /&gt;
modeID=DEFAULT_MONITOR_ID | HIRESLACE_KEY;&lt;br /&gt;
&lt;br /&gt;
/* Make the ViewExtra structure */&lt;br /&gt;
if( vextra=GfxNew(VIEW_EXTRA_TYPE) )&lt;br /&gt;
    {&lt;br /&gt;
    /* Attach the ViewExtra to the View */&lt;br /&gt;
    GfxAssociate(&amp;amp;amp;view , vextra);&lt;br /&gt;
    view.Modes |= EXTEND_VSTRUCT;&lt;br /&gt;
&lt;br /&gt;
    /* Initialize the MonitorSpec field of the ViewExtra */&lt;br /&gt;
    if( monspec=OpenMonitor(NULL,modeID) )&lt;br /&gt;
        vextra-&amp;amp;gt;Monitor=monspec;&lt;br /&gt;
    else&lt;br /&gt;
        fail(&amp;amp;quot;Could not get MonitorSpec\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
 else fail(&amp;amp;quot;Could not get ViewExtra\n&amp;amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Preparing the BitMap Structure ====&lt;br /&gt;
&lt;br /&gt;
The BitMap structure tells the system where to find the display and drawing memory and how this memory space is organized. The following code section prepares a BitMap structure, including allocation of memory for the bitmap. This is done with two functions, InitBitMap() and AllocRaster(). InitBitMap() takes four arguments–a pointer to a BitMap and the depth, width, and height of the desired bitmap. Once the bitmap is initialized, memory for its bitplanes must be allocated. AllocRaster() takes two arguments–width and height. Here is a code section to initialize a bitmap:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*  Init BitMap for RasInfo.  */&lt;br /&gt;
InitBitMap(&amp;amp;amp;bitMap, DEPTH, WIDTH, HEIGHT);&lt;br /&gt;
&lt;br /&gt;
/* Set the plane pointers to NULL so the cleanup routine will&lt;br /&gt;
   know if they were used. */&lt;br /&gt;
for(depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    bitMap.Planes[depth] = NULL;&lt;br /&gt;
&lt;br /&gt;
/*  Allocate space for BitMap.  */&lt;br /&gt;
for(depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    {&lt;br /&gt;
    bitMap.Planes[depth] = (PLANEPTR)AllocRaster(WIDTH, HEIGHT);&lt;br /&gt;
    if (bitMap.Planes[depth] == NULL)&lt;br /&gt;
        cleanExit(RETURN_WARN);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code allocates enough memory to handle the display area for as many bitplanes as the depth you have defined.&lt;br /&gt;
&lt;br /&gt;
==== Preparing the RasInfo Structure ====&lt;br /&gt;
&lt;br /&gt;
The RasInfo structure provides information to the system about the location of the BitMap as well as the positioning of the display area as a window against a larger drawing area.&lt;br /&gt;
&lt;br /&gt;
Use the following steps to prepare the RasInfo structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* Initialize the RasInfos. */&lt;br /&gt;
rasInfo.BitMap = &amp;amp;amp;bitMap;   /* Attach the corresponding BitMap.          */&lt;br /&gt;
rasInfo.RxOffset = 0;       /* Align upper left corners of display       */&lt;br /&gt;
rasInfo.RyOffset = 0;       /*   with upper left corner of drawing area. */&lt;br /&gt;
rasInfo.Next = NULL;        /* for a single playfield display, there&lt;br /&gt;
                             * is only one RasInfo structure present     */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The system may be made to reinterpret the RxOffset and RyOffset values in a ViewPort’s RasInfo structure by calling ScrollVPort() with the address of the ViewPort. Changing one or both offsets and calling ScrollVPort() has the effect of scrolling the ViewPort.&lt;br /&gt;
&lt;br /&gt;
==== Preparing the ViewPort Structure ====&lt;br /&gt;
&lt;br /&gt;
To prepare the ViewPort structure for further use, you call InitVPort() and initialize certain fields as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InitVPort(&amp;amp;amp;viewPort);            /* Initialize the ViewPort.             */&lt;br /&gt;
viewPort.RasInfo = &amp;amp;amp;rasInfo;     /* The rasInfo must also be initialized */&lt;br /&gt;
viewPort.DWidth  = WIDTH;&lt;br /&gt;
viewPort.DHeight = HEIGHT;&lt;br /&gt;
&lt;br /&gt;
/* Under 1.3, you should set viewPort.Modes here to select a display mode. */&lt;br /&gt;
/* Under Release 2, use VideoControl() with VTAG_NORMAL_DISP_SET to select */&lt;br /&gt;
/* a display mode by attaching a DisplayInfo structure to the ViewPort.    */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The InitVPort() routine presets certain default values in the ViewPort structure. The defaults include:&lt;br /&gt;
&lt;br /&gt;
* Modes variable set to zero–this means you select a low-resolution display. (To alter this, use VideoControl() with the VTAG_NORMAL_DISP_SET tag as explained below.)&lt;br /&gt;
&lt;br /&gt;
* Next variable set to NULL–no other ViewPort is linked to this one. If you want a display with multiple ViewPorts, you must fill in the link yourself.&lt;br /&gt;
&lt;br /&gt;
If you want to create a View with two or more ViewPorts you must declare and initialize the ViewPorts as above. Then link them together using the ViewPort.Next field with a NULL link for the ViewPort at the end of the chain:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
viewPortA.Next = &amp;amp;amp;viewPortB;    /* Tell first one the address of the second. */&lt;br /&gt;
viewPortB.Next = NULL;          /* There are no others after this one. */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once a ViewPort has been prepared, a ViewPortExtra structure must also be created with GfxNew(), initialized, and associated with the ViewPort via the VideoControl() function. In addition, a DisplayInfo for this mode must be attached to the ViewPort. The fragment below shows how to do this. For complete examples, refer to the program listings of RGBBoxes.c and WBClone.c.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct TagItem vcTags[] =              /* These tags will be passed to the  */&lt;br /&gt;
{                                      /* VideoControl() function to set up */&lt;br /&gt;
    { VTAG_ATTACH_CM_SET, NULL },      /* the extended ViewPort structures  */&lt;br /&gt;
    { VTAG_VIEWPORTEXTRA_SET, NULL },  /* required in Release 2.  The NULL  */&lt;br /&gt;
    { VTAG_NORMAL_DISP_SET, NULL },    /* ti_Data field of these tags must  */&lt;br /&gt;
    { VTAG_END_CM, NULL }              /* be filled in before making the    */&lt;br /&gt;
};                                     /* call to VideoControl().           */&lt;br /&gt;
&lt;br /&gt;
struct DimensionInfo dimquery; /* Release 2 structure for display size data */&lt;br /&gt;
&lt;br /&gt;
/* Make a ViewPortExtra and get ready to attach it */&lt;br /&gt;
if( vpextra = GfxNew(VIEWPORT_EXTRA_TYPE) )&lt;br /&gt;
    {&lt;br /&gt;
    vcTags[1].ti_Data = (ULONG) vpextra;&lt;br /&gt;
&lt;br /&gt;
    /* Initialize the DisplayClip field of the ViewPortExtra structure */&lt;br /&gt;
    if( GetDisplayInfoData( NULL , (UBYTE *) &amp;amp;amp;dimquery ,&lt;br /&gt;
                           sizeof(struct dimquery) , DTAG_DIMS, modeID) )&lt;br /&gt;
        {&lt;br /&gt;
        vpextra-&amp;amp;gt;DisplayClip = dimquery.Nominal;&lt;br /&gt;
&lt;br /&gt;
        /* Make a DisplayInfo and get ready to attach it */&lt;br /&gt;
        if( !(vcTags[2].ti_Data = (ULONG) FindDisplayInfo(modeID)) )&lt;br /&gt;
            fail(&amp;amp;quot;Could not get DisplayInfo\n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    else fail(&amp;amp;quot;Could not get DimensionInfo\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else fail(&amp;amp;quot;Could not get ViewPortExtra\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* This is for backwards compatibility with, for example,   */&lt;br /&gt;
/* a 1.3 screen saver utility that looks at the Modes field */&lt;br /&gt;
viewPort.Modes = (UWORD) (modeID &amp;amp;amp; 0x0000ffff);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Preparing the ColorMap Structure ====&lt;br /&gt;
&lt;br /&gt;
When the View is created, Copper instructions are generated to change the current contents of each color register just before the topmost line of a ViewPort so that this ViewPort’s color registers will be used for interpreting its display. To set the color registers you create a ColorMap for the ViewPort with GetColorMap() and call SetRGB4(). Here are the steps used in 1.3 to initialize a ColorMap:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if( view.ColorMap=GetColorMap( 4L ) )&lt;br /&gt;
    LoadRGB4((&amp;amp;amp;viewPort, colortable, 4);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A ColorMap is attached to the View–usually along with DisplayInfo and ViewExtra–by calling the VideoControl() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*  RGB values for the four colors used.  */&lt;br /&gt;
#define BLACK 0x000&lt;br /&gt;
#define RED   0xf00&lt;br /&gt;
#define GREEN 0x0f0&lt;br /&gt;
#define BLUE  0x00f&lt;br /&gt;
&lt;br /&gt;
/*  Define some colors in an array of UWORDS.  */&lt;br /&gt;
static UWORD colortable[] = { BLACK, RED, GREEN, BLUE };&lt;br /&gt;
&lt;br /&gt;
/* Fill the TagItem Data field with the address of the properly initialized&lt;br /&gt;
   (including ViewPortExtra) structure to be passed to VideoControl().      */&lt;br /&gt;
vc[0].ti_Data = (ULONG)viewPort;&lt;br /&gt;
&lt;br /&gt;
/* Init ColorMap.  2 planes deep, so 4 entries (2 raised to #planes power). */&lt;br /&gt;
if(cm = GetColorMap( 4L ) )&lt;br /&gt;
{&lt;br /&gt;
    /* For applications that must be compatible with 1.3, replace the next 2 */&lt;br /&gt;
    /* lines with: viewPort.ColorMap=cm;                                     */&lt;br /&gt;
    if( VideoControl( cm , vcTags ) )&lt;br /&gt;
        fail(&amp;amp;quot;Could not attach extended structures\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /*  Change colors to those in colortable.  */&lt;br /&gt;
    LoadRGB4(&amp;amp;amp;viewPort, colortable, 4);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The 4 Is For Bits, Not Entries.|text=The 4 in the name LoadRGB4() refers to the fact that each of the red, green, and blue values in a color table entry consists of four bits. It has nothing to do with the fact that this particular color table contains four entries. The call GetRGB4() returns the RGB value of a single entry of a ColorMap. SetRGB4CM() allows individual control of the entries in the ColorMap before or after linking it into the ViewPort.}}&lt;br /&gt;
&lt;br /&gt;
The LoadRGB4() call above could be replaced with the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
register USHORT entry;&lt;br /&gt;
&lt;br /&gt;
/*  Operate on the same four ColorMap entries as above.  */&lt;br /&gt;
for (entry = 0; entry &amp;amp;lt; 4; entry++)&lt;br /&gt;
    {&lt;br /&gt;
    /*  Call SetRGB4CM() with the address of the ColorMap, the entry to be&lt;br /&gt;
        changed, and the Red, Green, and Blue values to be stored there.&lt;br /&gt;
    */&lt;br /&gt;
    SetRGB4CM(viewPort.ColorMap, entry,&lt;br /&gt;
    /*  Extract the three color values from the one colortable entry.  */&lt;br /&gt;
        ((colortable[entry] &amp;amp;amp; 0x0f00) &amp;amp;gt;&amp;amp;gt; 8),&lt;br /&gt;
            ((colortable[entry] &amp;amp;amp; 0x00f0) &amp;amp;gt;&amp;amp;gt; 4),&lt;br /&gt;
                (colortable[entry] &amp;amp;amp; 0x000f));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice above how the four bits for each color are masked out and shifted right to get values from 0 to 15.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=WARNING!|text=It is important to use &#039;&#039;only&#039;&#039; the standard system ColorMap-related calls to access the ColorMap entries. These calls will remain compatible with recent and future enhancements to the ColorMap structure.}}&lt;br /&gt;
&lt;br /&gt;
You might need to specify more colors in the color map than you think. If you use a dual playfield display (covered later in this article) with a depth of 1 for each of the two playfields, this means a total of four colors (two for each playfield). However, because playfield 2 uses color registers starting from number 8 on up when in dual-playfield mode, the color map must be initialized to contain at least 10 entries.&lt;br /&gt;
&lt;br /&gt;
That is, it must contain entries for colors 0 and 1 (for playfield 1) and color numbers 8 and 9 (for playfield 2). Space for sprite colors must be allocated as well. For Amiga system software version 1.3 and earlier, &#039;&#039;when in doubt&#039;&#039;, allocate a ColorMap with 32 entries, just in case.&lt;br /&gt;
&lt;br /&gt;
==== Creating the Display Instructions ====&lt;br /&gt;
&lt;br /&gt;
Now that you have initialized the system data structures, you can request that the system prepare a set of display instructions for the Copper using these structures as input data.&lt;br /&gt;
&lt;br /&gt;
During the one or more blank vertical lines that precede each ViewPort, the Copper is busy changing the characteristics of the display hardware to match the characteristics you expect for this ViewPort.&lt;br /&gt;
&lt;br /&gt;
This may include a change in display resolution, a change in the colors to be used, or other user-defined modifications to system registers.&lt;br /&gt;
&lt;br /&gt;
Here is the code that creates the display instructions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*  Construct preliminary Copper instruction list.  */&lt;br /&gt;
MakeVPort( &amp;amp;amp;view, &amp;amp;amp;viewPort );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this line of code, &amp;amp;amp;view is the address of the View structure and &amp;amp;amp;viewPort is the address of the first ViewPort structure. Using these structures, the system has enough information to build the instruction stream that defines your display.&lt;br /&gt;
&lt;br /&gt;
MakeVPort() creates a special set of instructions that controls the appearance of the display.&lt;br /&gt;
&lt;br /&gt;
If you are using animation, the graphics animation routines create a special set of instructions to control the hardware sprites and the system color registers.&lt;br /&gt;
&lt;br /&gt;
In addition, the advanced user can create special instructions (called user Copper instructions) to change system operations based on the position of the video beam on the screen.&lt;br /&gt;
&lt;br /&gt;
All of these special instructions must be merged together before the system can use them to produce the display you have designed. This is done by the system routine MrgCop() (which stands for “Merge Coprocessor Instructions”). Here is a typical call:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*  Merge preliminary lists into a real Copper list in the view structure.  */&lt;br /&gt;
MrgCop( &amp;amp;amp;view );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Loading and Displaying the View ===&lt;br /&gt;
&lt;br /&gt;
To display the View, you need to load it using LoadView() and turn on the direct memory access (DMA). A typical call is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LoadView(&amp;amp;amp;view);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;amp;view argument is the address of the View structure defined in the example above.&lt;br /&gt;
&lt;br /&gt;
There are two macros, defined in &amp;amp;lt;graphics/gfxmacros.h&amp;amp;gt;, that control display DMA: ON_DISPLAY and OFF_DISPLAY. They simply turn the display DMA control bit in the DMA control register on or off.&lt;br /&gt;
&lt;br /&gt;
If you are drawing to the display area and do not want the user to see intermediate steps in the drawing, you can turn off the display. Because OFF_DISPLAY shuts down the display DMA and possibly speeds up other system operations, it can be used to provide additional memory cycles to the blitter or the &#039;&#039;68000&#039;&#039;. The distribution of system DMA, however, allows four-channel sound, disk read/write, and a sixteen-color, low-resolution display (or four-color, high-resolution display) to operate at the same time with no slowdown (7.1 MHz effective rate) in the operation of the &#039;&#039;68000&#039;&#039;. Using OFF_DISPLAY in a multitasking environment may, however, be an unfriendly thing to do to the other running processes. Use OFF_DISPLAY with discretion.&lt;br /&gt;
&lt;br /&gt;
==== A Custom ViewPort Example ====&lt;br /&gt;
&lt;br /&gt;
The following example creates a View consisting of one ViewPort set to an NTSC, high-resolution, interlaced display mode of nominal dimensions. This example shows both the old 1.3 way of setting up the ViewPort and the new method used in Release 2.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/*  RGBBoxes.c simple ViewPort example -- works with 1.3 and Release 2&lt;br /&gt;
LC -b1 -cfistq -v -y -j73 RGBBoxes.c&lt;br /&gt;
Blink FROM LIB:c.o,RGBBoxes.o TO RGBBoxes LIBRARY LIB:LC.lib,LIB:Amiga.lib&lt;br /&gt;
quit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfx.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxmacros.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/copper.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxnodes.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/videocontrol.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;utility/tagitem.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;clib/graphics_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/dos_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;stdlib.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DEPTH 2     /*  The number of bitplanes.  */&lt;br /&gt;
#define WIDTH 640   /*  Nominal width and height  */&lt;br /&gt;
#define HEIGHT 400  /*  used in 1.3.              */&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
VOID drawFilledBox(WORD , WORD );  /* Function prototypes */&lt;br /&gt;
VOID cleanup(int );&lt;br /&gt;
VOID fail(STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct GfxBase *GfxBase = NULL;&lt;br /&gt;
&lt;br /&gt;
/*  Construct a simple display.  These are global to make freeing easier.   */&lt;br /&gt;
struct View view, *oldview=NULL;  /*  Pointer to old View we can restore it.*/&lt;br /&gt;
struct ViewPort viewPort = { 0 };&lt;br /&gt;
struct BitMap bitMap = { 0 };&lt;br /&gt;
struct ColorMap *cm=NULL;&lt;br /&gt;
&lt;br /&gt;
struct ViewExtra *vextra=NULL;      /* Extended structures used in Release 2 */&lt;br /&gt;
struct MonitorSpec *monspec=NULL;&lt;br /&gt;
struct ViewPortExtra *vpextra=NULL;&lt;br /&gt;
struct DimensionInfo dimquery = { 0 };&lt;br /&gt;
&lt;br /&gt;
UBYTE *displaymem = NULL;     /*  Pointer for writing to BitMap memory.  */&lt;br /&gt;
&lt;br /&gt;
#define BLACK 0x000           /*  RGB values for the four colors used.   */&lt;br /&gt;
#define RED   0xf00&lt;br /&gt;
#define GREEN 0x0f0&lt;br /&gt;
#define BLUE  0x00f&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * main():  create a custom display; works under either 1.3 or Release 2&lt;br /&gt;
 */&lt;br /&gt;
VOID main(VOID)&lt;br /&gt;
{&lt;br /&gt;
WORD depth, box;&lt;br /&gt;
struct RasInfo rasInfo;&lt;br /&gt;
ULONG modeID;&lt;br /&gt;
&lt;br /&gt;
struct TagItem vcTags[] =&lt;br /&gt;
{&lt;br /&gt;
    {VTAG_ATTACH_CM_SET, NULL },&lt;br /&gt;
    {VTAG_VIEWPORTEXTRA_SET, NULL },&lt;br /&gt;
    {VTAG_NORMAL_DISP_SET, NULL },&lt;br /&gt;
    {VTAG_END_CM, NULL }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/*  Offsets in BitMap where boxes will be drawn.  */&lt;br /&gt;
static SHORT boxoffsets[] = { 802, 2010, 3218 };&lt;br /&gt;
&lt;br /&gt;
static UWORD colortable[] = { BLACK, RED, GREEN, BLUE };&lt;br /&gt;
&lt;br /&gt;
/* Open the graphics library */&lt;br /&gt;
GfxBase = (struct GfxBase *)OpenLibrary(&amp;amp;quot;graphics.library&amp;amp;quot;, 33L);&lt;br /&gt;
if(GfxBase == NULL)&lt;br /&gt;
    fail(&amp;amp;quot;Could not open graphics library\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/*  Example steals screen from Intuition if Intuition is around.      */&lt;br /&gt;
oldview = GfxBase-&amp;amp;gt;ActiView;   /* Save current View to restore later. */&lt;br /&gt;
&lt;br /&gt;
InitView(&amp;amp;amp;view);           /*  Initialize the View and set View.Modes.     */&lt;br /&gt;
view.Modes |= LACE;        /*  This is the old 1.3 way (only LACE counts). */&lt;br /&gt;
&lt;br /&gt;
if(GfxBase-&amp;amp;gt;LibNode.lib_Version &amp;amp;gt;= 36)&lt;br /&gt;
    {&lt;br /&gt;
    /* Form the ModeID from values in &amp;amp;lt;displayinfo.h&amp;amp;gt; */&lt;br /&gt;
    modeID=DEFAULT_MONITOR_ID | HIRESLACE_KEY;&lt;br /&gt;
&lt;br /&gt;
    /*  Make the ViewExtra structure   */&lt;br /&gt;
    if( vextra=GfxNew(VIEW_EXTRA_TYPE) )&lt;br /&gt;
        {&lt;br /&gt;
        /* Attach the ViewExtra to the View */&lt;br /&gt;
        GfxAssociate(&amp;amp;amp;view , vextra);&lt;br /&gt;
        view.Modes |= EXTEND_VSTRUCT;&lt;br /&gt;
&lt;br /&gt;
        /* Create and attach a MonitorSpec to the ViewExtra */&lt;br /&gt;
        if( monspec=OpenMonitor(NULL,modeID) )&lt;br /&gt;
            vextra-&amp;amp;gt;Monitor=monspec;&lt;br /&gt;
        else&lt;br /&gt;
            fail(&amp;amp;quot;Could not get MonitorSpec\n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
     else fail(&amp;amp;quot;Could not get ViewExtra\n&amp;amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*  Initialize the BitMap for RasInfo.  */&lt;br /&gt;
InitBitMap(&amp;amp;amp;bitMap, DEPTH, WIDTH, HEIGHT);&lt;br /&gt;
&lt;br /&gt;
/* Set the plane pointers to NULL so the cleanup routine */&lt;br /&gt;
/* will know if they were used.                          */&lt;br /&gt;
for(depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    bitMap.Planes[depth] = NULL;&lt;br /&gt;
&lt;br /&gt;
/*  Allocate space for BitMap.             */&lt;br /&gt;
for (depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    {&lt;br /&gt;
    bitMap.Planes[depth] = (PLANEPTR)AllocRaster(WIDTH, HEIGHT);&lt;br /&gt;
    if (bitMap.Planes[depth] == NULL)&lt;br /&gt;
        fail(&amp;amp;quot;Could not get BitPlanes\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
rasInfo.BitMap = &amp;amp;amp;bitMap;       /*  Initialize the RasInfo.  */&lt;br /&gt;
rasInfo.RxOffset = 0;&lt;br /&gt;
rasInfo.RyOffset = 0;&lt;br /&gt;
rasInfo.Next = NULL;&lt;br /&gt;
&lt;br /&gt;
InitVPort(&amp;amp;amp;viewPort);           /*  Initialize the ViewPort.  */&lt;br /&gt;
view.ViewPort = &amp;amp;amp;viewPort;      /*  Link the ViewPort into the View.  */&lt;br /&gt;
viewPort.RasInfo = &amp;amp;amp;rasInfo;&lt;br /&gt;
viewPort.DWidth = WIDTH;&lt;br /&gt;
viewPort.DHeight = HEIGHT;&lt;br /&gt;
&lt;br /&gt;
/* Set the display mode the old-fashioned way */&lt;br /&gt;
viewPort.Modes=HIRES | LACE;&lt;br /&gt;
&lt;br /&gt;
if(GfxBase-&amp;amp;gt;LibNode.lib_Version &amp;amp;gt;= 36)&lt;br /&gt;
{&lt;br /&gt;
    /* Make a ViewPortExtra and get ready to attach it */&lt;br /&gt;
    if( vpextra = GfxNew(VIEWPORT_EXTRA_TYPE) )&lt;br /&gt;
        {&lt;br /&gt;
        vcTags[1].ti_Data = (ULONG) vpextra;&lt;br /&gt;
&lt;br /&gt;
        /* Initialize the DisplayClip field of the ViewPortExtra */&lt;br /&gt;
        if( GetDisplayInfoData( NULL , (UBYTE *) &amp;amp;amp;dimquery ,&lt;br /&gt;
                                sizeof(dimquery) , DTAG_DIMS, modeID) )&lt;br /&gt;
            {&lt;br /&gt;
            vpextra-&amp;amp;gt;DisplayClip = dimquery.Nominal;&lt;br /&gt;
&lt;br /&gt;
            /* Make a DisplayInfo and get ready to attach it */&lt;br /&gt;
            if( !(vcTags[2].ti_Data = (ULONG) FindDisplayInfo(modeID)) )&lt;br /&gt;
                fail(&amp;amp;quot;Could not get DisplayInfo\n&amp;amp;quot;);&lt;br /&gt;
             }&lt;br /&gt;
        else fail(&amp;amp;quot;Could not get DimensionInfo \n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    else fail(&amp;amp;quot;Could not get ViewPortExtra\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* This is for backwards compatibility with, for example,   */&lt;br /&gt;
    /* a 1.3 screen saver utility that looks at the Modes field */&lt;br /&gt;
    viewPort.Modes = (UWORD) (modeID &amp;amp;amp; 0x0000ffff);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/*  Initialize the ColorMap.  */&lt;br /&gt;
/*  2 planes deep, so 4 entries (2 raised to the #_planes power).  */&lt;br /&gt;
cm = GetColorMap(4L);&lt;br /&gt;
if(cm == NULL)&lt;br /&gt;
    fail(&amp;amp;quot;Could not get ColorMap\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(GfxBase-&amp;amp;gt;LibNode.lib_Version &amp;amp;gt;= 36)&lt;br /&gt;
    {&lt;br /&gt;
    /* Get ready to attach the ColorMap, Release 2-style */&lt;br /&gt;
    vcTags[0].ti_Data = (ULONG) &amp;amp;amp;viewPort;&lt;br /&gt;
&lt;br /&gt;
    /* Attach the color map and Release 2 extended structures */&lt;br /&gt;
    if( VideoControl(cm,vcTags) )&lt;br /&gt;
        fail(&amp;amp;quot;Could not attach extended structures\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    /* Attach the ColorMap, old 1.3-style */&lt;br /&gt;
    viewPort.ColorMap = cm;&lt;br /&gt;
&lt;br /&gt;
LoadRGB4(&amp;amp;amp;viewPort, colortable, 4);  /* Change colors to those in colortable. */&lt;br /&gt;
&lt;br /&gt;
MakeVPort( &amp;amp;amp;view, &amp;amp;amp;viewPort ); /* Construct preliminary Copper instruction list.    */&lt;br /&gt;
&lt;br /&gt;
/* Merge preliminary lists into a real Copper list in the View structure. */&lt;br /&gt;
MrgCop( &amp;amp;amp;view );&lt;br /&gt;
&lt;br /&gt;
/* Clear the ViewPort */&lt;br /&gt;
for(depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    {&lt;br /&gt;
    displaymem = (UBYTE *)bitMap.Planes[depth];&lt;br /&gt;
    BltClear(displaymem, (bitMap.BytesPerRow * bitMap.Rows), 1L);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
LoadView(&amp;amp;amp;view);&lt;br /&gt;
&lt;br /&gt;
/*  Now fill some boxes so that user can see something.          */&lt;br /&gt;
/*  Always draw into both planes to assure true colors.          */&lt;br /&gt;
for (box=1; box&amp;amp;lt;=3; box++)  /* Three boxes; red, green and blue. */&lt;br /&gt;
    {&lt;br /&gt;
    for (depth=0; depth&amp;amp;lt;DEPTH; depth++)        /*  Two planes.   */&lt;br /&gt;
        {&lt;br /&gt;
        displaymem = bitMap.Planes[depth] + boxoffsets[box-1];&lt;br /&gt;
        drawFilledBox(box, depth);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Delay(10L * TICKS_PER_SECOND);   /*  Pause for 10 seconds.                */&lt;br /&gt;
LoadView(oldview);               /*  Put back the old View.               */&lt;br /&gt;
WaitTOF();                       /*  Wait until the the View is being     */&lt;br /&gt;
                                 /*    rendered to free memory.           */&lt;br /&gt;
FreeCprList(view.LOFCprList);    /*  Deallocate the hardware Copper list  */&lt;br /&gt;
if(view.SHFCprList)              /*    created by MrgCop().  Since this   */&lt;br /&gt;
    FreeCprList(view.SHFCprList);/*    is interlace, also check for a     */&lt;br /&gt;
                                 /*    short frame copper list to free.   */&lt;br /&gt;
FreeVPortCopLists(&amp;amp;amp;viewPort);    /*  Free all intermediate Copper lists   */&lt;br /&gt;
                                 /*    from created by MakeVPort().       */&lt;br /&gt;
cleanup(RETURN_OK);              /*  Success.                             */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * fail():  print the error string and call cleanup() to exit&lt;br /&gt;
 */&lt;br /&gt;
void fail(STRPTR errorstring)&lt;br /&gt;
{&lt;br /&gt;
printf(errorstring);&lt;br /&gt;
cleanup(RETURN_FAIL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * cleanup():  free everything that was allocated.&lt;br /&gt;
 */&lt;br /&gt;
VOID cleanup(int returncode)&lt;br /&gt;
{&lt;br /&gt;
WORD depth;&lt;br /&gt;
&lt;br /&gt;
/*  Free the color map created by GetColorMap().  */&lt;br /&gt;
if(cm) FreeColorMap(cm);&lt;br /&gt;
&lt;br /&gt;
/* Free the ViewPortExtra created by GfxNew() */&lt;br /&gt;
if(vpextra) GfxFree(vpextra);&lt;br /&gt;
&lt;br /&gt;
/*  Free the BitPlanes drawing area.  */&lt;br /&gt;
for(depth=0; depth&amp;amp;lt;DEPTH; depth++)&lt;br /&gt;
    {&lt;br /&gt;
    if (bitMap.Planes[depth])&lt;br /&gt;
        FreeRaster(bitMap.Planes[depth], WIDTH, HEIGHT);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Free the MonitorSpec created with OpenMonitor() */&lt;br /&gt;
if(monspec) CloseMonitor( monspec );&lt;br /&gt;
&lt;br /&gt;
/* Free the ViewExtra created with GfxNew() */&lt;br /&gt;
if(vextra) GfxFree(vextra);&lt;br /&gt;
&lt;br /&gt;
/* Close the graphics library */&lt;br /&gt;
CloseLibrary((struct Library *)GfxBase);&lt;br /&gt;
&lt;br /&gt;
exit(returncode);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * drawFilledBox(): create a WIDTH/2 by HEIGHT/2 box of color&lt;br /&gt;
 *                  &amp;amp;quot;fillcolor&amp;amp;quot; into the given plane.&lt;br /&gt;
 */&lt;br /&gt;
VOID drawFilledBox(WORD fillcolor, WORD plane)&lt;br /&gt;
{&lt;br /&gt;
UBYTE value;&lt;br /&gt;
WORD boxHeight, boxWidth, width;&lt;br /&gt;
&lt;br /&gt;
/*  Divide (WIDTH/2) by eight because each UBYTE that */&lt;br /&gt;
/* is written stuffs eight bits into the BitMap.      */&lt;br /&gt;
boxWidth = (WIDTH/2)/8;&lt;br /&gt;
boxHeight = HEIGHT/2;&lt;br /&gt;
&lt;br /&gt;
value = ((fillcolor &amp;amp;amp; (1 &amp;amp;lt;&amp;amp;lt; plane)) != 0) ?  0xff : 0x00;&lt;br /&gt;
&lt;br /&gt;
for( ; boxHeight; boxHeight--)&lt;br /&gt;
    {&lt;br /&gt;
    for(width=0 ; width &amp;amp;lt; boxWidth; width++)&lt;br /&gt;
        *displaymem++ = value;&lt;br /&gt;
&lt;br /&gt;
    displaymem += (bitMap.BytesPerRow - boxWidth);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Exiting Gracefully ====&lt;br /&gt;
&lt;br /&gt;
The preceding sample program provides a way of exiting gracefully with the cleanup() subroutine. This function returns to the memory manager all dynamically-allocated memory chunks. Notice the calls to FreeRaster() and FreeColorMap(). These calls correspond directly to the allocation calls AllocRaster() and GetColorMap() located in the body of the program. Now look at the calls within cleanup() to FreeVPortCopLists() and FreeCprList(). When you call MakeVPort(), the graphics system dynamically allocates some space to hold intermediate instructions from which a final Copper instruction list is created. When you call MrgCop(), these intermediate Copper lists are merged together into the final Copper list, which is then given to the hardware for interpretation. It is this list that provides the stable display on the screen, split into separate ViewPorts with their own colors and resolutions and so on.&lt;br /&gt;
&lt;br /&gt;
When your program completes, you must see that it returns all of the memory resources that it used so that those memory areas are again available to the system for reassignment to other tasks. Therefore, if you use the routines MakeVPort() or MrgCop(), you must also arrange to use FreeCprList() (pointing to each of those lists in the View structure) and FreeVPortCopLists() (pointing to the ViewPort that is about to be deallocated). If your View is interlaced, you will also have to call FreeCprList(&amp;amp;amp;view.SHFCprList) because an interlaced view has a separate Copper list for each of the two fields displayed. Do not confuse FreeVPortCopLists() with FreeCprList(). The former works on intermediate Copper lists for a specific ViewPort, the latter directly on a hardware Copper list from the View.&lt;br /&gt;
&lt;br /&gt;
As a final caveat, notice that when you do free everything, the memory manager or other programs may immediately change the contents of the freed memory. Therefore, if the Copper is still executing an instruction stream (as a result of a previous LoadView()) when you free that memory, the display will malfunction. Once another View has been installed via LoadView(), do a WaitTOF() for the new View to begin displaying, and then you can begin freeing up your resources. WaitTOF() waits for the vertical blanking period to begin and all vertical blank interrupts to complete before returning to the caller. The routine WaitBOVP() (for “WaitBottomOfViewPort”) &#039;&#039;busy&#039;&#039; waits until the vertical beam reaches the bottom of the specified ViewPort before returning to the caller. This means no other tasks run until this function returns.&lt;br /&gt;
&lt;br /&gt;
=== Custom ViewPort Example ===&lt;br /&gt;
&lt;br /&gt;
The following program will create a display with the same attributes as the user’s Workbench screen. It does this by first inquiring as to those attributes, duplicating them, and then creating a similar display.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/*       WBClone.c: To clone the Workbench using graphics calls        */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/*       Compile : SAS/C 5.10a LC -b1 -cfist -L -v -y                 */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/exec.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/screens.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/intuition.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/intuitionbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/intuition_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfx.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxnodes.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/videocontrol.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/graphics_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdio.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;stdlib.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define INTUITIONNAME &amp;amp;quot;intuition.library&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }  /* really */&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*********************************************************************/&lt;br /&gt;
/*                            GLOBAL VARIABLES                       */&lt;br /&gt;
/*********************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct IntuitionBase *IntuitionBase = NULL ;&lt;br /&gt;
struct GfxBase *GfxBase = NULL ;&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID Error (char *String)                                          */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Print string and exit                                              */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID Error (char *String)&lt;br /&gt;
{&lt;br /&gt;
        VOID CloseAll (VOID) ;&lt;br /&gt;
&lt;br /&gt;
        printf (String) ;&lt;br /&gt;
&lt;br /&gt;
        CloseAll () ;&lt;br /&gt;
        exit(0) ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID Init ()                                                       */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Opens all the required libraries allocates all memory, etc.        */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID Init ( VOID )&lt;br /&gt;
{&lt;br /&gt;
        /* Open the intuition library.... */&lt;br /&gt;
        if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary (INTUITIONNAME, 37L)) == NULL)&lt;br /&gt;
                Error (&amp;amp;quot;Could not open the Intuition.library&amp;amp;quot;) ;&lt;br /&gt;
&lt;br /&gt;
        /* Open the graphics library.... */&lt;br /&gt;
        if ((GfxBase = (struct GfxBase *)OpenLibrary (GRAPHICSNAME, 36L)) == NULL)&lt;br /&gt;
                Error (&amp;amp;quot;Could not open the Graphics.library&amp;amp;quot;) ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID CloseAll ()                                                   */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Closes and tidies up everything that was used.                     */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID CloseAll ( VOID )&lt;br /&gt;
{&lt;br /&gt;
        /* Close everything in the reverse order in which they were opened */&lt;br /&gt;
&lt;br /&gt;
        /* Close the Graphics Library */&lt;br /&gt;
        if (GfxBase)&lt;br /&gt;
                CloseLibrary ((struct Library *) GfxBase) ;&lt;br /&gt;
&lt;br /&gt;
        /* Close the Intuition Library */&lt;br /&gt;
        if (IntuitionBase)&lt;br /&gt;
                CloseLibrary ((struct Library *) IntuitionBase) ;&lt;br /&gt;
}&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID DestroyView(struct View *view)                                */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Close and free everything to do with the View                      */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID DestroyView(struct View *view)&lt;br /&gt;
{&lt;br /&gt;
        struct ViewExtra *ve;&lt;br /&gt;
&lt;br /&gt;
        if (view)&lt;br /&gt;
        {&lt;br /&gt;
                if (ve = (struct ViewExtra *)GfxLookUp(view))&lt;br /&gt;
                {&lt;br /&gt;
                        if (ve-&amp;amp;gt;Monitor)&lt;br /&gt;
                                CloseMonitor(ve-&amp;amp;gt;Monitor);&lt;br /&gt;
&lt;br /&gt;
                        GfxFree((struct ExtendedNode *)ve);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                /* Free up the copper lists */&lt;br /&gt;
                if (view-&amp;amp;gt;LOFCprList)&lt;br /&gt;
                        FreeCprList(view-&amp;amp;gt;LOFCprList);&lt;br /&gt;
&lt;br /&gt;
                if (view-&amp;amp;gt;SHFCprList)&lt;br /&gt;
                        FreeCprList(view-&amp;amp;gt;SHFCprList);&lt;br /&gt;
&lt;br /&gt;
                FreeVec(view);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* struct View *DupView(struct View *v, ULONG ModeID)                 */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Duplicate the View.                                                */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct View *DupView(struct View *v, ULONG ModeID)&lt;br /&gt;
{&lt;br /&gt;
        /* Allocate and init a View structure.  Also, get a ViewExtra&lt;br /&gt;
         * structure and attach the monitor type to the View.&lt;br /&gt;
         */&lt;br /&gt;
&lt;br /&gt;
        struct View *view = NULL;&lt;br /&gt;
        struct ViewExtra *ve = NULL;&lt;br /&gt;
        struct MonitorSpec *mspc = NULL;&lt;br /&gt;
&lt;br /&gt;
        if (view = AllocVec(sizeof(struct View), MEMF_PUBLIC | MEMF_CLEAR))&lt;br /&gt;
        {&lt;br /&gt;
                if (ve = GfxNew(VIEW_EXTRA_TYPE))&lt;br /&gt;
                {&lt;br /&gt;
                        if (mspc = OpenMonitor(NULL, ModeID))&lt;br /&gt;
                        {&lt;br /&gt;
                                InitView(view);&lt;br /&gt;
                                view-&amp;amp;gt;DyOffset = v-&amp;amp;gt;DyOffset;&lt;br /&gt;
                                view-&amp;amp;gt;DxOffset = v-&amp;amp;gt;DxOffset;&lt;br /&gt;
                                view-&amp;amp;gt;Modes = v-&amp;amp;gt;Modes;&lt;br /&gt;
                                GfxAssociate(view, (struct ExtendedNode *)ve);&lt;br /&gt;
                                ve-&amp;amp;gt;Monitor = mspc;&lt;br /&gt;
                        }&lt;br /&gt;
                        else printf(&amp;amp;quot;Could not open monitor\n&amp;amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                else printf(&amp;amp;quot;Could not get ViewExtra\n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else printf(&amp;amp;quot;Could not create View\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        if (view &amp;amp;amp;&amp;amp;amp; ve &amp;amp;amp;&amp;amp;amp; mspc)&lt;br /&gt;
                return(view);&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                DestroyView(view);&lt;br /&gt;
                return(NULL);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID DestroyViewPort(struct ViewPort *vp)                          */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Close and free everything to do with the ViewPort.                 */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID DestroyViewPort(struct ViewPort *vp)&lt;br /&gt;
{&lt;br /&gt;
        if (vp)&lt;br /&gt;
        {&lt;br /&gt;
                /* Find the ViewPort&#039;s ColorMap. From that use VideoControl&lt;br /&gt;
                 *  to get the ViewPortExtra, and free it.&lt;br /&gt;
                 * Then free the ColorMap, and finally the ViewPort itself.&lt;br /&gt;
                 */&lt;br /&gt;
                struct ColorMap *cm = vp-&amp;amp;gt;ColorMap;&lt;br /&gt;
                struct TagItem ti[] =&lt;br /&gt;
                {&lt;br /&gt;
                        {VTAG_VIEWPORTEXTRA_GET, NULL}, /* &amp;amp;lt;-- This field will be filled in */&lt;br /&gt;
                        {VTAG_END_CM, NULL}&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
                if (cm)&lt;br /&gt;
                {&lt;br /&gt;
                        if (VideoControl(cm, ti) == NULL)&lt;br /&gt;
                                GfxFree((struct ExtendedNode *)ti[0].ti_Data);&lt;br /&gt;
                        else&lt;br /&gt;
                                printf(&amp;amp;quot;VideoControl error in DestroyViewPort()\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                        FreeColorMap(cm);&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        printf(&amp;amp;quot;Could not free the ColorMap\n&amp;amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                FreeVPortCopLists(vp);&lt;br /&gt;
&lt;br /&gt;
                FreeVec(vp);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* struct ViewPort *DupViewPort(struct ViewPort *vp, ULONG ModeID)    */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Duplicate the ViewPort.                                            */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct ViewPort *DupViewPort(struct ViewPort *vp, ULONG ModeID)&lt;br /&gt;
{&lt;br /&gt;
        /* Allocate and initialise a ViewPort. Copy the ViewPort width and&lt;br /&gt;
         * heights, offsets, and modes values.  Allocate and initialize a&lt;br /&gt;
         * ColorMap.&lt;br /&gt;
         *&lt;br /&gt;
         * Also, allocate a ViewPortExtra, and copy the TextOScan values of the&lt;br /&gt;
         * ModeID from the database into the ViewPortExtra.&lt;br /&gt;
         */&lt;br /&gt;
&lt;br /&gt;
        #define COLOURS 32&lt;br /&gt;
        struct ViewPort *Myvp;&lt;br /&gt;
        struct ViewPortExtra *vpe;&lt;br /&gt;
        struct ColorMap *cm;&lt;br /&gt;
        struct TagItem ti[] =                   /* to attach everything */&lt;br /&gt;
        {&lt;br /&gt;
                {VTAG_ATTACH_CM_SET, NULL},     /* these NULLs will be replaced in the code */&lt;br /&gt;
                {VTAG_VIEWPORTEXTRA_SET, NULL},&lt;br /&gt;
                {VTAG_NORMAL_DISP_SET, NULL},&lt;br /&gt;
                {VTAG_END_CM, NULL}&lt;br /&gt;
        };&lt;br /&gt;
        struct DimensionInfo query = {0};&lt;br /&gt;
        UWORD colour;&lt;br /&gt;
        int c;&lt;br /&gt;
        ULONG gotinfo = NULL;&lt;br /&gt;
&lt;br /&gt;
        if (Myvp = AllocVec(sizeof(struct ViewPort), MEMF_CLEAR | MEMF_PUBLIC))&lt;br /&gt;
        {&lt;br /&gt;
                if (vpe = (struct ViewPortExtra *)GfxNew(VIEWPORT_EXTRA_TYPE))&lt;br /&gt;
                {&lt;br /&gt;
                        if (cm = GetColorMap(32))&lt;br /&gt;
                        {&lt;br /&gt;
                                if (gotinfo = GetDisplayInfoData(NULL, (APTR)&amp;amp;amp;query,&lt;br /&gt;
                                                                 sizeof(query), DTAG_DIMS, ModeID))&lt;br /&gt;
                                {&lt;br /&gt;
                                        InitVPort(Myvp);&lt;br /&gt;
&lt;br /&gt;
                                        /* duplicate the ViewPort structure */&lt;br /&gt;
                                        Myvp-&amp;amp;gt;DWidth = vp-&amp;amp;gt;DWidth;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;DHeight = vp-&amp;amp;gt;DHeight;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;DxOffset = vp-&amp;amp;gt;DxOffset;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;DyOffset = vp-&amp;amp;gt;DyOffset;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;Modes = vp-&amp;amp;gt;Modes;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;SpritePriorities = vp-&amp;amp;gt;SpritePriorities;&lt;br /&gt;
                                        Myvp-&amp;amp;gt;ExtendedModes = vp-&amp;amp;gt;ExtendedModes;&lt;br /&gt;
&lt;br /&gt;
                                        /* duplicate the Overscan values */&lt;br /&gt;
                                        vpe-&amp;amp;gt;DisplayClip = query.TxtOScan;&lt;br /&gt;
&lt;br /&gt;
                                        /* attach everything together */&lt;br /&gt;
                                        ti[0].ti_Data = (ULONG)Myvp;&lt;br /&gt;
                                        ti[1].ti_Data = (ULONG)vpe;&lt;br /&gt;
                                        ti[2].ti_Data = (ULONG)FindDisplayInfo(ModeID);&lt;br /&gt;
                                        if (VideoControl(cm, ti) != NULL)&lt;br /&gt;
                                        {&lt;br /&gt;
                                                printf(&amp;amp;quot;VideoControl error in CreateViewPort()\n&amp;amp;quot;);&lt;br /&gt;
                                        }&lt;br /&gt;
&lt;br /&gt;
                                        /* copy the colours from the workbench */&lt;br /&gt;
                                        for (c = 0; c &amp;amp;lt; COLOURS; c++)&lt;br /&gt;
                                        {&lt;br /&gt;
                                                if ((colour = GetRGB4(vp-&amp;amp;gt;ColorMap, c)) != -1)&lt;br /&gt;
                                                {&lt;br /&gt;
                                                SetRGB4CM(cm, c, (colour &amp;amp;gt;&amp;amp;gt; 8),&lt;br /&gt;
                                                          ((colour &amp;amp;gt;&amp;amp;gt; 4) &amp;amp;amp; 0xf), (colour &amp;amp;amp; 0xf));&lt;br /&gt;
                                                }&lt;br /&gt;
                                        }&lt;br /&gt;
                                }&lt;br /&gt;
                                else printf(&amp;amp;quot;Database error\n&amp;amp;quot;);&lt;br /&gt;
                        }&lt;br /&gt;
                        else printf(&amp;amp;quot;Could not get the ColorMap\n&amp;amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                else printf(&amp;amp;quot;Could not get the ViewPortExtra\n&amp;amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else printf(&amp;amp;quot;Could not get the ViewPort\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        if (Myvp &amp;amp;amp;&amp;amp;amp; vpe &amp;amp;amp;&amp;amp;amp; cm &amp;amp;amp;&amp;amp;amp; gotinfo)&lt;br /&gt;
                return(Myvp);&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                DestroyViewPort(vp);&lt;br /&gt;
                return(NULL);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/***********************************************************************************/&lt;br /&gt;
/*                                                                                 */&lt;br /&gt;
/* VOID DestroyBitMap(struct BitMap *Mybm, SHORT width, SHORT height, SHORT depth) */&lt;br /&gt;
/*                                                                                 */&lt;br /&gt;
/* Close and free everything to do with the BitMap                                 */&lt;br /&gt;
/*                                                                                 */&lt;br /&gt;
/***********************************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID DestroyBitMap(struct BitMap *Mybm, SHORT width, SHORT height, SHORT depth)&lt;br /&gt;
{&lt;br /&gt;
        int i;&lt;br /&gt;
&lt;br /&gt;
        if (Mybm)&lt;br /&gt;
        {&lt;br /&gt;
                for (i = 0; (i &amp;amp;lt; depth); i++)&lt;br /&gt;
                {&lt;br /&gt;
                        if (Mybm-&amp;amp;gt;Planes[i])&lt;br /&gt;
                                FreeRaster(Mybm-&amp;amp;gt;Planes[i], width, height);&lt;br /&gt;
                }&lt;br /&gt;
                FreeVec(Mybm);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/***********************************************************************/&lt;br /&gt;
/*                                                                     */&lt;br /&gt;
/* struct BitMap *CreateBitMap(SHORT width, SHORT height, SHORT depth) */&lt;br /&gt;
/*                                                                     */&lt;br /&gt;
/* Create the BitMap.                                                  */&lt;br /&gt;
/*                                                                     */&lt;br /&gt;
/***********************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct BitMap *CreateBitMap(SHORT width, SHORT height, SHORT depth)&lt;br /&gt;
{&lt;br /&gt;
        /* Allocate a BitMap structure, initialise it, and allocate each plane. */&lt;br /&gt;
&lt;br /&gt;
        struct BitMap *Mybm;&lt;br /&gt;
        PLANEPTR allocated = (PLANEPTR) 1;&lt;br /&gt;
        int i;&lt;br /&gt;
&lt;br /&gt;
        if (Mybm = AllocVec(sizeof(struct BitMap), MEMF_CLEAR | MEMF_PUBLIC))&lt;br /&gt;
        {&lt;br /&gt;
                InitBitMap(Mybm, depth, width, height);&lt;br /&gt;
                for (i = 0; ((i &amp;amp;lt; depth) &amp;amp;amp;&amp;amp;amp; (allocated)); i++)&lt;br /&gt;
                        allocated = (Mybm-&amp;amp;gt;Planes[i] = AllocRaster(width, height));&lt;br /&gt;
&lt;br /&gt;
                if (allocated == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                        printf(&amp;amp;quot;Could not allocate all the planes\n&amp;amp;quot;);&lt;br /&gt;
                        DestroyBitMap(Mybm, width, height, depth);&lt;br /&gt;
                        Mybm = NULL;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        else printf(&amp;amp;quot;Could not get BitMap\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        return(Mybm);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/********************************************************************************/&lt;br /&gt;
/*                                                                              */&lt;br /&gt;
/* VOID ShowView(struct View *view, struct ViewPort *vp, struct BitMap *bm,    */&lt;br /&gt;
/*                                                SHORT width, SHORT height)    */&lt;br /&gt;
/*                                                                              */&lt;br /&gt;
/* Assemble and display the View.                                               */&lt;br /&gt;
/*                                                                              */&lt;br /&gt;
/********************************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID ShowView(struct View *view, struct ViewPort *vp, struct BitMap *bm,&lt;br /&gt;
                                               SHORT width, SHORT height)&lt;br /&gt;
{&lt;br /&gt;
        /* Attach the BitMap to the ViewPort via a RasInfo.  Attach the ViewPort&lt;br /&gt;
         * to the View.  Clear the BitMap, and draw into it by attaching the BitMap&lt;br /&gt;
         * to a RastPort.  Then MakeVPort(), MrgCop() and LoadView().&lt;br /&gt;
         * Just wait for the user to press &amp;amp;lt;RETURN&amp;amp;gt; before returning.&lt;br /&gt;
         */&lt;br /&gt;
&lt;br /&gt;
        struct RastPort *rp;&lt;br /&gt;
        struct RasInfo *ri;&lt;br /&gt;
&lt;br /&gt;
        if (rp = AllocVec(sizeof(struct RastPort), MEMF_CLEAR | MEMF_PUBLIC))&lt;br /&gt;
        {&lt;br /&gt;
                if (ri = AllocVec(sizeof(struct RasInfo), MEMF_CLEAR | MEMF_PUBLIC))&lt;br /&gt;
                {&lt;br /&gt;
                        InitRastPort(rp);&lt;br /&gt;
                        ri-&amp;amp;gt;BitMap = rp-&amp;amp;gt;BitMap = bm;&lt;br /&gt;
                        vp-&amp;amp;gt;RasInfo = ri;&lt;br /&gt;
                        view-&amp;amp;gt;ViewPort = vp;&lt;br /&gt;
&lt;br /&gt;
                        /* render */&lt;br /&gt;
                        SetRast(rp, 0);         /* clear the background */&lt;br /&gt;
                        SetAPen(rp, ((1 &amp;amp;lt;&amp;amp;lt; bm-&amp;amp;gt;Depth) - 1));    /* use the last pen */&lt;br /&gt;
                        Move(rp, 0, 0);&lt;br /&gt;
                        Draw(rp, width, 0);&lt;br /&gt;
                        Draw(rp, width, height);&lt;br /&gt;
                        Draw(rp, 0, height);&lt;br /&gt;
                        Draw(rp, 0, 0);&lt;br /&gt;
&lt;br /&gt;
                        /* display it */&lt;br /&gt;
                        MakeVPort(view, vp);&lt;br /&gt;
                        MrgCop(view);&lt;br /&gt;
                        LoadView(view);&lt;br /&gt;
&lt;br /&gt;
                        getchar();&lt;br /&gt;
&lt;br /&gt;
                        /* bring back the system */&lt;br /&gt;
                        RethinkDisplay();&lt;br /&gt;
&lt;br /&gt;
                        FreeVec(ri);&lt;br /&gt;
                }&lt;br /&gt;
                else printf(&amp;amp;quot;Could not get RasInfo\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                FreeVec(rp);&lt;br /&gt;
        }&lt;br /&gt;
        else printf(&amp;amp;quot;Could not get RastPort\n&amp;amp;quot;);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* VOID main (int argc, char *argv[])                                 */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/* Clone the Workbench View using Graphics Library calls.             */&lt;br /&gt;
/*                                                                    */&lt;br /&gt;
/**********************************************************************/&lt;br /&gt;
&lt;br /&gt;
VOID main (int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
        struct Screen *wb;&lt;br /&gt;
        struct View *Myview;&lt;br /&gt;
        struct ViewPort *Myvp;&lt;br /&gt;
        struct BitMap *Mybm;&lt;br /&gt;
        ULONG ModeID;&lt;br /&gt;
        ULONG IbaseLock;&lt;br /&gt;
&lt;br /&gt;
        Init () ;               /* to open the libraries */&lt;br /&gt;
&lt;br /&gt;
        /* To clone the Workbench using graphics calls involves duplicating&lt;br /&gt;
         * the Workbench ViewPort, ViewPort mode, and Intuition&#039;s View.&lt;br /&gt;
         * This also involves duplicating the DisplayClip for the overscan&lt;br /&gt;
         * value, the colours, and the View position.&lt;br /&gt;
         *&lt;br /&gt;
         * When this is all done, the View, ViewPort, ColorMap and BitMap&lt;br /&gt;
         * (and ViewPortExtra, ViewExtra and RasInfo) all have to be linked&lt;br /&gt;
         * together, and the copperlists made to create the display.&lt;br /&gt;
         *&lt;br /&gt;
         * This is not as difficult as it sounds (trust me!)&lt;br /&gt;
         */&lt;br /&gt;
&lt;br /&gt;
        /* First, lock the Workbench screen, so no changes can be made to it&lt;br /&gt;
         * while we are duplicating it.&lt;br /&gt;
         */&lt;br /&gt;
        if (wb = LockPubScreen(&amp;amp;quot;Workbench&amp;amp;quot;))&lt;br /&gt;
        {&lt;br /&gt;
                /* Find the Workbench&#039;s ModeID. This is a 32-bit number that&lt;br /&gt;
                 * identifies the monitor type, and the display mode of that monitor.&lt;br /&gt;
                 */&lt;br /&gt;
                ModeID = GetVPModeID(&amp;amp;amp;wb-&amp;amp;gt;ViewPort);&lt;br /&gt;
&lt;br /&gt;
                /* We need to duplicate Intuition&#039;s View structure, so lock IntuitionBase&lt;br /&gt;
                 * to prevent the View changing under our feet.&lt;br /&gt;
                 */&lt;br /&gt;
                IbaseLock = LockIBase(0);&lt;br /&gt;
                if (Myview = DupView(&amp;amp;amp;IntuitionBase-&amp;amp;gt;ViewLord, ModeID))&lt;br /&gt;
                {&lt;br /&gt;
                        /* The View has been cloned, so we don&#039;t need to keep it locked. */&lt;br /&gt;
                        UnlockIBase(IbaseLock);&lt;br /&gt;
&lt;br /&gt;
                        /* Now duplicate the Workbench&#039;s ViewPort. Remember, we still have&lt;br /&gt;
                         * the Workbench locked.&lt;br /&gt;
                         */&lt;br /&gt;
                        if (Myvp = DupViewPort(&amp;amp;amp;wb-&amp;amp;gt;ViewPort, ModeID))&lt;br /&gt;
                        {&lt;br /&gt;
                                /* Create a BitMap to render into. This will be of the&lt;br /&gt;
                                 * same dimensions as the Workbench.&lt;br /&gt;
                                 */&lt;br /&gt;
                                if (Mybm = CreateBitMap(wb-&amp;amp;gt;Width, wb-&amp;amp;gt;Height, wb-&amp;amp;gt;BitMap.Depth))&lt;br /&gt;
                                {&lt;br /&gt;
                                        /* Now we have everything copied, show something */&lt;br /&gt;
                                        ShowView(Myview, Myvp, Mybm, wb-&amp;amp;gt;Width-1, wb-&amp;amp;gt;Height-1);&lt;br /&gt;
&lt;br /&gt;
                                        /* Now free up everything we have allocated */&lt;br /&gt;
                                        DestroyBitMap(Mybm, wb-&amp;amp;gt;Width, wb-&amp;amp;gt;Height, wb-&amp;amp;gt;BitMap.Depth);&lt;br /&gt;
                                }&lt;br /&gt;
                                DestroyViewPort(Myvp);&lt;br /&gt;
                        }&lt;br /&gt;
                        DestroyView(Myview);&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        UnlockIBase(IbaseLock);&lt;br /&gt;
                }&lt;br /&gt;
                UnlockPubScreen(NULL, wb);&lt;br /&gt;
        }&lt;br /&gt;
        CloseAll () ;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section covers advanced display topics such as dual-playfield mode, double-buffering, EHB mode and HAM mode.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Dual-Playfield Display ===&lt;br /&gt;
&lt;br /&gt;
In dual-playfield mode, you have two separately controllable playfields. You specify dual-playfield by using any ModeID that includes DPF in its name as listed in &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In dual-playfield mode, you always define two RasInfo data structures. Each of these structures defines one of the playfields. There are five different ways you can configure a dual-playfield display, because there are five different distributions of the bitplanes which the system hardware allows.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Bitplane Assignment in Dual-playfield Mode&lt;br /&gt;
! Number of Bitplanes !! Playfield 1 Depth !! Playfield 2 Depth&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 2 || 1&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2 || 2&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 3 || 2&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 3 || 3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the ModeID includes DPF2 in its name, then the playfield priorities are swapped and playfield 2 will be displayed in front of playfield 1. In this way, you can get more bitplanes in the background playfield than you have in the foreground playfield.&lt;br /&gt;
&lt;br /&gt;
The playfield priority affects only one ViewPort at a time. If you have multiple ViewPorts with dual-playfields, the playfield priority is set for each one individually.&lt;br /&gt;
&lt;br /&gt;
Here’s a summary of the steps you need to take to create a dual-playfield display:&lt;br /&gt;
&lt;br /&gt;
# Allocate one View structure and one ViewPort structure.&lt;br /&gt;
# Allocate two BitMap structures. Allocate two RasInfo structures (linked together), each pointing to a separate BitMap. The two RasInfo structures are linked together as follows: &amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct RasInfo playfield1, playfield2;&lt;br /&gt;
&lt;br /&gt;
playfield1.Next = &amp;amp;playfield2;&lt;br /&gt;
playfield2.Next = NULL;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Initialize each BitMap structure to describe one playfield, using one of the permissible bitplane distributions shown in the above table and allocate memory for the bitplanes themselves. Note that BitMap 1 and BitMap 2 need &#039;&#039;not&#039;&#039; be the same width and height.&lt;br /&gt;
# Initialize the ViewPort structure. Specify dual-playfield mode by selecting a ModeID that includes DPF (or DPF2) in its name as listed in &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt;. Set the ViewPort.RasInfo field to the address of the playfield 1 RasInfo.&lt;br /&gt;
# Set up the ColorMap information&lt;br /&gt;
# Call MakeVPort(), MrgCop() and LoadView() to display the newly created ViewPort.&lt;br /&gt;
&lt;br /&gt;
For display purposes, each of the two BitMaps is assigned to a separate ViewPort. To &#039;&#039;draw&#039;&#039; separately into the BitMaps, you must also assign these BitMaps to two separate RastPorts. The section called “Initializing a RastPort Structure” shows you how to use a RastPort data structure to control your drawing routines.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Double-Buffered Display ===&lt;br /&gt;
&lt;br /&gt;
To produce smooth animation or similar effects, it is occasionally necessary to double-buffer your display. To prevent the user from seeing your graphics rendering while it is in progress, you will want to draw into one memory area while actually displaying a different area.&lt;br /&gt;
&lt;br /&gt;
There are two methods of creating and displaying a double-buffered display. The simplest method is to create two complete Views and switch back and forth between them with LoadView() and WaitTOF().&lt;br /&gt;
&lt;br /&gt;
The second method consists of creating two separate display areas and two sets of pointers to those areas for a single View. This is more complicated but takes less memory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Allocate one ViewPort structure and one View structure.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Allocate two BitMap structures and one RasInfo structure. Initialize each BitMap structure to describe one drawing area and allocate memory for the bitplanes themselves. Initialize the RasInfo structure, setting the RasInfo.BitMap field to the address of one of the two BitMaps you created.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Call MakeVPort(), MrgCop() and LoadView(). When you call MrgCop(), the system uses the information you have provided to create a Copper instruction list for the Copper to execute. The system allocates memory for a long-frame (LOF) Copper list and, if this is an interlaced display, a short-frame (SHF) Copper list as well. The system places a pointer to the long-frame Copper list in View.LOFCprList and a pointer to a short-frame Copper list (if this is an interlaced display) in View.SHFCprList. The Copper instruction stream referenced by these pointers applies to the first BitMap.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Save the values in View.LOFCprList and View.SHFCprlist and reset these fields to zero. Place a pointer to the second BitMap structure in the RasInfo.BitMap field. Next call MakeVPort() and MrgCop().&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;When you perform MrgCop() with the Copper instruction list fields of the View set to zero, the system automatically allocates and fills in a new list of instructions for the Copper. Now you have created two sets of instruction streams for the Copper, one that works with data in the first BitMap and the other that works with data in the second BitMap.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;You can save pointers to the second list of Copper instructions as well. Then, to perform the double-buffering, alternate between the two Copper lists. The code for the double-buffering loop would be as follows: call WaitTOF(), change the Copper instruction list pointers in the View, call LoadView() to show one of the BitMaps while drawing into the other BitMap, and repeat.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember that you will have to call FreeCprList() on both sets of Copper lists when you have finished.&lt;br /&gt;
&lt;br /&gt;
=== Extra-Half-Brite Mode ===&lt;br /&gt;
&lt;br /&gt;
In the Extra-Half-Brite mode you can create a single-playfield, low-resolution display with up to 64 colors, double the normal maximum of 32. This requires your ViewPort to be defined with six bitplanes. You specify EHB by selecting any ModeID which includes EXTRAHALFBRITE in its name as defined in the include file &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
When setting up the color palette for an EHB display, you only specify values for registers 0 to 31. If you draw using color numbers 0 through 31, the pixel you draw will be the color specified in that particular system color register. If you draw using a color number from 32 to 63, then the color displayed will be half the intensity value of the corresponding color register from 0 to 31. For example, if color register 0 is set to 0xFFF (white), then color number 32 would be half this value or 0x777 (grey).&lt;br /&gt;
&lt;br /&gt;
EHB mode uses all six bitplanes. The color register (0 through 31) is obtained from the bit combinations from planes 5 to 1, in that order of significance. Plane 6 is used to determine whether the full intensity (bit value 0) color or half-intensity (bit value 1) color is to be displayed.&lt;br /&gt;
&lt;br /&gt;
=== Hold-And-Modify Mode ===&lt;br /&gt;
&lt;br /&gt;
In hold-and-modify mode you can create a single-playfield, low-resolution display in which 4,096 different colors can be displayed simultaneously. This requires your ViewPort to be defined with six bitplanes. You specify HAM by selecting any ModeID which includes HAM in its name as defined in &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
When you draw into the BitMap associated with this ViewPort, you can choose colors in one of four different ways. If you draw using color numbers 0 to 15, the pixel you draw will appear in the color specified in that particular system color register. If you draw with any other color value (16 to 63) the color displayed depends on the color of the pixel that is to the immediate left of this pixel on the screen. To see how this works, consider how the bitplanes are used in HAM.&lt;br /&gt;
&lt;br /&gt;
Hold-and-modify mode requires six bitplanes. Planes 5 and 6 are used to modify the way bits from planes 1 through 4 are treated, as follows:&lt;br /&gt;
&lt;br /&gt;
* If the bit combination from planes 6 and 5 for any given pixel is 00, normal color selection procedure is followed. Thus, the bit combinations from planes 4 to 1, in that order of significance, are used to choose one of 16 color registers (registers 0 through 15).&lt;br /&gt;
* If the bit combination in planes 6 and 5 is 01, the color of the pixel immediately to the left of this pixel is duplicated and then modified. The bit combinations from planes 4 through 1 are used to replace the four bits representing the blue value of the preceding pixel color. (No color registers are changed.)&lt;br /&gt;
* If the bit combination in planes 6 and 5 is 10, then the color of the pixel immediately to the left of this pixel is duplicated and modified. The bit combinations from planes 4 through 1 are used to replace the four bits representing the red value of the preceding pixel color.&lt;br /&gt;
* If the bit combination in planes 6 and 5 is 11, then the color of the pixel immediately to the left of this pixel is duplicated and modified. The bit combinations from planes 4 through 1 are used to replace the four bits representing the green value of the preceding pixel color.&lt;br /&gt;
&lt;br /&gt;
You can use just five bitplanes in HAM mode. In that case, the data for the sixth plane is automatically assumed to be 0. Note that for the first pixel in each line, hold-and-modify begins with the background color. The color choice does &#039;&#039;not&#039;&#039; carry over from the preceding line.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Since a typical hold-and-modify pixel only changes one of the three RGB color values at a time, color selection is limited. HAM mode does allow for the display of 4,096 colors simultaneously, but there are only 64 color options for any given pixel (not 4,096). The color of a pixel depends on the color of the preceding pixel.}}&lt;br /&gt;
&lt;br /&gt;
== User Copper Lists ==&lt;br /&gt;
&lt;br /&gt;
The Copper coprocessor allows you to produce mid-screen changes in certain hardware registers in addition to changes that the system software already provides. For example, it is the Copper that allows the Amiga to split the viewing area into multiple draggable screens, each with its own independent set of colors.&lt;br /&gt;
&lt;br /&gt;
To create your own mid-screen effects on the system hardware registers, you provide “user Copper lists” that can be merged into the system Copper lists.&lt;br /&gt;
&lt;br /&gt;
In the ViewPort data structure there is a pointer named UCopIns. If this pointer value is non-NULL, it points to a user Copper list that you have dynamically allocated and initialized to contain your own special hardware-stuffing instructions.&lt;br /&gt;
&lt;br /&gt;
You allocate a user Copper list by an instruction sequence such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct UCopList *uCopList = NULL;&lt;br /&gt;
&lt;br /&gt;
/* Allocate memory for the Copper list.  Make certain that the initial */&lt;br /&gt;
/* memory is cleared.                                                  */&lt;br /&gt;
uCopList = (struct UCopList *)&lt;br /&gt;
        AllocMem(sizeof(struct UCopList), MEMF_PUBLIC|MEMF_CLEAR);&lt;br /&gt;
&lt;br /&gt;
if (uCopList == NULL)&lt;br /&gt;
        return(FALSE);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt;oxNote:User Copper lists do &#039;&#039;not&#039;&#039; have to be in Chip RAM.&lt;br /&gt;
&lt;br /&gt;
=== Copper List Macros ===&lt;br /&gt;
&lt;br /&gt;
Once this pointer to a user Copper list is available, you can use it with system macros (&amp;amp;lt;graphics/gfxmacros.h&amp;amp;gt;) to instruct the system what to add to its own list of things for the Copper to do within a specific ViewPort. The file &amp;amp;lt;graphics/gfxmacros.h&amp;amp;gt; provides the following five macro functions that implement user Copper instructions.&lt;br /&gt;
&lt;br /&gt;
initializes the Copper list buffer. It is used to specify how many instructions are going to be placed in the Copper list. It is called as follows.&lt;br /&gt;
&lt;br /&gt;
CINIT(uCopList, num_entries);&lt;br /&gt;
&lt;br /&gt;
The uCopList argument is a pointer tot he user Copper list and num_entries is the number of entries in the list.&lt;br /&gt;
&lt;br /&gt;
waits for the video beam to reach a particular horizontal and vertical position. Its format is:&lt;br /&gt;
&lt;br /&gt;
CWAIT(uCopList, v, h)&lt;br /&gt;
&lt;br /&gt;
Again, uCopList is the pointer to the Copper list. The v argument is the vertical position for which to wait, specified relative to the top of the ViewPort. The legal range of values (for both NTSC and PAL) is from 0 to 255; h is the horizontal position for which to wait. The legal range of values (for both NTSC and PAL) is from 0 to 226.&lt;br /&gt;
&lt;br /&gt;
installs a particular value into a specified system register. Its format is:&lt;br /&gt;
&lt;br /&gt;
CMOVE(uCopList, reg, value)&lt;br /&gt;
&lt;br /&gt;
Again, uCopList is the pointer to the Copper list. The reg argument is the register to be affected, specified in this form: custom.&#039;&#039;register-name&#039;&#039; where the &#039;&#039;register-name&#039;&#039; is one of the registers listed in the Custom structure in &amp;amp;lt;hardware/custom.h&amp;amp;gt;. The value argument to CMOVE is the value to place in the register.&lt;br /&gt;
&lt;br /&gt;
increments the user Copper list pointer to the next position in the list. It is usually invoked for the programmer as part of the macro definitions CWAIT or CMOVE. Its format is:&lt;br /&gt;
&lt;br /&gt;
CBump(uCopList)&lt;br /&gt;
&lt;br /&gt;
where uCopList is the pointer to the user Copper list.&lt;br /&gt;
&lt;br /&gt;
terminates the user Copper list. Its format is:&lt;br /&gt;
&lt;br /&gt;
CEND(uCopList)&lt;br /&gt;
&lt;br /&gt;
where uCopList is the pointer to the user Copper list.&lt;br /&gt;
&lt;br /&gt;
Executing any of the user Copper list macros causes the system to dynamically allocate special data structures called intermediate Copper lists that are linked into your user Copper list (the list to which uCopList points) describing the operation. When you call the function MrgCop(&amp;amp;amp;view) as shown in the section called “Forming A Basic Display,” the system uses all of its intermediate Copper lists to sort and merge together the real Copper lists for the system (LOFCprList and SHFCprList).&lt;br /&gt;
&lt;br /&gt;
When your program exits, you must return to the system all of the memory that you allocated or caused to be allocated. This means that you must return the intermediate Copper lists, as well as the user Copper list data structure. Here are two different methods for returning this memory to the system.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/* Returning memory to the system if you have NOT&lt;br /&gt;
 * obtained the ViewPort from Intuition.  */&lt;br /&gt;
FreeVPortCopLists(viewPort);&lt;br /&gt;
&lt;br /&gt;
/* Returning memory to the system if you HAVE&lt;br /&gt;
 * obtained the ViewPort from Intuition.  */&lt;br /&gt;
CloseScreen(screen);    /* Intuition only */&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
User Copper lists may be clipped, under Release 2 and later, to ViewPort boundaries if the appropriate tag (VTAG_USERCLIP_SET) is passed to VideoControl(). Under earlier releases, the user Copper list would “leak” through to lower ViewPorts.&lt;br /&gt;
&lt;br /&gt;
=== Copper List Example ===&lt;br /&gt;
&lt;br /&gt;
The example program below shows the use of user Copper lists under Intuition.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/*  UserCopperExample.c&lt;br /&gt;
    User Copper List Example&lt;br /&gt;
    For SAS/C 5.10a,&lt;br /&gt;
    compile with:  LC -b1 -cfist -L -v -y UserCopperExample.c&lt;br /&gt;
    link with lc.lib and amiga.lib&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;exec/types.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;exec/memory.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxbase.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/gfxmacros.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/copper.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;graphics/videocontrol.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/intuition.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;intuition/preferences.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;hardware/custom.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/dos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;           /*  Prototypes.  */&lt;br /&gt;
#include &amp;amp;lt;clib/graphics_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/intuition_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/dos_protos.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;amp;lt;stdlib.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*  Use this structure to gain access to the custom registers.  */&lt;br /&gt;
extern struct Custom far custom;&lt;br /&gt;
&lt;br /&gt;
/*  Global variables.  */&lt;br /&gt;
struct GfxBase        *GfxBase = NULL;&lt;br /&gt;
struct IntuitionBase  *IntuitionBase = NULL;&lt;br /&gt;
struct Screen         *screen = NULL;&lt;br /&gt;
struct Window         *window = NULL;&lt;br /&gt;
&lt;br /&gt;
VOID main( VOID ), cleanExit( WORD );&lt;br /&gt;
WORD openAll( VOID ), loadCopper( VOID );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *   The main() routine -- just calls subroutines&lt;br /&gt;
 */&lt;br /&gt;
VOID main( VOID )&lt;br /&gt;
{&lt;br /&gt;
WORD ret_val;&lt;br /&gt;
struct IntuiMessage     *intuiMessage;&lt;br /&gt;
&lt;br /&gt;
        /*  Open the libraries, a screen and a window.  */&lt;br /&gt;
        ret_val = openAll();&lt;br /&gt;
        if (RETURN_OK == ret_val)&lt;br /&gt;
        {&lt;br /&gt;
                /*  Create and attach the user Copper list.  */&lt;br /&gt;
                ret_val = loadCopper();&lt;br /&gt;
                if (RETURN_OK == ret_val)&lt;br /&gt;
                {&lt;br /&gt;
                        /*  Wait until the user clicks in the close gadget.  */&lt;br /&gt;
                        (VOID) Wait(1&amp;amp;lt;&amp;amp;lt;window-&amp;amp;gt;UserPort-&amp;amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
                        while (intuiMessage = (struct IntuiMessage *)GetMsg(window-&amp;amp;gt;UserPort))&lt;br /&gt;
                                ReplyMsg((struct Message *)intuiMessage);&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        cleanExit(ret_val);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * openAll() -- opens the libraries, screen and window&lt;br /&gt;
 */&lt;br /&gt;
WORD openAll( VOID )&lt;br /&gt;
{&lt;br /&gt;
#define MY_WA_WIDTH 270 /*  Width of window.  */&lt;br /&gt;
&lt;br /&gt;
        WORD ret_val = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
        /*  Prepare to explicitly request Topaz 60 as the screen font.  */&lt;br /&gt;
        struct TextAttr topaz60 =&lt;br /&gt;
        {&lt;br /&gt;
                (STRPTR)&amp;amp;quot;topaz.font&amp;amp;quot;,&lt;br /&gt;
                (UWORD)TOPAZ_SIXTY, (UBYTE)0, (UBYTE)0&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        GfxBase = (struct GfxBase *)OpenLibrary(&amp;amp;quot;graphics.library&amp;amp;quot;, 37L);&lt;br /&gt;
        if (GfxBase == NULL)&lt;br /&gt;
                ret_val = ERROR_INVALID_RESIDENT_LIBRARY;&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                IntuitionBase = (struct IntuitionBase *)&lt;br /&gt;
                        OpenLibrary(&amp;amp;quot;intuition.library&amp;amp;quot;, 37L);&lt;br /&gt;
&lt;br /&gt;
                if (IntuitionBase == NULL)&lt;br /&gt;
                        ret_val = ERROR_INVALID_RESIDENT_LIBRARY;&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        screen = OpenScreenTags( NULL,&lt;br /&gt;
                                 SA_Overscan, OSCAN_STANDARD,&lt;br /&gt;
                                 SA_Title,    &amp;amp;quot;User Copper List Example&amp;amp;quot;,&lt;br /&gt;
                                 SA_Font,     (ULONG)&amp;amp;amp;topaz60,&lt;br /&gt;
                                 TAG_END);&lt;br /&gt;
&lt;br /&gt;
                        if (NULL == screen)&lt;br /&gt;
                                ret_val = ERROR_NO_FREE_STORE;&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                window = OpenWindowTags( NULL,&lt;br /&gt;
                                         WA_CustomScreen, screen,&lt;br /&gt;
                                         WA_Title,        &amp;amp;quot;&amp;amp;lt;- Click here to quit.&amp;amp;quot;,&lt;br /&gt;
                                         WA_IDCMP,        CLOSEWINDOW,&lt;br /&gt;
                                         WA_Flags,        WINDOWDRAG|WINDOWCLOSE|INACTIVEWINDOW,&lt;br /&gt;
                                         WA_Left,         (screen-&amp;amp;gt;Width-MY_WA_WIDTH)/2,&lt;br /&gt;
                                         WA_Top,          screen-&amp;amp;gt;Height/2,&lt;br /&gt;
                                         WA_Height,       screen-&amp;amp;gt;Font-&amp;amp;gt;ta_YSize + 3,&lt;br /&gt;
                                         WA_Width,        MY_WA_WIDTH,&lt;br /&gt;
                                         TAG_END);&lt;br /&gt;
&lt;br /&gt;
                                if (NULL == window)&lt;br /&gt;
                                        ret_val = ERROR_NO_FREE_STORE;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return(ret_val);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * loadCopper() -- creates a Copper list program and adds it to the system&lt;br /&gt;
 */&lt;br /&gt;
WORD loadCopper( VOID )&lt;br /&gt;
{&lt;br /&gt;
register USHORT   i, scanlines_per_color;&lt;br /&gt;
         WORD     ret_val    = RETURN_OK;&lt;br /&gt;
struct   ViewPort *viewPort;&lt;br /&gt;
struct   UCopList *uCopList  = NULL;&lt;br /&gt;
struct   TagItem  uCopTags[] =&lt;br /&gt;
          {&lt;br /&gt;
                { VTAG_USERCLIP_SET, NULL },&lt;br /&gt;
                { VTAG_END_CM, NULL }&lt;br /&gt;
          };&lt;br /&gt;
&lt;br /&gt;
UWORD    spectrum[] =&lt;br /&gt;
          {&lt;br /&gt;
                0x0604, 0x0605, 0x0606, 0x0607, 0x0617, 0x0618, 0x0619,&lt;br /&gt;
                0x0629, 0x072a, 0x073b, 0x074b, 0x074c, 0x075d, 0x076e,&lt;br /&gt;
                0x077e, 0x088f, 0x07af, 0x06cf, 0x05ff, 0x04fb, 0x04f7,&lt;br /&gt;
                0x03f3, 0x07f2, 0x0bf1, 0x0ff0, 0x0fc0, 0x0ea0, 0x0e80,&lt;br /&gt;
                0x0e60, 0x0d40, 0x0d20, 0x0d00&lt;br /&gt;
          };&lt;br /&gt;
&lt;br /&gt;
#define NUMCOLORS 32&lt;br /&gt;
&lt;br /&gt;
        /*  Allocate memory for the Copper list.  */&lt;br /&gt;
        /*  Make certain that the initial memory is cleared.  */&lt;br /&gt;
        uCopList = (struct UCopList *)&lt;br /&gt;
                AllocMem(sizeof(struct UCopList), MEMF_PUBLIC|MEMF_CLEAR);&lt;br /&gt;
&lt;br /&gt;
        if (NULL == uCopList)&lt;br /&gt;
                ret_val = ERROR_NO_FREE_STORE;&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                /*  Initialize the Copper list buffer.  */&lt;br /&gt;
                CINIT(uCopList, NUMCOLORS);&lt;br /&gt;
&lt;br /&gt;
                scanlines_per_color = screen-&amp;amp;gt;Height/NUMCOLORS;&lt;br /&gt;
&lt;br /&gt;
                /*  Load in each color.  */&lt;br /&gt;
                for (i=0; i&amp;amp;lt;NUMCOLORS; i++)&lt;br /&gt;
                        {&lt;br /&gt;
                        CWAIT(uCopList, (i*scanlines_per_color), 0);&lt;br /&gt;
                        CMOVE(uCopList, custom.color[0], spectrum[i]);&lt;br /&gt;
                        }&lt;br /&gt;
&lt;br /&gt;
                CEND(uCopList); /*  End the Copper list  */&lt;br /&gt;
&lt;br /&gt;
                viewPort = ViewPortAddress(window);     /*  Get a pointer to the ViewPort.  */&lt;br /&gt;
                Forbid();       /*  Forbid task switching while changing the Copper list.  */&lt;br /&gt;
                viewPort-&amp;amp;gt;UCopIns=uCopList;&lt;br /&gt;
                Permit();       /*  Permit task switching again.  */&lt;br /&gt;
&lt;br /&gt;
                /*  Enable user copper list clipping for this ViewPort.  */&lt;br /&gt;
                (VOID) VideoControl( viewPort-&amp;amp;gt;ColorMap, uCopTags );&lt;br /&gt;
&lt;br /&gt;
                RethinkDisplay();       /*  Display the new Copper list.  */&lt;br /&gt;
&lt;br /&gt;
                return(ret_val);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  cleanExit() -- returns all resources that were used.&lt;br /&gt;
 */&lt;br /&gt;
VOID cleanExit( WORD retval )&lt;br /&gt;
{&lt;br /&gt;
struct ViewPort *viewPort;&lt;br /&gt;
&lt;br /&gt;
if (NULL != IntuitionBase)&lt;br /&gt;
{&lt;br /&gt;
        if (NULL != screen)&lt;br /&gt;
        {&lt;br /&gt;
                if (NULL != window)&lt;br /&gt;
                {&lt;br /&gt;
                        viewPort = ViewPortAddress(window);&lt;br /&gt;
                        if (NULL != viewPort-&amp;amp;gt;UCopIns)&lt;br /&gt;
                        {&lt;br /&gt;
                                /*  Free the memory allocated for the Copper.  */&lt;br /&gt;
                                FreeVPortCopLists(viewPort);&lt;br /&gt;
                                RemakeDisplay();&lt;br /&gt;
                        }&lt;br /&gt;
                        CloseWindow(window);&lt;br /&gt;
                }&lt;br /&gt;
                CloseScreen(screen);&lt;br /&gt;
        }&lt;br /&gt;
        CloseLibrary((struct Library *)IntuitionBase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (NULL != GfxBase)&lt;br /&gt;
        CloseLibrary((struct Library *)GfxBase);&lt;br /&gt;
&lt;br /&gt;
exit((int)retval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ECS and Genlocking Features ==&lt;br /&gt;
&lt;br /&gt;
The Enhanced Chip Set (ECS) Denise chip (&#039;&#039;8373-R2a&#039;&#039;), coupled with the Release 2 graphics library, opens up a whole new set of genlocking possibilities. Unlike the old Denise, whose only genlocking ability allowed keying on color register zero, the ECS Denise allows keying on any color register. Also, the ECS Denise allows keying on any bitplane of the ViewPort being genlocked. With the ECS Denise, the border area surrounding the display can be made transparent (always passes video) or opaque (overlays using color 0). All the new features are set individually for each ViewPort. These features can be used in conjunction with each other, making interesting scenarios possible.&lt;br /&gt;
&lt;br /&gt;
=== Genlock Control ===&lt;br /&gt;
&lt;br /&gt;
Using VideoControl(), a program can enable, disable, or obtain the state of a ViewPort’s genlocking features. It returns NULL if no error occurred. The function uses a tag based interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;error = BOOL VideoControl( struct ColorMap *cm, struct TagItem *ti );&amp;lt;/pre&amp;gt;&lt;br /&gt;
The ti argument is a list of video commands stored in an array of TagItem structures. The cm argument specifies which ColorMap and, indirectly, which ViewPort these genlock commands will be applied to. The possible commands are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;VTAG_BITPLANEKEY_GET, _SET, _CLR&lt;br /&gt;
VTAG_CHROMA_PLANE_GET, _SET&lt;br /&gt;
VTAG_BORDERBLANK_GET, _SET, _CLR&lt;br /&gt;
VTAG_BORDERNOTRANS_GET, _SET, _CLR&lt;br /&gt;
VTAG_CHROMAKEY_GET, _SET, _CLR&lt;br /&gt;
VTAG_CHROMAPEN_GET, _SET, _CLR&amp;lt;/pre&amp;gt;&lt;br /&gt;
This section covers only the genlock VideoControl() tags. See &amp;amp;lt;graphics/videocontrol.h&amp;amp;gt; for a complete list of all the available tags you can use with VideoControl().&lt;br /&gt;
&lt;br /&gt;
VTAG_BITPLANEKEY_GET&lt;br /&gt;
&lt;br /&gt;
is used to find out the status of the bitplane keying mode. VTAG_BITPLANEKEY_SET and VTAG_BITPLANEKEY_CLR activate and deactivate bitplane keying mode. If bitplane key mode is on, genlocking will key on the bits set in a specific bitplane from the ViewPort (the specific bitplane is set with a different tag). The data portion of these tags is NULL.&lt;br /&gt;
&lt;br /&gt;
For inquiry commands like VTAG_BITPLANEKEY_GET (tags ending in _GET), VideoControl() changes the _GET tag ID (ti_Tag) to the corresponding _SET or _CLR tag ID, reflecting the current state of the genlock mode. For example, when passed the following tag array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct TagItem videocommands[] =&lt;br /&gt;
{&lt;br /&gt;
    {VTAG_BITPLANEKEY_GET, NULL},&lt;br /&gt;
    {VTAG_END_CM, NULL}&lt;br /&gt;
};&amp;lt;/pre&amp;gt;&lt;br /&gt;
VideoControl()&lt;br /&gt;
&lt;br /&gt;
changes the VTAG_BITPLANEKEY_GET tag ID (ti_Tag) to VTAG_BITPLANEKEY_SET if bitplane keying is currently on, or to VTAG_BITPLANEKEY_CLR if bitplane keying is off. In both of these cases, VideoControl() only uses the tag’s ID, ignoring the tag’s data field (ti_Data).&lt;br /&gt;
&lt;br /&gt;
The VTAG_CHROMA_PLANE_GET tag returns the number of the bitplane keyed on when bitplane keying mode is on. VideoControl() changes the tag’s data value to the bitplane number. VTAG_CHROMA_PLANE_SET sets the bitplane number to the tag’s data value.&lt;br /&gt;
&lt;br /&gt;
VTAG_BORDERBLANK_GET&lt;br /&gt;
&lt;br /&gt;
is used to obtain the border blank mode status. This tag works exactly like VTAG_BITPLANEKEY_GET. VideoControl() changes the tag’s ID to reflect the current border blanking state. VTAG_BORDERBLANK_SET and VTAG_BORDERBLANK_CLR activate and deactivate border blanking. If border blanking is on, the Amiga will not display anything in its display border, allowing an external video signal to show through the border area. On the Amiga display, the border appears black. The data portion of these tags is NULL.&lt;br /&gt;
&lt;br /&gt;
The VTAG_BORDERNOTRANS_GET, _SET and _CLR tags are used, respectively, to obtain the status of border-not-transparent mode, and to activate and to deactivate this mode. If set, the Amiga display’s border will overlay external video with the color in register 0. Because border blanking mode takes precedence over border-not-transparent mode, setting border-not-transparent has no effect if border blanking is on. The data portion of these tags is NULL.&lt;br /&gt;
&lt;br /&gt;
The VTAG_CHROMAKEY_GET, _SET and _CLR tags are used, respectively, to obtain the status of chroma keying mode, and to activate and deactivate chroma keying mode. If set, the genlock will key on colors from specific color registers (the specific color registers are set using a different tag). If chroma keying is not set, the genlock will key on color register 0. The data portion of these tags is NULL.&lt;br /&gt;
&lt;br /&gt;
VTAG_CHROMAPEN_GET&lt;br /&gt;
&lt;br /&gt;
obtains the chroma keying status of an individual color register. The tag’s ti_Data field contains the register number. Like the other _GET tags, VideoControl() changes the tag ID (ti_Tag) to one that reflects the current state of the mode. VTAG_CHROMAPEN_SET and VTAG_CHROMAPEN_CLR activate and deactivate chroma keying for each individual color register. Chroma keying can be active for more than one register. By turning off border blanking and activating chroma keying mode, but turning off chroma keying for each color register, a program can overlay every part of an external video source, completely blocking it out.&lt;br /&gt;
&lt;br /&gt;
After using VideoControl() to set values in the ColorMap, the corresponding ViewPort has to be rebuilt with MakeVPort(), MrgCop() and LoadView(), so the changes can take effect. A program that uses a screen’s ViewPort rather than its own ViewPort should use the Intuition functions MakeScreen() and RethinkDisplay() to make the display changes take effect.&lt;br /&gt;
&lt;br /&gt;
The following code fragment shows how to access the genlock modes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;struct Screen *genscreen;&lt;br /&gt;
struct ViewPort *vp;&lt;br /&gt;
struct TagItem vtags [24];&lt;br /&gt;
&lt;br /&gt;
        /* The complete example opened a window, rendered some colorbars,  */&lt;br /&gt;
        /* and added gadgets to allow the user to turn the various genlock */&lt;br /&gt;
        /* modes on and off.                                               */&lt;br /&gt;
&lt;br /&gt;
        vp = &amp;amp;amp;(genscreen-&amp;amp;gt;ViewPort);&lt;br /&gt;
&lt;br /&gt;
        /* Ascertain the current state of the various modes. */&lt;br /&gt;
&lt;br /&gt;
        /* Is borderblanking on? */&lt;br /&gt;
        vtags[0].ti_Tag = VTAG_BORDERBLANK_GET;&lt;br /&gt;
        vtags[0].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        /* Is bordertransparent set? */&lt;br /&gt;
        vtags[1].ti_Tag = VTAG_BORDERNOTRANS_GET;&lt;br /&gt;
        vtags[1].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        /* Key on bitplane? */&lt;br /&gt;
        vtags[2].ti_Tag = VTAG_BITPLANEKEY_GET;&lt;br /&gt;
        vtags[2].ti_Tag = NULL;&lt;br /&gt;
&lt;br /&gt;
        /* Get plane which is used to key on */&lt;br /&gt;
        vtags[3].ti_Tag = VTAG_CHROMA_PLANE_GET;&lt;br /&gt;
        vtags[3].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        /* Chromakey overlay on? */&lt;br /&gt;
        vtags[4].ti_Tag = VTAG_CHROMAKEY_GET;&lt;br /&gt;
        vtags[4].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        for (i = 0; i &amp;amp;lt; 16; i++)&lt;br /&gt;
        {&lt;br /&gt;
                /* Find out which colors overlay */&lt;br /&gt;
                vtags[i + 5].ti_Tag = VTAG_CHROMA_PEN_GET;&lt;br /&gt;
                vtags[i + 5].ti_Data = i;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Indicate end of tag array */&lt;br /&gt;
        vtags[21].ti_Tag = VTAG_END_CM;&lt;br /&gt;
        vtags[21].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        /* And send the commands. On return the Tags themselves will&lt;br /&gt;
        * indicate the genlock settings for this ViewPort&#039;s ColorMap.&lt;br /&gt;
        */&lt;br /&gt;
        error = VideoControl(vp-&amp;amp;gt;ColorMap, vtags);&lt;br /&gt;
&lt;br /&gt;
        /* The complete program sets gadgets to reflect current states. */&lt;br /&gt;
&lt;br /&gt;
        /* Will only send single commands from here on. */&lt;br /&gt;
        vtags[1].ti_Tag = VTAG_END_CM;&lt;br /&gt;
&lt;br /&gt;
        /* At this point the complete program gets an input event and sets/clears the&lt;br /&gt;
           genlock modes as requested using the vtag list and VideoControl().&lt;br /&gt;
        */&lt;br /&gt;
&lt;br /&gt;
        /* send video command */&lt;br /&gt;
        error = VideoControl(vp-&amp;amp;gt;ColorMap, vtags);&lt;br /&gt;
&lt;br /&gt;
        /* Now use MakeScreen() and RethinkDisplay() to make the VideoControl()&lt;br /&gt;
        *  changes take effect.  If we were using our own ViewPort rather than&lt;br /&gt;
        * borrowing one from a screen, we would instead do:&lt;br /&gt;
        *&lt;br /&gt;
        *   MakeVPort(ViewAddress(),vp);&lt;br /&gt;
        *   MrgCop(ViewAddress());&lt;br /&gt;
        *   LoadView(ViewAddres());&lt;br /&gt;
        */&lt;br /&gt;
        MakeScreen(genscreen);&lt;br /&gt;
        RethinkDisplay();&lt;br /&gt;
&lt;br /&gt;
/* The complete program closes and frees everything it had opened or allocated. */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* The complete example calls the CheckPAL function, which is included below in its&lt;br /&gt;
   entirety for illustrative purposes.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
BOOL CheckPAL(STRPTR screenname)&lt;br /&gt;
{&lt;br /&gt;
        struct Screen *screen;&lt;br /&gt;
        ULONG modeID = LORES_KEY;&lt;br /&gt;
        struct DisplayInfo displayinfo;&lt;br /&gt;
        BOOL IsPAL;&lt;br /&gt;
&lt;br /&gt;
        if (GfxBase-&amp;amp;gt;LibNode.lib_Version &amp;amp;gt;= 36)&lt;br /&gt;
        {&lt;br /&gt;
                /*&lt;br /&gt;
                * We got at least V36, so lets use the new calls to find out what&lt;br /&gt;
                * kind of videomode the user (hopefully) prefers.&lt;br /&gt;
                */&lt;br /&gt;
&lt;br /&gt;
                if (screen = LockPubScreen(screenname))&lt;br /&gt;
                {&lt;br /&gt;
                        /*&lt;br /&gt;
                        * Use graphics.library/GetVPModeID() to get the ModeID of the specified screen.&lt;br /&gt;
                        * Will use the default public screen (Workbench most of the time) if NULL It is&lt;br /&gt;
                        * _very_ unlikely that this would be invalid, heck it&#039;s impossible.&lt;br /&gt;
                        */&lt;br /&gt;
                        if ((modeID = GetVPModeID(&amp;amp;amp;(screen-&amp;amp;gt;ViewPort))) != INVALID_ID)&lt;br /&gt;
                        {&lt;br /&gt;
                                /*&lt;br /&gt;
                                * If the screen is in VGA mode, we can&#039;t tell whether the system is PAL&lt;br /&gt;
                                * or NTSC. So to be foolproof we fall back to the displayinfo of the default&lt;br /&gt;
                                * monitor by inquiring about just the LORES_KEY displaymode if we don&#039;t know.&lt;br /&gt;
                                * The default.monitor reflects the initial video setup of the system, thus&lt;br /&gt;
                                * for either ntsc.monitor or pal.monitor.  We only use the displaymode of the&lt;br /&gt;
                                * is an alias specified public screen if it&#039;s display mode is PAL or NTSC and&lt;br /&gt;
                                * NOT the default.&lt;br /&gt;
                                */&lt;br /&gt;
                                if (!((modeID &amp;amp;amp; MONITOR_ID_MASK) == NTSC_MONITOR_ID ||&lt;br /&gt;
                                (modeID &amp;amp;amp; MONITOR_ID_MASK) == PAL_MONITOR_ID))&lt;br /&gt;
                                modeID = LORES_KEY;&lt;br /&gt;
                        }&lt;br /&gt;
                        UnlockPubScreen(NULL, screen);&lt;br /&gt;
                } /* if fails modeID = LORES_KEY. Can&#039;t lock screen, so fall back on default monitor. */&lt;br /&gt;
&lt;br /&gt;
                if (GetDisplayInfoData(NULL, (UBYTE *) &amp;amp;amp; displayinfo,&lt;br /&gt;
                sizeof(struct DisplayInfo), DTAG_DISP, modeID))&lt;br /&gt;
                {&lt;br /&gt;
                        if (displayinfo.PropertyFlags &amp;amp;amp; DIPF_IS_PAL)&lt;br /&gt;
                                IsPAL = TRUE;&lt;br /&gt;
                        else&lt;br /&gt;
                                IsPAL = FALSE;&lt;br /&gt;
                        /* Currently the default monitor is always either PAL or NTSC. */&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
                /* &amp;amp;lt; V36. The enhancements to the videosystem in V36 (and above) cannot be better&lt;br /&gt;
                * expressed than with the simple way to determine PAL in V34.&lt;br /&gt;
                */&lt;br /&gt;
                IsPAL= (GfxBase-&amp;amp;gt;DisplayFlags &amp;amp;amp; PAL) ? TRUE : FALSE;&lt;br /&gt;
&lt;br /&gt;
        return(IsPAL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Accessing the Blitter Directly ==&lt;br /&gt;
&lt;br /&gt;
To use the blitter directly, you must first be familiar with how its registers control its operation. This topic is covered thoroughly in the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; and is not repeated here. There are two basic approaches you can take to perform direct programming of the blitter: synchronous and asynchronous.&lt;br /&gt;
&lt;br /&gt;
* Synchronous programming of the blitter is used when you want to do a job with the blitter right away. For synchronous programming, you first get exclusive access to the blitter with OwnBlitter(). Next call WaitBlit() to ensure that any previous blitter operation that might have been in progress is completed. Then set up your blitter operation by programming the blitter registers. Finally, start the blit and call DisownBlitter().&lt;br /&gt;
&lt;br /&gt;
* Asynchronous programming of the blitter is used when the blitter operation you want to perform does not have to happen immediately. In that case, you can use the QBlit() and QBSBlit() functions in order to queue up requests for the use of the blitter on a non-exclusive basis. You share the blitter with system tasks.&lt;br /&gt;
&lt;br /&gt;
Whichever approach you take, there is one rule you should generally keep in mind about using the blitter directly:&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don’t Tie Up The Blitter|text=The system uses the blitter extensively for disk and display operation. While your task is using the blitter, many other system processes will be locked out. Therefore, use it only for brief periods and relinquish it as quickly as possible.}}&lt;br /&gt;
&lt;br /&gt;
To use QBlit() and QBSBlit(), you must create a data structure called a bltnode (blitter node) that contains a pointer to the blitter code you want to execute. The system uses this structure to link blitter usage requests into a first-in, first-out (FIFO) queue. When your turn comes, your own blitter routine can be repeatedly called until your routine says it is finished using the blitter.&lt;br /&gt;
&lt;br /&gt;
Two separate blitter queues are maintained. One queue is for the QBlit() routine. You use QBlit() when you simply want something done and you do not necessarily care when it happens. This may be the case when you are moving data in a memory area that is not currently being displayed.&lt;br /&gt;
&lt;br /&gt;
The second queue is maintained for QBSBlit(). QBS stands for “queue-beam-synchronized”. QBSBlit() requests form a beam-synchronized FIFO queue. When the video beam gets to a predetermined position, your blitter routine is called. Beam synchronization takes precedence over the simple FIFO. This means that if the beam sync matches, the beam-synchronous blit will be done before the non-synchronous blit in the first position in the queue. You might use QBSBlit() to draw into an area of memory that is currently being displayed to modify memory that has already been “passed-over” by the video beam. This avoids display flicker as an area is being updated.&lt;br /&gt;
&lt;br /&gt;
The sole input to both QBlit() and QBSBlit() is a pointer to a bltnode data structure, defined in the include file &amp;amp;lt;hardware/blit.h&amp;amp;gt;. Here is a copy of the structure, followed by details about the items you must initialize:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct bltnode&lt;br /&gt;
{&lt;br /&gt;
    struct  bltnode *n;&lt;br /&gt;
    int     (*function)();&lt;br /&gt;
    char    stat;&lt;br /&gt;
    short   blitsize;&lt;br /&gt;
    short   beamsync;&lt;br /&gt;
    int     (*cleanup)();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a pointer to the next bltnode, which, for most applications will be zero. You should not link bltnodes together. This is to be performed by the system in a separate call to QBlit() or QBSBlit().&lt;br /&gt;
&lt;br /&gt;
This is the address of your blitter function that the blitter queuer will call when your turn comes up. Your function must be formed as a subroutine, with an RTS instruction at the end. Follow Amiga programming conventions by placing the return value in D0 (or in C, use return(value)).&lt;br /&gt;
&lt;br /&gt;
If you return a nonzero value, the system will call your routine again next time the blitter is idle until you finally return 0. This is done so that you can maintain control over the blitter; for example, it allows you to handle all five bitplanes if you are blitting an object with 32 colors. For display purposes, if you are blitting multiple objects and then saving and restoring the background, you must be sure that all planes of the object are positioned before another object is overlaid. This is the reason for the lockup in the blitter queue; it allows all work per object to be completed before going on to the next one.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Not all C compilers can handle (*function)() properly! The system actually tests the processor &#039;&#039;status codes&#039;&#039; for a condition of equal-to-zero (Z flag set) or not-equal-to-zero (Z flag clear) when your blitter routine returns. Some C compilers do not set the processor status code properly (i.e., according to the value returned), thus it is not possible to use such compilers to write the (*function)() routine. In that case assembly language should be used. Blitter functions are normally written in assembly language anyway so they can take advantage of the ability of QBlit() and QBSBlit() to pass them parameters in processor registers.}}&lt;br /&gt;
&lt;br /&gt;
The register passing conventions for these routines are as follows. Register A0 receives a pointer to the system hardware registers so that all hardware registers can be referenced as an offset from that address. Register A1 contains a pointer to the current bltnode. You may have queued up multiple blits, each of which perhaps uses the same blitter routine. You can access the data for this particular operation as an offset from the value in A1. For instance, a typical user of these routines can precalculate the blitter register values to be placed in the blitter registers and, when the routine is called, simply copy them in. For example, you can create a new structure such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    INCLUDE &amp;amp;quot;exec/types.i&amp;amp;quot;&lt;br /&gt;
    INCLUDE &amp;amp;quot;hardware/blit.i&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
    STRUCTURE mybltnode,0&lt;br /&gt;
                      ; Make this new structure compatible with a bltnode&lt;br /&gt;
                      ; by making the first element a bltnode structure.&lt;br /&gt;
    STRUCT bltnode,bn_SIZEOF&lt;br /&gt;
            UWORD   bltcon1         ; Blitter control register 1.&lt;br /&gt;
            UWORD   fwmask          ; First and last word masks.&lt;br /&gt;
            UWORD   lwmask&lt;br /&gt;
            UWORD   bltmda          ; Modulos for sources a, b,and c.&lt;br /&gt;
            UWORD   bltmdb&lt;br /&gt;
            UWORD   bltmdc&lt;br /&gt;
            UWORD   any_more_data   ; add anything else you want&lt;br /&gt;
    LABEL mbn_SIZEOF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other forms of data structures are certainly possible, but this should give you the general idea.&lt;br /&gt;
&lt;br /&gt;
Tells the system whether or not to execute the clean-up routine at the end. This byte should be set to CLEANUP (0x40) if cleanup is to be performed. If not, then the bltnode cleanup variable can be zero.&lt;br /&gt;
&lt;br /&gt;
The value that should be in the VBEAM counter for use during a beam-synchronous blit before the function() is called.&lt;br /&gt;
&lt;br /&gt;
The system cooperates with you in planning when to start a blit in the routine QBSBlit() by not calling your routine until, for example, the video beam has already passed by the area on the screen into which you are writing. This is especially useful during single buffering of your displays. There may be time enough to write the object between scans of the video display. You will not be visibly writing while the beam is trying to scan the object. This avoids flicker (part of an old view of an object along with part of a new view of the object).&lt;br /&gt;
&lt;br /&gt;
The address of a routine that is to be called after your last return from the QBlit() routine. When you finally return a zero, the queuer will call this subroutine (ends in RTS or return()) as the clean-up. Your first entry to the function may have dynamically allocated some memory or may have done something that must be undone to make for a clean exit. This routine must be specified.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12540</id>
		<title>Exec Signals</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12540"/>
		<updated>2025-01-26T19:27:19Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Signals ==&lt;br /&gt;
&lt;br /&gt;
[[Exec Tasks|Tasks]] often need to coordinate with other concurrent system activities (like other [[Exec Tasks|tasks]] and [[Exec Interrupts|interrupts]]). This coordination is handled by Exec through the synchronized exchange of specific event indicators called &#039;&#039;signals&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is the primary mechanism responsible for all inter-task communication and synchronization on the Amiga. This signal mechanism operates at a low level and is designed for high performance. Signals are used extensively by the Exec message system as a way to indicate the arrival of an inter-task message. The message system is described in more detail in [[Exec Messages and Ports]].&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Not for Beginners.|text=This section concentrates on details about signals that most applications do not need to understand for general Amiga programming. For a general overview of signals, see [[Introduction to Exec]].}}&lt;br /&gt;
&lt;br /&gt;
== The Signal System ==&lt;br /&gt;
&lt;br /&gt;
The signal system is designed to support independent simultaneous events, so several signals can occur at the same time. Each task has 32 independent signals, 16 of which are pre-allocated for use by the operating system. The signals in use by a particular task are represented as bits in a 32-bit field in its Task structure (&amp;amp;lt;exec/tasks.h&amp;amp;gt;). Two other 32-bit fields in the Task structure indicate which signals the task is waiting for, and which signals have been received.&lt;br /&gt;
&lt;br /&gt;
Signals are &#039;&#039;task relative&#039;&#039;. A task can only allocate its own signals, and may only wait on its own signals. In addition, a task may assign its own significance to a particular signal. Signals are not broadcast to all tasks; they are directed only to individual tasks. A signal has meaning to the task that defined it and to those tasks that have been informed of its meaning.&lt;br /&gt;
&lt;br /&gt;
For example, signal bit 12 may indicate a timeout event to one task, but to another task it may indicate a message arrival event. You can never wait on a signal that you did not directly or indirectly allocate yourself, and any other task that wishes to signal you must use a signal that &#039;&#039;you&#039;&#039; allocated.&lt;br /&gt;
&lt;br /&gt;
=== Signal Allocation ===&lt;br /&gt;
&lt;br /&gt;
As mentioned above, a task assigns its own meaning to a particular signal. Because certain system libraries may occasionally require the use of a signal, there is a convention for signal allocation. It is unwise ever to make assumptions about which signals are actually in use.&lt;br /&gt;
&lt;br /&gt;
Before a signal can be used, it must be allocated with the AllocSignal() function. When a signal is no longer needed, it should be freed for reuse with FreeSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BYTE AllocSignal( LONG signalNum );&lt;br /&gt;
VOID FreeSignal( LONG signalNum );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AllocSignal() marks a signal as being in use and prevents the accidental use of the same signal for more than one event. You may ask for either a specific signal number, or more commonly, you would pass -1 to request the next available signal. The state of the newly allocated signal is cleared (ready for use). Generally it is best to let the system assign you the next free signal. Of the 32 available signals, the lower 16 are reserved for system use (see [[#Reserved_System_Signals|Reserved System Signals]]). This leaves the upper 16 signals free for application programs to allocate. Other subsystems that you may call depend on AllocSignal().&lt;br /&gt;
&lt;br /&gt;
The following example asks for the next free signal to be allocated for its use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
if (-1 == (signal = IExec-&amp;gt;AllocSignal(-1)))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;no signal bits available\n&amp;quot;);&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;allocated signal number %ld\n&amp;quot;, signal);&lt;br /&gt;
    /* Other code could go here */&lt;br /&gt;
    IExec-&amp;gt;FreeSignal(signal)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The value returned by AllocSignal() is a signal bit number. This value cannot be used directly in calls to signal-related functions without first being converted to a mask:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 mask = 1UL &amp;lt;&amp;lt; signal;&lt;br /&gt;
 or&lt;br /&gt;
uint32 mask = (uint32)1 &amp;lt;&amp;lt; signal;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is important to realize that signal bit allocation is relevant &#039;&#039;only&#039;&#039; to the running task. You &#039;&#039;cannot&#039;&#039; allocate a signal from another task. Note that functions which create a signal MsgPort will allocate a signal from the task that calls the function. Such functions include OpenWindow() and AllocSysObject(). For this reason, only the creating task may Wait() (directly or indirectly) on the MsgPort&#039;s signal. Functions which call Wait() include DoIO(), WaitIO() and WaitPort().&lt;br /&gt;
&lt;br /&gt;
=== Waiting for a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals are most often used to wake up a task upon the occurrence of some external event. Applications call the Exec Wait() function, directly or indirectly, in order to enter a wait state until some external event triggers a signal which awakens the task.&lt;br /&gt;
&lt;br /&gt;
Though signals are usually not used to interrupt an executing task, they can be used this way. Task &#039;&#039;exceptions&#039;&#039;, described in [[Exec_Interrupts|Exec Interrupts]], allow signals to act as a task-local interrupt.&lt;br /&gt;
&lt;br /&gt;
The Wait() function specifies the set of signals that will wake up the task and then puts the task to sleep (into the waiting state).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG Wait( ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any one signal or any combination of signals from this set are sufficient to awaken the task. Wait() returns a mask indicating which signals satisfied the Wait() call. Note that when signals are used in conjunction with a message port, a set signal bit does not necessarily mean that there is a message at the message port.&lt;br /&gt;
&lt;br /&gt;
See [[Exec_Messages_and_Ports|Exec Messages and Ports]] for details about proper handling of messages.&lt;br /&gt;
&lt;br /&gt;
Because tasks (and interrupts) normally execute asynchronously, it is often possible to receive a particular signal before a task actually Wait()s for it. In such cases the Wait() will be immediately satisfied, and the task will not be put to sleep.&lt;br /&gt;
&lt;br /&gt;
The Wait() function implicitly clears those signal bits that satisfied the wait condition. This effectively resets those signals for reuse. However, keep in mind that a task might get more signals while it is still processing the previous signal. If the same signal is received multiple times and the signal bit is not cleared between them, some signals will go unnoticed.&lt;br /&gt;
&lt;br /&gt;
Be aware that using Wait() will break a Forbid() or Disable() state. Wait() cannot be used in supervisor mode or within interrupts.&lt;br /&gt;
&lt;br /&gt;
A task may Wait() for a combination of signal bits and will wake up when any of the signals occur. Wait() returns a signal mask specifying which signal or signals were received. Usually the program must check the returned mask for each signal it was waiting on and take the appropriate action for each that occurred. The order in which these bits are checked is often important.&lt;br /&gt;
&lt;br /&gt;
Here is a hypothetical example of a process that is using the console and timer devices, and is waiting for a message from either device and a possible break character issued by the user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 consoleSignal = 1L &amp;lt;&amp;lt; ConsolePort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 timerSignal   = 1L &amp;lt;&amp;lt; TimerPort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 userSignal    = SIGBREAKF_CTRL_C;    /* Defined in &amp;lt;dos/dos.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
uint32 signals = IExec-&amp;gt;Wait(consoleSignal | timerSignal | userSignal);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; consoleSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;new character\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; timeOutSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;timeout\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; userSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;User Ctrl-C Abort\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will put the task to sleep waiting for a new character, or the expiration of a time period, or a &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt; break character issued by the user. Notice that this code checks for an incoming character signal before checking for a timeout. Although a program can check for the occurrence of a particular event by checking whether its signal has occurred, this may lead to busy wait polling. Such polling is wasteful of the processor and is usually harmful to the proper function of the Amiga system. However, if a program needs to do constant processing and also check signals (a compiler for example) SetSignal(0,0) can be used to get a copy of your task&#039;s current signals.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG SetSignal( ULONG newSignals, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetSignal() can also be used to set or clear the state of the signals. Implementing this can be dangerous and should generally not be done. The following fragment illustrates a possible use of SetSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
uint32 signals = SetSignal(0,0);           /* Get current state of signals */&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; SIGBREAKF_CTRL_C)            /* Check for Ctrl-C.           */&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Break\n&amp;quot;);               /* Ctrl-C signal has been set. */&lt;br /&gt;
    IExec-&amp;gt;SetSignal(0, SIGBREAKF_CTRL_C)  /* Clear Ctrl-C signal.        */&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Looking for Break Keys ====&lt;br /&gt;
&lt;br /&gt;
One common usage of signals on the Amiga is for processing a user break. The OS reserves 16 of a tasks 32 signals for system use. Four of those 16 signals are used to tell a task about the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; break keys. An application can process these signals. Usually, only CLI-based programs receive these signals because the Amiga&#039;s console handler is about the only user input source that sets these signals when it sees the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; key presses.&lt;br /&gt;
&lt;br /&gt;
The signal masks for each of these key presses are defined in &amp;amp;lt;dos/dos.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
SIGBREAKF_CTRL_C&lt;br /&gt;
SIGBREAKF_CTRL_D&lt;br /&gt;
SIGBREAKF_CTRL_E&lt;br /&gt;
SIGBREAKF_CTRL_F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that these are &#039;&#039;bit masks&#039;&#039; and &#039;&#039;&#039;not&#039;&#039;&#039; &#039;&#039;bit numbers&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Generating a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals may be generated from both tasks and system interrupts with the Signal() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID Signal( struct Task *task, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example Signal(tc,mask) would signal the task with the specified mask signals. More than one signal can be specified in the mask. The following example code illustrates Wait() and Signal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// signals.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static CONST_STRPTR VersTag = &amp;quot;$VER: signals 53.1 (20.6.2012)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void);    /* prototype for our subtask routine */&lt;br /&gt;
&lt;br /&gt;
struct Task *maintask = NULL;&lt;br /&gt;
uint32 mainsig = 0;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  BOOL Done = FALSE;&lt;br /&gt;
  BOOL WaitingForSubtask = TRUE;&lt;br /&gt;
&lt;br /&gt;
  /* We must allocate any special signals we want to receive. */&lt;br /&gt;
  int8 mainsignum = IExec-&amp;gt;AllocSignal(-1);&lt;br /&gt;
  if (mainsignum == -1)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;No signals available\n&amp;quot;);&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    mainsig = 1U &amp;lt;&amp;lt; mainsignum;        /* subtask can access this global */&lt;br /&gt;
    maintask = IExec-&amp;gt;FindTask(NULL);  /* subtask can access this global */&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;We alloc a signal, create a task, wait for signals\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    struct Task *subtask = IDOS-&amp;gt;CreateTaskTags(&amp;quot;subtask&amp;quot;, 0, subtaskcode, 16000, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (subtask == NULL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t create subtask\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;After subtask signals, press CTRL-C or CTRL-D to exit\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      while (!Done || WaitingForSubtask)&lt;br /&gt;
      {&lt;br /&gt;
        /* Wait on the combined mask for all of the signals we are&lt;br /&gt;
         * interested in.  All processes have the CTRL_C thru CTRL_F&lt;br /&gt;
         * signals.  We&#039;re also Waiting on the mainsig we allocated&lt;br /&gt;
         * for our subtask to signal us with.  We could also Wait on&lt;br /&gt;
         * the signals of any ports/windows our main task created ... */&lt;br /&gt;
&lt;br /&gt;
        uint32 wakeupsigs = IExec-&amp;gt;Wait(mainsig | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);&lt;br /&gt;
&lt;br /&gt;
        /* Deal with all signals that woke us up - may be more than one */&lt;br /&gt;
        if (wakeupsigs &amp;amp; mainsig)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Signalled by subtask\n&amp;quot;);&lt;br /&gt;
          WaitingForSubtask = FALSE;   /* OK to kill subtask now */&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if (wakeupsigs &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-C signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(wakeupsigs &amp;amp; SIGBREAKF_CTRL_D)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-D signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    IExec-&amp;gt;FreeSignal(mainsignum);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void)&lt;br /&gt;
{&lt;br /&gt;
  IExec-&amp;gt;Signal(maintask, mainsig);&lt;br /&gt;
  IExec-&amp;gt;RemTask(0);  // Remove myself from the system. &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reserved System Signals ==&lt;br /&gt;
&lt;br /&gt;
There are 16 signal bits which are reserved for system use. Applications are never allowed to use these signal bits unless explicitly documented (e.g. SIGF_SINGLE).&lt;br /&gt;
&lt;br /&gt;
=== SIGB_ABORT ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_CHILD ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE (SIGB_BLIT) ===&lt;br /&gt;
&lt;br /&gt;
When a system function needs a Task to stop and IExec-&amp;gt;Wait() for a single signal it will use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
This signal used to be named SIGB_BLIT when it was used to wait on the classic hardware blitter.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_INTUITION ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_NET ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_DOS ===&lt;br /&gt;
&lt;br /&gt;
SIGB_DOS is currently used as the wait signal bit for the embedded message port in the process structure. This message port is initialised by the IDOS-&amp;gt;CreateNewProc() function. This message port is used by default for DosPacket transactions via IDOS-&amp;gt;DoPkt() and IDOS-&amp;gt;WaitPkt() and it is also used for sending the initial ACTION_STARTUP DosPacket for DOS handlers. This message port is also where the workbench.library sends the initial struct WBStartup message to every process it starts.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_C ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_C signal bit is used extensively as the general purpose &amp;quot;Break&amp;quot; signal. It is used by all shell handler commands&lt;br /&gt;
and many applications to invoke a normal exit of the program. It is up to all applications to follow this recommendation.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_C signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_D ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_D signal bit is used mostly by the shell handler to stop execution of a script file or non-interactive stream. It may also be used by applications.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_D signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_E ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_E signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signaling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_E signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_F ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_F signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signaling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_F signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
== Signalling with SIGB_SINGLE ==&lt;br /&gt;
&lt;br /&gt;
Many of a task&#039;s 32 signal bits are reserved for the operating system&#039;s private use, but, like any good rule, there is an exception. One of these bits, the SIGB_SINGLE bit, can be useful to some applications, if used correctly.&lt;br /&gt;
&lt;br /&gt;
Many system functions need to put their task to sleep while waiting for a single event, which requires using one of the task&#039;s signals. Rather than forcing each of these system functions to allocate a signal, then Wait(), then deallocate the signal, the operating system&lt;br /&gt;
has permanently allocated one signal, the SIGB_SINGLE, for this type of signalling. When a system function needs stop a task to Wait()&lt;br /&gt;
for a single signal, it can use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
The only purpose a program can use SIGB_SINGLE for is Wait()ing because the task cannot call any system functions while it is using SIGB_SINGLE. A program that calls system functions while using SIGB_SINGLE can cause itself and the operating system serious problems because the system functions can use SIGB_SINGLE as well. If a program calls a system function while using SIGB_SINGLE, two bad&lt;br /&gt;
things can happen:&lt;br /&gt;
&lt;br /&gt;
1) The errant task&#039;s event takes place before the system function waits on SIGB_SINGLE (or while the system function is waiting on&lt;br /&gt;
SIGB_SINGLE). In this case, the system function will think its event has taken place because its signal became set. The errant task will never find out that its event has taken place, as the system function will clear the SIGB_SINGLE bit after Wait()ing on it.&lt;br /&gt;
&lt;br /&gt;
2) The errant task&#039;s event and the system function&#039;s event take place while the system function is waiting on SIGB_SINGLE. In this case,&lt;br /&gt;
the system function will function normally, clear the SIGB_SINGLE bit, and exit. The errant task will never know that its event has&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
Before Wait()ing on SIGB_SINGLE, clear it using SetSignal():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IExec-&amp;gt;SetSignal(0, SIGF_SINGLE); // Note SIGF_SINGLE is the bit mask. SIGB_SINGLE is the signal bit.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This step is necessary because it is possible that the last system function that used the SIGB_SINGLE signal did not clear the SIGB_SINGLE bit.&lt;br /&gt;
&lt;br /&gt;
Also, an application should not wait on other signals while it is waiting on SIGB_SINGLE. Waiting on other signals at the same time&lt;br /&gt;
makes it possible for a program to wake up while the SIGB_SINGLE is still outstanding. If this happens, the program will still have to&lt;br /&gt;
go back to sleep, which requires calling a system function.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a simple example of using the SIGB_SINGLE signal. It starts a child process and waits for that child process to signal&lt;br /&gt;
the main process using the SIGB_SINGLE signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This example program illustrates simple usage of the SIGB_SINGLE&lt;br /&gt;
// signal for &amp;quot;single shot&amp;quot; signalling. This signal is one of the&lt;br /&gt;
// system private signals, but applications can use it in certain&lt;br /&gt;
// cases, but only if used carefully. Specifically, applications&lt;br /&gt;
// should use it only to Wait() on, and using only that signal&lt;br /&gt;
// (applications cannot Wait() on other signals in the same&lt;br /&gt;
// Wait()). Not following these rules can cause serious system&lt;br /&gt;
// problems.&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dostags.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void);      /* prototype for our childprocess routine. */&lt;br /&gt;
&lt;br /&gt;
struct Process *mainprocess = NULL, *childprocess = NULL;&lt;br /&gt;
UBYTE childprocessname[] = &amp;quot;RKM_signal_childprocess&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
BPTR output;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (output = IDOS-&amp;gt;Open(&amp;quot;CONSOLE:&amp;quot;, MODE_OLDFILE))  /* Open the console for the  */&lt;br /&gt;
                                                        /*            child process. */&lt;br /&gt;
    {&lt;br /&gt;
        mainprocess = (struct Process *)IExec-&amp;gt;FindTask(NULL); /* childprocess can   */&lt;br /&gt;
                                                               /* access this global.*/&lt;br /&gt;
&lt;br /&gt;
        if (childprocess = IDOS-&amp;gt;CreateNewProcTags(&lt;br /&gt;
                    NP_Entry,       childprocesscode,  /* The child process  */&lt;br /&gt;
                    NP_Name,        childprocessname,&lt;br /&gt;
                    NP_Output,      output,&lt;br /&gt;
                    NP_FreeSeglist, FALSE,&lt;br /&gt;
                    NP_CloseOutput, TRUE,&lt;br /&gt;
                    NP_Child,       TRUE,&lt;br /&gt;
                    TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Created a child process and waiting on SIGB_SINGLE.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());  /* Make sure the Printf() above appears      */&lt;br /&gt;
                                           /* in the console window before the child    */&lt;br /&gt;
                                           /* process starts printing to the console. */&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;SetSignal(0, SIGF_SINGLE);  /* Use SIGF_SINGLE only after */&lt;br /&gt;
                                               /* clearing it.               */&lt;br /&gt;
&lt;br /&gt;
            /* Wake up the child. */&lt;br /&gt;
            IExec-&amp;gt;Signal((struct Task *)childprocess, SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;Wait(SIGF_SINGLE);  /* Only use SIGF_SINGLE for Wait()ing and */&lt;br /&gt;
                                       /* Wait on that signal alone!             */&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Received signal from child.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t create child process. Exiting.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t open CONSOLE:.  Exiting.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void)     /* This function is what CreateNewProcTags() */&lt;br /&gt;
{                               /* loads as the child process.  This child   */&lt;br /&gt;
                                /* signals the parent using SIGF_SINGLE.     */&lt;br /&gt;
&lt;br /&gt;
    /* Wait for a startup signal. This is to allow the parent process to&lt;br /&gt;
     * print its banner message and clear SIGF_SINGLE.&lt;br /&gt;
     */&lt;br /&gt;
    IExec-&amp;gt;Wait(SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: I&#039;m alive and starting a 5 second TimeDelay()&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    for (uint32 x = 0; x &amp;lt; 5; x++)&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;Delay(50);        /* Delay for 5 seconds, printing a */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot; .&amp;quot;);     /* dot during each second.         */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot; Finished.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: Signalling main process and exiting.  Bye.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Signal((struct Task *)mainprocess, SIGF_SINGLE);  /* Finished waiting, */&lt;br /&gt;
                                                             /* signal the main   */&lt;br /&gt;
                                                             /* process and exit  */&lt;br /&gt;
                                                             /* child process.    */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control task signalling. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Exec Signal Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocSignal()&lt;br /&gt;
| Allocate a signal bit.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSignal()&lt;br /&gt;
| Free a signal bit allocated with AllocSignal().&lt;br /&gt;
|-&lt;br /&gt;
| SetSignal()&lt;br /&gt;
| Query or set the state of the signals for the current task.&lt;br /&gt;
|-&lt;br /&gt;
| Signal()&lt;br /&gt;
| Signal a task by setting signal bits in its Task structure.&lt;br /&gt;
|-&lt;br /&gt;
| Wait()&lt;br /&gt;
| Wait for one or more signals from other tasks or interrupts.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=How_to_Build_Stubs_for_68k_Libraries&amp;diff=12539</id>
		<title>How to Build Stubs for 68k Libraries</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=How_to_Build_Stubs_for_68k_Libraries&amp;diff=12539"/>
		<updated>2025-01-26T19:27:08Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Author =&lt;br /&gt;
&lt;br /&gt;
Roman Kargin&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright &amp;amp;copy; 2013 Roman Kargin&amp;lt;br/&amp;gt;&lt;br /&gt;
Proofreading and grammar corrections by the AmigaOS Wiki team.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
The idea for this tutorial came from when we was playing with Dopus5 source code and wanted to make one single function from the 68k version of dopus5.library to work from native AmigaOS 4 code. While we have all kind of auto generation tools to build all kind of protos/interfaces/inlines/stubs/etc, it still wasn&#039;t clear to me how to do what I needed.&lt;br /&gt;
&lt;br /&gt;
Since the beginning a very helpful article was Thomas Rapp&#039;s [http://thomas-rapp.homepage.t-online.de/ppclib.html How to create PPC stubs for M68K libraries] where he shows how to use fd2pragma and IDLTool to build a jpeg.l.main stub from a 68k jpeg.library, as well as helpful answers from Fredrik Wikstrom. In the end, all this information was important to write down so I put everything into this tutorial. I hope other developers will find it useful.&lt;br /&gt;
&lt;br /&gt;
= PowerPC-&amp;gt;68k Glue Stubs =&lt;br /&gt;
&lt;br /&gt;
While 68k libraries are old and almost all libraries on AmigaOS today are PowerPC native ones, there are still some old AmigaOS 3.x libraries which are necessary and have no native PowerPC replacement. In that case we need to create for them necessary PowerPC-&amp;gt;68k glue stub files, so we can call those 68k library routines from native, PowerPC code, or, we can use EmulateTags(). The latter makes it easy for your code to use both PowerPC and 68k native libraries transparently with only a small bit of small overhead; just a few instructions. But this only works when the PowerPC native code is available and when there are just a few functions in the library. Quite often, you just don&#039;t want to deal with all this because of the large number of functions in a 68k library and the different ways of using them from the code. In this case, creating of PowerPC-&amp;gt;68k stubs is a simpler and faster way.&lt;br /&gt;
&lt;br /&gt;
For a better explanation of what a PowerPC-&amp;gt;68k stub is, refer to the [[Migration_Guide|Migration Guide]]. In short, if you have, let&#039;s say, the 68k version of dopus5.library and want to use it from native AmigaOS 4 binaries then you need to create dopus5.l.main. When code in your native binary needs to interface to a old AmigaOS3 library, the system (ramlib to be more precise) will automatically scan its library search path for the file &amp;quot;dopus5.l.main&amp;quot; and if found, it will open it and obtain the interface from there. That l.main file is pure non-startup code which contains the necessary stubs. You do not need to write the stubs from scratch manually. This is a job for different set of tools (fd2pragma, fdtrans and IDLTool). The library.l.main code consists of 2 files if you auto generate them: PowerPC-&amp;gt;68k cross call stubs and vectors. Of course you can write them from scratch manually, but its a pain and there are already tools which do it for you. &lt;br /&gt;
&lt;br /&gt;
The name of the file in general does not matter. A library does not necessarily have to be named &amp;quot;.library&amp;quot; and could be named &amp;quot;.module&amp;quot; or whatever. Ramlib will search for the first &amp;quot;.&amp;quot; in the library name, starting from the end, the first part up to and including the dot plus one more character, usually the &#039;l&#039; in library, is used, another dot is appended, then the interface name is added. It then calls the exec function.&lt;br /&gt;
&lt;br /&gt;
For example, if the interface name is &#039;main&#039; and the library name is &#039;foobar&#039; then this is what it looks for:&lt;br /&gt;
&lt;br /&gt;
 foobar.library  -&amp;gt; foobar.l.main&lt;br /&gt;
 foobar.blabla   -&amp;gt; foobar.b.main&lt;br /&gt;
 foobar.module   -&amp;gt; foobar.m.main&lt;br /&gt;
&lt;br /&gt;
{{Note|text=Because of the way RamLib parses glue-stubs names, you can&#039;t call your glue stub for &amp;quot;foobar.library&amp;quot; as &amp;quot;foobar.library.main&amp;quot;. It is understandable from the explanation above, but it can be misleading when you run for example &amp;quot;snoopy&amp;quot; and can see how it tries to search for &amp;quot;foobar.library.main&amp;quot;, but it didn&#039;t tries to search for a file, just for &amp;quot;resident&amp;quot; code in memory. The real file ramlib will look for is only foobar.l.main when the main library is called as foobar.library.}}&lt;br /&gt;
	 &lt;br /&gt;
{{Note|text=You can&#039;t put your glue stub files in directories other than PROGDIR:, PROGDIR:Libs or LIBS: as RamLib will not find them. If you have let&#039;s say a directory &amp;quot;modules&amp;quot; where you store some of your plugins (which in reality are libraries), then you can&#039;t put your glue stub files in the same &amp;quot;modules&amp;quot; directory, but to the place mention above.}}&lt;br /&gt;
&lt;br /&gt;
= pragmas/fd/sfd/protos =&lt;br /&gt;
&lt;br /&gt;
Often, old 68k libraries were not done with GCC. On old m68k machines, SAS/C was the popular C compiler and pure assembler was even more popular for everyday needs. So many of those old libraries were provided with includes for SAS/C only or for assembler (like .i files) or a mix of the two. You may have such library with which you have only a pragma file and a bunch of proto files for functions which are present in more than one proto file (i.e. no .fd file at all, no single include with all protos). The first thing to do is to create a single proto file from those different proto files you have in different places, manually make your .fd file from pragma (check all offsets and order how they are placed in pragma file, and in the same order put them to your new .fd). Then when you have only one proto file and one .fd file, you can generate a .sfd file and an .xml one with fd2pragma. Then generate from it all includes/vectors/stubs and only after that you can generate lib.l.main :) Usually its not that hard, and most old libraries have .fd files and a single proto include file, but sometimes you will have to do it for difficult libs, so you will know what to do.&lt;br /&gt;
&lt;br /&gt;
All we need to know now is that libraries have a jump table. Jump tables are a list of library functions at specific offsets. Offsets can&#039;t be changed because they are function offsets in the library that your code will call. Be it with the old SAS/C, an assembler or the new GCC on AmigaOS 4, if we work with a 68k library and create any fd/proto files from scratch then the offsets should always be the same.&lt;br /&gt;
&lt;br /&gt;
For example you have dopus_pragmas.h file and dopus.fd files (fd files are a standard way to describe the interface for libraries) and dopus_pragmas entries look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#pragma libcall DOpusBase RemovedFunc0 1e 0&lt;br /&gt;
#pragma libcall DOpusBase Random 24 001&lt;br /&gt;
#pragma libcall DOpusBase Atoh 2a 0802&lt;br /&gt;
....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It means that the first &amp;quot;user&amp;quot; library function in a jump table starts at offset 0x1e (30) and then multiply by 6 for every function. So first function is at 30 (0x1e), second one is at 36 (0x24), the next one is at 42 (0x2a) and so on for all the functions. That means that if for example function Atoh() in the jump table is placed at offset 0x2a, then every single code and includes file which we will generate for AmigaOS 4 should respect that jump-table offset.&lt;br /&gt;
&lt;br /&gt;
If we look at the dopus_lib.fd file we see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
* &amp;quot;dopus.library&amp;quot;                                             &lt;br /&gt;
##base _DOpusBase&lt;br /&gt;
##bias 30&lt;br /&gt;
*                                                             &lt;br /&gt;
* Support routines for Directory Opus and associated programs &lt;br /&gt;
* (c) Copyright 1995 Jonathan Potter                          &lt;br /&gt;
*                                                             &lt;br /&gt;
##public&lt;br /&gt;
RemovedFunc0()()&lt;br /&gt;
Random(limit)(d0)&lt;br /&gt;
*&lt;br /&gt;
Atoh(str,len)(a0,d0)&lt;br /&gt;
....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This means that public functions start from offset 30 (bias 30), and they are placed in the same order as they are in the pragma file. So if you change the order of function names in the FD file you will end calling a function instead of another one, which of course means crashes, bugs and problems. &lt;br /&gt;
&lt;br /&gt;
So respect offsets in the jump table.&lt;br /&gt;
&lt;br /&gt;
Note that .sfd files work like .fd files but with more possibilities. You won&#039;t find them in old 68k dev archives of libraries as its something new. They contain a lot more information than .fd and are easier to use. But more or less they follow the same logic, the same respect for offsets, etc. To see a full description of .fd and .sfd formats check the fd2pragma guide.&lt;br /&gt;
&lt;br /&gt;
= Now What? =&lt;br /&gt;
&lt;br /&gt;
We have 2 methods to build lib.l.main and required includes. But for both of them you need .fd file as a minimum (if you have .sfd its even better), and a proto file which will describe all (or not all) your functions. While .fd/.sfd should be done by the original developer, you still can make them from scratch if you have a pragma file with lib calls (you make this file with the same order and the same offsets). If you don&#039;t have one proto file, but many in different includes, then you can make your own one, just copy one by one all the protos from all the include files, while all your FD entries will not have necessary proto descriptions in proto file (by the way, if you don&#039;t have proto for some functions, then entries from the fd/sfd file will be reserved when you will auto generate AmigaOS code/includes, so respect offsets).&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say we have dopus_lib.fd and only protos in different includes in different places. We then create proto file from all those includes (in referring to the dopus_lib.fd), and then auto-tools time coming. As I say we have 2 ways:&lt;br /&gt;
&lt;br /&gt;
== Method 1: fdtrans + sfd ==&lt;br /&gt;
&lt;br /&gt;
1. generate a .sfd file:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; fd2pragma dopus5_lib.fd clib our_proto.h special 112&lt;br /&gt;
 SourceFile: dopus5_lib.fd&lt;br /&gt;
 ResultFile: dopus5_lib.sfd&lt;br /&gt;
&lt;br /&gt;
2. generate xml and ppc-&amp;gt;68k crosscall stubs via fdtrans:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; fdtrans dopus5_lib.sfd -x -s&lt;br /&gt;
&lt;br /&gt;
Change manually in the cross call-stubs file (dopus5.c) &amp;quot;main_vectors&amp;quot; in &amp;quot;main_v1_vectors&amp;quot;, as it seems some typo between different tools. No other changes is needed, fdtrans automatically insert including of interface (while fd2pragma will skip it, see later).&lt;br /&gt;
&lt;br /&gt;
3. generate proto/interface/inline (i.e. includes) and vectors:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; idltool -p -i -n -c dopus5.xml &lt;br /&gt;
&lt;br /&gt;
4. build dopus5.l.main&lt;br /&gt;
 ram:&amp;gt; gcc -Iinclude -nostartfiles dopus5.c -o dopus5.l.main&lt;br /&gt;
&lt;br /&gt;
5. build a test program to use one function from dopus5.library, here StrConcat()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dopus5.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *DOpusBase;&lt;br /&gt;
struct DOpusIFace *IDOpus;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  TEXT buffer[256] = &amp;quot;First part-&amp;quot;;&lt;br /&gt;
  CONST_STRPTR concat = &amp;quot;Second part&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Need dopus library&lt;br /&gt;
  if (!(DOpusBase = IExec-&amp;gt;OpenLibrary(&amp;quot;PROGDIR:dopus5.library&amp;quot;, 1))) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open library\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    if (!(IDOpus = (struct DOpusIFace*)IExec-&amp;gt;GetInterface(DOpusBase, &amp;quot;main&amp;quot;, 1, NULL))) {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t get interface\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      IDOpus-&amp;gt;StrConcat(buffer, concat, 36);&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Result=%s\n&amp;quot;, buffer);&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;DropInterface((struct Interface*)IDOpus);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(DOpusBase);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; gcc -Iinclude testcase-native-fdtrans.c &lt;br /&gt;
 ram:&amp;gt; a.out&lt;br /&gt;
 ram:&amp;gt; Result=First part-Second part&lt;br /&gt;
&lt;br /&gt;
It works!&lt;br /&gt;
&lt;br /&gt;
== Method 2: fd2pragma + fd ==&lt;br /&gt;
&lt;br /&gt;
1. Create an XML file:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; fd2pragma dopus5_lib.fd clib proto.h special 140&lt;br /&gt;
 SourceFile: dopus5_lib.fd&lt;br /&gt;
 Resultfile: dopus5.xml&lt;br /&gt;
&lt;br /&gt;
2. generate ppc-&amp;gt;68k crosscall stubs:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; fd2pragma dopus5_lib.fd clib proto.h special 141&lt;br /&gt;
 SourceFile: dopus5_lib.fd&lt;br /&gt;
 Resultfile: dopus5.c&lt;br /&gt;
&lt;br /&gt;
In the result file (dopus5.c) manually add including of interfaces/dopus5.h file and replace value &amp;quot;main_vectors&amp;quot; with &amp;quot;main_v1_vectors&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
3. generate proto/interface/inline (i.e. includes) and vectors: &lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; idltool -p -i -n -c dopus5.xml &lt;br /&gt;
&lt;br /&gt;
4. build dopus5.l.main&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; gcc -Iinclude -nostartfiles dopus5.c -o dopus5.l.main&lt;br /&gt;
&lt;br /&gt;
5. build a test program (its a bit different compared to the previous test program because the name of the interface is a bit different (big/small letters, 5 at end, etc) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dopus5.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *DOpusBase;&lt;br /&gt;
struct Dopus5IFace *IDopus5;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  TEXT buffer[256] = &amp;quot;First part-&amp;quot;;&lt;br /&gt;
  CONST_STRPTR concat = &amp;quot;Second part&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Need dopus library&lt;br /&gt;
  if (!(DOpusBase = IExec-&amp;gt;OpenLibrary(&amp;quot;PROGDIR:dopus5.library&amp;quot;, 1))) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open library&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    if (!(IDopus5 = IExec-&amp;gt;GetInterface(DOpusBase, &amp;quot;main&amp;quot;, 1, NULL))) {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t get interface&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      IDopus5-&amp;gt;StrConcat(buffer, concat, 36);&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Result=%s\n&amp;quot;, buffer);&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;DropInterface((struct Interface*)IDopus5);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(DOpusBase);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then hard-reboot is needed if you did the previous test with dopus5.l.main done from fd2trans as it is still in memory (and it can freeze the OS because of different code of the same stub libs), and then:&lt;br /&gt;
&lt;br /&gt;
 ram:&amp;gt; gcc -Iinclude testcase-native-fd2pragma.c &lt;br /&gt;
 ram:&amp;gt; a.out&lt;br /&gt;
 ram:&amp;gt; Result=First part-Second part&lt;br /&gt;
&lt;br /&gt;
It works as well!&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Which method is better? In my opinion, the fdtrans one because:&lt;br /&gt;
&lt;br /&gt;
# You need less changes (no need to manually include interfaces/dopus5.h file).&lt;br /&gt;
# The code of stubs from fdtrans look better and cleaner in comparison with the code of stubs from fd2pragma. For example even small things like comments saying what values in hex mean in your offset jump table is pretty nice (especially when you work manually with pragmas, where hex offsets are present).&lt;br /&gt;
# Size of dopus.l.main done from fd2pragma output is larger in comparison with the size of dopus.l.main created from fdtrans output.&lt;br /&gt;
&lt;br /&gt;
= I want one single function to work =&lt;br /&gt;
&lt;br /&gt;
Now for something interesting. You may want to check if it all works from AmigaOS 4 and with your new lib.l.main and native AmigaOS includes, code, etc. You then choose any &amp;quot;simple&amp;quot; function from the library and make a simple AmigaOS 4 native test program for it. Let&#039;s say it will be the StrConcat() function from dopus5.library, which is described in the pragma file as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#pragma libcall DOpusBase StrConcat 47a 09803&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and in dopus_lib.fd like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
StrConcat(s1,s2,len)(a0/a1,d0)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then simply calculate:&lt;br /&gt;
&lt;br /&gt;
1. 0x47a = 1146&amp;lt;br&amp;gt; &lt;br /&gt;
2. 1146 / 6 = 191&amp;lt;br&amp;gt;&lt;br /&gt;
3. 191 - 4 (i.e. 30/6 from beginning 5, but 30 is our first one, so -4) = 187&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
i.e. entry in jump table for StrConcat is 187. The entry in the dopus_lib.fd file for Strconcat is also 187 in the list of functions (+ header of .fd)&lt;br /&gt;
&lt;br /&gt;
In other words you can build your .fd file from scratch, which will look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
* &amp;quot;dopus.library&amp;quot;                                             &lt;br /&gt;
##base _DOpusBase&lt;br /&gt;
##bias 30&lt;br /&gt;
*                                                             &lt;br /&gt;
* Support routines for Directory Opus and associated programs &lt;br /&gt;
* (c) Copyright 1995 Jonathan Potter                          &lt;br /&gt;
*                                                             &lt;br /&gt;
##public&lt;br /&gt;
aa()()&lt;br /&gt;
aa()()&lt;br /&gt;
aa()()&lt;br /&gt;
aa()()&lt;br /&gt;
.... 186 entries to fill the gap in jump table ....&lt;br /&gt;
StrConcat(s1,s2,len)(a0/a1,d0)&lt;br /&gt;
....&lt;br /&gt;
##end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obviously, you can go the more elegant way and use .sfd instead, which have &amp;quot;reserved&amp;quot; keyword, so no need to put all those empty-functions like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
==id $Id: dopus5_lib.sfd,v 1.0 2013/03/29 09:24:08 noname Exp $ &lt;br /&gt;
* &amp;quot;dopus5.library&amp;quot; &lt;br /&gt;
==base _DopusBase &lt;br /&gt;
==basetype struct Library * &lt;br /&gt;
==libname dopus5.library &lt;br /&gt;
==bias 30 &lt;br /&gt;
==public &lt;br /&gt;
==include &amp;lt;exec/types.h&amp;gt; &lt;br /&gt;
==reserve 186 &lt;br /&gt;
BOOL StrConcat(char * s1, char * s2, int len) (a0,a1,d0) &lt;br /&gt;
==end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And when you will generate all necessary stuff via fd2grama/fdtrans/idltool from .fd or from .sfd, offsets will be respected. When you will call IDopus-&amp;gt;StrConcat(blablab) from your code it will work because StrConcat will be found in the jump-table at the right offset.&lt;br /&gt;
&lt;br /&gt;
= Final words =&lt;br /&gt;
&lt;br /&gt;
As usual, there is nothing hardcore in here. This is just a short article about the problems which I encountered. I hope that it can be of any help for new developers. Thanks to Fredrik Wikstrom for hints about offsets calculation, Colin Wenzel for explaining how RamLib works when it scans paths for stubs, to Jeffrey Gilpin for his knowledge and many others for proofreading and grammar corrections.&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&lt;br /&gt;
[1] Libraries and devices: https://wiki.amigaos.net/wiki/Libraries_and_Devices&lt;br /&gt;
&lt;br /&gt;
[2] RamLib autodoc.&lt;br /&gt;
&lt;br /&gt;
[3] fd2pragma guide from: http://aminet.net/dev/misc/fd2pragma.lha&lt;br /&gt;
&lt;br /&gt;
[4] How to create PPC stubs for M68K libraries: http://thomas-rapp.homepage.t-online.de/ppclib.html&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Input_and_Output_Methods&amp;diff=12538</id>
		<title>Intuition Input and Output Methods</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Input_and_Output_Methods&amp;diff=12538"/>
		<updated>2025-01-26T19:26:50Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Intuition Input and Output Methods ==&lt;br /&gt;
&lt;br /&gt;
This article discusses the input and output (I/O) techniques used with Intuition. I/O facilities are also available through Exec&#039;s device subsystems, such as the console, serial and parallel devices. For more information on these, see [[Devices|Devices]].&lt;br /&gt;
&lt;br /&gt;
For graphical output to the Amiga&#039;s display, programs can use Intuition&#039;s drawing features or handle rendering directly through calls to the graphics library. See the graphics library articles for more information on display rendering. For more about Intuition&#039;s drawing features see [[Intuition_Images,_Line_Drawing_and_Text|Intuition Images, Line Drawing and Text]].&lt;br /&gt;
&lt;br /&gt;
== Overview of System I/O ==&lt;br /&gt;
&lt;br /&gt;
This section provides a very simplified model of how Amiga I/O and application programs interact. The main elements of the Amiga&#039;s I/O system are shown in the diagram below. Input events begin when mouse movement is detected by the gameport device or key presses are received by the keyboard device. These and other input events are merged into a single stream by the input device, which then submits the stream to Intuition for further processing.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig9-1.png|frame|center|Amiga Input Block Diagram]]&lt;br /&gt;
&lt;br /&gt;
The application program can receive its input from Intuition or the Console device. The application may choose to listen to neither, one or both of these input sources.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig9-2.png|frame|center|Amiga Output Block Diagram]]&lt;br /&gt;
&lt;br /&gt;
An application&#039;s display output can go through the high level interfaces of the console device or through the Intuition library. Additionally, display output may be sent directly to the graphics library. Notice that both the Console and Intuition call the graphics library to render to the display.&lt;br /&gt;
&lt;br /&gt;
== Intuition Input ==&lt;br /&gt;
&lt;br /&gt;
The Amiga has an input device to monitor all input activity. The input activity nominally includes keyboard and mouse events, but which can be extended to include other types of input signals. When the user moves the mouse, presses a mouse button or types on the keyboard, the input device detects the activity from the specific device, and constructs an InputEvent.&lt;br /&gt;
&lt;br /&gt;
An InputEvent is a message describing a single event, such as the transition of a key on the keyboard from up to down.&lt;br /&gt;
&lt;br /&gt;
The input device then passes the input events down a prioritized chain of input handlers, which are routines in memory that process the input events. The sequence of input events passing through this chain of input handlers is known as the &#039;&#039;input stream&#039;&#039;. Any handler linked into this chain can monitor and modify the event stream.&lt;br /&gt;
&lt;br /&gt;
Each input handler may block (consume) events, allow events to pass through to the next handler in the chain or add new events to the sequence.&lt;br /&gt;
&lt;br /&gt;
Other devices and programs can add input events to the input stream by sending messages to the input device. For instance, AmigaDOS is able to generate an input event whenever a disk is inserted or removed.&lt;br /&gt;
&lt;br /&gt;
See the [[Input Device]] for more information on the Input device.&lt;br /&gt;
&lt;br /&gt;
=== Intuition as an Input Handler ===&lt;br /&gt;
&lt;br /&gt;
Intuition is an input handler linked into the input stream, and it monitors and modifies events that it receives. The input arrives at Intuition as a single stream of events. These events are filtered, altered, and enhanced by Intuition, then dispatched to windows as appropriate, or passed down to input handlers lower in the chain. If the active window has a console attached to it, then it can receive the input events that are still left in the stream, which can include some events that Intuition played a role in forming.&lt;br /&gt;
&lt;br /&gt;
Many kinds of input event undergo little conversion by Intuition. For instance, raw keyboard events are not modified by Intuition (with the exception of a few keystrokes that have special meaning). Other events may produce differing results based on Intuition&#039;s view of the system. For example, when the mouse select button is pressed, the event may become a gadget down-press event, a window activation event, or it may remain a simple button press, depending on the mouse position and the arrangement of windows and screens. Still other events are consumed by Intuition, and the application is not directly notified. An example would be when the select button is pressed over a system gadget.&lt;br /&gt;
&lt;br /&gt;
Intuition is also the originator of certain kinds of events. For example, a window-refreshing event is generated when Intuition discovers that part of a window is in need of redrawing. This might have resulted indirectly from some other input (for example, the user might have dragged a window), but not necessarily (the refresh might have been necessitated by a program bringing a window to the front).&lt;br /&gt;
&lt;br /&gt;
=== Receiving Input Events from Intuition ===&lt;br /&gt;
&lt;br /&gt;
There are two channels through which a window can receive events destined for it. The usual way is for the application to ask Intuition to send it messages which are based on the input event that Intuition has processed. These messages, called IntuiMessages, are standard Amiga Exec messages, and are sent to a port called an Intuition Direct Communications Message Port, or IDCMP. Every window may have an IDCMP associated with it (pointed to by Window.UserPort).&lt;br /&gt;
&lt;br /&gt;
There are many classes of IntuiMessages, and the application can control which classes of events are routed to its window&#039;s port by setting the appropriate IDCMP flags. When Intuition has an event to send, but the window does not have the corresponding IDCMP flag set, the event is generally passed along to the next input handler in the chain. One input handler that resides below Intuition&#039;s is the console device&#039;s handler. If your application&#039;s window has a console attached to it, the console device will generally convert events it receives into console code sequences, and send those to your console. In this manner, you can hear these events.&lt;br /&gt;
&lt;br /&gt;
Because IntuiMessages and the IDCMP are the primary way in which applications receive communication from Intuition, discussions elsewhere in the manual frequently refer to events from Intuition as messages, IntuiMessages, or IDCMP messages. However, most of the information sent as IntuiMessages is also available through the console device, though that option is used less often. Elsewhere in this article, you can learn how getting your events through the console differs from getting them through your IDCMP.&lt;br /&gt;
&lt;br /&gt;
Whichever way an application chooses to get its messages, it is frequently designed to be event-driven. That is to say, after some amount of initialization, the application will go into a state where it is waiting for some event to happen. This event could be an input event, or some other kind of event. Based on the event received, the application would take appropriate action, and return to its waiting state.&lt;br /&gt;
&lt;br /&gt;
=== IDCMP Events and the Input Focus ===&lt;br /&gt;
&lt;br /&gt;
Although at any given time many applications may be waiting for input, in most cases only the active application (the one with the currently active window) will receive IDCMP messages.&lt;br /&gt;
&lt;br /&gt;
Since the IDCMP messages are, in general, directed to a single window, this window is said to have the &#039;&#039;input focus&#039;&#039;; the input from a variety of sources is focused on this single location.&lt;br /&gt;
&lt;br /&gt;
The active window is generally selected by the user, although it is possible for applications to change the active window. See [[Intuition_Windows|Intuition Windows]] for information on selecting or setting the active window. Be aware that changing the active window will change the input focus. Usually this change is performed following user action; the user selects a window with the mouse, or activates a new application. Changes to the input focus without user control, such as activating another window while the user is working in an application, may confuse the user. Perform such changes with great care.&lt;br /&gt;
&lt;br /&gt;
Not all events are sent only to the active IDCMP. Some events, such as &amp;quot;disk inserted,&amp;quot; may be useful to many programs, so Intuition translates these events into separate messages, one for each application.&lt;br /&gt;
&lt;br /&gt;
== Intuition Output ==&lt;br /&gt;
&lt;br /&gt;
Visual program output, the information written to the display, is sent through one of three channels.&lt;br /&gt;
&lt;br /&gt;
* Imagery may be sent to the graphics library primitives. Graphics library includes functions for line drawing, area fill, specialized animation and output of text. See [[Graphics_Primitives|Graphics Primitives]], [[Graphics_Library_and_Text|Graphics Library and Text]] and [[Graphics_Sprites,_Bobs_and_Animation|Graphics Sprites, Bobs and Animation]] for more information on these functions.&lt;br /&gt;
&lt;br /&gt;
* Use the Intuition library support functions for rendering text, graphical imagery, and line drawing. These provide some of the same functions as the graphics library routines, but the Intuition functions perform more of the detail work for you. See [[Intuition_Images,_Line_Drawing_and_Text|Intuition Images, Line Drawing and Text]] for more information on Intuition rendering functions. Also see, of course, the articles on screens, windows, gadgets menus and requesters for information on managing the display.&lt;br /&gt;
&lt;br /&gt;
* Output character-based data via the console device. The console device is discussed in the next section.&lt;br /&gt;
&lt;br /&gt;
== Console Device I/O ==&lt;br /&gt;
&lt;br /&gt;
A program receives its input stream either directly from Intuition or via another mechanism known as the console device.&lt;br /&gt;
&lt;br /&gt;
The console device may be used both as a source for input and as a mechanism for output. Often, it is convenient to use only the console device for input and output. In particular, character-based programs can open the console and use it for all I/O without worrying about windows, bitmaps, or message ports.&lt;br /&gt;
&lt;br /&gt;
The console device gives the program &amp;quot;cooked&amp;quot; input data, including key code conversions to ASCII and conversions of Intuition generated events, such as IDCMP_CLOSEWINDOW, to ANSI escape sequences.&lt;br /&gt;
&lt;br /&gt;
The console device output provides features such as automatic line wrapping and scrolling. If an application just wants to output text, it may choose to use the console device, which provides formatted text with little fuss.&lt;br /&gt;
&lt;br /&gt;
If the application is not character-based, it may be better for the it to use an IDCMP for input and render graphics and text directly through Intuition and the graphics library primitives.&lt;br /&gt;
&lt;br /&gt;
If necessary, it is possible to open both the console device and an IDCMP for input. Such a program might need ASCII input, formatted output and the IDCMP verification functions (for example, to verify that it has finished writing to the window before the user can bring up a requester).&lt;br /&gt;
&lt;br /&gt;
For more information on the console device, see the [[Console_Device|Console Device]].&lt;br /&gt;
&lt;br /&gt;
== Using the IDCMP ==&lt;br /&gt;
&lt;br /&gt;
The IDCMP allow the application to receive information directly from Intuition. The program can use the IDCMP to learn about mouse, keyboard and other Intuition events. Also, certain useful Intuition features, most notably the verification functions (described under &amp;quot;IDCMP Flags&amp;quot; below), require that the IDCMP be opened, as this is the only mechanism available for accessing these features.&lt;br /&gt;
&lt;br /&gt;
The IDCMP consists of a pair of message ports, which may be allocated and initialized by Intuition at the request of the program. Alternately, the application may choose to manage part of the allocation, such that one port is supplied by the application and one port is supplied by Intuition. These ports are standard Exec message ports, used to allow interprocess communications in the Amiga multitasking environment. To learn more about message ports and message passing, see [[Exec_Messages_and_Ports|Exec Messages and Ports]].&lt;br /&gt;
&lt;br /&gt;
The IDCMP is always associated with a window, it is not possible to have an IDCMP without an open window. The IDCMP is made up of several fields in the Window structure:&lt;br /&gt;
&lt;br /&gt;
; IDCMPFlags&lt;br /&gt;
: stores the IDCMP flags currently set for this port. This field should never be directly set by the application; use the function ModifyIDCMP() or set them when the window is opened instead.&lt;br /&gt;
&lt;br /&gt;
; UserPort&lt;br /&gt;
: is a pointer to the standard Exec message port where the application receives input event messages from Intuition&lt;br /&gt;
&lt;br /&gt;
; WindowPort&lt;br /&gt;
: is a pointer to the reply message port used by Intuition. The messages sent by Intuition are set up such that ReplyMsg() will return them to this port.&lt;br /&gt;
&lt;br /&gt;
To open these ports automatically, set at least one of the IDCMP flags in the OpenWindowTagList() call. To free these ports later in the program, call the function ModifyIDCMP() with NULL for the IDCMP flags or simply close the window.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t Reply Any Messages After the IDCMP is Freed|text=If an IDCMP is freed, either by calling ModifyIDCMP() or by closing the window, Intuition will reclaim and deallocate all messages waiting at that port without waiting for a ReplyMsg(). If the program attempts to ReplyMsg() to an IntuiMessages after the IDCMP is closed, the system will probably crash.}}&lt;br /&gt;
&lt;br /&gt;
If the IDCMP flags are NULL when the window is opened, no ports will be allocated when the window is created. To have Intuition allocate these ports later, call the function ModifyIDCMP() with any of the IDCMP flags set.&lt;br /&gt;
&lt;br /&gt;
Once the IDCMP is opened, with the ports allocated, the program can receive many types of information directly from Intuition, based on the IDCMP flags that are set.&lt;br /&gt;
&lt;br /&gt;
The IDCMP allows the application to receive only the events that it considers important. The program can, for instance, choose to learn about gadget events but may not want to learn about other mouse or keyboard events. This is done by providing a &amp;quot;filter&amp;quot; or &amp;quot;mask&amp;quot; value for the IDCMP which tells Intuition which events it should send to this specific port. Only messages with a type matching one of the flags set in the Window structure&#039;s IDCMPFlags field will be sent to this port. These values may be set at creation time, or modified by calling the function ModifyIDCMP().&lt;br /&gt;
&lt;br /&gt;
Messages sent to the IDCMP are instances of the structure IntuiMessage. This is an extended form of the Exec Message structure which allows Intuition to send user interface specific information to the application. The IntuiMessage structure is discussed at length below.&lt;br /&gt;
&lt;br /&gt;
After the application opens an IDCMP, it must monitor the port for messages. At a minimum, this involves removing all messages from the port and replying to them. An event loop which processes messages arriving at the IDCMP is discussed below.&lt;br /&gt;
&lt;br /&gt;
=== Standard IntuiMessage Event Loop ===&lt;br /&gt;
&lt;br /&gt;
The application should handle events quickly. Any delay in this handling will make the user interface appear sluggish to the user. Additionally, certain events such as IDCMP_SIZEVERIFY may time-out if the application does not respond to them quickly (this is to help prevent system deadlocks). The action taken by Intuition when an event times-out may not match the action desired by the program. When IDCMP_SIZEVERIFY times out, the window sizing operation is cancelled by Intuition.&lt;br /&gt;
&lt;br /&gt;
Code should be able to handle the case where there are multiple events waiting at the port. When events are being generated quickly, Intuition may post many events to the IDCMP before the application regains control. This can happen regardless of how fast the application processes the messages waiting at the port. Since messages queue up but signals do not, the application may not see a signal for each message posted. Because of these facts, the code should remove all the messages waiting at the port, regardless of the number, each time Wait() returns.&lt;br /&gt;
&lt;br /&gt;
Code should also be able to handle the case where the signal is set but no events are waiting at the port. This could happen if a new message arrives at the IDCMP while an application is still processing the previous message. Since applications typically process all queued messages before returning to Wait(), the second message gets handled with the signal bit still set. The subsequent call to Wait() will return immediately even though no message is present. These cases should be quietly ignored.&lt;br /&gt;
&lt;br /&gt;
=== Event Loop Example ===&lt;br /&gt;
&lt;br /&gt;
This example shows how to receive Intuition events. It reports on a variety of events: close window, keyboard, disk insertion and removal, select button up and down and menu button up and down. Note that the menu button events will only be received by the program if the WA_RMBTrap attribute is set for the window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** eventloop.c - standard technique to handle IntuiMessages from an IDCMP.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our function prototypes */&lt;br /&gt;
BOOL handleIDCMP(struct Window *win, BOOL done);&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase = NULL;&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** main routine.&lt;br /&gt;
** Open required library and window, then process the events from the&lt;br /&gt;
** window.  Free all resources when done.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
BOOL done;&lt;br /&gt;
struct Window *win;&lt;br /&gt;
&lt;br /&gt;
IntuitionBase = (struct IntuitionBase *)IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
IIntuition = (struct IntuitionIFace *)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
if (IIntuition != NULL)&lt;br /&gt;
    {&lt;br /&gt;
    if (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                        WA_Title,       &amp;quot;Press Keys and Mouse in this Window&amp;quot;,&lt;br /&gt;
                        WA_Width,       500,&lt;br /&gt;
                        WA_Height,      50,&lt;br /&gt;
                        WA_Activate,    TRUE,&lt;br /&gt;
                        WA_CloseGadget, TRUE,&lt;br /&gt;
                        WA_RMBTrap,     TRUE,&lt;br /&gt;
                        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY |&lt;br /&gt;
                            IDCMP_RAWKEY | IDCMP_DISKINSERTED |&lt;br /&gt;
                            IDCMP_DISKREMOVED | IDCMP_MOUSEBUTTONS,&lt;br /&gt;
                        TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
        done = FALSE;&lt;br /&gt;
&lt;br /&gt;
        /* perform this loop until the message handling routine signals&lt;br /&gt;
        ** that we are done.&lt;br /&gt;
        **&lt;br /&gt;
        ** When the Wait() returns, check which signal hit and process&lt;br /&gt;
        ** the correct port.  There is only one port here, so the test&lt;br /&gt;
        ** could be eliminated.  If multiple ports were being watched,&lt;br /&gt;
        ** the test would become:&lt;br /&gt;
        **&lt;br /&gt;
        **    signals = Wait( (1L &amp;lt;&amp;lt; win1-&amp;gt;UserPort-&amp;gt;mp_SigBit) |&lt;br /&gt;
        **                    (1L &amp;lt;&amp;lt; win2-&amp;gt;UserPort-&amp;gt;mp_SigBit) |&lt;br /&gt;
        **                    (1L &amp;lt;&amp;lt; win3-&amp;gt;UserPort-&amp;gt;mp_SigBit))&lt;br /&gt;
        **    if (signals &amp;amp; (1L &amp;lt;&amp;lt; win1-&amp;gt;UserPort-&amp;gt;mp_SigBit))&lt;br /&gt;
        **        done = handleWin1IDCMP(win1,done);&lt;br /&gt;
        **    else if (signals &amp;amp; (1L &amp;lt;&amp;lt; win2-&amp;gt;UserPort-&amp;gt;mp_SigBit))&lt;br /&gt;
        **        done = handleWin2IDCMP(win2,done);&lt;br /&gt;
        **    else if (signals &amp;amp; (1L &amp;lt;&amp;lt; win3-&amp;gt;UserPort-&amp;gt;mp_SigBit))&lt;br /&gt;
        **        done = handleWin3IDCMP(win3,done);&lt;br /&gt;
        **&lt;br /&gt;
        ** Note that these could all call the same routine with different&lt;br /&gt;
        ** window pointers (if the handling was identical).&lt;br /&gt;
        **&lt;br /&gt;
        ** handleIDCMP() should remove all of the messages from the port.&lt;br /&gt;
        */&lt;br /&gt;
        while (!done)&lt;br /&gt;
            {&lt;br /&gt;
            uint32 signals = IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
            if (signals &amp;amp; (1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit))&lt;br /&gt;
                done = handleIDCMP(win,done);&lt;br /&gt;
            };&lt;br /&gt;
        IExec-&amp;gt;CloseWindow(win);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** handleIDCMP() - handle all of the messages from an IDCMP.&lt;br /&gt;
*/&lt;br /&gt;
BOOL handleIDCMP(struct Window *win, BOOL done)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *message;&lt;br /&gt;
uint16 code;&lt;br /&gt;
int16 mousex, mousey;&lt;br /&gt;
uint32 class;&lt;br /&gt;
&lt;br /&gt;
/* Remove all of the messages from the port by calling GetMsg()&lt;br /&gt;
** until it returns NULL.&lt;br /&gt;
**&lt;br /&gt;
** The code should be able to handle three cases:&lt;br /&gt;
**&lt;br /&gt;
** 1.  No messages waiting at the port, and the first call to GetMsg()&lt;br /&gt;
** returns NULL.  In this case the code should do nothing.&lt;br /&gt;
**&lt;br /&gt;
** 2.  A single message waiting.  The code should remove the message,&lt;br /&gt;
** processes it, and finish.&lt;br /&gt;
**&lt;br /&gt;
** 3.  Multiple messages waiting.  The code should process each waiting&lt;br /&gt;
** message, and finish.&lt;br /&gt;
*/&lt;br /&gt;
while (NULL != (message = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort)))&lt;br /&gt;
    {&lt;br /&gt;
    /* It is often convenient to copy the data out of the message.&lt;br /&gt;
    ** In many cases, this lets the application reply to the message&lt;br /&gt;
    ** quickly.  Copying the data is not required, if the code does&lt;br /&gt;
    ** not reply to the message until the end of the loop, then&lt;br /&gt;
    ** it may directly reference the message information anywhere&lt;br /&gt;
    ** before the reply.&lt;br /&gt;
    */&lt;br /&gt;
    class  = message-&amp;gt;Class;&lt;br /&gt;
    code   = message-&amp;gt;Code;&lt;br /&gt;
    mousex = message-&amp;gt;MouseX;&lt;br /&gt;
    mousey = message-&amp;gt;MouseY;&lt;br /&gt;
&lt;br /&gt;
    /* The loop should reply as soon as possible.  Note that the code&lt;br /&gt;
    ** may not reference data in the message after replying to the&lt;br /&gt;
    ** message.  Thus, the application should not reply to the message&lt;br /&gt;
    ** until it is done referencing information in it.&lt;br /&gt;
    **&lt;br /&gt;
    ** Be sure to reply to every message received with GetMsg().&lt;br /&gt;
    */&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg((struct Message *)message);&lt;br /&gt;
&lt;br /&gt;
    /* The class contains the IDCMP type of the message. */&lt;br /&gt;
    switch (class)&lt;br /&gt;
        {&lt;br /&gt;
        case IDCMP_CLOSEWINDOW:&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
            break;&lt;br /&gt;
        case IDCMP_VANILLAKEY:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;IDCMP_VANILLAKEY (%lc)\n&amp;quot;,code);&lt;br /&gt;
            break;&lt;br /&gt;
        case IDCMP_RAWKEY:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;IDCMP_RAWKEY\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case IDCMP_DISKINSERTED:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;IDCMP_DISKINSERTED\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case IDCMP_DISKREMOVED:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;IDCMP_DISKREMOVED\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case IDCMP_MOUSEBUTTONS:&lt;br /&gt;
            /* the code often contains useful data, such as the ASCII&lt;br /&gt;
            ** value (for IDCMP_VANILLAKEY), or the type of button&lt;br /&gt;
            ** event here.&lt;br /&gt;
            */&lt;br /&gt;
            switch (code)&lt;br /&gt;
                {&lt;br /&gt;
                case SELECTUP:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;SELECTUP at %d,%d\n&amp;quot;,mousex,mousey);&lt;br /&gt;
                    break;&lt;br /&gt;
                case SELECTDOWN:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;SELECTDOWN at %d,%d\n&amp;quot;,mousex,mousey);&lt;br /&gt;
                    break;&lt;br /&gt;
                case MENUUP:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;MENUUP\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
                case MENUDOWN:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;MENUDOWN\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;UNKNOWN CODE\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Unknown IDCMP message\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
return(done);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Setting up a Custom User Port ===&lt;br /&gt;
&lt;br /&gt;
An application can use its own message port for the IDCMP instead of the one set up by Intuition, although some care is required.&lt;br /&gt;
&lt;br /&gt;
As described earlier, IDCMP communication takes place through a pair of Exec message ports attached to a window: the UserPort and the WindowPort. The UserPort is the port where the application receives IDCMP messages from Intuition. The WindowPort is the reply port where Intuition receives replies from the application (via the ReplyMsg() function).&lt;br /&gt;
&lt;br /&gt;
In the simplest case, Intuition allocates (and deallocates) both of these ports when the program opens a window with non-NULL IDCMP flags. Intuition will also allocate these ports if the application calls ModifyIDCMP() with non-NULL flags for a window that has NULL IDCMP flags. These port variables will be set to NULL if there is no message port allocated, otherwise they will contain a pointer to a message port.&lt;br /&gt;
&lt;br /&gt;
If the WindowPort is not already opened when either OpenWindow() or ModifyIDCMP() is called, it will be allocated and initialized.&lt;br /&gt;
&lt;br /&gt;
The UserPort is checked separately to see whether it is already opened.&lt;br /&gt;
&lt;br /&gt;
When Intuition initializes the UserPort, it also allocates a signal bit with a call to AllocSignal(). Since the application makes the call to OpenWindowTagList() or ModifyIDCMP(), this signal bit is valid for the application&#039;s task. The address of the application&#039;s task is saved in the SigTask variable of the message port.&lt;br /&gt;
&lt;br /&gt;
The program may choose to supply its own UserPort. This might be done in an environment where the program is using several windows and would prefer to monitor the input using only one message port. This is done by with the following procedure:&lt;br /&gt;
&lt;br /&gt;
# Create a port for the IDCMP by calling either the Exec function AllocSysObject(ASOT_PORT) which returns a pointer to a port.&lt;br /&gt;
# Open the windows with no IDCMP flags set. This will prevent Intuition from allocating a port for this window.&lt;br /&gt;
# Place a pointer to the port created in step 1 into the UserPort field of the Window structure.&lt;br /&gt;
# Call ModifyIDCMP() to set the desired IDCMP flags for the port. Intuition will use the port supplied with the window.&lt;br /&gt;
# When an application decides to close a window that has a shared IDCMP, there may be messages waiting at the port for any of the windows including the window being closed. It is essential that messages destined for a given window be removed and replied to before that window is closed.&lt;br /&gt;
# CloseWindowSafely() performs proper message cleanup before closing such a window. It also sets the window&#039;s UserPort to NULL so that Intuition knows not to delete the port which should be done by the application in this case. It is incorrect (and dangerous) to simply call CloseWindow() on a window that has a shared IDCMP.&lt;br /&gt;
# After all windows have been closed, and the port has been removed from each, delete the port that was created in step 1. &lt;br /&gt;
&lt;br /&gt;
{{Note|title=Be Careful with Shared IDCMP Ports|text=If the application is sharing an IDCMP among several windows, it must be very careful not to call ModifyIDCMP(window,NULL) for any of the windows that are using the shared port, as this will free the port and the signal bit.}}&lt;br /&gt;
&lt;br /&gt;
==== Closing a Window with a Shared IDCMP ====&lt;br /&gt;
&lt;br /&gt;
As promised in the last section, this example shows the CloseWindowSafely() function. Use this function to close any windows that share an IDCMP port with another window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* CloseWindowSafely&lt;br /&gt;
**&lt;br /&gt;
** these functions close an Intuition window that shares a port with other&lt;br /&gt;
** Intuition windows.&lt;br /&gt;
**&lt;br /&gt;
** We are careful to set the UserPort to NULL before closing, and to free&lt;br /&gt;
** any messages that it might have been sent.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Entry point to CloseWindowSafely()&lt;br /&gt;
** Strip all IntuiMessages from an IDCMP which are waiting for a specific&lt;br /&gt;
** window.  When the messages are gone, set the UserPort of the window to NULL&lt;br /&gt;
** and call ModifyIDCMP(win,0).  This will free the Intuition arts of the&lt;br /&gt;
** IDMCMP and trun off message to this port without changing the original&lt;br /&gt;
** UserPort (which may be in use by other windows).&lt;br /&gt;
*/&lt;br /&gt;
VOID CloseWindowSafely(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
/* we forbid here to keep out of race conditions with Intuition */&lt;br /&gt;
IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
/* send back any messages for this window  that have not yet been processed */&lt;br /&gt;
IIntuition-&amp;gt;StripIntuiMessages(win-&amp;gt;UserPort, win);&lt;br /&gt;
&lt;br /&gt;
/* clear UserPort so Intuition will not free it */&lt;br /&gt;
win-&amp;gt;UserPort = NULL;&lt;br /&gt;
&lt;br /&gt;
/* tell Intuition to stop sending more messages */&lt;br /&gt;
IIntuition-&amp;gt;ModifyIDCMP(win, 0L);&lt;br /&gt;
&lt;br /&gt;
/* turn multitasking back on */&lt;br /&gt;
IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
/* Now it&#039;s safe to really close the window */&lt;br /&gt;
IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Use WA_UserPort Instead|text=Use of the WA_UserPort tag can be used to achieve the same result as CloseWindowSafely(). Using WA_UserPort is the preferred way to share IDCMP ports between multiple windows.}}&lt;br /&gt;
&lt;br /&gt;
=== IntuiMessages ===&lt;br /&gt;
&lt;br /&gt;
The IntuiMessage structure is an Exec Message that has been extended to include Intuition specific information. The ExecMessage field in the IntuiMessage is an actual instance of a Message structure and is used by Exec to manage the transmission of the message. The Intuition extensions of the IntuiMessage are used to transmit specialized Intuition data to the program.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IntuiMessage&lt;br /&gt;
    {&lt;br /&gt;
    struct Message ExecMessage;&lt;br /&gt;
    ULONG Class;&lt;br /&gt;
    UWORD Code;&lt;br /&gt;
    UWORD Qualifier;&lt;br /&gt;
    APTR IAddress;&lt;br /&gt;
    WORD MouseX, MouseY;&lt;br /&gt;
    ULONG Seconds, Micros;&lt;br /&gt;
    struct Window *IDCMPWindow;&lt;br /&gt;
    struct IntuiMessage *SpecialLink;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IntuiMessage structure fields are as follows:&lt;br /&gt;
&lt;br /&gt;
; ExecMessage&lt;br /&gt;
: This field is maintained by Exec. It is used for linking the message into the system and broadcasting it to a message port. See [[Exec_Messages_and_Ports|Exec Messages and Ports]] for more information on the Message structure and its use.&lt;br /&gt;
&lt;br /&gt;
; Class&lt;br /&gt;
: Class contains the IDCMP type of this specific message. By comparing the Class field to the IDCMP flags, the application can determine the type of this message. Each message may only have a single IDCMP type.&lt;br /&gt;
&lt;br /&gt;
; Code&lt;br /&gt;
: Code contains data set by Intuition, such as menu numbers or special code values. The meaning of the Code field changes depending on the IDCMP type, or Class, of the message. Often the code field will simply contain a copy of the code of the input event which generated this IntuiMessage.&lt;br /&gt;
&lt;br /&gt;
: For example, when the message is of class IDCMP_RAWKEY, Code contains the raw key code generated by the keyboard device. When the message is of class IDCMP_VANILLAKEY, Code contains the key mapped ASCII character.&lt;br /&gt;
&lt;br /&gt;
; Qualifier&lt;br /&gt;
: This contains a copy of the ie_Qualifier field that is transmitted to Intuition by the input device. This field is useful if your program handles raw key codes, since the Qualifier tells the program, for instance, whether or not the Shift key or Ctrl key is currently pressed. Check the &amp;amp;lt;devices/inputevent.h&amp;amp;gt; file for the definitions of the qualifier bits.&lt;br /&gt;
&lt;br /&gt;
; MouseX and MouseY&lt;br /&gt;
: Every IntuiMessage will have the mouse coordinates in these variables. The coordinates can either be expressed as absolute offsets from the upper left corner of the window, or expressed as the amount of change since the last reported positions (delta).&lt;br /&gt;
&lt;br /&gt;
: If IDCMP_DELTAMOVE is set, then these numbers will represent delta positions from the last position. All messages will have zero in these values except IDCMP_MOUSEMOVE and IDCMP_MOUSEBUTTON events, which will have the correct delta values for the movement. If IDCMP_DELTAMOVE is not set, then these numbers are the actual window offset values.&lt;br /&gt;
&lt;br /&gt;
; Seconds and Micros&lt;br /&gt;
: These values are copies of the current system clock, in seconds and microseconds. They are set when Intuition generates the message.&lt;br /&gt;
&lt;br /&gt;
: Microseconds (Micros) range from zero up to one million minus one. The 32 bits allocated to the Seconds variable has enough accuracy to count up to 139 years. Time is measured from January 1, 1978.&lt;br /&gt;
&lt;br /&gt;
; IAddress&lt;br /&gt;
: Typically this variable contains the address of some Intuition object, such as a gadget. The type of the object depends on the Class of the IntuiMessage. Do not assume that the object is of a certain type before checking the Class of the object.&lt;br /&gt;
&lt;br /&gt;
: The IAddress pointer is defined &#039;&#039;only&#039;&#039; for the following IDCMP Classes. Do not attempt to dereference or otherwise interpret the IAddress field of any other type of IntuiMessage.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IntuiMessage Class&lt;br /&gt;
! Meaning of IAddress Field&lt;br /&gt;
|-&lt;br /&gt;
| IDCMP_GADGETDOWN || IAddress points to the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| IDCMP_GADGETUP || IAddress points to the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| IDCMP_RAWKEY || IAddress points to the dead-key information.&lt;br /&gt;
|-&lt;br /&gt;
| IDCMP_IDCMPUPDATE || IAddress points to a tag item list.&lt;br /&gt;
|-&lt;br /&gt;
| Other classes || No meaning.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
: In particular, for IDCMP_MOUSEMOVE IntuiMessages emanating from GACT_FOLLOWMOUSE gadgets, the IAddress field does not point to the gadget. Interpreting the IAddress as a gadget pointer and trying to access the gadget&#039;s fields before ascertaining that the event is an IDCMP_GADGETUP or IDCMP_GADGETDOWN event is incorrect, and can lead to subtle or serious problems.&lt;br /&gt;
&lt;br /&gt;
: (Note that GadTools gadgets do arrange for the IAddress to point to the gadget when IDCMP_MOUSEMOVE messages appear).&lt;br /&gt;
&lt;br /&gt;
; IDCMPWindow&lt;br /&gt;
: Contains the address of the window to which this message was sent. If the application is sharing the window&#039;s UserPort between multiple windows, IDCMPWindow allows it to determine which of the windows the message was sent to.&lt;br /&gt;
&lt;br /&gt;
; SpecialLink&lt;br /&gt;
: For system use only.&lt;br /&gt;
&lt;br /&gt;
=== IDCMP Flags ===&lt;br /&gt;
&lt;br /&gt;
The application specifies the information it wants Intuition to send to it via the IDCMP by setting IDCMP flags. These may be set either when opening the window or by calling ModifyIDCMP().&lt;br /&gt;
&lt;br /&gt;
The flags set may be viewed as a filter, in that Intuition will only post IntuiMessages to an IDCMP if the matching flag is set. Thus, the application will only receive the IDCMP messages whose Class matches one of the bits set in the window&#039;s IDCMP.&lt;br /&gt;
&lt;br /&gt;
For many of these messages, there is a separation of the act of filtering these messages and causing Intuition to send the messages in the first place. For instance, menu help events may be activated for a window by setting the WA_MenuHelp attribute when the window is opened. However, the IDCMP will only receive the messages if the IDCMP_MENUHELP flag is set. If this flag is not set, then the events are passed downstream in the input and may be picked up by the console device.&lt;br /&gt;
&lt;br /&gt;
=== Mouse Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_MOUSEBUTTONS&lt;br /&gt;
: Contains reports about mouse button up and down events. The events will be sent to the application only if they are not used internally by Intuition.&lt;br /&gt;
&lt;br /&gt;
: The Code field contains information on the specific mouse button event this message represents. The Code field will be equal to SELECTDOWN, SELECTUP, MENUDOWN, MENUUP, MIDDLEDOWN or MIDDLEUP, depending on the button pressed or released. In general, the select button is the left mouse button, the menu button is the right mouse button and the middle button is an optional third button usually located between the select and menu buttons.&lt;br /&gt;
&lt;br /&gt;
: Often, a mouse button event has extra meaning to Intuition, and the application may hear about it through a more specific message, for example a gadget or menu event. Other times, no event is generated at all, such as when the user depth-arranges a screen by clicking on the screen depth gadget. Note that menu button events are normally consumed by Intuition for menu handling. If an application wishes to hear IDCMP_MOUSEBUTTONS events for the menu button, it must set the WA_RMBTrap attribute for its window. See [[Intuition_Windows|Intuition Windows]] for more information.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_MOUSEMOVE&lt;br /&gt;
: Reports about mouse movements, sent in the form of &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; coordinates relative to the upper left corner of the window. One message will be sent to the application for each &amp;quot;tick&amp;quot; of the mouse.&lt;br /&gt;
&lt;br /&gt;
: The application can opt to receive IDCMP_MOUSEMOVE events only while certain gadgets are active, or during normal window operation. These events are sent whenever a gadget with GACT_FOLLOWMOUSE gadget is active, or for any window that has the WA_ReportMouse attribute set. This window attribute can be set or cleared by the application at will. See [[Intuition_Windows|Intuition Windows]] for full details.&lt;br /&gt;
&lt;br /&gt;
: Requesting IDCMP_MOUSEMOVE messages can create a very large volume of messages arriving at the window&#039;s IDCMP. Do not request these messages unless the program is prepared to keep up with them. Intuition limits the number of mouse move events that pile up at your IDCMP.&lt;br /&gt;
&lt;br /&gt;
: All IDCMP messages contain a mouse &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; position that can be absolute values or delta values. See IDCMP_DELTAMOVE, below. If the application requires a less frequent reporting of the mouse position, consider using IDCMP_INTUITICKS. While IDCMP_MOUSEMOVE events are generated by changes in the mouse&#039;s position, IDCMP_INTUITICKS IntuiMessages are based on a timer. Since they contain mouse coordinates, they effectively sample the mouse position. These message come often enough for many applications, but not so frequently as to swamp the application.&lt;br /&gt;
&lt;br /&gt;
: The program will not be sent IDCMP_MOUSEMOVE messages while Intuition has the layers of the screen locked (during menu operations and window sizing/dragging). This avoids problems of messages accumulating while the program is blocked, waiting to render into a locked layer.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_DELTAMOVE&lt;br /&gt;
: IDCMP_DELTAMOVE is not a message type, and events will never be sent to the application with this IDCMP identifier. This flag is a modifier, which changes how mouse movements are reported. When this flag is set, mouse movements are sent as delta values rather than as absolute positions.&lt;br /&gt;
&lt;br /&gt;
: The deltas are the amount of change of the mouse position from the last reported position. If the mouse does not move, then the delta values will be zero.&lt;br /&gt;
&lt;br /&gt;
: This flag works in conjunction with the IDCMP_MOUSEMOVE flag. When IDCMP_DELTAMOVE is set, IDCMP_MOUSEBUTTONS messages will also have relative values, instead of the absolute window position of the mouse.&lt;br /&gt;
&lt;br /&gt;
: Delta mouse movements are reported even after the Intuition pointer has reached the limits of the display. That is, if the pointer has reached the edge of the display and the user continues to move the mouse in the same direction, the IDCMP_MOUSEMOVE messages will continue to report changes in the mouse position even though the pointer is no longer moving.&lt;br /&gt;
&lt;br /&gt;
=== Gadget Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_GADGETDOWN&lt;br /&gt;
: IDCMP_GADGETDOWN messages are sent when the user selects a gadget that was created with the GACT_IMMEDIATE flag set. The IntuiMessage structure&#039;s IAddress field will contain a pointer to the selected gadget.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_GADGETUP&lt;br /&gt;
: IDCMP_GADGETUP messages are sent when the user selects a gadget that was created with the GACT_RELVERIFY flag set. The IntuiMessage structure&#039;s IAddress field will contain a pointer to the selected gadget.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_CLOSEWINDOW&lt;br /&gt;
: IDCMP_CLOSEWINDOW messages are sent when the user selects the window&#039;s close gadget. Intuition does not close the window when the close gadget is selected. Rather, an IDCMP_CLOSEWINDOW message is sent to the window&#039;s IDCMP. It is up to the application to clean up and close the window itself. If closing a window means losing some data (perhaps the spreadsheet the user was working on), it would be appropriate for the application to first confirm that the user really meant to close the window.&lt;br /&gt;
&lt;br /&gt;
=== Menu Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_MENUPICK&lt;br /&gt;
: This flag indicates that the user has pressed the menu button. If a menu item was selected, the menu number of the menu item can be found in the Code field of the IntuiMessage. If no item was selected, the Code field will be equal to MENUNULL.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_MENUVERIFY&lt;br /&gt;
: This is a special verification mode which allows the program to confirm that it is prepared to handle Intuition rendering, in this case, allowing menus to be drawn in the screen.&lt;br /&gt;
&lt;br /&gt;
: This is a special kind of verification, in that any window in the entire screen that has this flag set must respond before the menu operations may proceed. Also, the active window of the screen is allowed to cancel the menu operation. This is unique to IDCMP_MENUVERIFY. Refer to [[Intuition_Menus|Intuition Menus]] for a complete description.&lt;br /&gt;
&lt;br /&gt;
: Also see the &amp;quot;Verification Functions&amp;quot; section below for more information.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_MENUHELP&lt;br /&gt;
: This message is sent by Intuition when the user selects the Help key while the menu system is activated. If a menu item was selected, the menu number of the menu item can be found in the Code field of the IntuiMessage. If no item was selected, the Code field will be equal to MENUNULL.&lt;br /&gt;
&lt;br /&gt;
: These messages will only be sent if the WA_MenuHelp attribute is set for the window.&lt;br /&gt;
&lt;br /&gt;
: The menu number returned in IDCMP_MENUHELP may specify a position that cannot be generated through normal menu activity. For instance, the menu number may indicate one of the menu headers with no item or sub-item. See [[Intuition_Menus|Intuition Menus]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== Requester Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQSET&lt;br /&gt;
: Intuition sends an IDCMP_REQSET message to the window each time a requester opens in that window.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQCLEAR&lt;br /&gt;
: Intuition sends an IDCMP_REQCLEAR message to the window each time a requester is cleared from that window.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REQVERIFY&lt;br /&gt;
: Set this flag to allow the application to ensure it is prepared for Intuition to render a requester in the window. With this flag set, Intuition sends the application a message that a requester is pending, and then waits for the application to reply before drawing the requester in the window.&lt;br /&gt;
&lt;br /&gt;
: If several requesters open in the window, Intuition asks the application to verify only the first one. After that, Intuition assumes that all output is being held off until all the requesters are gone.&lt;br /&gt;
&lt;br /&gt;
: By setting the IDCMP_REQSET and IDCMP_REQCLEAR flags, the application can track how many requesters are open in the window and when the last requester is cleared. Once all of the requesters are cleared from the window, it is safe to write to the window until another IDCMP_REQVERIFY is received.&lt;br /&gt;
&lt;br /&gt;
: See the &amp;quot;Verification Functions&amp;quot; section below for more discussion on using this flag.&lt;br /&gt;
&lt;br /&gt;
=== Window Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_NEWSIZE&lt;br /&gt;
: Intuition sends this message after the user has resized the window. After receiving this, the program can examine the size variables in the window structure to discover the new size of the window. The message is sent, even if the size of the window did not actually change.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_REFRESHWINDOW&lt;br /&gt;
: This message is sent whenever the window needs refreshing. This flag makes sense only with windows that have a refresh type of WA_SimpleRefresh or WA_SmartRefresh.&lt;br /&gt;
&lt;br /&gt;
: As a minimum, the application must call BeginRefresh() and EndRefresh() for the window after receiving an IDCMP_REFRESHWINDOW event. Create the window with the WA_NoCareRefresh attribute if you do not want to manage these events. See [[Intuition_Windows|Intuition Windows]] for details.&lt;br /&gt;
&lt;br /&gt;
: Most of the graphics library calls used for display output are compatible with Intuition, with the exception of ScrollRaster(). Intuition will not send an IDCMP_REFRESHWINDOW event when damage is caused to a window by ScrollRaster(). This may happen in a simple refresh window which is partially obscured by another window; the region that scrolls out from behind the front window will be damaged, but the window will receive no notification. Check the LAYERREFRESH bit in the Layer structure Flags field to see if damage did happen as a result of ScrollRaster().&lt;br /&gt;
&lt;br /&gt;
; IDCMP_SIZEVERIFY&lt;br /&gt;
: Set this flag if the program must complete some operation before the user sizes the window. When the user sizes the window, Intuition sends an IDCMP_SIZEVERIFY message to the application and then waits until the program replies before allowing the user to size the window. See the &amp;quot;Verification Functions&amp;quot; section below for some things to consider when using this flag.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_ACTIVEWINDOW and IDCMP_INACTIVEWINDOW&lt;br /&gt;
: Set these flags to discover when the window becomes activated or deactivated.&lt;br /&gt;
&lt;br /&gt;
=== Other Event Message Classes and Flags ===&lt;br /&gt;
&lt;br /&gt;
; IDCMP_VANILLAKEY&lt;br /&gt;
: IDCMP_VANILLAKEY messages return keyboard events translated into the current default character keymap. The mapped character value is returned in the Code field of the IntuiMessage structure.&lt;br /&gt;
&lt;br /&gt;
: An IDCMP_VANILLAKEY message is sent only if the translation results in a single byte value, therefore the program cannot read the &amp;quot;Help&amp;quot; or function keys using IDCMP_VANILLAKEY.&lt;br /&gt;
&lt;br /&gt;
: Programs using IDCMP_VANILLAKEY which also require the additional information of special keys, such as the &amp;quot;Help&amp;quot; key and the function keys, may set both IDCMP_VANILLAKEY and IDCMP_RAWKEY. When this combination is used, all keypresses that map to single character values will be returned as IDCMP_VANILLAKEY events; all other keyboard events will be sent as IDCMP_RAWKEY messages. Note that IDCMP_VANILLAKEY processing uses all of the key-up events, so the application will only receive key-down events in the IDCMP_RAWKEY format.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_RAWKEY&lt;br /&gt;
: IDCMP_RAWKEY messages give the raw keycodes from the keyboard. The numeric value of the keycode is sent in the Code field. Separate codes are returned for key down and key up. Qualifier codes, such as &amp;quot;Shift&amp;quot; or &amp;quot;Alt&amp;quot; and whether this key is a repeat, may be found in the Qualifier field of the message.&lt;br /&gt;
&lt;br /&gt;
: In general, the application should not assume any correspondence between the keycode and the key value. Character positions on the keyboard change from country to country, and the application should respect the keymap set by the user.&lt;br /&gt;
&lt;br /&gt;
: Programs using IDCMP_RAWKEY messages should perform their own key mapping by calling the console.device function RawKeyConvert(), or the keymap.library function MapRawKey(). (The latter is a bit more convenient, but is only available under V36 and higher). The Autodoc for the MapRawKey() function shows how you can process so-called &#039;&#039;dead keys&#039;&#039;. A dead key is a key combination that has no immediate effect, but instead modifies a subsequent keystroke. For example, on the default keymap, Alt-F is a dead key for the acute accent mark. The sequence of Alt-F followed by the E key yields an e with an acute accent.&lt;br /&gt;
&lt;br /&gt;
: For an example of key mapping using the RawKeyConvert() call, see the rawkey.c example in [[Intuition_Keyboard|Intuition Keyboard]].&lt;br /&gt;
&lt;br /&gt;
: The application can assume that certain keys will always return the same raw keycode, these keys do not have to be mapped. In general these keys are in the high part of the keymap, above hex 40, and includes all non-alphanumeric keys. The fixed keys include the function keys, backspace, delete, help and cursor keys.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_NEWPREFS&lt;br /&gt;
: IDCMP_NEWPREFS messages are sent when the system Preferences are changed by a call to SetPrefs(). The program can learn of these changes by setting this flag.&lt;br /&gt;
&lt;br /&gt;
: After receiving a message of class IDCMP_NEWPREFS, the application should call GetPrefs() to obtain a copy of the new Preferences.&lt;br /&gt;
&lt;br /&gt;
: Under the new Preferences scheme used in Release 2 and later versions of the OS, an IDCMP_NEWPREFS message will not always be sent when the user changes a Preferences setting. Only Preferences values available under V34, i.e., those that can be modified by a call to SetPrefs(), will cause an IDCMP_NEWPREFS message to be sent. New Preferences items such as overscan or font settings rely on file system notification for monitoring changes. See [[Preferences]] for more information.&lt;br /&gt;
&lt;br /&gt;
: This message type is broadcast to all IDCMP that have this flag set, not just to the active window. If the application has this flag set, it should be prepared to handle the event even if it is not active.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_DISKINSERTED and IDCMP_DISKREMOVED&lt;br /&gt;
: When the user inserts or removes a disk from any drive, Intuition will send one of these message types.&lt;br /&gt;
&lt;br /&gt;
: This message type is broadcast to all IDCMP that have this flag set, not just to the active window. If the application has this flag set, it should be prepared to handle the event even if it is not active.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_INTUITICKS&lt;br /&gt;
: Intuition sends these messages to the active window based on an internal timer which &amp;quot;ticks&amp;quot; roughly ten times a second. This provides the application with simple timer events from Intuition.&lt;br /&gt;
&lt;br /&gt;
: Intuition does not allow IDCMP_INTUITICKS events to accumulate at a port. After an IDCMP_INTUITICKS message has been sent to a port, Intuition will not send another until the application replies to the first. This means that an application that has not been able to service the IDCMP for an extended period can expect at most one IDCMP_INTUITICKS message to be waiting at the port.&lt;br /&gt;
&lt;br /&gt;
: These events are to be used as &amp;quot;prods‚&amp;quot; and not as time counters. Do not rely on the timing accuracy of the event, or on the exact frequency at which they appear. Remember, IDCMP_INTUITICKS will only be sent to the active window. If the user selects another window, the events will no longer be received at the first window.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_IDCMPUPDATE&lt;br /&gt;
: Used for notification from BOOPSI custom gadgets. See the [[BOOPSI_-_Object_Oriented_Intuition|BOOPSI section]] for more information. The IAddress field contains a pointer to a tag item list. Tag lists are described in [[Utility_Library|Utility Library]].&lt;br /&gt;
&lt;br /&gt;
; IDCMP_CHANGEWINDOW&lt;br /&gt;
: This message provides the window with notification of any change in the size or position of a window.&lt;br /&gt;
&lt;br /&gt;
There are two other message classes reserved for system use:&lt;br /&gt;
&lt;br /&gt;
; IDCMP_WBENCHMESSAGE&lt;br /&gt;
: Special messages for Workbench, system use only.&lt;br /&gt;
&lt;br /&gt;
; IDCMP_LONELYMESSAGE&lt;br /&gt;
: For internal tracking by Intuition, system use only.&lt;br /&gt;
&lt;br /&gt;
=== Verification Functions ===&lt;br /&gt;
&lt;br /&gt;
IDCMP_SIZEVERIFY, IDCMP_REQVERIFY and IDCMP_MENUVERIFY are exceptional in that Intuition sends an IntuiMessage to the application and then waits for the application to reply before Intuition proceeds. The application replies by calling the Exec function ReplyMsg().&lt;br /&gt;
&lt;br /&gt;
The implication is that the user requested some operation but the operation will not happen immediately and, in fact, will not happen at all until the application says it is safe. Because this delay can be frustrating and intimidating, the program should strive to make the delay as short as possible. An application should always reply to a verification message as soon as possible.&lt;br /&gt;
&lt;br /&gt;
These problems may be overcome by setting up a separate task to monitor the IDCMP and respond to incoming IntuiMessages immediately. This is recommended where there is heavy traffic through the IDCMP, which occurs when many IDCMP flags are set. Monitoring with a separate task may not be appropriate if the main program must synchronize with the event before it can respond to the message.&lt;br /&gt;
&lt;br /&gt;
In previous versions of the operating system, it was not safe to leave any of the VERIFY functions enabled at a time when the task is unable to respond for a long period. This restriction included calls to AmigaDOS directly (with Open(), for example), or indirectly (with OpenLibrary(), for a disk based library, for example), when a VERIFY function was active. This was because there are many cases where AmigaDOS will put up a requester prompting the user for input, and Intuition may end up waiting for the application to reply to the VERIFY message, while the application waits for the AmigaDOS call to finish. This deadlock would freeze the Amiga.&lt;br /&gt;
&lt;br /&gt;
Beginning with V36, Intuition will no longer wait forever for the application to respond to the verify messages. These messages will now time-out; that is, if the application does not respond within a set period, Intuition will act as if it had. Even in this case, though, the machine will appear to be locked up until the time-out occurs.&lt;br /&gt;
&lt;br /&gt;
The application should use ModifyIDCMP() to turn off all VERIFY messages before calling AmigaDOS, or functions that may call AmigaDOS.&lt;br /&gt;
&lt;br /&gt;
If the application sets up a separate task to monitor the IDCMP, and the task monitoring the IDCMP does not call AmigaDOS functions, and if the monitor task will always be able to reply to the VERIFY message without any help from the other task, then the above warning does not apply.&lt;br /&gt;
&lt;br /&gt;
For additional information, see the IDCMP_MENUVERIFY discussion in [[Intuition_Menus|Intuition Menus]], the IDCMP_REQVERIFY discussion in [[Intuition_Requesters|Intuition Requesters]] and the IDCMP_SIZEVERIFY discussion in [[Intuition_Windows|Intuition Windows]].&lt;br /&gt;
&lt;br /&gt;
This message type is broadcast to all IDCMP on the screen that have this flag set, not just to the active window. If the application has this flag set, it should be prepared to handle the event even if it is not active.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions that relate to the use of input and output under Intuition. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| ModifyIDCMP()&lt;br /&gt;
| Change the message filter for an IDCMP.&lt;br /&gt;
|-&lt;br /&gt;
| StripIntuiMessages()&lt;br /&gt;
| Remove and reply all IntuiMessages on a port that have been sent to a particular window.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Datatypes_Library&amp;diff=12537</id>
		<title>Datatypes Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Datatypes_Library&amp;diff=12537"/>
		<updated>2025-01-26T19:26:35Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
The purpose of the DataTypes Library is to provide tools for handling data in an object-oriented way. The object-oriented approach means that your application can work with numerous data file standards without having to worry about the complex details of each one. Instead you only need to understand the simple conventions of the library.&lt;br /&gt;
&lt;br /&gt;
The DataTypes Library is built on Intuition&#039;s BOOPSI facility (BOOPSI is an acronym for Basic Object-Oriented Programming System for Intuition). Although not required, it is very helpful to know a little about [[BOOPSI_-_Object_Oriented_Intuition|how BOOPSI works]] before trying to use the DataTypes Library. Some familiarity with object-oriented theory and practice is also helpful, though not required.&lt;br /&gt;
&lt;br /&gt;
Since the library uses the TagItem structure for passing parameters to functions, you will have to understand how TagItems work before you can call the library functions. For more information on TagItems refer to the [[Utility_Library|Utility Library]].&lt;br /&gt;
&lt;br /&gt;
== Spelling Conventions ==&lt;br /&gt;
&lt;br /&gt;
In software development, the term “datatype” or “data type” is often used in the general sense, whereas the AmigaOS DataTypes framework establishes a rather special context. In order to avoid possible confusion, this documentation uses capitalized spelling in reference to components and programming entities that are part of the said framework, such as: the DataTypes Library, a DataType subclass, a DataType object, Picture DataType, etc.&lt;br /&gt;
&lt;br /&gt;
== Why Use DataTypes? ==&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a summary of main properties and features:&lt;br /&gt;
&lt;br /&gt;
* Support for multiple file formats across various types of data (text, sound, image, animation etc.). Load data the easy way without having to write dedicated loader code!&lt;br /&gt;
&lt;br /&gt;
* Simple and consistent handling of multiple data standards. Once you have learned how to manipulate one type of data with the DataTypes Library, you will find that the other types are handled in much the same way.&lt;br /&gt;
&lt;br /&gt;
* Extensible. New data types can be added to those already supported.&lt;br /&gt;
&lt;br /&gt;
* Clipboard support. The DataTypes Library provides a consistent and easy-to-use interface to the Amiga&#039;s [[Clipboard_Device|clipboard device]] to encourage data sharing between applications.&lt;br /&gt;
&lt;br /&gt;
* Intuition gadget support. Because the DataTypes Library is implemented with BOOPSI, the data objects it handles can also be treated as gadgets. Gadget operations can be performed on data objects within Intuition&#039;s task context, the same as other BOOPSI gadgets.&lt;br /&gt;
&lt;br /&gt;
* Automatic conversion from one format to another. Future versions of the DataTypes Library will support other types of data objects. Conversion from one format to another will be automatically handled by the library.&lt;br /&gt;
&lt;br /&gt;
* Validation. For example, you can easily check to see if a file is a valid JPEG (AIFF, AmigaGuide etc.) or not.&lt;br /&gt;
&lt;br /&gt;
= Classes, Objects and Methods =&lt;br /&gt;
&lt;br /&gt;
The jargon used to describe the DataTypes Library may be a little confusing if you have never worked with object-oriented systems before. For instance, the kinds of data supported by the library are divided into &#039;&#039;classes&#039;&#039; and &#039;&#039;subclasses&#039;&#039;.  The term &#039;&#039;class&#039;&#039; is used here in a familiar way; the members of a class simply have a common set of properties. The members of a subclass have all the properties of the parent class (superclass) and additional properties specific to the subclass. (Each subclass can be further broken down into sub-subclasses and so on.)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Class || Ungulate || Has hooves, can run.&lt;br /&gt;
|-&lt;br /&gt;
| Subclass || Cow || Has udder, can be milked (also has hooves and can run).&lt;br /&gt;
|-&lt;br /&gt;
| Object || Daisy || An instance of class Cow; can run and can be milked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
An actual instance of a class or subclass is referred to as an &#039;&#039;object&#039;&#039;. The term &#039;&#039;object&#039;&#039; is appropriate because in general we want to ignore the details of each individual case and concentrate instead on what we can do with an object based on its class. In the example above the Daisy object can run and can be milked. The operations that can be performed with an object are referred to as &#039;&#039;methods&#039;&#039; and the object is said to &#039;&#039;inherit&#039;&#039; the methods and other attributes of its parent class (which in turn inherits the methods and attributes of its parent class, if it has one).&lt;br /&gt;
&lt;br /&gt;
The datatypes.library implements &#039;&#039;datatypesclass&#039;&#039; from which all other DataType classes inherit from. The following BOOPSI class diagram illustrates how the various DataTypes classes relate to each other.&lt;br /&gt;
&lt;br /&gt;
[[File:DataTypesClassDiagram.png|frame|center|DataTypes Class Diagram]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ DataTypes Library Object Classes&lt;br /&gt;
! Object Classes&lt;br /&gt;
! Autodoc File Showing the Methods Supported&lt;br /&gt;
! Type of Data Object&lt;br /&gt;
|-&lt;br /&gt;
| Picture class&lt;br /&gt;
| picture_dtc.doc&lt;br /&gt;
| IFF graphic image file&lt;br /&gt;
|-&lt;br /&gt;
| Sound class&lt;br /&gt;
| sound_dtc.doc&lt;br /&gt;
| IFF audio sample file&lt;br /&gt;
|-&lt;br /&gt;
| Text class&lt;br /&gt;
| text_dtc.doc&lt;br /&gt;
| ASCII characters&lt;br /&gt;
|-&lt;br /&gt;
| AmigaGuide class&lt;br /&gt;
| amigaguide_dtc.doc&lt;br /&gt;
| Hypertext databases&lt;br /&gt;
|-&lt;br /&gt;
| Animation class&lt;br /&gt;
| animation_dtc.doc&lt;br /&gt;
| Animations containing graphics and sound&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The examples programs listed below demonstrate how to perform some basic &#039;&#039;methods&#039;&#039; on ILBM and 8SVX class objects.&lt;br /&gt;
&lt;br /&gt;
= DataTypes Class Attributes =&lt;br /&gt;
&lt;br /&gt;
DataType classes have other attributes in addition to the methods (operations) that they support. For each attribute, there is a corresponding TagItem defined in the DataTypes Library that you can use to examine or set that attribute in a particular object. For example, picture objects have a display mode attribute. The tag that controls this attribute is named PDTA_ModeID and is described in the Autodoc file picture_dtc.doc. See the Autodoc files for each class (as shown in Table 1) for a complete list of all class attributes.&lt;br /&gt;
&lt;br /&gt;
The class attribute descriptions in the include files also have a set of codes that indicate the &#039;&#039;applicability&#039;&#039; of the attribute. The codes are as follows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| I - Initialize&lt;br /&gt;
| You can initialize the attribute when the object is created&lt;br /&gt;
|-&lt;br /&gt;
| S - Set&lt;br /&gt;
| You can set the attribute to a new value after the object is created&lt;br /&gt;
|-&lt;br /&gt;
| G - Get&lt;br /&gt;
| You can get the value of the attribute after the object is created&lt;br /&gt;
|-&lt;br /&gt;
| N - Notify&lt;br /&gt;
| Changing the attribute triggers the object to send notification&lt;br /&gt;
|-&lt;br /&gt;
| U - Update&lt;br /&gt;
| Attribute can be set using the object&#039;s OM_UPDATE method&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These codes may seem a little mysterious until you have actually tried using the DataTypes Library. The N and U codes in particular are for special applications that want to implement their own object classes, an advanced topic beyond the scope of this article.&lt;br /&gt;
&lt;br /&gt;
= Basic Functions of the DataTypes Library =&lt;br /&gt;
&lt;br /&gt;
If all these new concepts seem a little daunting, rest assured; the DataTypes Library uses conventional C language function calls to get the job done. The calls you will be using most often are listed below. Notice that for each of these basic functions of DataTypes Library there is an equivalent BOOPSI call in the Intuition Library.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! datatypes.library&lt;br /&gt;
! intuition.library&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| NewDTObject()&lt;br /&gt;
| NewObject()&lt;br /&gt;
| Create a DataType object in memory from a file or clip&lt;br /&gt;
|-&lt;br /&gt;
| DisposeDTObject()&lt;br /&gt;
| DisposeObject()&lt;br /&gt;
| Free an object created earlier with NewDTObject() (or NewObject() )&lt;br /&gt;
|-&lt;br /&gt;
| GetDTAttrs()&lt;br /&gt;
| GetAttr()&lt;br /&gt;
| Get attributes of a DataType object&lt;br /&gt;
|-&lt;br /&gt;
| SetDTAttrs()&lt;br /&gt;
| SetAttrs()&lt;br /&gt;
| Set attributes for a DataType object&lt;br /&gt;
|-&lt;br /&gt;
| DoDTMethod()&lt;br /&gt;
| IDoMethod()&lt;br /&gt;
| Perform the given method (operation) with a DataType object&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are also additional functions used to access DataType objects.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function Name&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| AddDTObject() || Add a DataType object to a window.&lt;br /&gt;
|-&lt;br /&gt;
| RefreshDTObjectA() || Refresh the rendering of a DataType object.&lt;br /&gt;
|-&lt;br /&gt;
| RemoveDTObject() || Remove a DataType object from a window.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTMethods() || Get a list of the methods that a DataTypes object supports. Write, Copy, and Select are examples of methods that an object may support.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTTriggerMethods() || Get a list of the trigger methods that a DataType object supports. An action like Play, Pause, and Resume are examples of trigger methods that an object may support.&lt;br /&gt;
|-&lt;br /&gt;
| PrintDTObject() || Asynchronously print a DataType object.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTString() || Get the localized text string for a DataTypes text id. Useful for obtaining localized error messages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In a typical application the sequence of calls might be performed&lt;br /&gt;
like this:&lt;br /&gt;
&lt;br /&gt;
# Use NewDTObject() to create an object in memory from given data.&lt;br /&gt;
# Get (or perhaps set) attributes of the object using GetDTAttr() (or SetDTAttrs() ).&lt;br /&gt;
# Perform &#039;&#039;methods&#039;&#039; (operations) with the object using DoDTMethod().&lt;br /&gt;
# Free the object and any memory or other resources it was using with the DisposeDTObject() call.&lt;br /&gt;
&lt;br /&gt;
= Basic Structures of the DataTypes Library =&lt;br /&gt;
&lt;br /&gt;
There are a lot of structures used with DataTypes Library function calls; too many to summarize in this article. However, here&#039;s a listing of the relevant include files that contain the structure definitions of interest to class users.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &amp;lt;datatypes/datatypes.h&amp;gt;&lt;br /&gt;
| Group IDs, error numbers plus library overhead&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/datatypesclass.h&amp;gt;&lt;br /&gt;
| Defines DataType methods and associated structures&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/picture.h&amp;gt;&lt;br /&gt;
| Structures specific to the picture class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/sound.h&amp;gt;&lt;br /&gt;
| Structures specific to the sound class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/text.h&amp;gt;&lt;br /&gt;
| Structures specific to the text class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;libraries/amigaguide.h&amp;gt;&lt;br /&gt;
| Structures and methods for AmigaGuide databases&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
| Defines general BOOPSI object methods&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
| Defines gadget methods and associated structures&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The two most important definitions in these include files appear in &amp;lt;intuition/classusr.h&amp;gt;. The objects used with DataTypes Library functions (and the BOOPSI functions in Intuition) are defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
typedef ULONG   Object;     /* abstract handle */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since we want to treat objects as black boxes and don&#039;t really care how they are implemented, this definition is very appropriate. When a method is performed with an object, the parameter used to identify the method is a Msg structure defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
    ULONG MethodID;&lt;br /&gt;
    /* method-specific data goes here */&lt;br /&gt;
} *Msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some methods require more information than just the method identifier. Such methods have a custom structure defined in the include files.  All method structures, however, begin with a field that contains the method ID.&lt;br /&gt;
&lt;br /&gt;
= Creating a DataType Object =&lt;br /&gt;
&lt;br /&gt;
The DataTypes function NewDTObjectA() must be used to create a new DataType object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
Object *dto = IDataTypes-&amp;gt;NewDTObjectA (APTR name, struct TagItem *attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pointer that NewDTObjectA() returns is a pointer to a BOOPSI object. Like other BOOPSI objects, DataType objects are &amp;quot;black boxes&amp;quot; and are not to be peeked and poked without using the provided interface.&lt;br /&gt;
&lt;br /&gt;
To create a DataType object, NewDTObjectA() needs to know the where to obtain the data used to create the object. By default the name is treated as file name.&lt;br /&gt;
&lt;br /&gt;
The attrs tag list is a list of tag/value pairs, each of which contains an initial value for an attribute. There are a number of attributes defined in &amp;lt;datatypes/datatypesclass.h&amp;gt; that can be used at creation time.&lt;br /&gt;
&lt;br /&gt;
; DTA_SourceType&lt;br /&gt;
: Specify the type of the source data. The default is DTST_FILE.&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| DTST_RAM || Source data is in RAM.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_FILE || Source data is a file. Name is the name of the file.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_CLIPBOARD || Source data is in the clipboard. Name is the unit number. For example, (APTR)0, for clipboard unit zero.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_HOTLINK || Reserved for future use.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; DTA_Handle&lt;br /&gt;
: Can be used instead of the name field. If the source type is DTST_FILE then handle must be a valid BPTR file handle. If the source type is DTST_CLIPBOARD then handle must be a valid IFFHandle.&lt;br /&gt;
&lt;br /&gt;
; DTA_DataType&lt;br /&gt;
: Can be used to specify the class for handling the data. Data must be a pointer to a valid DataType. This should only be used when attempting to create a new object that doesn&#039;t have source data, or could be handled by multiple classes (for example, could be used to force an object to be handled by the AmigaGuide class).&lt;br /&gt;
&lt;br /&gt;
; DTA_GroupID&lt;br /&gt;
: If this tag is present, then the data must be of the specified type, or the object creation will fail with ERROR_OBJECT_WRONG_TYPE. This can be used by a Sound editor to ensure that only sounds can be loaded, for example.&lt;br /&gt;
&lt;br /&gt;
There are additional attributes that can specified at creation time, but are dependant on the data type of the object being created. See the header files &amp;lt;datatypes/#?class.h&amp;gt; for more attributes.&lt;br /&gt;
&lt;br /&gt;
The following attributes, which are defined in &amp;lt;intuition/gadgetclass.h&amp;gt;, are also valid.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Tag !! Description&lt;br /&gt;
|-&lt;br /&gt;
| GA_Left || Specify the left edge of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelRight || Specify the left edge of the gadget being relative to the right edge of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Top || Specify the top edge of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelBottom || Specify the top edge of the gadget being relative to the bottom edge of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Width || Specify the width of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelWidth || Specify the width of the gadget being relative to the width of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Height || Specify the heigh of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelHeight || Specify the heigh of the gadget being relative to the height of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_ID || Specify a ID associated with the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_UserData || Attach application data to the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Immediate || Indicate that the application should be notified of gadget down events for this gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelVerify || Indicate that the application should be notified of gadget up events for this gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Previous || For adding the gadget to a list of gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| GA_DrawInfo || A pointer to a struct DrawInfo.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order for the application to receive information from the DataType object, it must set up a target for the notification attributes that the object sends out.&lt;br /&gt;
&lt;br /&gt;
; ICA_TARGET&lt;br /&gt;
: Specify a target for the notification attributes that the DataType object sends out.&lt;br /&gt;
&lt;br /&gt;
; ICA_MAP&lt;br /&gt;
: Specify an attribute mapping for the notification attributes that the DataType object sends out.&lt;br /&gt;
&lt;br /&gt;
The usual method to obtain notification is to set up an ICA_TARGET of ICTARGET_IDCMP so that the application will receive the attributes via the IDCMP_IDCMPUPDATE Intuition message class. But it is also possible to set up another BOOPSI object as the receiver.&lt;br /&gt;
&lt;br /&gt;
If NewDTObjectA() is successful, it returns a pointer to a DataType object. Otherwise it returns NULL and the reason for failure can be obtained using IoErr(). See the Autodocs for datatypes.library for more information.&lt;br /&gt;
&lt;br /&gt;
= Obtaining Environment Information for a DataType =&lt;br /&gt;
&lt;br /&gt;
In order to embed a DataType object in a window, it is neccessary to ask the object what its minimum environment is. For example, since the remap code doesn&#039;t handle remapping HAM pictures, they must be shown on a HAM screen, and therefore can&#039;t be added to a window that is on a non-HAM screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG modeid = INVALID_ID;&lt;br /&gt;
LONG nomwidth, nomheight;&lt;br /&gt;
BOOL useScreen = FALSE;&lt;br /&gt;
struct dtFrameBox dtf;&lt;br /&gt;
struct FrameInfo fri;&lt;br /&gt;
&lt;br /&gt;
/* Get the attributes that we are interested in */&lt;br /&gt;
IDataTypes-&amp;gt;GetDTAttrs(dto,&lt;br /&gt;
    /* Get the mode ID */&lt;br /&gt;
    PDTA_ModeID,    &amp;amp;modeid,&lt;br /&gt;
&lt;br /&gt;
    /* Get the desired size */&lt;br /&gt;
    DTA_NominalHoriz, &amp;amp;nomwidth,&lt;br /&gt;
    DTA_NominalVert,  &amp;amp;nomheight,&lt;br /&gt;
&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Clear the structures */&lt;br /&gt;
IUtility-&amp;gt;ClearMem(&amp;amp;dtf, sizeof (struct dtFrameBox));&lt;br /&gt;
IUtility-&amp;gt;ClearMem(&amp;amp;fri, sizeof (struct FrameInfo));&lt;br /&gt;
&lt;br /&gt;
/* Fill in the message */&lt;br /&gt;
dtf.MethodID          = DTM_FRAMEBOX;&lt;br /&gt;
dtf.dtf_FrameInfo     = &amp;amp;fri;&lt;br /&gt;
dtf.dtf_ContentsInfo  = &amp;amp;fri;&lt;br /&gt;
dtf.dtf_SizeFrameInfo = sizeof (struct FrameInfo);&lt;br /&gt;
&lt;br /&gt;
/* Perform the frame method */&lt;br /&gt;
if (IDataTypes-&amp;gt;DoDTMethodA (dto, NULL, NULL, (Msg) &amp;amp;dtf))&lt;br /&gt;
{&lt;br /&gt;
  /* Check to see if the object requires a HAM screen */&lt;br /&gt;
  if (fri.fri_PropertyFlags &amp;amp; DIPF_IS_HAM)&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;HAM\n&amp;quot;);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
  /* Check to see if the object requires an ExtraHalfBrite screen */&lt;br /&gt;
  else if (fri.fri_PropertyFlags &amp;amp; DIPF_IS_EXTRAHALFBRITE)&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;ExtraHalfBrite\n&amp;quot;);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
  /* A safety check to see if a screen is required */&lt;br /&gt;
  else if ((fri.fri_PropertyFlags == 0) &amp;amp;&amp;amp; (modeid &amp;amp; 0x800) &amp;amp;&amp;amp; (modeid != INVALID_ID))&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;ModeID=0x%08lx\n&amp;quot;, modeid);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
  /* No special environment required, can be attached to any screen mode */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Adding a DataType Object to a Window =&lt;br /&gt;
&lt;br /&gt;
A DataType object must be added to a window using the AddDTObject() function of DataTypes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
LONG AddDTObject (struct Window *w, struct Requester *r, Object *dto, LONG pos)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function will add a DataTypes object to the existing gadget list for the specified window. The recommended value for pos is -1 which will cause the DataType object to be added to the end of the list.&lt;br /&gt;
&lt;br /&gt;
DataType objects should not be added using the WA_Gadgets attribute to OpenWindowTagList() or by using the AddGList() function. There is special information that DataTypes requires that will not obtained if any method other than AddDTObject() is used.&lt;br /&gt;
&lt;br /&gt;
When the DataType object is added to the window, the layout method for the object will be invoked. It is possible that the layout will take a while to perform, in that case the object will spawn a process to handle the layout asynchronously. In order to refresh the object&#039;s visual information, it is necessary to obtain IDCMP_IDCMPUPDATE messages from the object and refresh the object when a DTA_Sync attribute is received.&lt;br /&gt;
&lt;br /&gt;
The following code fragment illustrates adding a DataType object to a window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
Object *dto;&lt;br /&gt;
&lt;br /&gt;
struct IntuiMessage *imsg;&lt;br /&gt;
struct Window *win;&lt;br /&gt;
ULONG sigr;&lt;br /&gt;
&lt;br /&gt;
struct TagItem *tstate, *tags;&lt;br /&gt;
ULONG tidata;&lt;br /&gt;
ULONG errnum;&lt;br /&gt;
&lt;br /&gt;
BOOL going = TRUE;&lt;br /&gt;
&lt;br /&gt;
/* Set the pertinent attributes of the DataType object */&lt;br /&gt;
IDataTypes-&amp;gt;SetDTAttrs (dto, NULL, NULL,&lt;br /&gt;
&lt;br /&gt;
    /* Set the dimensions of the object */&lt;br /&gt;
    GA_Left,    win-&amp;gt;BorderLeft,&lt;br /&gt;
    GA_Top,     win-&amp;gt;BorderTop,&lt;br /&gt;
    GA_Width,   win-&amp;gt;Width - win-&amp;gt;BorderLeft - win-&amp;gt;BorderRight,&lt;br /&gt;
    GA_Height,  win-&amp;gt;Height - win-&amp;gt;BorderTop - win-&amp;gt;BorderBottom,&lt;br /&gt;
&lt;br /&gt;
    /* Make sure we receive IDCMP_IDCMPUPDATE messages from&lt;br /&gt;
     * the object */&lt;br /&gt;
    ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Add the object to the window */&lt;br /&gt;
IDataTypes-&amp;gt;AddDTObject (win, NULL, dto, -1);&lt;br /&gt;
&lt;br /&gt;
/* Refresh the DataType object */&lt;br /&gt;
IDataTypes-&amp;gt;RefreshDTObjects (dto, win, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
/* Keep going until we&#039;re told to stop */&lt;br /&gt;
while (going)&lt;br /&gt;
{&lt;br /&gt;
    /* Wait for an event */&lt;br /&gt;
    sigr = IExec-&amp;gt;Wait ((1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit) | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
    /* Did we get a break signal */&lt;br /&gt;
    if (sigr &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        going = FALSE;&lt;br /&gt;
&lt;br /&gt;
    /* Pull Intuition messages */&lt;br /&gt;
    while (imsg = (struct IntuiMessage *) IExec-&amp;gt;GetMsg (win-&amp;gt;UserPort))&lt;br /&gt;
    {&lt;br /&gt;
        /* Handle each message */&lt;br /&gt;
        switch (imsg-&amp;gt;Class)&lt;br /&gt;
        {&lt;br /&gt;
            case IDCMP_IDCMPUPDATE:&lt;br /&gt;
                /* Get a pointer to the attribute list */&lt;br /&gt;
                tstate = tags = (struct TagItem *) imsg-&amp;gt;IAddress;&lt;br /&gt;
&lt;br /&gt;
                /* Step through the attribute list */&lt;br /&gt;
                while (tag = IUtility-&amp;gt;NextTagItem (&amp;amp;tstate))&lt;br /&gt;
                {&lt;br /&gt;
                    tidata = tag-&amp;gt;ti_Data;&lt;br /&gt;
                    switch (tag-&amp;gt;ti_Tag)&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Change in busy state */&lt;br /&gt;
                        case DTA_Busy:&lt;br /&gt;
                            if (tidata)&lt;br /&gt;
                                IIntuition-&amp;gt;SetWindowPointer (win, WA_BusyPointer, TRUE, TAG_END);&lt;br /&gt;
                            else&lt;br /&gt;
                                IIntuition-&amp;gt;SetWindowPointer (win, WA_Pointer, NULL, TAG_END);&lt;br /&gt;
                            break;&lt;br /&gt;
&lt;br /&gt;
                        /* Error message */&lt;br /&gt;
                        case DTA_ErrorLevel:&lt;br /&gt;
                            if (tidata)&lt;br /&gt;
                            {&lt;br /&gt;
                                errnum = IUtility-&amp;gt;GetTagData (DTA_ErrorNumber, NULL, tags);&lt;br /&gt;
                                IDOS-&amp;gt;PrintErrorMsg (errnum, (STRPTR) options[OPT_NAME]);&lt;br /&gt;
                            }&lt;br /&gt;
                            break;&lt;br /&gt;
&lt;br /&gt;
                        /* Time to refresh */&lt;br /&gt;
                        case DTA_Sync:&lt;br /&gt;
                            /* Refresh the DataType object */&lt;br /&gt;
                            IDataTypes-&amp;gt;RefreshDTObjects (dto, win, NULL, NULL);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Done with the message, so reply to it */&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg ((struct Message *) imsg);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Removing a DataType Object from a Window =&lt;br /&gt;
&lt;br /&gt;
A DataType object must be removed from the window using the RemoveDTObject() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
LONG RemoveDTObject (struct Window *w, Object *dto)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function removes the DataType object from the window&#039;s gadget list.&lt;br /&gt;
&lt;br /&gt;
This is the only way that a DataType object should be removed from the window list. Using RemoveGList() is not supported, nor is removing the object manually.&lt;br /&gt;
&lt;br /&gt;
= Setting an Existing DataType Object&#039;s Attributes =&lt;br /&gt;
&lt;br /&gt;
An objects attributes are not necessarily static. An application can ask an object to set certain attributes using the SetDTAttrs() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG SetDTAttrsA (Object *dto, struct Window *w, struct Requester *r, struct TagItem *attrs)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The return value is DataType object specific, but generally a non-zero value means that the object needs to be visually refreshed.&lt;br /&gt;
&lt;br /&gt;
The following fragment illustrates how to set the current top values for a DataType object, using the VarArgs version of SetDTAttrsA().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IDataTypes-&amp;gt;SetDTAttrs(dto, window, NULL,&lt;br /&gt;
    DTA_TopVert,  0,&lt;br /&gt;
    DTA_TopHoriz, 0,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will cause the DataType object to update its vertical and horizontal top values. If the object has been added to a window, then the display will be updated accordingly.&lt;br /&gt;
&lt;br /&gt;
Note that it is not OK to call SetGadgetAttrs() or SetAttrs() on a DataType object.&lt;br /&gt;
&lt;br /&gt;
= Getting a DataType Object&#039;s Attributes =&lt;br /&gt;
&lt;br /&gt;
The DataTypes function GetDTAttrsA() is used to obtain the values for a list of attributes from a DataType object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ULONG GetDTAttrsA (Object *dto, struct TagItem *attrs)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where dto is a pointer to a DataType object returned by NewDTObjectA().&lt;br /&gt;
&lt;br /&gt;
And attrs is a TAG_END terminated array of attributes. Where the data element of each pair contains the address of the storage variable for that attribute.&lt;br /&gt;
&lt;br /&gt;
This function will return a number that indicates that number of attributes that it was able to obtain. For example if four attributes asked for and GetDTAttrs returns a four, then all the attributes were obtained.&lt;br /&gt;
&lt;br /&gt;
The following code fragment illustrates how to get the current top values&lt;br /&gt;
for a DataTypes object using the VarArgs form of GetDTAttrsA().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
LONG topv, toph;&lt;br /&gt;
&lt;br /&gt;
if (IDataTypes-&amp;gt;GetDTAttrs (dto, DTA_TopVert, &amp;amp;topv, DTA_TopHoriz, &amp;amp;toph, TAG_END) == 2)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf (&amp;quot;Top: vertical=%ld, horizontal=%ld\n&amp;quot;, topv, toph);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf (&amp;quot;couldn&#039;t obtain the top values\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= A Simple DataTypes Example =&lt;br /&gt;
&lt;br /&gt;
The example program listed here should clarify some of the concepts discussed so far. Suppose you have a communications program and want to add the capability of playing back a user-specified 8SVX sample file for the bell sound (Ctrl-G). The program below shows how to play a sound with the DataTypes Library.&lt;br /&gt;
&lt;br /&gt;
In this program, objects are of class 8SVX (a subclass of the Sound DataType). The method performed with the object is named DTM_TRIGGER (described in the Autodoc file sound_dtc.doc). The DTM_TRIGGER method (with type set to STM_PLAY) causes a sampled sound to be played on the Amiga&#039;s audio hardware. Since the DTM_TRIGGER method requires other information in addition to the method ID, a dtTrigger structure is used. This structure is defined in &amp;lt;datatypes/datatypesclass.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Note that if the Sound DataType is enhanced to support other types of sound files in a future version of AmigaOS, the code given here will automatically support the new type. This example expects the file name and path to a sound file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Run from CLI only. */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypesclass.h&amp;gt;  /* This includes other files we need */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;          /* Prototypes for system functions */&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/datatypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
struct DataTypesIFace *IDataTypes = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  APTR dtobject = NULL       /* Pointer to a DataTypes object */&lt;br /&gt;
  struct dtTrigger mydtt;    /* A trigger structure for the DTM_TRIGGER method */&lt;br /&gt;
&lt;br /&gt;
  if(argc &amp;lt;= 1 ) /* CLI only, at least one argument please. */&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Give a file name too.\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *DataTypesBase = IExec-&amp;gt;OpenLibrary(&amp;quot;datatypes.library&amp;quot;, 50);&lt;br /&gt;
  IDataTypes = (struct DataTypesIFace*)IExec-&amp;gt;GetInterface(DataTypesBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IDataTypes != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    /* Attempt to make an 8svx sound object from the file name the user */&lt;br /&gt;
    /* specified in the command line.  For a list of possible error     */&lt;br /&gt;
    /* returns, see the Autodocs for NewDTObjectA().  The group ID tag  */&lt;br /&gt;
    /* will allow only Sound DataType files to be accepted for the call.*/&lt;br /&gt;
    if (dtobject = IDataTypes-&amp;gt;NewDTObject(argv[1], DTA_GroupID, GID_SOUND,&lt;br /&gt;
                                                    TAG_END) )&lt;br /&gt;
    {&lt;br /&gt;
      mydtt.MethodID     = DTM_TRIGGER; /* Fill in the dtTrigger struct */&lt;br /&gt;
      mydtt.dtt_GInfo    = NULL;&lt;br /&gt;
      mydtt.dtt_Function = STM_PLAY;&lt;br /&gt;
      mydtt.dtt_Data     = NULL;&lt;br /&gt;
&lt;br /&gt;
      /* The return value of the DTM_TRIGGER method used with the 8svx */&lt;br /&gt;
      /* Sound DataType is undefined in V39.  This is likely to change */&lt;br /&gt;
      /* in future versions of the Amiga operating system.             */&lt;br /&gt;
      uint32 dores = IDataTypes-&amp;gt;DoDTMethodA(dtobject, NULL, NULL, &amp;amp;mydtt);&lt;br /&gt;
&lt;br /&gt;
      // Let the 8svx sound finish playing. Another way is to use&lt;br /&gt;
      // SDTA_SignalTask and SDTA_SignalBit to find out when it is&lt;br /&gt;
      // finished playing.&lt;br /&gt;
      IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
      IDataTypes-&amp;gt;DisposeDTObject(dtobject);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create new object or not a sound data file\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open interfaces\n&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IDataTypes);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(DataTypesBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to playing back a sampled sound, the DataTypes Library allows sound objects to become gadgets (the library includes default imagery for a sound gadget). Since all DataTypes are implemented as a subclass of the BOOPSI &#039;&#039;gadgetclass&#039;&#039;, they all support the methods of gadget objects as described in [[BOOPSI_-_Object_Oriented_Intuition|BOOPSI]].&lt;br /&gt;
&lt;br /&gt;
= Embedding DataTypes =&lt;br /&gt;
&lt;br /&gt;
Since DataTypes are a subclass of the Intuition &#039;&#039;gadgetclass&#039;&#039;, DataType objects can be attached to an Intuition window in a similiar way that gadgets can be added to a window. DataTypes use a parallel set of functions because it requires additional information that the Intuition functions weren&#039;t able to provide.&lt;br /&gt;
&lt;br /&gt;
Currently the handling of the data is limited to reading, writing, printing, viewing (audio or visual), and clipboard access. Utilities like MultiView or MultiViewer are examples of applications that can embed DataType objects.&lt;br /&gt;
&lt;br /&gt;
= Determining Data Type =&lt;br /&gt;
&lt;br /&gt;
One of the main features of the DataTypes system is its ability to determine the type of a block of data. This data block can reside in a file or the clipboard.&lt;br /&gt;
&lt;br /&gt;
The following functions are used to determine the DataType of a data block:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ObtainDataTypeA() || Obtain the DataType descriptor for a data block.&lt;br /&gt;
|-&lt;br /&gt;
| ReleaseDataType() || Release the DataType descriptor for a data block.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data type detection functions use the DataType structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DataType&lt;br /&gt;
{&lt;br /&gt;
    struct Node            dtn_Node1;&lt;br /&gt;
    struct Node            dtn_Node2;&lt;br /&gt;
    struct DataTypeHeader *dtn_Header;&lt;br /&gt;
    struct List            dtn_ToolList;&lt;br /&gt;
    STRPTR                 dtn_FunctionName;&lt;br /&gt;
    struct TagItem        *dtn_AttrList;&lt;br /&gt;
    ULONG                  dtn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The DataType structure is read-only. The only pertinent field is the dtn_Header field, which points to a DataTypeHeader structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DataTypeHeader&lt;br /&gt;
{&lt;br /&gt;
    STRPTR   dth_Name;&lt;br /&gt;
    STRPTR   dth_BaseName;&lt;br /&gt;
    STRPTR   dth_Pattern;&lt;br /&gt;
    WORD    *dth_Mask;&lt;br /&gt;
    ULONG    dth_GroupID;&lt;br /&gt;
    ULONG    dth_ID;&lt;br /&gt;
    WORD     dth_MaskLen;&lt;br /&gt;
    WORD     dth_Pad;&lt;br /&gt;
    UWORD    dth_Flags;&lt;br /&gt;
    WORD     dth_Priority;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The DataTypeHeader structure fields are as follows:&lt;br /&gt;
&lt;br /&gt;
; dth_Name&lt;br /&gt;
: Descriptive name of the data type. For example, the description for an ILBM data type could possibly be &amp;quot;Amiga BitMap Picture&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
; dth_BaseName&lt;br /&gt;
: This is the base name for the data type and is used to obtain the class that handles this data type.&lt;br /&gt;
&lt;br /&gt;
; dth_GroupID&lt;br /&gt;
: This identifies the general type of data that the object contains. Following are the possible values:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| GID_SYSTEM || Fonts, Executables, Libraries, Devices, etc...&lt;br /&gt;
|-&lt;br /&gt;
| GID_TEXT || Formatted or unformatted text.&lt;br /&gt;
|-&lt;br /&gt;
| GID_DOCUMENT || Formatted text with embedded DataTypes (such as pictures).&lt;br /&gt;
|-&lt;br /&gt;
| GID_SOUND || Audio samples.&lt;br /&gt;
|-&lt;br /&gt;
| GID_INSTRUMENT || Audio samples used for playing music.&lt;br /&gt;
|-&lt;br /&gt;
| GID_MUSIC || Musical scores.&lt;br /&gt;
|-&lt;br /&gt;
| GID_PICTURE || Graphic picture or brush.&lt;br /&gt;
|-&lt;br /&gt;
| GID_ANIMATION || Moving picture or cartoon.&lt;br /&gt;
|-&lt;br /&gt;
| GID_MOVIE || Moving picture or cartoon with sound.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; dth_ID&lt;br /&gt;
: This is an individual indentifier for the DataType. For IFF files it is the same as the FORM type, for example ILBM for an Amiga BitMap picture. For non-IFF files, it is the first four characters of dth_Name.&lt;br /&gt;
&lt;br /&gt;
; dth_Flags&lt;br /&gt;
: The flags field contains, among other information, the coarse type of data. The type can be obtained by ANDing DTF_TYPE_MASK with this field.&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| DTF_IFF || Interchange File Format&lt;br /&gt;
|-&lt;br /&gt;
| DTF_BINARY || Non-readable characters&lt;br /&gt;
|-&lt;br /&gt;
| DTF_ASCII || Readable characters&lt;br /&gt;
|-&lt;br /&gt;
| DTF_MISC || Disks and drawers&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; dth_Pattern&lt;br /&gt;
; dth_Mask&lt;br /&gt;
; dth_MaskLen&lt;br /&gt;
; dth_Priority&lt;br /&gt;
: These fields are used by the detection code in datatypes.library for determining the data type. See the &amp;quot;Defining a DataType Descriptor&amp;quot; section for more information.&lt;br /&gt;
&lt;br /&gt;
Following is a code fragment that shows how to determine the data type of a file. This fragment uses functions from the DataTypes Library, DOS Library, and IFFParse Library.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    STRPTR name = &amp;quot;somefilename&amp;quot;;&lt;br /&gt;
    BPTR lock;&lt;br /&gt;
&lt;br /&gt;
    struct DataTypeHeader *dth;&lt;br /&gt;
    struct DataType *dtn;&lt;br /&gt;
    UBYTE idesc[5];&lt;br /&gt;
    STRPTR tdesc;&lt;br /&gt;
    STRPTR gdesc;&lt;br /&gt;
    UWORD ttype;&lt;br /&gt;
&lt;br /&gt;
    /* Obtain a lock on the file that we want information on */&lt;br /&gt;
    if (lock = IDOS-&amp;gt;Lock (name, ACCESS_READ))&lt;br /&gt;
    {&lt;br /&gt;
	/* Get a pointer to the appropriate DataType structure */&lt;br /&gt;
	if (dtn = IDataTypes-&amp;gt;ObtainDataTypeA (DTST_FILE, (APTR)lock, NULL))&lt;br /&gt;
	{&lt;br /&gt;
	    /* Get a pointer to the DataTypeHeader structure */&lt;br /&gt;
	    dth = dtn-&amp;gt;dtn_Header;&lt;br /&gt;
&lt;br /&gt;
	    /* Get the coarse type */&lt;br /&gt;
	    ttype = dth-&amp;gt;dth_Flags &amp;amp; DTF_TYPE_MASK;&lt;br /&gt;
&lt;br /&gt;
	    /* Get a pointer to the text strings */&lt;br /&gt;
	    tdesc = IDataTypes-&amp;gt;GetDTString (ttype + DTMSG_TYPE_OFFSET);&lt;br /&gt;
	    gdesc = IDataTypes-&amp;gt;GetDTString (dth-&amp;gt;dth_GroupID);&lt;br /&gt;
&lt;br /&gt;
	    /* Convert the ID to a string. */&lt;br /&gt;
	    IIFFParse-&amp;gt;IDtoStr (dth-&amp;gt;dth_ID, idesc);&lt;br /&gt;
&lt;br /&gt;
	    /* Display the information */&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;   Description: %s\n&amp;quot;, dth-&amp;gt;dth_Name);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;     Base Name: %s\n&amp;quot;, dth-&amp;gt;dth_BaseName);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;          Type: %d - %s\n&amp;quot;, ttype, tdesc);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;         Group: %s\n&amp;quot;, gdesc);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;            ID: %s\n&amp;quot;, idesc);&lt;br /&gt;
&lt;br /&gt;
	    /* Release the DataType structure now that we are done with it */&lt;br /&gt;
	    IDataTypes-&amp;gt;ReleaseDataType (dtn);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Release the DOS lock on the file */&lt;br /&gt;
	IDOS-&amp;gt;UnLock (lock);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= A Picture Class Example =&lt;br /&gt;
&lt;br /&gt;
Here is a second, more complex example showing how to use all the DataTypes Library functions described so far. In this example the objects used are of class ILBM, a subclass of the Picture DataType.&lt;br /&gt;
&lt;br /&gt;
Two methods will be performed with the object, DTM_PROCLAYOUT and DTM_FRAMEBOX. Both these methods have associated structures (gpLayout and dtFrameBox respectively). DTM_PROCLAYOUT makes the object available within the context of your application task (as opposed to Intuition&#039;s). DTM_FRAMEBOX queries the display environment required by the picture.&lt;br /&gt;
&lt;br /&gt;
Other attributes of the picture are obtained with a call to GetDTAttrs() and then a matching Intuition screen is created and the ILBM object is displayed. This example expects the file and path name of a picture file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Run from CLI only.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/pictureclass.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/datatypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
struct GraphicsIFace *IGraphics = NULL;&lt;br /&gt;
struct DataTypesIFace *IDataTypes = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  APTR dtobject=NULL;                   /* Pointer to a DataTypes object       */&lt;br /&gt;
  uint32 res;                           /* Variable for function return values */&lt;br /&gt;
  struct dtFrameBox mydtFrameBox;       /* Use this with DTM_FRAMEBOX method   */&lt;br /&gt;
  struct FrameInfo myFrameInfo;         /* For info returned from DTM_FRAMEBOX */&lt;br /&gt;
  struct gpLayout mygpLayout;           /* Use this with DTM_PROCLAYOUT method */&lt;br /&gt;
&lt;br /&gt;
  uint32 modeID = INVALID_ID;           /* Variables for storing the display */&lt;br /&gt;
  struct Screen *myScreen=NULL;         /* environment information obtained  */&lt;br /&gt;
  struct BitMap *bm = NULL;             /* the DataType object.              */&lt;br /&gt;
  uint32 *cregs = NULL;&lt;br /&gt;
  uint32 i,r,g,b,numcolors;&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
  struct Library *DataTypesBase = IExec-&amp;gt;OpenLibrary(&amp;quot;datatypes.library&amp;quot;, 50);&lt;br /&gt;
&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  IDataTypes = (struct DataTypesIFace*)IExec-&amp;gt;GetInterface(DataTypesBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuition != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (IGraphics != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      if (IDataTypes != NULL)&lt;br /&gt;
      {&lt;br /&gt;
        if(argc &amp;gt; 1 ) /* CLI only, at least one argument please.  */&lt;br /&gt;
        {&lt;br /&gt;
            /* Attempt to create a picture object in memory from the file    */&lt;br /&gt;
            /* name given by the user in the command line.  If we wanted to  */&lt;br /&gt;
            /* show the picture in a screen set up ahead of time, we could   */&lt;br /&gt;
            /* set PDTA_Remap to TRUE and provide a pointer to the screen    */&lt;br /&gt;
            /* with the PDTA_Screen tag (datatypes.library handles the rest).*/&lt;br /&gt;
            /* However in this case we want to first find out the attributes */&lt;br /&gt;
            /* of the picture object and set up a matching screen and do the */&lt;br /&gt;
            /* remapping later.  Therefore PDTA_Remap is set to false.       */&lt;br /&gt;
            /* The group ID tag ensures that we get only a picture file type.*/&lt;br /&gt;
            if (dtobject = IDataTypes-&amp;gt;NewDTObject(argv[1], PDTA_Remap,  FALSE,&lt;br /&gt;
                                                DTA_GroupID, GID_PICTURE,&lt;br /&gt;
                                                TAG_END) )&lt;br /&gt;
            {&lt;br /&gt;
                /* Here we want to find the display environment required by  */&lt;br /&gt;
                /* this picture.  To do that, perform the DTM_FRAMEBOX method */&lt;br /&gt;
                /* on the object.  The DataTypes Library fills in the struct */&lt;br /&gt;
                /* FrameBox you give it with the info on the display needed.  */&lt;br /&gt;
                mydtFrameBox.MethodID         = DTM_FRAMEBOX;&lt;br /&gt;
                mydtFrameBox.dtf_GInfo        = NULL;&lt;br /&gt;
                mydtFrameBox.dtf_ContentsInfo = NULL;&lt;br /&gt;
                mydtFrameBox.dtf_FrameInfo    = &amp;amp;myFrameInfo;&lt;br /&gt;
                mydtFrameBox.dtf_SizeFrameInfo= sizeof (struct FrameInfo);&lt;br /&gt;
                mydtFrameBox.dtf_FrameFlags   = 0L;&lt;br /&gt;
&lt;br /&gt;
                /* The return value from DTM_FRAMEBOX is currently undefined */&lt;br /&gt;
                res = IIntuition-&amp;gt;IDoMethodA(dtobject, &amp;amp;mydtFrameBox);&lt;br /&gt;
&lt;br /&gt;
                /* OK, now do the layout (remap) of the object on our process */&lt;br /&gt;
                mygpLayout.MethodID   = DTM_PROCLAYOUT;&lt;br /&gt;
                mygpLayout.gpl_GInfo  = NULL;&lt;br /&gt;
                mygpLayout.gpl_Initial= 1L;&lt;br /&gt;
&lt;br /&gt;
                /* The return value of DTM_PROCLAYOUT is non-zero for success */&lt;br /&gt;
                if( res = IIntuition-&amp;gt;IDoMethodA(dtobject, &amp;amp;mygpLayout) )&lt;br /&gt;
                {&lt;br /&gt;
                   /* Get the attributes of this picture object.  You could  */&lt;br /&gt;
                   /* use a series of GetAttr() function calls here instead.  */&lt;br /&gt;
                   res = IDataTypes-&amp;gt;GetDTAttrs(dtobject, PDTA_ModeID, &amp;amp;modeID,&lt;br /&gt;
                                              PDTA_CRegs, &amp;amp;cregs,&lt;br /&gt;
                                              PDTA_BitMap, &amp;amp;bm,&lt;br /&gt;
                                              TAG_END);&lt;br /&gt;
&lt;br /&gt;
                   /* Did we get all threee attributes? */&lt;br /&gt;
                   if( (modeID!=INVALID_ID) &amp;amp;&amp;amp; (cregs) &amp;amp;&amp;amp; (bm) )&lt;br /&gt;
                   {&lt;br /&gt;
                       /* Open a screen that matches the picture object */&lt;br /&gt;
                       if( myScreen = IIntuition-&amp;gt;OpenScreenTags( NULL,&lt;br /&gt;
                           SA_Width,     myFrameInfo.fri_Dimensions.Width,&lt;br /&gt;
                           SA_Height,    myFrameInfo.fri_Dimensions.Height,&lt;br /&gt;
                           SA_Depth,     myFrameInfo.fri_Dimensions.Depth,&lt;br /&gt;
                           SA_DisplayID, modeID,&lt;br /&gt;
                           SA_BitMap,    bm,&lt;br /&gt;
                           TAG_END) )&lt;br /&gt;
                       {&lt;br /&gt;
                           /* Now fill in the color registers for this screen */&lt;br /&gt;
                           numcolors = 2&amp;lt;&amp;lt;(myFrameInfo.fri_Dimensions.Depth-1);&lt;br /&gt;
                           for( i=0; i &amp;lt; numcolors; i++ )&lt;br /&gt;
                           {&lt;br /&gt;
                              r = cregs[i * 3 + 0];&lt;br /&gt;
                              g = cregs[i * 3 + 1];&lt;br /&gt;
                              b = cregs[i * 3 + 2];&lt;br /&gt;
                              IGraphics-&amp;gt;SetRGB32(&amp;amp;myScreen-&amp;gt;ViewPort, i, r, g, b);&lt;br /&gt;
                           }&lt;br /&gt;
&lt;br /&gt;
                           IDOS-&amp;gt;Printf(&amp;quot;Ctrl-C in this window to quit\n&amp;quot;);&lt;br /&gt;
                           /* Wait for the user to have a look...  */&lt;br /&gt;
                           IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                           IIntuition-&amp;gt;CloseScreen(myScreen);&lt;br /&gt;
                       }&lt;br /&gt;
                       else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t open required screen\n&amp;quot;);&lt;br /&gt;
                   }&lt;br /&gt;
                   else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get picture attributes\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                   IDataTypes-&amp;gt;DisposeDTObject(dtobject);&lt;br /&gt;
                }&lt;br /&gt;
                else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t perform PROC_LAYOUT\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create new object or not a picture file\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Give a file name too.\n&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open DataTypes Library\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open Graphics Library\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open Intuition Library\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IDataTypes);&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(DataTypesBase);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with 8SVX objects, the DataTypes Library allows ILBM objects to be treated as gadgets. Remember that all DataTypes are a subclass of the BOOPSI &#039;&#039;gadgetclass&#039;&#039; and therefore support the gadget methods described in [[BOOPSI Gadgets]].&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&amp;diff=12536</id>
		<title>How to open and use the exec debug interface</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&amp;diff=12536"/>
		<updated>2025-01-26T19:26:27Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Debug]]&lt;br /&gt;
== Author ==&lt;br /&gt;
&lt;br /&gt;
Alfkil Wennermark&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright (c) 2010 Alfkil Wennermark&amp;lt;br/&amp;gt;&lt;br /&gt;
Used by permission.&lt;br /&gt;
&lt;br /&gt;
== Tutorial ==&lt;br /&gt;
&lt;br /&gt;
Next step in my small series concerns itself with how to open and use the &amp;quot;secret&amp;quot; (but very useful) debug interface. Thanks to Steven Solie and Thomas Frieden.&lt;br /&gt;
&lt;br /&gt;
Probably the best way to trap exceptions from within your code is to use the debug interface, that is &amp;quot;hidden&amp;quot; inside exec.library. The main problem with this interface is, that it is not very well documented. My main source of documentation on this issue is the amigaos-nat.c file from Thomas Friedens [[GDB_for_Beginners|GDB]] sources. These can be found inside [https://sourceforge.net/projects/adtools/ the adtools project on sourceforge.net].&lt;br /&gt;
&lt;br /&gt;
The following code just plainly opens the interface, attaches a debug hook to itself, causes an exception and tells you what has happened. Normally you wouldn&#039;t attach the hook to your own process. Rather you would open whatever code you want to debug with fx. LoadSeg(), run it with CreateNewProc() (or some other way) and attach the debug hook to it. To keep things simple, though, this code just attaches the hook to itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* debugtrap.cExample of use of the exec debug interface&lt;br /&gt;
by Alfkil Wennermark 2010&lt;br /&gt;
&lt;br /&gt;
Thanks to Steven Solie, Thomas Frieden and others&lt;br /&gt;
&lt;br /&gt;
This code is partially copied from Thomas&#039; GDB source&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/interrupts.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/tasks.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
struct DebugIFace *IDebug = 0;&lt;br /&gt;
&lt;br /&gt;
struct KernelDebugMessage&lt;br /&gt;
{&lt;br /&gt;
  uint32 type;&lt;br /&gt;
  union&lt;br /&gt;
  {&lt;br /&gt;
    struct ExceptionContext *context;&lt;br /&gt;
    struct Library *library;&lt;br /&gt;
  } message;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static ULONG amigaos_debug_callback(struct Hook *, struct Task *, struct KernelDebugMessage *);&lt;br /&gt;
&lt;br /&gt;
struct Hook debug_hook;&lt;br /&gt;
struct Task *amiga_task;&lt;br /&gt;
&lt;br /&gt;
BPTR exec_seglist;&lt;br /&gt;
ULONG debug_data = 1234;&lt;br /&gt;
&lt;br /&gt;
void init()&lt;br /&gt;
{&lt;br /&gt;
  IDebug = (struct DebugIFace *)IExec-&amp;gt;GetInterface((struct Library *)SysBase, &amp;quot;debug&amp;quot;, 1, 0);&lt;br /&gt;
  if (!IDebug)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Can&#039;t get DEBUG access\n&amp;quot;);&lt;br /&gt;
    exit(RETURN_FAIL);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  debug_hook.h_Entry = (ULONG (*)())amigaos_debug_callback;&lt;br /&gt;
  debug_hook.h_Data =(APTR)&amp;amp;debug_data;&lt;br /&gt;
&lt;br /&gt;
  /* NB: Ideally we would start up another task, that&lt;br /&gt;
  we want to debug, and attach ourselves to that&lt;br /&gt;
  task using the debug hook, but for simplicity&lt;br /&gt;
  we just use our own task here */&lt;br /&gt;
  amiga_task = IExec-&amp;gt;FindTask(NULL);&lt;br /&gt;
  IDebug-&amp;gt;AddDebugHook(amiga_task, &amp;amp;debug_hook);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end()&lt;br /&gt;
{&lt;br /&gt;
  IDebug-&amp;gt;AddDebugHook(amiga_task, 0);&lt;br /&gt;
&lt;br /&gt;
  if (IDebug)IExec-&amp;gt;DropInterface((struct Interface *)IDebug);&lt;br /&gt;
  IDebug = NULL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ULONG&lt;br /&gt;
amigaos_debug_callback(struct Hook *hook, struct Task *currentTask,&lt;br /&gt;
struct KernelDebugMessage *dbgmsg)&lt;br /&gt;
{&lt;br /&gt;
  struct ExecIFace *IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)-&amp;gt;MainInterface;&lt;br /&gt;
&lt;br /&gt;
  uint32 *data = (uint32 *)hook-&amp;gt;h_Data;&lt;br /&gt;
&lt;br /&gt;
  /* these are the 4 types of debug msgs: */&lt;br /&gt;
  switch (dbgmsg-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case DBHMT_REMTASK:&lt;br /&gt;
    *data = 9;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_EXCEPTION:&lt;br /&gt;
    *data = 11;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_OPENLIB:&lt;br /&gt;
    *data = 13;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_CLOSELIB:&lt;br /&gt;
    *data = 15;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
    *data = 0;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  /* returning 1 will suspend the task ! */&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  init();&lt;br /&gt;
&lt;br /&gt;
  /* Cause an exception on purpose: */&lt;br /&gt;
  uint32 *beef = 0;&lt;br /&gt;
  *beef = 0L;&lt;br /&gt;
&lt;br /&gt;
  printf(&amp;quot;We received a&amp;quot;);&lt;br /&gt;
  switch (debug_data)&lt;br /&gt;
  {&lt;br /&gt;
  case 9:&lt;br /&gt;
    printf(&amp;quot; REMTASK&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 11:&lt;br /&gt;
    printf(&amp;quot;n EXCEPTION&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 13:&lt;br /&gt;
    printf(&amp;quot;n OPENLIB&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 15:&lt;br /&gt;
    printf(&amp;quot; CLOSELIB&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    printf(&amp;quot;n unknown&amp;quot;);&lt;br /&gt;
  break;&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot; signal!n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  end();&lt;br /&gt;
&lt;br /&gt;
  return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmiDock_and_Dockies&amp;diff=12535</id>
		<title>AmiDock and Dockies</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmiDock_and_Dockies&amp;diff=12535"/>
		<updated>2025-01-26T19:26:20Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
AmiDock is a tool to maintain a graphical menu bars (Docks) at the Workbench screen that can be used or to execute other programs from a selection of icons (either in the main Dock or in sub-Docks as specified by the AmiDock preferences), or to handle special kind of application called &amp;quot;Dockies&amp;quot;, which can provide different functionality: render different kind of data right into the dock, provide user with a menu related to the Docky of choice and so on.&lt;br /&gt;
&lt;br /&gt;
AmiDock implemented as commodity, and together with being responsible for creating and controlling Docks and Dockies it provide different functionality such as ARexx support and a rich API by which your Dockies can control most of AmiDock&#039;s features.&lt;br /&gt;
&lt;br /&gt;
= What is a Dock and a Docky? =&lt;br /&gt;
&lt;br /&gt;
== Dock ==&lt;br /&gt;
&lt;br /&gt;
A Dock is an area or window where the user can put a program icon or an interactive program.&lt;br /&gt;
&lt;br /&gt;
[[File:Laucher_dock.png|center]]&lt;br /&gt;
&lt;br /&gt;
== Docky ==&lt;br /&gt;
&lt;br /&gt;
A Docky is a little program to be placed in a dock. Dockies are able to control most of AmiDock&#039;s features and provides a great way to expand AmiDock beyond its default functionality. Dockies may be invisible to the user or show static or dynamic (animated) content. You may change their behavior, size and look according to requirements. Dockies are one of two main types:&lt;br /&gt;
&lt;br /&gt;
=== Standalone Dockies ===&lt;br /&gt;
&lt;br /&gt;
A standalone Docky is a special type of program which is made to show an icon in a Dock, delivering some functionality to the user. It is the most common type of Docky. This Docky type uses the standard shared library feature of AmigaOS as a common interface.&lt;br /&gt;
&lt;br /&gt;
=== Application Dockies ===&lt;br /&gt;
&lt;br /&gt;
The second type of Docky is an application Docky which may sometimes be known as an &#039;AppDocky&#039; or &#039;AppDockIcon Docky&#039;. An AppDocky is a Docky which is introduced to the system at run time by an application using the application.library registration mechanism for applications. The biggest difference between the two types of Dockies types is that AppDockies belong to a running application and usually are used to represent the current state of the owning application.&lt;br /&gt;
&lt;br /&gt;
To sum up in &amp;quot;standalone dockies&amp;quot; the docky itself is the application (the only reason of existence of the application is the docky) while for &amp;quot;AppDockIcon dockies&amp;quot; the docky is just a graphical representation of a bigger application (the docky is just here to add user friendliness or visual feedback of the surrounding application)&lt;br /&gt;
&lt;br /&gt;
= How to create and manipulate a simple docky =&lt;br /&gt;
&lt;br /&gt;
You write a docky just like you&#039;d write a standard Exec library. In your code you must implement certain specifically-named functions like DockyGet(), DockyProcess(), DockySet() etc. The docky manager (AmiDock) calls these functions when it needs to.&lt;br /&gt;
&lt;br /&gt;
See the [http://os4depot.net/share/utility/docky/datetime_docky.lha datetime.docky source code] to get started.&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Is it possible to create an application docky and then update its content on the fly and how? ==&lt;br /&gt;
&lt;br /&gt;
Set the icon type to APPICONT_Docky; but you&#039;ll need to create a real docky as well then. If it doesn&#039;t need to be truly active it is possible just to change the icon&#039;s imagery (I think you have to un-register/re-register to get it to change though, so no good for frequent changes)&lt;br /&gt;
&lt;br /&gt;
== I have successfully drawn a datatype (a picture) in a Docky and the picture file has more than 256 colors but it is rendered in 256 colors only. Why? ==&lt;br /&gt;
&lt;br /&gt;
Just add PDTA_DestMode, PMODE_V43 to the NewDTObject call. You might also want to add DTA_GroupID, GID_PICTURE in order to restrict the file type to pictures.&lt;br /&gt;
&lt;br /&gt;
== How are context menus created? ==&lt;br /&gt;
&lt;br /&gt;
Context menus are dynamically built in response to DOCKYGET_ContextMenu attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL DockyGet (struct DockyIFace *Self, uint32 msgType, uint32 *msgData) &lt;br /&gt;
{ &lt;br /&gt;
switch (msgType) &lt;br /&gt;
{ &lt;br /&gt;
/* ... */ &lt;br /&gt;
&lt;br /&gt;
case DOCKYGET_ContextMenu: &lt;br /&gt;
{ &lt;br /&gt;
Object *contextMenu = (Object *)msgData; &lt;br /&gt;
&lt;br /&gt;
Object *item1 = PopupMenuItemObject, &lt;br /&gt;
        PMIA_Title, GetString(&amp;amp;li, LOCALE_ITEM_PREFS), &lt;br /&gt;
        PMIA_ID, PMID_PREFS, &lt;br /&gt;
    PopupMenuItemEnd; &lt;br /&gt;
&lt;br /&gt;
    Object *item2 = PopupMenuItemObject, &lt;br /&gt;
        PMIA_Title, GetString(&amp;amp;li, LOCALE_ITEM_SAVEASDEFAULT), &lt;br /&gt;
        PMIA_ID, PMID_SAVEASDEFAULT, &lt;br /&gt;
    PopupMenuItemEnd; &lt;br /&gt;
&lt;br /&gt;
    Object *item3 = PopupMenuItemObject, &lt;br /&gt;
        PMIA_Title, GetString(&amp;amp;li, LOCALE_ITEM_USEASDEFAULT), &lt;br /&gt;
        PMIA_ID, PMID_USEASDEFAULT, &lt;br /&gt;
    PopupMenuItemEnd; &lt;br /&gt;
&lt;br /&gt;
if (item1 &amp;amp;&amp;amp; item2 &amp;amp;&amp;amp; item3) &lt;br /&gt;
    { &lt;br /&gt;
        IIntuition-&amp;gt;IDoMethod(contextMenu, OM_ADDMEMBER, item1); &lt;br /&gt;
        IIntuition-&amp;gt;IDoMethod(contextMenu, OM_ADDMEMBER, item2); &lt;br /&gt;
        IIntuition-&amp;gt;IDoMethod(contextMenu, OM_ADDMEMBER, item3); &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
} &lt;br /&gt;
break; &lt;br /&gt;
&lt;br /&gt;
/* ... */ &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How do you use an alpha layer for a Docky? ==&lt;br /&gt;
&lt;br /&gt;
On non-composited screens, AmiDock uses a &amp;quot;fake&amp;quot; transparency effect (i.e. the bitmap is filled with the contents of the window behind the dock), meaning that you can only add things over the background but you cannot render half or totally transparent contents without doing the blending at the same time (or you&#039;ll lose background information).&lt;br /&gt;
&lt;br /&gt;
So if you want to support this configuration you need to follow this rule or provide an alternate rendering if you want to do fancy stuff when compositing is enabled.&lt;br /&gt;
&lt;br /&gt;
Now, what you are trying to do should work on composited screens, but you have to tell AmiDock that you are using composited mode (see DOCKYGET_SupportsComposite and DOCKYGET_CompositeMode) so that it does not try to de-multiply the docky bitmap. Also I&#039;m not sure you can use legacy pens to fill alpha channel, you&#039;re better off with direct ARGB painting (see SetRPAttrs() with RPTAG_APenColor).&lt;br /&gt;
&lt;br /&gt;
== Is there a way to trigger a redraw from outside the docky? ==&lt;br /&gt;
&lt;br /&gt;
Use DOCKYGET_NeedsAttention for this. This is how a docky can be notified from an external task by signaling AmiDock process. Quite straightforward, this is how e.g. [http://os4depot.net/share/utility/docky/winbar-docky.lha winbar.docky] ([http://os4depot.net/share/utility/docky/winbar_docky_src.zip source code is here], prefsobjects_macro.h include file which is required [http://openamiga.org/attachment/project/29/prefsobjects_macros.h here]) is told to rethink its layout in response to a change in preferences, or Intuition windows list. A shared structure and [[Exec_Mutexes|Mutex]] to protect the data. By the way, winbar.docky source code can be helpful not only for that, but for all other things such as adding context menus, firing requests to AmiDock or other of the many still undocumented features of the API.&lt;br /&gt;
&lt;br /&gt;
You are notified about the task/bit to signal through DOCKYSET_DockyAttention (you would store it in the library base for an easy access by the main program). When your program signals AmiDock, every docky is sent the DOCKYGET_NeedsAttention message, so it is a good idea to use a flag to know if the notification really comes from your program.&lt;br /&gt;
&lt;br /&gt;
== How does DOCKYGET_SupportsComposite work? ==&lt;br /&gt;
&lt;br /&gt;
When compositing is enabled, a docky receives a DOCKYGET_SupportsComposite query. If it returns TRUE, it will then receive a DOCKYGET_CompositeMode query to which it can reply either DOCKYCOMPMODE_PreblendedRGB (0) or DOCKYCOMPMODE_RawRGB (1). The former has no advantages over the previous method, but was included for backward compatibility. The latter, however, allows you to directly *copy* the source ARGB data to the destination bitmap (or buffer) rather than blend it yourself. This way, it will be AmiDock itself that does the blending (once) without the need to undo the&lt;br /&gt;
previous blending to get the raw RGB data.&lt;br /&gt;
&lt;br /&gt;
One thing to take into account when using this method, though, is that on a 16-bit screen you will get an actual 16-bit destination bitmap, not 32-bit as it happens when returning FALSE to DOCKYGET_SupportsComposite. In this case, you will need to write the alpha channel separately to the&lt;br /&gt;
(8-bit) bitmap provided in the DockyRenderDestination&#039;s alpha.RP field.&lt;br /&gt;
&lt;br /&gt;
= See Also =&lt;br /&gt;
&lt;br /&gt;
1. SDK:Documentation/AutoDocs/docky.doc&lt;br /&gt;
&lt;br /&gt;
2. SYS:Documentation/Commodities/AmiDock_Arexx.doc&lt;br /&gt;
&lt;br /&gt;
3. SDK:Examples/AmiDock/&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Multiple_Assigns&amp;diff=12534</id>
		<title>Multiple Assigns</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Multiple_Assigns&amp;diff=12534"/>
		<updated>2025-01-26T19:26:03Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
One of the features in AmigaDOS since the OS 2.x days is Multiple Assigning. This feature allows an AmigaDOS assign to carry over several directories which can be on different volumes. This makes it possible to split up assigns such as libs: and fonts:.&lt;br /&gt;
&lt;br /&gt;
The article [[Directory Scanning]] contains an example called find.c that illustrates scanning a path that can contain a multiassign. However, besides being rather complicated, find.c makes a special case of scanning assigns, which isn&#039;t necessary (find.c also did something evil--find.c uses DOSBase&#039;s private pointer to the utility.library, essentially using the utility.library without opening it). The method needed to scan a multiassign directory also works on conventional directories.&lt;br /&gt;
&lt;br /&gt;
Scanning a multiassign requires calling the dos.library function GetDeviceProc() in a loop to see each directory of the multiassign. Using GetDeviceProc(), the application doesn&#039;t have to concern itself with the differences between assigns, multiassigns, and volumes. The application just keeps calling GetDeviceProc() until it gets back a NULL.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DevProc *GetDeviceProc( STRPTR name, struct DevProc *dp );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The name can be any valid dos path. If there is a device name present, GetDeviceProc() will find the device&#039;s entry in the dos list and copy some information into a DevProc structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct DevProc {&lt;br /&gt;
        struct MsgPort *dvp_Port;   /* Device&#039;s Message port, also called a Process identifier */&lt;br /&gt;
        BPTR            dvp_Lock;   /* Lock on root of assign or lock on root of volume */&lt;br /&gt;
        ULONG           dvp_Flags;&lt;br /&gt;
        struct DosList *dvp_DevNode;/* DON&#039;T TOUCH OR USE! */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The important fields here are dvp_Lock and dvp_Port. The dvp_Lock field is a lock on the root of the object named in GetDeviceProc(). It serves as a starting point in locating the named object. If the object name contains an assign (i.e. libs:), dvp_Lock is the root of the assign. For example, on a typical system, the libs: assign refers to the libs directory on the SYS: volume. Calling GetDeviceProc() on libs: in this case will yield a lock on SYS:libs.&lt;br /&gt;
&lt;br /&gt;
If the named object contains a dos volume, dvp_Lock is either a lock on the root of the dos volume or NULL. If the object named in GetDeviceProc() contains a non-file system device (i.e., ser:, par:, prt:, etc.) or it does not contain a device name, dvp_Lock is NULL.&lt;br /&gt;
&lt;br /&gt;
The dvp_Port field points to a message port. This message port is connected to the handler process of a DOS device. The handler process controls a DOS device. DOS functions (like the Lock() function) use this message port to talk to the handler process of the named object. For example, from the libs: example above, the dvp_Port field refers to the message port of the handler process for the SYS: volume.&lt;br /&gt;
&lt;br /&gt;
Note that dvp_Lock is only a lock on the root of the named object. If the named object is a path several directories deep (for example, libs:gadgets/colorwheel.gadget), it&#039;s up to the application to handle the rest of the path. The application also has to handle the case where the named object is a path without a device name.&lt;br /&gt;
&lt;br /&gt;
Although an application can send DOS packets directly to the message port (dvp_Port) of a handler process, normally it is easier to use functions from dos.library. The multilist.c example uses the Lock() function to lock the named object. Multilist has to do something a little unorthodox to use Lock(). Lock() accepts a path name to the object to lock. Lock() understands absolute paths (i.e. paths with a logical device name like dh1: or libs:) and relative paths. If Lock() receives an absolute path name, Lock() can find the device&#039;s handler process using the logical device name in the absolute path. For a relative path, Lock() does not have enough information to find the named object, so it assumes the path is relative to the current directory and file system (each process has a current directory and file system).&lt;br /&gt;
&lt;br /&gt;
This makes Lock() a little more difficult to use in multilist.c because, when processing an absolute path, multilist has to process the logical device name separately from the rest of the path. It has to use GetDeviceProc() to find the root of a logical device name (which can be an assign, multiassign, volume name, etc.) then it has to strip the logical device name from the absolute path. Without a logical device name, the path has become relative rather than absolute. The path is now relative to dvp_Lock and dvp_Port. In order for Lock() to work with this relative path, multilist must temporarily set the current directory and file system to the values in dvp_Lock and dvp_Port, respectively.&lt;br /&gt;
&lt;br /&gt;
Note that the Autodoc for GetDeviceProc() says to check IoErr() for ERROR_NO_MORE_ENTRIES after receiving a NULL from GetDeviceProc(). Due to a bug, DOS does not set the error value correctly. Also note that the Autodoc says to check the DevProc structure&#039;s dvp_Flags field for the DVPF_ASSIGN flag. This was necessary in the 2.00 and 2.01 releases of the operating system due to a bug in DOS, but is no longer necessary.&lt;br /&gt;
&lt;br /&gt;
= Code Example =&lt;br /&gt;
&lt;br /&gt;
The following example, multilist.c, accepts an arbitrary path name and lists the contents of it. The function DoAllAssigns() does all of the multiassign work. DoAllAssigns() accepts a path and a function pointer. It gets a lock on the object named in the path, and passes the lock to the function. This example is based on a Usenet posting by Randell Jesup.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
 /* This example illustrates how to scan DOS file names without      */&lt;br /&gt;
 /* having to make a special case for assigns and multiassigns.      */&lt;br /&gt;
 /* The DoAllAssigns() routine accepts an arbitrary dos path and     */&lt;br /&gt;
 /* a pointer to a function.  DoAllAssigns() will call this function */&lt;br /&gt;
 /* passing it a lock to the object named in the path.               */&lt;br /&gt;
 &lt;br /&gt;
 #define BUFSIZE 1024&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;dos/exall.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;clib/dos_protos.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;clib/exec_protos.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;strings.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 BOOL DoAllAssigns(char *, BOOL (*)());&lt;br /&gt;
 BOOL ListContents(BPTR);&lt;br /&gt;
 &lt;br /&gt;
 extern struct DosLibrary *DOSBase;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 void main(int argc, char **argv) &lt;br /&gt;
 {&lt;br /&gt;
     if (DOSBase-&amp;gt;dl_lib.lib_Version &amp;gt;= 37)&lt;br /&gt;
     {&lt;br /&gt;
         if (argc &amp;gt; 1)&lt;br /&gt;
         {&lt;br /&gt;
             (void) DoAllAssigns(argv[1], &amp;amp;ListContents);&lt;br /&gt;
         }&lt;br /&gt;
     } &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
        /* Pass this routine a directory lock and it prints the names of the  */&lt;br /&gt;
        /* files and directories in that directory.  If you pass this routine */&lt;br /&gt;
        /* a file lock, it just prints the file&#039;s name.                       */ &lt;br /&gt;
 BOOL ListContents(BPTR lock) &lt;br /&gt;
 {&lt;br /&gt;
     struct ExAllControl  *myeac;&lt;br /&gt;
     struct ExAllData     *myead;&lt;br /&gt;
     APTR                 buffer;&lt;br /&gt;
     BOOL                 done;&lt;br /&gt;
     struct FileInfoBlock *myfib;&lt;br /&gt;
 &lt;br /&gt;
     if (myfib = AllocDosObject(DOS_FIB, NULL))&lt;br /&gt;
     {&lt;br /&gt;
         if (Examine(lock, myfib) == DOSTRUE)&lt;br /&gt;
         {&lt;br /&gt;
             if (myfib-&amp;gt;fib_DirEntryType &amp;gt; 0)&lt;br /&gt;
             {&lt;br /&gt;
                 if (buffer = AllocVec(BUFSIZE, MEMF_PUBLIC))&lt;br /&gt;
                 {&lt;br /&gt;
                     if (myeac = AllocDosObject(DOS_EXALLCONTROL, NULL))&lt;br /&gt;
                     {&lt;br /&gt;
                         myeac-&amp;gt;eac_LastKey = 0; &lt;br /&gt;
 &lt;br /&gt;
                         do&lt;br /&gt;
                         {&lt;br /&gt;
                             done = ExAll(lock, buffer, BUFSIZE, ED_NAME, myeac);&lt;br /&gt;
                             myead = buffer;&lt;br /&gt;
                             while (myead)&lt;br /&gt;
                             {&lt;br /&gt;
                                 printf(&amp;quot;%s\n&amp;quot;, myead-&amp;gt;ed_Name);&lt;br /&gt;
                                 myead = myead-&amp;gt;ed_Next;&lt;br /&gt;
                             }&lt;br /&gt;
                         } while (done != 0);&lt;br /&gt;
                         FreeDosObject(DOS_EXALLCONTROL, myeac);&lt;br /&gt;
                     }&lt;br /&gt;
                     FreeVec(buffer);&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
             else printf(&amp;quot;%s\n&amp;quot;, myfib-&amp;gt;fib_FileName);&lt;br /&gt;
         }&lt;br /&gt;
         FreeDosObject(DOS_FIB, myfib);&lt;br /&gt;
     }&lt;br /&gt;
     return TRUE;&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 /* This routine accepts a path string that may include a device name. From    */&lt;br /&gt;
 /* that string, this routine locks the object named in the path and calls     */&lt;br /&gt;
 /* the function passback_func() on the lock.  DoAllAssigns() should work on   */&lt;br /&gt;
 /* paths with assigns and multiassigns, as well as a file system-based device */&lt;br /&gt;
 /* (i.e. df0:, work:, ram:, etc.)                                             */&lt;br /&gt;
 &lt;br /&gt;
 BOOL DoAllAssigns(char *dos_path, BOOL (*passback_func)(BPTR lock))&lt;br /&gt;
 {&lt;br /&gt;
     struct DevProc *dp=NULL;&lt;br /&gt;
     struct MsgPort *old_fsport;&lt;br /&gt;
     BPTR           lock, old_curdir;&lt;br /&gt;
     char           *rest_of_path; &lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
     while(dp = GetDeviceProc(dos_path, dp))&lt;br /&gt;
     {                                        /* I need to cut the device name from  */&lt;br /&gt;
         rest_of_path = strchr(dos_path,&#039;:&#039;); /* the front of dos_path so I can give */&lt;br /&gt;
                                              /* that substring to Lock().           */&lt;br /&gt;
         if (rest_of_path == NULL)                 /* There was no device name to    */&lt;br /&gt;
             rest_of_path = dos_path;              /* cut off, use the whole string. */&lt;br /&gt;
         else&lt;br /&gt;
             rest_of_path++;     /* Increment string pointer to just past the colon. */&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
         old_fsport = SetFileSysTask(dp-&amp;gt;dvp_Port); /* in case dp-&amp;gt;dvp_Lock is NULL. */&lt;br /&gt;
         old_curdir = CurrentDir(dp-&amp;gt;dvp_Lock);     /* Lock() locks relative to the  */&lt;br /&gt;
                                 /* current directory and falls back to the root of  */&lt;br /&gt;
                                 /* the current file system if dp-&amp;gt;dvp_Lock is NULL. */&lt;br /&gt;
&lt;br /&gt;
         lock = Lock(rest_of_path,SHARED_LOCK);&lt;br /&gt;
 &lt;br /&gt;
         (void) SetFileSysTask(old_fsport); /* reset the process&#039; default file system */&lt;br /&gt;
         (void) CurrentDir(old_curdir);     /* port and current dir to their initial  */&lt;br /&gt;
                                            /* values for clean up later.             */ &lt;br /&gt;
&lt;br /&gt;
         if (lock)&lt;br /&gt;
         {&lt;br /&gt;
             if (!(*passback_func)(lock))&lt;br /&gt;
             {&lt;br /&gt;
                 printf(&amp;quot;function returned false\n&amp;quot;);&lt;br /&gt;
                 UnLock(lock);         /* UnLock() will ignore NULL lock */&lt;br /&gt;
                 FreeDeviceProc(dp);&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
             }&lt;br /&gt;
             UnLock(lock);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (IoErr() == ERROR_NO_MORE_ENTRIES)&lt;br /&gt;
         return TRUE;               /* At present, a bug in DOS prevents this case, */&lt;br /&gt;
     else                           /* so DoAllAssigns() always returns FALSE.      */&lt;br /&gt;
         return FALSE;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Writing_IFF&amp;diff=12533</id>
		<title>Writing IFF</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Writing_IFF&amp;diff=12533"/>
		<updated>2025-01-26T19:25:59Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Writing IFF Files ===&lt;br /&gt;
&lt;br /&gt;
IFFParse provides facilities for writing IFF files. Again, IFFParse makes no assumptions about the data you’re writing, and concerns itself with verifying the syntax of your output.&lt;br /&gt;
&lt;br /&gt;
=== Creating Chunks in a File ===&lt;br /&gt;
&lt;br /&gt;
Because the IFF specification has nesting and scoping rules, you can nest chunks inside one another. One instance is the BMHD chunk, which is commonly nested inside a FORM chunk. Thus, it is necessary for you to inform IFFParse when you are starting and ending chunks.&lt;br /&gt;
&lt;br /&gt;
==== PushChunk() ====&lt;br /&gt;
&lt;br /&gt;
To tell IFFParse you are about to begin writing a new chunk, you use the function PushChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;PushChunk(iff, ID_ILBM, ID_BMHD,chunksize);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The chunk ID and size are written to the stream. IFFParse will enforce the chunk size you specified; attempts to write past the end of the chunk will be truncated. If, as a chunk size argument, you pass IFFSIZE_UNKNOWN, the chunk will be expanded in size as you write data to it.&lt;br /&gt;
&lt;br /&gt;
==== PopChunk() ====&lt;br /&gt;
&lt;br /&gt;
When you are through writing data to a chunk, you complete the write by calling PopChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you wrote fewer bytes than you declared with PushChunk(), PopChunk() will return an error. If you specified IFFSIZE_UNKNOWN, PopChunk() will seek backward on the stream and write the final size. If you specified a chunk size that was odd, PopChunk() will write the pad byte automatically.&lt;br /&gt;
&lt;br /&gt;
PushChunk() and PopChunk() nest; every call to PushChunk() must have a corresponding call to PopChunk().&lt;br /&gt;
&lt;br /&gt;
=== Writing Chunk Data ===&lt;br /&gt;
&lt;br /&gt;
Writing the IFF chunk data is done with either the WriteChunkBytes() or WriteChunkRecords() functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;WriteChunkBytes(iff, buf, size);&lt;br /&gt;
error = IIFFParse-&amp;gt;WriteChunkRecords(iff, buf, recsize, numrec);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you specified a valid chunk size when you called PushChunk(), WriteChunkBytes() and WriteChunkRecords() will truncate attempts to write past the end of the chunk.&lt;br /&gt;
&lt;br /&gt;
Code to write an ILBM file might take the following form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
iff = IIFFParse-&amp;gt;AllocIFF ();&lt;br /&gt;
iff-&amp;gt;iff_Stream = IDOS-&amp;gt;Open (&amp;quot;foo&amp;quot;, MODE_NEWFILE);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFFasDOS (iff);&lt;br /&gt;
IIFFParse-&amp;gt;OpenIFF (iff, IFFF_WRITE);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;PushChunk (iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;PushChunk (iff, ID_ILBM, ID_BMHD, sizeof (struct BitMapHeader));&lt;br /&gt;
IIFFParse-&amp;gt;WriteChunkBytes (iff, &amp;amp;bmhd, sizeof (bmhd));&lt;br /&gt;
IIFFParse-&amp;gt;PopChunk (iff);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;PushChunk (iff, ID_ILBM, ID_CMAP, cmapsize);&lt;br /&gt;
IIFFParse-&amp;gt;WriteChunkBytes (iff, cmapdata, cmapsize);&lt;br /&gt;
IIFFParse-&amp;gt;PopChunk (iff);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;PushChunk (iff, ID_ILBM, ID_BODY, IFFSIZE_UNKNOWN);&lt;br /&gt;
packwritebody (iff);&lt;br /&gt;
IIFFParse-&amp;gt;PopChunk (iff);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;PopChunk (iff);&lt;br /&gt;
&lt;br /&gt;
IIFFParse-&amp;gt;CloseIFF (iff);&lt;br /&gt;
IDOS-&amp;gt;Close (iff-&amp;gt;iff_Stream);&lt;br /&gt;
IIFFParse-&amp;gt;FreeIFF (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again, error checking is not present for clarity. See the example code ClipFTXT.c which writes a simple FTXT clip to the clipboard.&lt;br /&gt;
&lt;br /&gt;
=== A Note on Seekability ===&lt;br /&gt;
&lt;br /&gt;
As you can see from the above examples, IFFParse works best with a stream that can seek randomly. However, it is not possible to seek on some streams (e.g., pipes).&lt;br /&gt;
&lt;br /&gt;
IFFParse will read and write streams with limited or no seek capability. In the case of reading, only forward-seek capability is desirable. Failing this, IFFParse will fake forward seeks with a number of short reads.&lt;br /&gt;
&lt;br /&gt;
In the case of writing, if the stream lacks random seek capability, IFFParse will buffer &#039;&#039;the entire contents&#039;&#039; of the file until you do the final PopChunk(), or when you CloseIFF() the handle. At that time, the entire stream will be written in one go. This buffering happens whether or not you specify all the chunk sizes to PushChunk().&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Internal Buffering|text=The current implementation of this internal buffering could be more efficient. Be aware that the AmigaOS development team reserves the right to alter this behavior of the parser, to improve performance or reduce memory requirements. We mention this behavior on the off chance it is important to you.}}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=ASL_Font_Requester&amp;diff=12532</id>
		<title>ASL Font Requester</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=ASL_Font_Requester&amp;diff=12532"/>
		<updated>2025-01-26T19:25:55Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The ASL library also contains a font requester. Using the font requester is very similar to using the file requester. First, allocate a requester structure with AllocAslRequest() or AllocAslRequestTags(). The type should be set to ASL_FontRequest in order to get a FontRequester structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct FontRequester    {&lt;br /&gt;
    APTR    fo_Reserved1[2];&lt;br /&gt;
    struct TextAttr fo_Attr;    /* Returned TextAttr            */&lt;br /&gt;
    UBYTE   fo_FrontPen;        /* Returned pens, if selected   */&lt;br /&gt;
    UBYTE   fo_BackPen;&lt;br /&gt;
    UBYTE   fo_DrawMode;&lt;br /&gt;
    APTR    fo_UserData;&lt;br /&gt;
    /* missing from asl.h but present in this structure */&lt;br /&gt;
    SHORT   fo_LeftEdge, fo_TopEdge, fo_Width, fo_Height;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the requester is set up, call AslRequest() or AslRequestTags() to make the requester appear on screen. These functions return TRUE if the user makes a selection. In that case, the font selected is returned as a TextAttr structure in the fo_Attr field of the FontRequester structure. (The TextAttr structure is defined in &amp;amp;lt;graphics/text.h&amp;amp;gt;. See the SDK for a complete listing.) If the user cancels the font requester FALSE is returned.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig16-2.png|frame|center|The ASL Font Requester]]&lt;br /&gt;
&lt;br /&gt;
When the requester is no longer needed, call FreeAslRequest() to deallocate the requester data structure.&lt;br /&gt;
&lt;br /&gt;
= Specifying Font Requester Options With TagItems =&lt;br /&gt;
&lt;br /&gt;
As with a file requester, the font requester is specified with a TagItem list. There are several tags that are specific to the font requester:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Font Requester Tag Name&lt;br /&gt;
! Used For&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialName || Initial font name selection&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialSize || Initial font size&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialStyle || Initial setting of font Style gadget&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_Flags || Various font requester options&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialFrontPen || Initial setting of Front Color gadget&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialBackPen || Initial setting of Back Color gadget&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_ModeList || Alternate strings for Mode cycle gadget (see below)&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_MinHeight || Specifies the minimum height of fonts to be listed&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_MaxHeight || Specifies the maximum height of fonts to be listed&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_DoFrontPen || Causes the requester to display the Front Color selection gadget (this replaces the FONF_FRONTCOLOR flag in ASL_FuncFlags used in V37).&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_DoBackPen || Causes the requester to display the Back Color selection gadget (this replaces the FONF_BACKCOLOR flag in ASL_FuncFlags used in V37).&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_DoStyle || Causes the requester to display Style checkbox gadgets (this replaces the FONF_STYLES flag in ASL_FuncFlags used in V37).&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_DoDrawMode || Causes the requester to display the Mode cycle gadget.(this replaces the FONF_DRAWMODE flag in ASL_FuncFlags used in V37).&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_FixedWidthOnly || Causes the requester to list only fixed-width fonts (this replaces the FONF_FIXEDWIDTH flag in ASL_FuncFlags  used in V37).&lt;br /&gt;
|-&lt;br /&gt;
| ASLFO_InitialDrawMode || Initial setting of the font Mode gadget.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that the last two tags only limit the range of font sizes that the font requester displays, the user is free to type in any value.&lt;br /&gt;
&lt;br /&gt;
Font requesters have additional special options that are controlled through the ASL_FuncFlags tag. This tag works the same way as it does with file requesters but with different options available. Recall that the data for this tag is divided into bit fields, each of which controls a requester option. The flags used with the ASL_FuncFlags tag in a font requester are defined in &amp;amp;lt;libraries/asl.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Font Requester Flags&lt;br /&gt;
! Used For&lt;br /&gt;
|-&lt;br /&gt;
| FONF_FRONTCOLOR&lt;br /&gt;
| Enables font color selection gadgets&lt;br /&gt;
|-&lt;br /&gt;
| FONF_BACKCOLOR&lt;br /&gt;
| Enables font background color selection gadget&lt;br /&gt;
|-&lt;br /&gt;
| FONF_STYLES&lt;br /&gt;
| Enables font style selection gadget&lt;br /&gt;
|-&lt;br /&gt;
| FONF_FIXEDWIDTH&lt;br /&gt;
| Limits display to fixed width fonts only&lt;br /&gt;
|-&lt;br /&gt;
| FONF_DRAWMODE&lt;br /&gt;
| Enables font draw mode gadget&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A simple font requester (one without any of the above FONF_ flags set) only lets the user choose a font and a Y size. Setting the flags above adds options to the font requester. FONF_FRONTCOLOR and FONF_BACKCOLOR add color selection gadgets to the requester, one for choosing a font&#039;s foreground color (labeled &amp;quot;Text&amp;quot;) and the other for choosing the background color (labeled &amp;quot;Field&amp;quot;). The font requester records the user&#039;s setting in the FontRequester&#039;s fo_FrontPen and fo_BackPen fields.&lt;br /&gt;
&lt;br /&gt;
FONF_STYLES sets up several gadgets to choose the style of the font (bold, italics, underline). The font requester saves these settings in the fo_Attr.ta_Style bit field according to the style flags defined in &amp;amp;lt;graphics/text.h&amp;amp;gt;. FONF_FIXEDWIDTH limits the font name display to fixed width (non-proportional) fonts (note that this does not prevent the user from typing in a proportional font name).&lt;br /&gt;
&lt;br /&gt;
FONF_DRAWMODE adds a cycle gadget to the font requester so the user can choose the draw mode. The draw mode is saved in the requester&#039;s fo_DrawMode field. The number stored there corresponds to the draw mode&#039;s position in the gadget&#039;s cycle.&lt;br /&gt;
&lt;br /&gt;
The draw mode cycle gadget initially is labeled &amp;quot;Mode&amp;quot; and has three elements in its cycle: &amp;quot;JAM1&amp;quot;, &amp;quot;JAM2&amp;quot;, and &amp;quot;Complement&amp;quot;. These yield a result of 0, 1, and 2, respectively. It is possible to change the names and number of draw modes with the ASL_ModeList tag. This tag accepts a pointer to an array of strings. The first string replaces &amp;quot;Mode&amp;quot; as the label for the draw mode cycle gadget. The strings that follow replace the elements of the cycle gadget. The last entry in the array has to be NULL to tell the requester where the list of entries ends.&lt;br /&gt;
&lt;br /&gt;
= Example Font Requester =&lt;br /&gt;
&lt;br /&gt;
The following example illustrates how to use a font requester.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** fontreq.c&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/asl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/asl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct AslIFace *IAsl = NULL;&lt;br /&gt;
&lt;br /&gt;
/* Our replacement strings for the &amp;quot;mode&amp;quot; cycle gadget.  The&lt;br /&gt;
** first string is the cycle gadget&#039;s label.  The other strings&lt;br /&gt;
** are the actual strings that will appear on the cycle gadget.&lt;br /&gt;
*/&lt;br /&gt;
CONST_STRPTR modelist[] =&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;Amiga Modes&amp;quot;,&lt;br /&gt;
    &amp;quot;Mode 0&amp;quot;,&lt;br /&gt;
    &amp;quot;Mode 1&amp;quot;,&lt;br /&gt;
    &amp;quot;Mode 2&amp;quot;,&lt;br /&gt;
    &amp;quot;Mode 3&amp;quot;,&lt;br /&gt;
    &amp;quot;Mode 4&amp;quot;,&lt;br /&gt;
    NULL&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct FontRequester *fr;&lt;br /&gt;
&lt;br /&gt;
    struct Library *AslBase = IExec-&amp;gt;OpenLibrary(&amp;quot;asl.library&amp;quot;, 50);&lt;br /&gt;
    IAsl = (struct AslIFace*)IExec-&amp;gt;GetInterface(AslBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IAsl != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        if (fr = (struct FontRequester *)&lt;br /&gt;
            IAsl-&amp;gt;AllocAslRequestTags(ASL_FontRequest,&lt;br /&gt;
                /* tell the requester to use my custom mode names */&lt;br /&gt;
                ASL_ModeList, modelist,&lt;br /&gt;
&lt;br /&gt;
                /* Supply initial values for requester */&lt;br /&gt;
                ASL_FontName, &amp;quot;topaz.font&amp;quot;,&lt;br /&gt;
                ASL_FontHeight, 11,&lt;br /&gt;
                ASL_FontStyles, FSF_BOLD | FSF_ITALIC,&lt;br /&gt;
                ASL_FrontPen,  0x00,&lt;br /&gt;
                ASL_BackPen,   0x01,&lt;br /&gt;
&lt;br /&gt;
                /* Only display font sizes between 8 and 14, inclusive. */&lt;br /&gt;
                ASL_MinHeight, 8,&lt;br /&gt;
                ASL_MaxHeight, 14,&lt;br /&gt;
&lt;br /&gt;
                /* Give all the gadgetry, but only display fixed width fonts */&lt;br /&gt;
                ASL_FuncFlags, FONF_FRONTCOLOR | FONF_BACKCOLOR |&lt;br /&gt;
                    FONF_DRAWMODE | FONF_STYLES | FONF_FIXEDWIDTH,&lt;br /&gt;
                TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            /* Pop up the requester */&lt;br /&gt;
            if (IAsl-&amp;gt;AslRequest(fr, NULL))&lt;br /&gt;
            {&lt;br /&gt;
                /* The user selected something,  report their choice */&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n  YSize = %ld  Style = 0x%lx   Flags = 0x%lx\n&amp;quot;&lt;br /&gt;
                       &amp;quot;  FPen = 0x%lx   BPen = 0x%lx   DrawMode = 0x%lx\n&amp;quot;,&lt;br /&gt;
                               fr-&amp;gt;fo_Attr.ta_Name,&lt;br /&gt;
                               fr-&amp;gt;fo_Attr.ta_YSize,&lt;br /&gt;
                               fr-&amp;gt;fo_Attr.ta_Style,&lt;br /&gt;
                               fr-&amp;gt;fo_Attr.ta_Flags,&lt;br /&gt;
                               fr-&amp;gt;fo_FrontPen,&lt;br /&gt;
                               fr-&amp;gt;fo_BackPen,&lt;br /&gt;
                               fr-&amp;gt;fo_DrawMode);&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
                /* The user cancelled the requester, or some kind of error&lt;br /&gt;
                ** occurred preventing the requester from opening. */&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Request Cancelled\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IAsl-&amp;gt;FreeAslRequest(fr);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IAsl);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(AslBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Revision_5&amp;diff=12531</id>
		<title>Revision 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Revision_5&amp;diff=12531"/>
		<updated>2025-01-26T19:25:52Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Authors ==&lt;br /&gt;
&lt;br /&gt;
Hook-based callback function extensions for SANA-II (SANA-IIR5) &amp;lt;br /&amp;gt;&lt;br /&gt;
by Olaf Barthel and Heinz Wrobel&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The last changes proposed for the SANA-II networking driver standard were intended to address the demands of dial-up networking. A new issue has come up which concerns the application and deployment of PowerPC native networking drivers. So far the SANA-II standard only caters well for the Motorola 68000 platform, and the only way to support it on the PowerPC involves a considerable overhead. This is what the following proposal intends to address.&lt;br /&gt;
&lt;br /&gt;
Please feel free to comment; you can contact me through the [http://www.amigaos.net/contact AmigaOS contact form]. Note that the issues discussed in this document are just a list of proposed changes.&lt;br /&gt;
&lt;br /&gt;
== The problem: parameter passing to the copying functions ==&lt;br /&gt;
&lt;br /&gt;
The SANA-II driver interface is intended to transform data between the hardware layer and the link layer, to be used by networking software such as TCP/IP stacks. This transformation is performed by call-back functions which are supplied by the networking software at the time the device driver is opened. The device driver then invokes these functions later in order to transfer data received and data to be sent.&lt;br /&gt;
&lt;br /&gt;
These copying functions are designed for low overhead. They use up to three parameters and are documented in the copybuff.doc file which comes with the SANA-II specification. Here is an excerpt which describes the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt; function, which is associated with the &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
any_sana2_protocol/CopyFromBuff               any_sana2_protocol/CopyFromBuff&lt;br /&gt;
&lt;br /&gt;
   NAME&lt;br /&gt;
        CopyFromBuff -- Copy n bytes from an abstract data structure.&lt;br /&gt;
&lt;br /&gt;
   SYNOPSIS&lt;br /&gt;
        success = CopyFromBuff(to, from, n)&lt;br /&gt;
        d0                     a0  a1    d0&lt;br /&gt;
&lt;br /&gt;
        BOOL CopyToBuff(VOID *, VOID *, ULONG);&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        This function copies &#039;n&#039; bytes of data in the abstract data structure&lt;br /&gt;
        pointed to by &#039;from&#039; into the contigous memory pointed to by &#039;to&#039;.&lt;br /&gt;
        &#039;to&#039; must contain at least &#039;n&#039; bytes of usable memory or innocent&lt;br /&gt;
        memory will be overwritten.&lt;br /&gt;
&lt;br /&gt;
   INPUTS&lt;br /&gt;
        to              - pointer to contiguous memory to copy to.&lt;br /&gt;
        from            - pointer to abstract structure to copy from.&lt;br /&gt;
        n               - number of bytes to copy.&lt;br /&gt;
&lt;br /&gt;
   RESULT&lt;br /&gt;
        success         - TRUE if operation was successful, else FALSE.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        This function must be callable from interupts.  In particular, this&lt;br /&gt;
        means that this function may not directly or indirectly call any&lt;br /&gt;
        system memory functions (since those functions rely on Forbid() to&lt;br /&gt;
        protect themselves) and that  you must not compile this function&lt;br /&gt;
        with stack checking enabled.  See the Exec Interupts&lt;br /&gt;
        chapter for more details on what is legal in a routine called from&lt;br /&gt;
        an interupt handler.&lt;br /&gt;
&lt;br /&gt;
        &#039;C&#039; programmers should not compile with stack checking (option &#039;-v&#039;&lt;br /&gt;
        in SAS) and should geta4() or __saveds.&amp;lt;/pre&amp;gt;&lt;br /&gt;
As one can see, the function parameters are passed in 68000 registers. Compare this to the packet filter function, which is associated with the &amp;lt;tt&amp;gt;S2_PacketFilter&amp;lt;/tt&amp;gt; tag: it is intended to be invoked as a hook function, as part of the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; interface in &amp;lt;tt&amp;gt;utility.library/CallHookPkt&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The problem with 68000 register parameters is that on the PowerPC platform, this form of parameter passing may require the use of emulation code. This is costly and may incur a severe performance penalty. It is an even greater problem if PowerPC native networking software is calling PowerPC native networking driver software and the other way round. In both cases the runtime environment will have to enter emulation mode, return to to PowerPC native execution, dip into emulation mode and return to native PowerPC execution. It would be much better if the chain of execution would stay in PowerPC mode all the time.&lt;br /&gt;
&lt;br /&gt;
However, if a hook function is used (such as for the packet filter), the operating system may be able to decide whether the function to be invoked needs emulating or could be called straight away. The &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; effectively works as an abstraction which makes the function invocation platform independent.&lt;br /&gt;
&lt;br /&gt;
The following proposal therefore intends to wrap the copying functions into the standardized &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; interface.&lt;br /&gt;
&lt;br /&gt;
== New data structures and commands ==&lt;br /&gt;
&lt;br /&gt;
=== Data structures ===&lt;br /&gt;
&lt;br /&gt;
The SANA-IIR4 proposal lists ten copying functions, which can be grouped into two categories: data copying and direct memory access. These functions are:&lt;br /&gt;
&lt;br /&gt;
* Data copying:&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyToBuff16&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyFromBuff16&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyToBuff32&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_CopyFromBuff32&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Direct memory access:&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_DMACopyToBuff32&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_DMACopyFromBuff32&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_DMACopyToBuff64&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;S2_DMACopyFromBuff64&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three &#039;flavours&#039; of the data copying functions. These cater for certain buffer memory alignment requirements which the underlying hardware requires or which can be used to boost transfer efficiency. For the direct memory access functions there are two variants which cater for certain memory alignment requirements in the same manner as the copying functions.&lt;br /&gt;
&lt;br /&gt;
All these functions are using basically the same set of parameters and 68000 registers. The parameters and result codes are used for different purposes, though. In order to translate the parameters for use with the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; interface one needs to define a &#039;hook message&#039; and a set of parameters the hook function is invoked with.&lt;br /&gt;
&lt;br /&gt;
I propose the following hook messages to be used :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2HookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG shm_Method;&lt;br /&gt;
  ULONG shm_MsgSize;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this data structure the &amp;lt;tt&amp;gt;shm_Method&amp;lt;/tt&amp;gt; field would indicate the task to be performed. This can a be request to copy data or to store a log message. The &amp;lt;tt&amp;gt;shm_MsgSize&amp;lt;/tt&amp;gt; field tells you how large the data structure is (for future enhancements which may cause the data structure to grow).&lt;br /&gt;
&lt;br /&gt;
For copying operations the message would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2CopyHookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG schm_Method;&lt;br /&gt;
  ULONG schm_MsgSize;&lt;br /&gt;
&lt;br /&gt;
  APTR  schm_To;&lt;br /&gt;
  APTR  schm_From;&lt;br /&gt;
  ULONG schm_Size;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure members would be used as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;schm_Method&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;This must be one &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyToBuff16&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff16&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyToBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_CopyFromBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyToBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyFromBuff32&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;S2_DMACopyToBuff64&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;S2_DMACopyFromBuff64&amp;lt;/tt&amp;gt; to identify the function to be performed.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;schm_MsgSize&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Size of this message data structure in bytes. This must be &amp;amp;gt;= 20 for this message type.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;The driver shall set &amp;lt;tt&amp;gt;schm_MsgSize&amp;lt;/tt&amp;gt; always correctly to be compliant. The protocol stack shall use this field to validate the message and to reject/ignore bad messages via a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; hook function return value. For DMA related hooks, a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; return value is equivalent to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;L pointer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;schm_To&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Equivalent to the &amp;lt;tt&amp;gt;to&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DMACopyToBuff&amp;lt;/tt&amp;gt; functions.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;schm_From&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Equivalent to the &amp;lt;tt&amp;gt;from&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; functions.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;schm_Size&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Equivalent to the &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; parameter of the &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt;, functions.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For logging operations the message would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct SANA2LogHookMsg&lt;br /&gt;
{&lt;br /&gt;
  ULONG  slhm_Method;&lt;br /&gt;
  ULONG  slhm_MsgSize;&lt;br /&gt;
&lt;br /&gt;
  ULONG  slhm_Priority;&lt;br /&gt;
  STRPTR slhm_Name;&lt;br /&gt;
  STRPTR slhm_Message;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure members would be used as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;slhm_Method&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;This must be &amp;lt;tt&amp;gt;S2_Log&amp;lt;/tt&amp;gt;, as defined in the SANA-IIR4 specification.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;slhm_MsgSize&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Size of this message data structure in bytes. This must be &amp;amp;gt;= 20 for this message type.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;The driver shall set &amp;lt;tt&amp;gt;slhm_MsgSize&amp;lt;/tt&amp;gt; always correctly to be compliant. The protocol stack shall use this field to validate the message and to reject/ignore bad messages via a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; hook function return value. For DMA related hooks, a &amp;lt;tt&amp;gt;FALSE&amp;lt;/tt&amp;gt; return value is equivalent to a &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; pointer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt;slhm_Priority&amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;The smaller this value, the more important the message to be logged or displayed. The following priority levels are defined (similar to the Unix syslog() mechanism):&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Emergency 0 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;A panic condition.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Alert 1 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;A condition that should be corrected immediately.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Critical 2 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Critical conditions.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Error 3 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;A plain error.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Warning 4 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;A warning message.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Notice 5 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Conditions that are not error conditions, but should possibly be handled specially.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Information 6 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;An informational message.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; #define S2LOG_Debug 7 &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Messages that contain information normally of use only when debugging.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Only these priority values may be used by a driver. It is suggested that a driver is configurable to generate different types of messages or not, e.g., a driver may be configured to only emit &amp;lt;tt&amp;gt;S2LOG_Emergency&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2LOG_Debug&amp;lt;/tt&amp;gt; messages.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; slhm_Name &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which identifies the source of this message. This can be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; in which case the OS device name of the driver shall be used by the protocol stack.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;tt&amp;gt; slhm_Message &amp;lt;/tt&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Pointer to a &amp;lt;tt&amp;gt;NUL&amp;lt;/tt&amp;gt;-terminated string which contains the log message. The text should not contain any formatting characters such as line feeds or carriage returns. The &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; member must never be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;All message texts shall preferrably be formatted in the current user&#039;s locale. If that is not possible, the english language shall be used. &amp;lt;tt&amp;gt;slhm_Name&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; shall only contain printable characters.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The hook function itself would be invoked with the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
   result = hook_function(hook,sana2req,sana2hookmsg)&lt;br /&gt;
&lt;br /&gt;
   ULONG hook_function(struct Hook *hook,&lt;br /&gt;
                       struct IOSana2Req *sana2req,&lt;br /&gt;
                       struct SANA2HookMsg *sana2hookmsg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the result would not necessarily be of type &amp;lt;tt&amp;gt;ULONG&amp;lt;/tt&amp;gt;. It would be a 32 bit value, which would either be a boolean result code (for &amp;lt;tt&amp;gt;CopyFromBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;CopyToBuff&amp;lt;/tt&amp;gt; and their like) or the pointer to a memory address (for &amp;lt;tt&amp;gt;DMACopyToBuff&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;DMACopyFromBuff&amp;lt;/tt&amp;gt; and their like).&lt;br /&gt;
&lt;br /&gt;
=== Commands ===&lt;br /&gt;
&lt;br /&gt;
The &#039;traditional&#039; copy call-back functions are installed at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time, and they are found in a &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list which is passed along with the &amp;lt;tt&amp;gt;IOSana2Req&amp;lt;/tt&amp;gt;. For the new &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;-based call-back functionality, I propose to introduce a new command. This command would take care of installing one single &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; which will be invoked with the parameters described in section 3.1. The hook function can key off the &amp;lt;tt&amp;gt;SANA2HookMsg-&amp;amp;gt;schm_Method&amp;lt;/tt&amp;gt; field to figure out which function should be performed. Once the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; is installed via the command as follows, the driver shall ignore any and all tags passed to it during &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time.&lt;br /&gt;
&lt;br /&gt;
The command is tentatively defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sana2.device/S2_SANA2HOOK                             sana2.device/S2_SANA2HOOK&lt;br /&gt;
&lt;br /&gt;
   NAME&lt;br /&gt;
        S2_SANA2HOOK -- Install a Hook to perform operations such as copying,&lt;br /&gt;
                        overriding the call-back functions installed at&lt;br /&gt;
                        OpenDevice() time.&lt;br /&gt;
&lt;br /&gt;
   FUNCTION&lt;br /&gt;
        The S2_SANA2HOOK command is to replace the &#039;traditional&#039; call-back&lt;br /&gt;
        functions installed through the TagItem list found in the&lt;br /&gt;
        IOSana2Req-&amp;amp;gt;ios2_BufferManagement field. Instead of assigning a&lt;br /&gt;
        function pointer for each copying function, all operations are&lt;br /&gt;
        to be performed through a Hook. This is intended to make the&lt;br /&gt;
        interface more portable and less dependant on a certain hardware&lt;br /&gt;
        architecture.&lt;br /&gt;
&lt;br /&gt;
        The hook message and the hook data structures allow for more than&lt;br /&gt;
        copying to be done.&lt;br /&gt;
&lt;br /&gt;
   IO REQUEST&lt;br /&gt;
        ios2_Command    - S2_SANA2HOOK&lt;br /&gt;
        ios2_Data       - Points to a struct Sana2Hook, which looks&lt;br /&gt;
                          like this:&lt;br /&gt;
&lt;br /&gt;
                             struct Sana2Hook&lt;br /&gt;
                             {&lt;br /&gt;
                                struct Hook s2h_Hook;&lt;br /&gt;
                                Tag *       s2h_Methods;&lt;br /&gt;
                             };&lt;br /&gt;
&lt;br /&gt;
                          The structure fields have the following purposes:&lt;br /&gt;
&lt;br /&gt;
                             s2h_Hook&lt;br /&gt;
                                A standard Hook structure, ready to be&lt;br /&gt;
                                called. Once installed, the complete Hook&lt;br /&gt;
                                structure including its Node structure is&lt;br /&gt;
                                off limits! The s2h_Hook remains installed&lt;br /&gt;
                                until CloseDevice().&lt;br /&gt;
&lt;br /&gt;
                             s2h_Methods&lt;br /&gt;
                                Points to a table of Tag values, each&lt;br /&gt;
                                identifying a copy method supported&lt;br /&gt;
                                (S2_CopyToBuff, S2_CopyFromBuff,&lt;br /&gt;
                                S2_CopyToBuff16, S2_CopyFromBuff16,&lt;br /&gt;
                                S2_CopyToBuff32, S2_CopyFromBuff32,&lt;br /&gt;
                                S2_DMACopyToBuff32, S2_DMACopyFromBuff32,&lt;br /&gt;
                                S2_DMACopyToBuff64 or S2_DMACopyFromBuff64)&lt;br /&gt;
                                or the logging facility (S2_Log).&lt;br /&gt;
                                The table must be terminated by TAG_END.&lt;br /&gt;
&lt;br /&gt;
                                The driver will check the table and&lt;br /&gt;
                                verify that the mandatory S2_CopyToBuff&lt;br /&gt;
                                and S2_CopyFromBuff commands are present.&lt;br /&gt;
                                Additional functionality is used as&lt;br /&gt;
                                available if the driver supports it.&lt;br /&gt;
&lt;br /&gt;
        ios2_DataLength - Must be &amp;amp;gt;= 20, which is the default length of&lt;br /&gt;
                          the Sana2Hook structure. This may grow in&lt;br /&gt;
                          the future, and larger values for ios2_DataLength&lt;br /&gt;
                          may indicate additional functionality associated&lt;br /&gt;
                          with the Sana2Hook.&lt;br /&gt;
&lt;br /&gt;
   RESULTS&lt;br /&gt;
        ios2_Error      - IOERR_NOCMD if this command is not supported&lt;br /&gt;
                          by the driver.&lt;br /&gt;
&lt;br /&gt;
                          IOERR_BADLENGTH if IOSana2Req-&amp;amp;gt;ios2_DataLength&lt;br /&gt;
                          is &amp;amp;lt; 20.&lt;br /&gt;
&lt;br /&gt;
                          IOERR_UNITBUSY if the Hook was already&lt;br /&gt;
                          installed or if any of the CMD_READ, CMD_WRITE,&lt;br /&gt;
                          S2_MULTICAST or S2_BROADCAST have already been&lt;br /&gt;
                          invoked.&lt;br /&gt;
&lt;br /&gt;
                          S2WERR_FUNCTIONS_MISSING if the table pointed&lt;br /&gt;
                          to by Sana2Hook-&amp;amp;gt;s2h_Methods does not&lt;br /&gt;
                          include the S2_CopyToBuff and S2_CopyFromBuff&lt;br /&gt;
                          tags.&lt;br /&gt;
&lt;br /&gt;
   NOTES&lt;br /&gt;
        The S2_SANA2HOOK command shall be invoked right after OpenDevice()&lt;br /&gt;
        as very first command.&lt;br /&gt;
&lt;br /&gt;
        When the command has been executed, the driver must use the&lt;br /&gt;
        newly installed Hook for all its copying or logging and cease to&lt;br /&gt;
        use the call-back functions provided at OpenDevice() time.&lt;br /&gt;
&lt;br /&gt;
        The contents of the Sana2Hook structure, as passed to the&lt;br /&gt;
        driver, must not be modified. This includes the MinNode&lt;br /&gt;
        at the beginning of the Hook structure which the driver may&lt;br /&gt;
        need to use for its own purposes.&lt;br /&gt;
&lt;br /&gt;
        The table pointed to by Sana2Hook-&amp;amp;gt;s2h_Methods must&lt;br /&gt;
        include at least the S2_CopyToBuff and S2_CopyFromBuff tags.&lt;br /&gt;
        It must be valid until CloseDevice() is called.&lt;br /&gt;
&lt;br /&gt;
        This field is to be treated as private by a protocol stack.&lt;br /&gt;
        IOSana2Req structures may be duplicated by copying&lt;br /&gt;
        ios2_BufferManagement, io_Device, and io_Unit.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new command value is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   #define S2_SANA2HOOK 0xC008&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new error code is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   #define S2WERR_FUNCTIONS_MISSING 24 /* mandatory copy functions are missing */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application and driver software use of the new functions ==&lt;br /&gt;
&lt;br /&gt;
Since plenty of software exists which uses the &#039;traditional&#039; &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list provided at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time it is recommend that drivers always examine these parameters and do not expect a &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; command to be sent later.&lt;br /&gt;
&lt;br /&gt;
If the new &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;-based callback functions are to be used, then the driver must invoke the Hooks via &amp;lt;tt&amp;gt;utility.library/CallHookPkt&amp;lt;/tt&amp;gt;. It must not invoke the hook functions through local assembly language stubs or the &amp;lt;tt&amp;gt;amiga.lib/CallHook&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;amiga.lib/CallHookA&amp;lt;/tt&amp;gt; functions.&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
The functionality proposed above suggests that one could do entirely without the &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list passed in at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time. However, at this time it is hard to tell how existing driver software will react to empty &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; lists or even a NULL pointer in the &amp;lt;tt&amp;gt;IOSana2Req-&amp;amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field. It is therefore recommend to always provide for a &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; list which includes proper (i.e. they must point to working functions and may not be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;) function pointers for the &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt; tags. Once the device has been opened successfully, the next step is to try and install the copy &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; through the proposed &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; command. If the command fails, the application can still expect that the &amp;lt;tt&amp;gt;S2_CopyToBuff&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;S2_CopyFromBuff&amp;lt;/tt&amp;gt; tags supplied at &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time will work.&lt;br /&gt;
&lt;br /&gt;
Note that the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field provided by the driver on &amp;lt;tt&amp;gt;OpenDevice()&amp;lt;/tt&amp;gt; time in conjunction with &amp;lt;tt&amp;gt;io_Device&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;io_Unit&amp;lt;/tt&amp;gt; is the unique identifier for all requests coming from this protocol stack until &amp;lt;tt&amp;gt;CloseDevice()&amp;lt;/tt&amp;gt;. The driver must not ever change the &amp;lt;tt&amp;gt;ios2_BufferManagement&amp;lt;/tt&amp;gt; field for a protocol stack at run time, even if &amp;lt;tt&amp;gt;S2_SANAHOOK&amp;lt;/tt&amp;gt; is called to request extended features.&lt;br /&gt;
&lt;br /&gt;
== Changes ==&lt;br /&gt;
&lt;br /&gt;
02-May-2012:&lt;br /&gt;
&lt;br /&gt;
* Conversion to MediaWiki format.&lt;br /&gt;
&lt;br /&gt;
22-Mar-2004:&lt;br /&gt;
&lt;br /&gt;
* Conversion to HTML&lt;br /&gt;
&lt;br /&gt;
21-Jan-2004:&lt;br /&gt;
&lt;br /&gt;
* Added clarifications for &amp;lt;tt&amp;gt;schm_MsgSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_MsgSize&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Priority&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Name&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;slhm_Message&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;s2h_Hook&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Updated the &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt; documentation.&lt;br /&gt;
* Shortened section 4.&lt;br /&gt;
* Updated section 5.&lt;br /&gt;
&lt;br /&gt;
30-Nov-2003:&lt;br /&gt;
&lt;br /&gt;
* Extended the applicability of the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; to logging.&lt;br /&gt;
* Renamed &amp;lt;tt&amp;gt;S2_COPYHOOK&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;S2_SANA2HOOK&amp;lt;/tt&amp;gt;, which matches the extended scope it should cover.&lt;br /&gt;
* The message passed to the &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt; now includes a size field which holds the size of the message, expressed in bytes.&lt;br /&gt;
&lt;br /&gt;
06-Oct-2003:&lt;br /&gt;
&lt;br /&gt;
* Replaced the list of new tag items with a single &amp;lt;tt&amp;gt;Hook&amp;lt;/tt&amp;gt;, which is installed through a new command.&lt;br /&gt;
* Added &amp;amp;quot;Caveats&amp;amp;quot; and &amp;amp;quot;Changes&amp;amp;quot; sections&lt;br /&gt;
* Removed the documentation for the originally proposed &amp;lt;tt&amp;gt;TagItem&amp;lt;/tt&amp;gt; (sections 3.2.1 through 3.2.10)&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BattClock_Resource&amp;diff=12530</id>
		<title>BattClock Resource</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BattClock_Resource&amp;diff=12530"/>
		<updated>2025-01-26T19:25:31Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The battery-backed clock (BattClock) keeps Amiga time while the system is powered off. The time from the BattClock is loaded into the Amiga system clock as part of the boot sequence.&lt;br /&gt;
&lt;br /&gt;
The BattClock Resource component provides access to the BattClock. Three [[#Function reference|functions]] allow you to read the BattClock value, reset it and set it to a value you desire.&lt;br /&gt;
&lt;br /&gt;
The [[Utility Library]] contains time functions which convert the number of seconds since 12:00 AM, January 1, 1978 to a date and time we can understand, and vice versa. You will find these functions useful when dealing with the BattClock. The example program below uses the Amiga2Date() utility function to convert the value returned by ReadBattClock(). See the [[Utility Library]] page for a discussion of the scope of the library, and the [[Autodocs:Main|autodocs]] for a listing of its functions.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=&#039;&#039;So, you want to be a Time Lord?&#039;&#039;|This resource will allow you to set the BattClock to any value you desire. Keep in mind that this time will endure a reboot and could adversely affect your system!}}&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/date.h&amp;gt;&lt;br /&gt;
#include &amp;lt;resources/battclock.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/battclock.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    STRPTR ampm;&lt;br /&gt;
    uint32 AmigaTime;&lt;br /&gt;
    struct ClockData MyClock;&lt;br /&gt;
    CONST CONST_STRPTR Days[] = {&amp;quot;Sunday&amp;quot;, &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;};&lt;br /&gt;
    CONST CONST_STRPTR Months[] = {&amp;quot;January&amp;quot;, &amp;quot;February&amp;quot;, &amp;quot;March&amp;quot;, &amp;quot;April&amp;quot;, &amp;quot;May&amp;quot;, &amp;quot;June&amp;quot;, &amp;quot;July&amp;quot;, &amp;quot;August&amp;quot;, &lt;br /&gt;
    &amp;quot;September&amp;quot;, &amp;quot;October&amp;quot;, &amp;quot;November&amp;quot;, &amp;quot;December&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    struct Library *BattClockBase = IExec-&amp;gt;OpenResource(BATTCLOCKNAME);&lt;br /&gt;
    struct BattClockIFace *IBattClock = (struct BattClockIFace *) IExec-&amp;gt;GetInterface(BattClockBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IBattClock != NULL) {&lt;br /&gt;
        /* Get number of seconds till now */&lt;br /&gt;
        AmigaTime = IBattClock-&amp;gt;ReadBattClock();&lt;br /&gt;
 &lt;br /&gt;
        /* Convert to a ClockData structure */&lt;br /&gt;
        IUtility-&amp;gt;Amiga2Date(AmigaTime, &amp;amp;MyClock);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\nRobin, tell everyone the BatDate and BatTime&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print the Date */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\n\nOkay Batman, the BatDate is &amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s, %s %ld, %ld&amp;quot;, Days[MyClock.wday], Months[MyClock.month-1], MyClock.mday, MyClock.year);&lt;br /&gt;
&lt;br /&gt;
        /* Convert military time to normal time and set AM/PM */&lt;br /&gt;
        if (MyClock.hour &amp;lt; 12) {&lt;br /&gt;
            ampm = &amp;quot;AM&amp;quot;;        /* hour less than 12, must be morning */&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            ampm = &amp;quot;PM&amp;quot;;         /* hour greater than 12,must be night */&lt;br /&gt;
            MyClock.hour -= 12;  /* subtract the extra 12 of military */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        if (MyClock.hour == 0) {&lt;br /&gt;
                MyClock.hour = 12;   /* don&#039;t forget the 12s */&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Print the time */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\n             the BatTime is &amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%02ld:%02ld:%02ld %s\n\n&amp;quot;, MyClock.hour, MyClock.min, MyClock.sec, ampm);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*) IBattClock);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
       IDOS-&amp;gt;Printf(&amp;quot;Error: Unable to open the %s\n&amp;quot;, BATTCLOCKNAME);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the BattClock Resource can be found in the respective include files and in the BattClock Resource and Utility Library [[Autodocs:Main|autodocs]].&lt;br /&gt;
&lt;br /&gt;
== Function reference ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ReadBattClock() || Read the time from the BattClock. Returns the time as the number of seconds since 12:00 AM, January 1, 1978.&lt;br /&gt;
|-&lt;br /&gt;
| ResetBattClock() || Reset the BattClock to 12:00 AM, January 1, 1978.&lt;br /&gt;
|-&lt;br /&gt;
| WriteBattClock() || Set the BattClock to the number of seconds you pass it relative to 12:00 AM, January 1, 1978.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=GadTools_Gadgets&amp;diff=12529</id>
		<title>GadTools Gadgets</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=GadTools_Gadgets&amp;diff=12529"/>
		<updated>2025-01-26T19:25:24Z</updated>

		<summary type="html">&lt;p&gt;Costel Mincea: Text replacement - &amp;quot;&amp;lt;syntaxhighlight&amp;gt;&amp;quot; to &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
{{Note|text=GadTools-based GUIs are largely obsolete. Prefer to use a modern BOOPSI-based GUI system instead.}}&lt;br /&gt;
&lt;br /&gt;
The heart of GadTools is in its ability to easily create and manipulate a sophisticated and varied array of gadgets. GadTools supports the following kinds of gadgets:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Standard Gadget Types Supported by the GadTools Library&lt;br /&gt;
! Gadget Type&lt;br /&gt;
! Description or Example Usage&lt;br /&gt;
|-&lt;br /&gt;
| Button&lt;br /&gt;
| Familiar action gadgets, such as &amp;quot;OK&amp;quot; or &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| String&lt;br /&gt;
| For text entry.&lt;br /&gt;
|-&lt;br /&gt;
| Integer&lt;br /&gt;
| For numeric entry.&lt;br /&gt;
|-&lt;br /&gt;
| Checkboxes&lt;br /&gt;
| For on/off items.&lt;br /&gt;
|-&lt;br /&gt;
| Mutually exclusive&lt;br /&gt;
| Radio buttons, select one choice among several.&lt;br /&gt;
|-&lt;br /&gt;
| Cycle&lt;br /&gt;
| Multiple-choice, pick one of a small number of choices.&lt;br /&gt;
|-&lt;br /&gt;
| Sliders&lt;br /&gt;
| To indicate a level within a range.&lt;br /&gt;
|-&lt;br /&gt;
| Scrollers&lt;br /&gt;
| To indicate a position in a list or area.&lt;br /&gt;
|-&lt;br /&gt;
| Listviews&lt;br /&gt;
| Scrolling lists of text.&lt;br /&gt;
|-&lt;br /&gt;
| Palette&lt;br /&gt;
| Color selection.&lt;br /&gt;
|-&lt;br /&gt;
| Text-display&lt;br /&gt;
| Read-only text.&lt;br /&gt;
|-&lt;br /&gt;
| Numeric-display&lt;br /&gt;
| Read-only numbers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
GadTools gadget handling consists of a body of routines to create, manage and delete any of the 12 kinds of standard gadgets listed in the previous table, such as buttons, sliders, mutually exclusive buttons and scrolling lists.&lt;br /&gt;
&lt;br /&gt;
To illustrate the flexibility, power and simplicity that GadTools offers, consider the GadTools slider gadget. This gadget is used to indicate and control the level of something, for example volume, speed or color intensity. Without GadTools, applications have to deal directly with Intuition proportional and their arcane variables, such as HorizBody to control the slider knob&#039;s size and HorizPot to control the knob&#039;s position. Using the GadTools slider allows direct specification of the minimum and maximum levels of the slider, as well as its current level. For example, a color slider might have a minimum level of 0, a maximum level of 15 and a current level of 11.&lt;br /&gt;
&lt;br /&gt;
To simplify event-processing for the slider, GadTools only sends the application a message when the knob has moved far enough to cause the slider level, as expressed in application terms, to change. If a user were to slowly drag the knob of this color slider all the way to the right, the program will only hear messages for levels 12, 13, 14 and 15, with an optional additional message when the user releases the mouse-button.&lt;br /&gt;
&lt;br /&gt;
Changing the current level of the slider from within the program is as simple as specifying the new level in a function call. For instance, the application might set the slider&#039;s value to 5.&lt;br /&gt;
&lt;br /&gt;
As a final point, the slider is very well-behaved. When the user releases the mouse-button, the slider immediately snaps to the centered position for the level. If a user sets their background color to light gray, which might have red = green = blue = 10, all three color sliders will have their knobs at precisely the same relative position, instead of anywhere in the range that means &amp;quot;ten&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== The NewGadget Structure ====&lt;br /&gt;
&lt;br /&gt;
For most gadgets, the NewGadget structure is used to specify its common attributes. Additional attributes that are unique to specific kinds of gadgets are specified as tags sent to the CreateGadget() function (described below).&lt;br /&gt;
&lt;br /&gt;
The NewGadget structure is defined in &amp;amp;lt;intuition/gadtools.h&amp;amp;gt; as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct NewGadget&lt;br /&gt;
    {&lt;br /&gt;
    WORD ng_LeftEdge, ng_TopEdge;&lt;br /&gt;
    WORD ng_Width, ng_Height;&lt;br /&gt;
    UBYTE *ng_GadgetText;&lt;br /&gt;
    struct TextAttr *ng_TextAttr;&lt;br /&gt;
    UWORD ng_GadgetID;&lt;br /&gt;
    ULONG ng_Flags;&lt;br /&gt;
    APTR ng_VisualInfo;&lt;br /&gt;
    APTR ng_UserData;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fields of the NewGadget structure are used as follows:&lt;br /&gt;
&lt;br /&gt;
; ng_LeftEdge, ng_TopEdge&lt;br /&gt;
: Define the position of the gadget being created.&lt;br /&gt;
&lt;br /&gt;
; ng_Width, ng_Height&lt;br /&gt;
: Define the size of the gadget being created.&lt;br /&gt;
&lt;br /&gt;
; ng_GadgetText&lt;br /&gt;
: Most gadgets have an associated label, which might be the text in a button or beside a checkmark. This field contains a pointer to the appropriate string. Note that only the pointer to the text is copied, the text itself is not. The string supplied must remain constant and valid for the life of the gadget.&lt;br /&gt;
&lt;br /&gt;
; ng_TextAttr&lt;br /&gt;
: The application must specify a font to use for the label and any other text that may be associated with the gadget.&lt;br /&gt;
&lt;br /&gt;
: Used to describe general aspects of the gadget, which includes where the label is to be placed and whether the label should be rendered in the highlight color. The label may be positioned on the left side, the right side, centered above, centered below or dead-center on the gadget. For most gadget kinds, the label is placed on the left side by default, exceptions will be noted.&lt;br /&gt;
&lt;br /&gt;
; ng_GadgetID, ng_Flags, ng_UserData&lt;br /&gt;
: These user fields are copied into the resulting Gadget structure.&lt;br /&gt;
&lt;br /&gt;
; ng_VisualInfo&lt;br /&gt;
: This field must contain a pointer to an instance of the VisualInfo structure, which contains information needed to create and render GadTools gadgets. The VisualInfo structure itself is private to GadTools and subject to change. Use the specialized GadTools functions for accessing the VisualInfo pointer, defined below. Never access or modify fields within this structure.&lt;br /&gt;
&lt;br /&gt;
==== Creating Gadgets ====&lt;br /&gt;
&lt;br /&gt;
The main call used to create a gadget with GadTools is CreateGadget(). This function can be used to create a single gadget or it can be called repeatedly to create a linked list of gadgets. It takes three arguments followed by a set of tags:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Gadget *CreateGadget( ULONG kind, struct Gadget *prevgad, struct NewGadget *newgad,&lt;br /&gt;
                             struct TagItem *taglist);&lt;br /&gt;
struct Gadget *CreateGadgetA(ULONG kind, struct Gadget *prevgad, struct NewGadget *newgad,&lt;br /&gt;
                             Tag tag1, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the kind argument to one of the 12 gadget types supported by GadTools. Set the prevgad argument to the gadget address returned by CreateContext() if this is the first (or only) gadget in the list. Subsequent calls to CreateGadget() can be used to create and link gadgets together in a list in which case the prevgad argument is set to the address of the gadget returned by the preceding call to CreateGadget().&lt;br /&gt;
&lt;br /&gt;
Set the newgad argument to the address of the NewGadget structure describing the gadget to be created and set any special attributes for this gadget type using the tag arguments, tag1 or taglist. For instance, the following code fragment might be used to create the color slider discussed earlier:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
slidergad = IGadTools-&amp;gt;CreateGadget(SLIDER_KIND, newgadget, prevgad,&lt;br /&gt;
    GTSL_Min, 0,&lt;br /&gt;
    GTSL_Max, 15,&lt;br /&gt;
    GTSL_Level, 11,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CreateGadget() typically allocates and initializes all the necessary Intuition structures, including in this case the Gadget, IntuiText and PropInfo structures, as well as certain buffers. For more about these underlying structures, see [[Intuition_Gadgets|Intuition Gadgets]].&lt;br /&gt;
&lt;br /&gt;
Since CreateGadget() is a tag-based function, it is easy to add more tags to get a fancier gadget. For example, GadTools can optionally display the running level beside the slider. The caller must supply a printf()-style formatting string and the maximum length that the string will resolve to when the number is inserted:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
slidergad = IGadTools-&amp;gt;CreateGadget(SLIDER_KIND, newgadget, prevgad,&lt;br /&gt;
    GTSL_Min, 0,&lt;br /&gt;
    GTSL_Max, 15,&lt;br /&gt;
    GTSL_Level, 11,&lt;br /&gt;
    GTSL_LevelFormat, &amp;quot;%2ld&amp;quot;, /* printf()-style formatting string */&lt;br /&gt;
    GTSL_MaxLevelLen, 2,      /* maximum length of string         */&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The level, 0 to 15 in this example, would then be displayed beside the slider. The formatting string could instead be &amp;quot;%2ld/15&amp;quot; so the level would be displayed as &amp;quot;0/15&amp;quot; through &amp;quot;15/15&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== Handling Gadget Messages ====&lt;br /&gt;
&lt;br /&gt;
GadTools gadgets follow the same input model as other Intuition components. When the user operates a GadTools gadget, Intuition notifies the application about the input event by sending an IntuiMessage. The application can get these messages at the Window.UserPort. However GadTools gadgets use different message handling functions to get and reply these messages. Instead of the Exec functions GetMsg() and ReplyMsg(), applications should get and reply these messages through a pair of special GadTools functions, GT_GetIMsg() and GT_ReplyIMsg().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IntuiMessage *GT_GetIMsg(struct MsgPort *iport);&lt;br /&gt;
VOID GT_ReplyIMsg(struct IntuiMessage *imsg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For GT_GetIMsg(), the iport argument should be set to the window&#039;s UserPort. For GT_ReplyIMsg(), the imsg argument should be set to a pointer to the IntuiMessage returned by GT_GetIMsg().&lt;br /&gt;
&lt;br /&gt;
These functions ensure that the application only sees the gadget events that concern it and in a desirable form. For example, with a GadTools slider gadget, a message only gets through to the application when the slider&#039;s level actually changes and that level can be found in the IntuiMessage&#039;s Code field:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
imsg = IGadTools-&amp;gt;GT_GetIMsg(win-&amp;gt;UserPort);&lt;br /&gt;
object = imsg-&amp;gt;IAddress;&lt;br /&gt;
class = imsg-&amp;gt;Class;&lt;br /&gt;
code = imsg-&amp;gt;Code;&lt;br /&gt;
IGadTools-&amp;gt;GT_ReplyIMsg(imsg);&lt;br /&gt;
switch (class)&lt;br /&gt;
    {&lt;br /&gt;
    case IDCMP_MOUSEMOVE:&lt;br /&gt;
        if (object == slidergad)&lt;br /&gt;
            {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Slider at level %ld\n&amp;quot;, code);&lt;br /&gt;
            }&lt;br /&gt;
        ...&lt;br /&gt;
        break;&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In general, the IntuiMessages received from GadTools contain more information in the Code field than is found in regular Intuition gadget messages. Also, when dealing with GadTools a lot of messages (mostly IDCMP_MOUSEMOVEs) do not have to be processed by the application. These are two reasons why dealing with GadTools gadgets is much easier than dealing with regular Intuition gadgets. Unfortunately this processing cannot happen magically, so applications must use GT_GetIMsg() and GT_ReplyIMsg() where they would normally have used GetMsg() and ReplyMsg().&lt;br /&gt;
&lt;br /&gt;
GT_GetIMsg() actually calls GetMsg() to remove a message from the specified window&#039;s UserPort. If the message pertains to a GadTools gadget then some dispatching code in GadTools will be called to process the message. What the program will receive from GT_GetIMsg() is actually a copy of the real IntuiMessage, possibly with some supplementary information from GadTools, such as the information typically found in the Code field.&lt;br /&gt;
&lt;br /&gt;
The GT_ReplyIMsg() call will take care of cleaning up and replying to the real IntuiMessage.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=When an IDCMP_MOUSEMOVE message is received from a GadTools gadget, GadTools arranges to have the gadget&#039;s pointer in the IAddress field of the IntuiMessage. While this is extremely convenient, it is also untrue of messages from regular Intuition gadgets (described in [[Intuition_Gadgets|Intuition Gadgets]]). Do not make the mistake of assuming it to be true.}}&lt;br /&gt;
&lt;br /&gt;
This description of the inner workings of GT_GetIMsg() and GT_ReplyIMsg() is provided for understanding only; it is crucial that the program make no assumptions or interpretations about the real IntuiMessage. Any such inferences are not likely to hold true in the future. See the section on documented side-effects for more information.&lt;br /&gt;
&lt;br /&gt;
=== IDCMP Flags ===&lt;br /&gt;
&lt;br /&gt;
The various GadTools gadget types require certain classes of IDCMP messages in order to work. Applications specify these IDCMP classes when the window is opened or later with ModifyIDCMP() (see [[Intuition_Windows|Intuition Windows]] for more on this). Each kind of GadTools gadget requires one or more of these IDCMP classes: IDCMP_GADGETUP, IDCMP_GADGETDOWN, IDCMP_MOUSEMOVE, IDCMP_MOUSEBUTTONS and IDCMP_INTUITICKS. As a convenience, the IDCMP classes required by each kind of gadget are defined in &amp;amp;lt;libraries/gadtools.h&amp;amp;gt;. For example, SLIDERIDCMP is defined to be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#define SLIDERIDCMP (IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_MOUSEMOVE)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Always OR the IDCMP Flag Bits|text=When specifying the IDCMP classes for a window, never add the flags together, always OR the bits together. Since many of the GadTools IDCMP constants have multiple bits set, adding the values will not lead to the proper flag combination.}}&lt;br /&gt;
&lt;br /&gt;
If a certain kind of GadTools gadget is used, the window must use all IDCMP classes required by that kind of gadget. Do not omit any that are given for that class, even if the application does require the message type.&lt;br /&gt;
&lt;br /&gt;
Because of the way GadTools gadgets are implemented, programs that use them always require notification about window refresh events. Even if the application performs no rendering of its own, it may not use the WFLG_NOCAREREFRESH window flag and must always set IDCMP_REFRESHWINDOW. See the section on &amp;quot;Gadget Refresh Functions&amp;quot; below for more on this.&lt;br /&gt;
&lt;br /&gt;
=== Freeing Gadgets ===&lt;br /&gt;
&lt;br /&gt;
After closing the window, the gadgets allocated using CreateGadget() must be released. FreeGadgets() is a simple call that will free all the GadTools gadgets that it finds, beginning with the gadget whose pointer is passed as an argument.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID FreeGadgets( struct Gadget *gad );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gad argument is a pointer to the first gadget to be freed. It is safe to call FreeGadgets() with a NULL gadget pointer, the function will then return immediately. Before calling FreeGadgets(), the application must first either remove the gadgets or close the window.&lt;br /&gt;
&lt;br /&gt;
When the gadget passed to FreeGadgets() is the first gadget in a linked list, the function frees all the GadTools gadgets on the list without patching pointers or trying to maintain the integrity of the list. Any non-GadTools gadgets found on the list will not be freed, hence the result will not necessarily form a nice list since any intervening GadTools gadgets will be gone.&lt;br /&gt;
&lt;br /&gt;
See the section on &amp;quot;Creating Gadget Lists&amp;quot; for more information on using linked lists of gadgets.&lt;br /&gt;
&lt;br /&gt;
=== Simple GadTools Gadget Example ===&lt;br /&gt;
&lt;br /&gt;
The example listed here shows how to use the NewGadget structure and the GadTools library functions discussed above to create a simple button gadget.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* simplegtgadget.c&lt;br /&gt;
Simple example of a GadTools gadget.&lt;br /&gt;
*/&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Gadget defines of our choosing, to be used as GadgetID&#039;s. */&lt;br /&gt;
#define MYGAD_BUTTON    (4)&lt;br /&gt;
&lt;br /&gt;
VOID process_window_events(struct Window *);&lt;br /&gt;
VOID gadtoolsWindow(VOID);&lt;br /&gt;
&lt;br /&gt;
struct TextAttr Topaz80 = { &amp;quot;topaz.font&amp;quot;, 8, 0, 0, };&lt;br /&gt;
&lt;br /&gt;
struct InuitionIFace *IIntuition;&lt;br /&gt;
struct GadToolsIFace *IGadTools;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Open all libraries and run.  Clean up when finished or on error..&lt;br /&gt;
*/&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *GadToolsBase = IExec-&amp;gt;OpenLibrary(&amp;quot;gadtools.library&amp;quot;, 50);&lt;br /&gt;
  IGadTools = (struct GadToolsIFace*)IExec-&amp;gt;GetInterface(GadToolsBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IGadTools != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    gadtoolsWindow();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGadTools);&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(GadToolsBase);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Prepare for using GadTools, set up gadgets and open window.&lt;br /&gt;
** Clean up and when done or on error.&lt;br /&gt;
*/&lt;br /&gt;
VOID gadtoolsWindow(VOID)&lt;br /&gt;
{&lt;br /&gt;
struct Screen    *mysc;&lt;br /&gt;
struct Window    *mywin;&lt;br /&gt;
struct Gadget    *glist, *gad;&lt;br /&gt;
struct NewGadget ng;&lt;br /&gt;
void             *vi;&lt;br /&gt;
&lt;br /&gt;
glist = NULL;&lt;br /&gt;
&lt;br /&gt;
if ( (mysc = IIntuition-&amp;gt;LockPubScreen(NULL)) != NULL )&lt;br /&gt;
    {&lt;br /&gt;
    if ( (vi = IGadTools-&amp;gt;GetVisualInfo(mysc, TAG_END)) != NULL )&lt;br /&gt;
        {&lt;br /&gt;
        /* GadTools gadgets require this step to be taken */&lt;br /&gt;
        gad = IGadTools-&amp;gt;CreateContext(&amp;amp;glist);&lt;br /&gt;
&lt;br /&gt;
        /* create a button gadget centered below the window title */&lt;br /&gt;
        ng.ng_TextAttr   = &amp;amp;Topaz80;&lt;br /&gt;
        ng.ng_VisualInfo = vi;&lt;br /&gt;
        ng.ng_LeftEdge   = 150;&lt;br /&gt;
        ng.ng_TopEdge    = 20 + mysc-&amp;gt;WBorTop + (mysc-&amp;gt;Font-&amp;gt;ta_YSize + 1);&lt;br /&gt;
        ng.ng_Width      = 100;&lt;br /&gt;
        ng.ng_Height     = 12;&lt;br /&gt;
        ng.ng_GadgetText = &amp;quot;Click Here&amp;quot;;&lt;br /&gt;
        ng.ng_GadgetID   = MYGAD_BUTTON;&lt;br /&gt;
        ng.ng_Flags      = 0;&lt;br /&gt;
        gad = IGadTools-&amp;gt;CreateGadget(BUTTON_KIND, gad, &amp;amp;ng, TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (gad != NULL)&lt;br /&gt;
            {&lt;br /&gt;
            if ( (mywin = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                    WA_Title,     &amp;quot;GadTools Gadget Demo&amp;quot;,&lt;br /&gt;
                    WA_Gadgets,   glist,      WA_AutoAdjust,    TRUE,&lt;br /&gt;
                    WA_Width,       400,      WA_InnerHeight,    100,&lt;br /&gt;
                    WA_DragBar,    TRUE,      WA_DepthGadget,   TRUE,&lt;br /&gt;
                    WA_Activate,   TRUE,      WA_CloseGadget,   TRUE,&lt;br /&gt;
                    WA_IDCMP, IDCMP_CLOSEWINDOW |&lt;br /&gt;
                              IDCMP_REFRESHWINDOW | BUTTONIDCMP,&lt;br /&gt;
                    WA_PubScreen,   mysc,&lt;br /&gt;
                    TAG_END)) != NULL )&lt;br /&gt;
                {&lt;br /&gt;
                IGadTools-&amp;gt;GT_RefreshWindow(mywin, NULL);&lt;br /&gt;
&lt;br /&gt;
                process_window_events(mywin);&lt;br /&gt;
&lt;br /&gt;
                IIntuition-&amp;gt;CloseWindow(mywin);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        /* FreeGadgets() must be called after the context has been&lt;br /&gt;
        ** created.  It does nothing if glist is NULL&lt;br /&gt;
        */&lt;br /&gt;
        IGadTools-&amp;gt;FreeGadgets(glist);&lt;br /&gt;
        IGadTools-&amp;gt;FreeVisualInfo(vi);&lt;br /&gt;
        }&lt;br /&gt;
    IIntuition-&amp;gt;UnlockPubScreen(NULL, mysc);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Standard message handling loop with GadTools message handling functions&lt;br /&gt;
** used (GT_GetIMsg() and GT_ReplyIMsg()).&lt;br /&gt;
*/&lt;br /&gt;
VOID process_window_events(struct Window *mywin)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *imsg;&lt;br /&gt;
struct Gadget *gad;&lt;br /&gt;
BOOL  terminated = FALSE;&lt;br /&gt;
&lt;br /&gt;
while (!terminated)&lt;br /&gt;
    {&lt;br /&gt;
    IExec-&amp;gt;Wait (1 &amp;lt;&amp;lt; mywin-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
    /* Use GT_GetIMsg() and GT_ReplyIMsg() for handling */&lt;br /&gt;
    /* IntuiMessages with GadTools gadgets.             */&lt;br /&gt;
    while ((!terminated) &amp;amp;&amp;amp; (imsg = IGadTools-&amp;gt;GT_GetIMsg(mywin-&amp;gt;UserPort)))&lt;br /&gt;
        {&lt;br /&gt;
        /* GT_ReplyIMsg() at end of loop */&lt;br /&gt;
&lt;br /&gt;
        switch (imsg-&amp;gt;Class)&lt;br /&gt;
            {&lt;br /&gt;
            case IDCMP_GADGETUP:       /* Buttons only report GADGETUP */&lt;br /&gt;
                gad = (struct Gadget *)imsg-&amp;gt;IAddress;&lt;br /&gt;
                if (gad-&amp;gt;GadgetID == MYGAD_BUTTON)&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Button was pressed.\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case IDCMP_CLOSEWINDOW:&lt;br /&gt;
                terminated = TRUE;&lt;br /&gt;
                break;&lt;br /&gt;
            case IDCMP_REFRESHWINDOW:&lt;br /&gt;
                /* This handling is REQUIRED with GadTools. */&lt;br /&gt;
                IGadTools-&amp;gt;GT_BeginRefresh(mywin);&lt;br /&gt;
                IGadTools-&amp;gt;GT_EndRefresh(mywin, TRUE);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        /* Use the toolkit message-replying function here... */&lt;br /&gt;
        IGadTools-&amp;gt;GT_ReplyIMsg(imsg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying Gadgets ===&lt;br /&gt;
&lt;br /&gt;
The attributes of a gadget are set up when the gadget is created. Some of these attributes can be changed later by using the GT_SetGadgetAttrs() function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID GT_SetGadgetAttrs (struct Gadget *gad, struct Window *win, struct Requester *req, Tag tag1, ... );&lt;br /&gt;
VOID GT_SetGadgetAttrsA(struct Gadget *gad, struct Window *win, struct Requester *req, struct TagItem *taglist);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gad argument specifies the gadget to be changed while the win argument specifies the window the gadget is in. Currently, the req argument is unused and must be set to NULL.&lt;br /&gt;
&lt;br /&gt;
The gadget attributes are changed by passing tag arguments to these functions. The tag arguments can be either a set of TagItems on the stack for GT_SetGadgetAttrs(), or a pointer to an array of TagItems for GT_SetGadgetAttrsA(). The tag items specify the attributes that are to be changed for the gadget. Keep in mind though that not every gadget attribute can be modified this way.&lt;br /&gt;
&lt;br /&gt;
For example, in the slider gadget presented earlier, the level-formatting string may not be changed after the gadget is created. However, the slider&#039;s level may be changed to 5 as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
IGadTools-&amp;gt;GT_SetGadgetAttrs(slidergad, win, req,&lt;br /&gt;
    GTSL_Level, 5,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are some other example uses of GT_SetGadgetAttrs() to change gadget attributes after it is created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Disable a button gadget */&lt;br /&gt;
IGadTools-&amp;gt;GT_SetGadgetAttrs(buttongad, win, NULL,&lt;br /&gt;
                             GA_Disabled, TRUE,&lt;br /&gt;
                             TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Change a slider&#039;s range to be 1 to 100, currently at 50 */&lt;br /&gt;
IGadTools-&amp;gt;GT_SetGadgetAttrs(slidergad, win, NULL,&lt;br /&gt;
                             GTSL_Min, 1,&lt;br /&gt;
                             GTSL_Max, 100,&lt;br /&gt;
                             GTSL_Level, 50,&lt;br /&gt;
                             TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Add a node to the head of listview&#039;s list, and&lt;br /&gt;
   make it the selected one */&lt;br /&gt;
IGadTools-&amp;gt;GT_SetGadgetAttrs(listviewgad, win, NULL,&lt;br /&gt;
                             /* detach list before modifying */&lt;br /&gt;
                             GTLV_Labels, ~0,&lt;br /&gt;
                             TAG_END);&lt;br /&gt;
IExec-&amp;gt;AddHead(&amp;amp;lvlabels, &amp;amp;newnode);&lt;br /&gt;
IGadTools-&amp;gt;GT_SetGadgetAttrs(listviewgad, win, NULL,&lt;br /&gt;
                             /* re-attach list */&lt;br /&gt;
                             GTLV_Labels, &amp;amp;amp;lvlabels,&lt;br /&gt;
                             GTLV_Selected, 0,&lt;br /&gt;
                             TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When changing a gadget using these functions, the gadget will automatically update its visuals. No refresh is required, nor should any refresh call be performed.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=The GT_SetGadgetAttrs() functions may not be called inside of a GT_BeginRefresh()/GT_EndRefresh() pair. This is true of Intuition gadget functions generally, including those discussed in [[Intuition_Gadgets|Intuition Gadgets]].}}&lt;br /&gt;
&lt;br /&gt;
In the sections that follow all the possible attributes for each kind of gadget are discussed. The tags are also described in the Autodocs for GT_SetGadgetAttrs() in the SDK.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Important|text=Tags that can only be sent to CreateGadget() and not to GT_SetGadgetAttrs() will be marked as &#039;&#039;create only&#039;&#039; in the discussion that follows. Those that are valid parameters to both functions will be marked as &#039;&#039;create and set&#039;&#039;.}}&lt;br /&gt;
&lt;br /&gt;
=== The Kinds of GadTools Gadgets ===&lt;br /&gt;
&lt;br /&gt;
This section discusses the unique features of each kind of gadget supported by the GadTools library.&lt;br /&gt;
&lt;br /&gt;
==== Button Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Button gadgets (BUTTON_KIND) are perhaps the simplest kind of GadTools gadget. Button gadgets may be used for objects like the &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot; buttons in requesters. GadTools will create a hit-select button with a raised bevelled border. The label supplied will be centered on the button&#039;s face. Since the label is not clipped, be sure that the gadget is large enough to contain the text supplied.&lt;br /&gt;
&lt;br /&gt;
Button gadgets recognize only one tag:&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable or ghost the button gadget, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
When the user selects a button gadget, the program will receive an IDCMP_GADGETUP event.&lt;br /&gt;
&lt;br /&gt;
If clicking on a button causes a requester to appear, for example a button that brings up a color requester, then the button text should end in ellipsis (...), as in &amp;quot;Quit...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Text-Entry and Number-Entry Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Text-entry (STRING_KIND) and number-entry (INTEGER_KIND) gadgets are fairly typical Intuition string gadgets. The typing area is contained by a border which is a raised ridge.&lt;br /&gt;
&lt;br /&gt;
Text-entry gadgets accept the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTST_String (STRPTR)&lt;br /&gt;
: A pointer to the string to be placed into the text-entry gadget buffer or NULL to get an empty text-entry gadget. The string itself is actually copied into the gadget&#039;s buffer. The default is NULL. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTST_MaxChars (UWORD)&lt;br /&gt;
: The maximum number of characters that the text-entry gadget should hold. The string buffer that gets created for the gadget will actually be one bigger than this number, in order to hold the trailing NULL. The default is 64. (Create only.)&lt;br /&gt;
&lt;br /&gt;
Number-entry gadgets accept the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTIN_Number (ULONG)&lt;br /&gt;
: The number to be placed into the number-entry gadget. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTIN_MaxChars (UWORD)&lt;br /&gt;
: The maximum number of digits that the number-entry gadget should hold. The string buffer that gets created for the gadget will actually be one bigger than this, in order to hold the trailing NULL. The default is 10. (Create only.)&lt;br /&gt;
&lt;br /&gt;
Both text-entry and number-entry gadgets, which are collectively called string gadgets, accept these common tags:&lt;br /&gt;
&lt;br /&gt;
; STRINGA_Justification&lt;br /&gt;
: This attribute controls the placement of the string or number within its box and can be one of GACT_STRINGLEFT, GACT_STRINGRIGHT or GACT_STRINGCENTER. The default is GACT_STRINGLEFT. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; STRINGA_ReplaceMode (BOOL)&lt;br /&gt;
: Set STRINGA_ReplaceMode to TRUE to get a string gadget which is in replace-mode, as opposed to auto-insert mode. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the string gadget, otherwise to FALSE. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; STRINGA_ExitHelp (BOOL)&lt;br /&gt;
: Set this attribute to TRUE if the application wants to hear the Help key from within this string gadget. This feature allows the program to hear the press of the Help key in all cases. If TRUE, pressing the help key while this gadget is active will terminate the gadget and send a message. The program will receive an IDCMP_GADGETUP message having a Code value of 0x5F, the rawkey code for Help. Typically, the program will want to reactivate the gadget after performing the help-display. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_TabCycle (BOOL)&lt;br /&gt;
: If the user types Tab or Shift Tab into a GA_TabCycle gadget, Intuition will activate the next or previous such gadget in sequence. This gives the user easy keyboard control over which text-entry or number-entry gadget is active. Tab moves to the next GA_TabCycle gadget in the gadget list and Shift Tab moves to the previous one. When the user presses Tab or Shift Tab, Intuition will deactivate the gadget and send this program an IDCMP_GADGETUP message with the code field set to 0x09, the ASCII value for a tab. Intuition will then activate the next indicated gadget. Check the shift bits of the qualifier field to learn if Shift Tab was typed. The ordering of the gadgets may only be controlled by the order in which they were added to the window. For special cases, for example, if there is only one string gadget in the window, this feature can be suppressed by specifying the tagitem pair {GA_TabCycle, FALSE}. The default is TRUE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTST_EditHook (struct Hook *)&lt;br /&gt;
: Pointer to a custom editing hook for this string or integer gadget. See [[Intuition_Gadgets|Intuition Gadgets]] for more information on string gadget edit-hooks.&lt;br /&gt;
&lt;br /&gt;
As with all Intuition string gadgets, the program will receive an IDCMP_GADGETUP message only when the user presses Enter, Return, Help, Tab or Shift Tab while typing in the gadget. Note that, like Intuition string gadgets, the program will not hear anything if the user deactivates the string gadget by clicking elsewhere. Therefore, it is a good idea to always check the string gadget&#039;s buffer before using its contents, instead of just tracking its value as IDCMP_GADGETUP messages are received for this gadget.&lt;br /&gt;
&lt;br /&gt;
Be sure the code is designed so that nothing drastic happens, like closing a requester or opening a file, if the IDCMP_GADGETUP message has a non-zero Code field; the program will want to handle the Tab and Help cases intelligently.&lt;br /&gt;
&lt;br /&gt;
To read the string gadget&#039;s buffer, look at the Gadget&#039;s StringInfo Buffer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
((struct StringInfo *)gad-&amp;gt;SpecialInfo)-&amp;gt;Buffer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To determine the value of an integer gadget, look at the Gadget&#039;s StringInfo LongInt in the same way.&lt;br /&gt;
&lt;br /&gt;
Always use the GTST_String or GTIN_Number tags to set these values. Never write to the StringInfo-&amp;amp;gt;Buffer or StringInfo-&amp;amp;gt;LongInt fields directly.&lt;br /&gt;
&lt;br /&gt;
GadTools string and integer gadgets do not directly support the GA_Immediate property (which would cause Intuition to send an IDCMP_GADGETDOWN event when such a gadget is first selected). However, this property can be very important. Therefore, the following technique can be used to enable this property.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=Note that the technique shown here relies on directly setting flags in a GadTools gadget; this is not normally allowed since it hinders future compatibility. Do not attempt to change other flags or properties of GadTools gadgets except through the defined interfaces of CreateGadgetA() and GT_SetGadgetAttrsA(). Directly modifying flags or properties is legal only when officially sanctioned by the AmigaOS development team.}}&lt;br /&gt;
&lt;br /&gt;
To get the GA_Immediate property, pass the {GA_Immediate, TRUE} tag to CreateGadgetA().&lt;br /&gt;
&lt;br /&gt;
==== Checkbox Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Checkboxes (CHECKBOX_KIND) are appropriate for presenting options which may be turned on or off. This kind of gadget consists of a raised box which contains a checkmark if the option is selected or is blank if the option is not selected. Clicking on the box toggles the state of the checkbox.&lt;br /&gt;
&lt;br /&gt;
The width and height of a checkbox are by default not scaled and fixed (CHECKBOXWIDTH by CHECKBOXHEIGHT). To scale a checkbox gadget, set the GTCB_Scaled tag to TRUE, and set the desired width and height in the ng_Width and ng_Height fields of the NewGadget structure.&lt;br /&gt;
&lt;br /&gt;
The checkbox may be controlled with the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTCB_Checked (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to set the gadget&#039;s state to &#039;&#039;checked&#039;&#039;. Set it to FALSE to mark the gadget as &#039;&#039;unchecked&#039;&#039;. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTCB_Scaled (BOOL)&lt;br /&gt;
: Scales the checkbox gadget if set to TRUE. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the checkbox, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
When the user selects a checkbox, the program will receive an IntuiMessage with a class of IDCMP_GADGETUP. As this gadget always toggles, the program can easily track the state of the gadget. Feel free to read the gadget-&amp;amp;gt;Flags GFLG_SELECTED bit. Note, however, that the Gadget structure itself is not synchronized to the IntuiMessages received. If the user clicks a second time, the GFLG_SELECTED bit can toggle again before the program gets a chance to read it. This is true of any of the dynamic fields of the Gadget structure, and is worth being aware of, although only rarely will an application have to account for it.&lt;br /&gt;
&lt;br /&gt;
==== Mutually-Exclusive Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Use mutually exclusive gadgets (MX_KIND), or &#039;&#039;radio buttons&#039;&#039;, when the user must choose only one option from a short list of possibilities. Mutually exclusive gadgets are appropriate when there are a small number of choices, perhaps eight or less.&lt;br /&gt;
&lt;br /&gt;
A set of mutually exclusive gadgets consists of a list of labels and beside each label, a small raised oval that looks like a button. Exactly one of the ovals is recessed and highlighted, to indicate the selected choice. The user can pick another choice by clicking on any of the raised ovals. This choice will become active and the previously selected choice will become inactive. That is, the selected oval will become recessed while the previous one will pop out, like the buttons on a car radio.&lt;br /&gt;
&lt;br /&gt;
Mutually exclusive gadgets recognize these tags:&lt;br /&gt;
&lt;br /&gt;
; GTMX_Labels (STRPTR *)&lt;br /&gt;
: A NULL-pointer-terminated array of strings which are to be the labels beside each choice in the set of mutually exclusive gadgets. This array determines how many buttons are created. This array must be supplied to CreateGadget() and may not be changed. The strings themselves must remain valid for the lifetime of the gadget. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTMX_Active (UWORD)&lt;br /&gt;
: The ordinal number, counting from zero, of the active choice of the set of mutually exclusive gadgets. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTMX_Spacing (UWORD)&lt;br /&gt;
: The amount of space, in pixels, that will be placed between successive choices in a set of mutually exclusive gadgets. The default is one. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTMX_Scaled (BOOL)&lt;br /&gt;
: Indicates whether the mutually exclusive gadget should be scaled or not. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTMX_TitlePlace (UWORD)&lt;br /&gt;
: Where to place the title of the mutually exclusive gadget. The gadget obtains its title from the ng_GadgetText field of the NewGadget structure. The default is no title. (Create only.)&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| PLACETEXT_LEFT || Right-align text on left side&lt;br /&gt;
|-&lt;br /&gt;
| PLACETEXT_RIGHT || Left-align text on right side&lt;br /&gt;
|-&lt;br /&gt;
| PLACETEXT_ABOVE || Center text above&lt;br /&gt;
|-&lt;br /&gt;
| PLACETEXT_BELOW || Center text below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
When the user selects a new choice from a set of mutually exclusive gadgets, the program will receive an IDCMP_GADGETDOWN IntuiMessage. Look in the IntuiMessage&#039;s Code field for the ordinal number of the new active selection.&lt;br /&gt;
&lt;br /&gt;
The ng_GadgetText field of the NewGadget structure is used to display the title for mutually exclusive gadgets. The text position specified in ng_Flags determines whether the item labels are placed to the left or the right of the radio buttons themselves. By default, the labels appear on the left. Do not specify PLACETEXT_ABOVE, PLACETEXT_BELOW or PLACETEXT_IN for this kind of gadget.&lt;br /&gt;
&lt;br /&gt;
To scale a mutually exclusive gadget, set the GTMX_Scaled tag to TRUE, and set the desired width and height in the ng_Width and ng_Height fields of the NewGadget structure. The default value of GTMX_Scaled is FALSE, meaning use the default size of MXWIDTH by MXHEIGHT respectively. If you scale mutually exclusive gadgets, you must also set the&lt;br /&gt;
GTMX_Spacing tag to NewGadget.ng_TextAttr-&amp;gt;ta_YSize + 1 to properly space the buttons with respect to the font.&lt;br /&gt;
&lt;br /&gt;
==== Cycle Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Like mutually exclusive gadgets, cycle gadgets (CYCLE_KIND) allow the user to choose exactly one option from among several.&lt;br /&gt;
&lt;br /&gt;
The cycle gadget appears as a raised rectangular button with a vertical divider near the left side. A circular arrow glyph appears to the left of the divider, while the current choice appears to the right. Clicking on the cycle gadget advances to the next choice, while shift-clicking on it changes it to the previous choice.&lt;br /&gt;
&lt;br /&gt;
Cycle gadgets are more compact than mutually exclusive gadgets, since only the current choice is displayed. They are preferable to mutually exclusive gadgets when a window needs to have several such gadgets as in the PrinterGfx Preferences editor, or when there is a medium number of choices. If the number of choices is much more than about a dozen, it may become too frustrating and inefficient for the user to find the desired choice. In that case, use a listview (scrolling list) instead.&lt;br /&gt;
&lt;br /&gt;
The tags recognized by cycle gadgets are:&lt;br /&gt;
&lt;br /&gt;
; GTCY_Labels (STRPTR *)&lt;br /&gt;
: Like GTMX_Labels, this tag is associated with a NULL-pointer-terminated array of strings which are the choices that this gadget allows. This array must be supplied to CreateGadget() and can be changed. The strings themselves must remain valid for the lifetime of the gadget. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTCY_Active (UWORD)&lt;br /&gt;
: The ordinal number, counting from zero, of the active choice of the cycle gadget. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the cycle gadget, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
When the user clicks or shift-clicks on a cycle gadget, the program will receive an IDCMP_GADGETUP IntuiMessage. Look in the Code field of the IntuiMessage for the ordinal number of the new active selection.&lt;br /&gt;
&lt;br /&gt;
==== Slider Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Sliders are one of the two kinds of proportional gadgets offered by GadTools. Slider gadgets (SLIDER_KIND) are used to control an amount, a level or an intensity, such as volume or color. Scroller gadgets (SCROLLER_KIND) are discussed below.&lt;br /&gt;
&lt;br /&gt;
Slider gadgets accept the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTSL_Min (WORD)&lt;br /&gt;
: The minimum level of a slider. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_Max (WORD)&lt;br /&gt;
: The maximum level of a slider. The default is 15. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_Level (WORD)&lt;br /&gt;
: The current level of a slider. The default is zero. When the level is at its minimum, the knob will be all the way to the left for a horizontal slider or all the way at the bottom for a vertical slider. Conversely, the maximum level corresponds to the knob being to the extreme right or top. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_LevelFormat (STRPTR)&lt;br /&gt;
: The current level of the slider may be displayed in real-time alongside the gadget. To use the level-display feature, the program must be using a monospace font for this gadget.&lt;br /&gt;
&lt;br /&gt;
: GTSL_LevelFormat specifies a printf()-style formatting string used to render the slider level beside the slider (the complete set of formatting options is described in the Exec library function RawDoFmt()). Be sure to use the &amp;quot;l&amp;quot; (long word) modifier for the number. Field-width specifiers may be used to ensure that the resulting string is always of constant width. The simplest would be &amp;quot;%2ld&amp;quot;. A 2-digit hexadecimal slider might use &amp;quot;%02lx&amp;quot;, which adds leading zeros to the number. Strings with extra text, such as &amp;quot;%3ld hours&amp;quot;, are permissible. If this tag is specified, the program must also provide GTSL_MaxLevelLen. By default, the level is not displayed. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_MaxLevelLen (UWORD)&lt;br /&gt;
: The maximum length of the string that will result from the given level-formatting string. If this tag is specified, the program must also provide GTSL_LevelFormat. By default, the level is not displayed. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_LevelPlace&lt;br /&gt;
: To choose where the optional display of the level is positioned. It must be one of PLACETEXT_LEFT, PLACETEXT_RIGHT, PLACETEXT_ABOVE or PLACETEXT_BELOW. The level may be placed anywhere with the following exception: the level and the label may not be both above or both below the gadget. To place them both on the same side, allow space in the gadget&#039;s label (see the example). The default is PLACETEXT_LEFT. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTSL_DispFunc (LONG (*function)(struct Gadget *, WORD))&lt;br /&gt;
: Optional function to convert the level for display. A slider to select the number of colors for a screen may operate in screen depth (1 to 5, for instance), but actually display the number of colors (2, 4, 8, 16 or 32). This may be done by providing a GTSL_DispFunc function which returns 1 &amp;amp;lt;&amp;amp;lt; level. The function must take a pointer to the Gadget as the first parameter and the level, a WORD, as the second and return the result as a LONG. The default behavior for displaying a level is to do so without any conversion. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Immediate (BOOL)&lt;br /&gt;
: Set this to TRUE to receive an IDCMP_GADGETDOWN IntuiMessage when the user presses the mouse button over the slider. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_RelVerify (BOOL)&lt;br /&gt;
: Set this to TRUE to receive an IDCMP_GADGETUP IntuiMessage when the user releases the mouse button after using the slider. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; PGA_Freedom&lt;br /&gt;
: Specifies which direction the knob may move. Set to LORIENT_VERT for a vertical slider or LORIENT_HORIZ for a horizontal slider. The default is LORIENT_HORIZ. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the slider, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
Up to three different classes of IntuiMessage may be received at the port when the user plays with a slider, these are IDCMP_MOUSEMOVE, IDCMP_GADGETUP and IDCMP_GADGETDOWN. The program may examine the IntuiMessage Code field to discover the slider&#039;s level.&lt;br /&gt;
&lt;br /&gt;
IDCMP_MOUSEMOVE IntuiMessages will be heard whenever the slider&#039;s level changes. IDCMP_MOUSEMOVE IntuiMessages will not be heard if the knob has not moved far enough for the level to actually change. For example if the slider runs from 0 to 15 and is currently set to 12, if the user drags the slider all the way up the program will hear no more than three IDCMP_MOUSEMOVEs, one each for 13, 14 and 15.&lt;br /&gt;
&lt;br /&gt;
If {GA_Immediate, TRUE} is specified, then the program will always hear an IDCMP_GADGETDOWN IntuiMessage when the user begins to adjust a slider. If {GA_RelVerify, TRUE} is specified, then the program will always hear an IDCMP_GADGETUP IntuiMessage when the user finishes adjusting the slider. If IDCMP_GADGETUP or IDCMP_GADGETDOWN IntuiMessages are requested, the program will always hear them, even if the level has not changed since the previous IntuiMessage.&lt;br /&gt;
&lt;br /&gt;
Note that the Code field of the IntuiMessage structure is a UWORD, while the slider&#039;s level may be negative, since it is a WORD. Be sure to copy or cast the IntuiMessage-&amp;amp;gt;Code into a WORD if the slider has negative levels.&lt;br /&gt;
&lt;br /&gt;
If the user clicks in the container next to the knob, the slider level will increase or decrease by one. If the user drags the knob itself, then the knob will snap to the nearest integral position when it is released.&lt;br /&gt;
&lt;br /&gt;
Here is an example of the screen-depth slider discussed earlier:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* NewGadget initialized here. Note the three spaces&lt;br /&gt;
 * after &amp;quot;Slider:&amp;quot;, to allow a blank plus the two digits&lt;br /&gt;
 * of the level display&lt;br /&gt;
 */&lt;br /&gt;
ng.ng_Flags = PLACETEXT_LEFT;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;Slider:   &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
LONG DepthToColors(struct Gadget *gad, WORD level)&lt;br /&gt;
{&lt;br /&gt;
return ((WORD)(1 &amp;lt;&amp;lt; level));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(SLIDER_KIND, gad, &amp;amp;ng,&lt;br /&gt;
    GTSL_Min, 1,&lt;br /&gt;
    GTSL_Max, 5,&lt;br /&gt;
    GTSL_Level, current_depth,&lt;br /&gt;
    GTSL_MaxLevelLen, 2,&lt;br /&gt;
    GTSL_LevelFormat, &amp;quot;%2ld&amp;quot;,&lt;br /&gt;
    GTSL_DispFunc, DepthToColors,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Scroller Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Scrollers (SCROLLER_KIND) bear some similarity to sliders, but are used for a quite different job: they allow the user to adjust the position of a limited view into a larger area. For example, Workbench&#039;s windows have scrollers that allow the user to see icons that are outside the visible portion of a window. Another example is a scrolling list in a file requester which has a scroller that allows the user to see different parts of the whole list.&lt;br /&gt;
&lt;br /&gt;
A scroller consists of a proportional gadget and usually also has a pair of arrow buttons.&lt;br /&gt;
&lt;br /&gt;
While the slider deals in minimum, maximum and current level, the scroller understands Total, Visible and Top. For a scrolling list, Total would be the number of items in the entire list, Visible would be the number of lines visible in the display area and Top would be the number of the first line displayed in the visible part of the list. Top would run from zero to Total - Visible. For an area-scroller such as those in Workbench&#039;s windows, Total would be the height (or width) of the whole area, Visible would be the visible height (or width) and Top would be the top (or left) edge of the visible part.&lt;br /&gt;
&lt;br /&gt;
Note that the position of a scroller should always represent the position of the visible part of the project and never the position of a cursor or insertion point.&lt;br /&gt;
&lt;br /&gt;
Scrollers respect the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTSC_Top (WORD)&lt;br /&gt;
: The top line or position visible in the area that the scroller represents. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSC_Total (WORD)&lt;br /&gt;
: The total number of lines or positions that the scroller represents. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSC_Visible (WORD)&lt;br /&gt;
: The visible number of lines or positions that the scroller represents. The default is two. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTSC_Arrows (UWORD)&lt;br /&gt;
: Asks for arrow gadgets to be attached to the scroller. The value supplied will be used as the width of each arrow button for a horizontal scroller or the height of each arrow button for a vertical scroller, the other dimension will be set by GadTools to match the scroller size. It is generally recommend that arrows be provided. The default is no arrows. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Immediate (BOOL)&lt;br /&gt;
: Set this to TRUE to receive an IDCMP_GADGETDOWN IntuiMessage when the user presses the mouse button over the scroller. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_RelVerify (BOOL)&lt;br /&gt;
: Set this to TRUE to receive an IDCMP_GADGETUP IntuiMessage when the user releases the mouse button after using the scroller. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; PGA_Freedom&lt;br /&gt;
: Specifies which direction the knob may move. Set to LORIENT_VERT for a vertical scroller or LORIENT_HORIZ for a horizontal scroller. The default is LORIENT_HORIZ. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the scroller, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
The IntuiMessages received for a scroller gadget are the same in nature as those for a slider defined above, including the fact that messages are only heard by the program when the knob moves far enough for the Top value to actually change. The Code field of the IntuiMessage will contain the new Top value of the scroller.&lt;br /&gt;
&lt;br /&gt;
If the user clicks on an arrow gadget, the scroller moves by one unit. If the user holds the button down over an arrow gadget, it repeats.&lt;br /&gt;
&lt;br /&gt;
If the user clicks in the container next to the knob, the scroller will move by one page, which is the visible amount less one. This means that when the user pages through a scrolling list, any pair of successive views will overlap by one line. This helps the user understand the continuity of the list. If the program is using a scroller to pan through an area then there will be an overlap of one unit between successive views. It is recommended that Top, Visible and Total be scaled so that one unit represents about five to ten percent of the visible amount.&lt;br /&gt;
&lt;br /&gt;
==== Listview Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Listview gadgets (LISTVIEW_KIND) are scrolling lists. They consist of a scroller with arrows, an area where the list itself is visible and optionally a place where the current selection is displayed, which may be editable. The user can browse through the list using the scroller or its arrows and may select an entry by clicking on that item.&lt;br /&gt;
&lt;br /&gt;
There are a number of tags that are used with listviews:&lt;br /&gt;
&lt;br /&gt;
; GTLV_Labels (struct List *)&lt;br /&gt;
: An Exec list whose nodes&#039; ln_Name fields are to be displayed as items in the scrolling list. If the list is empty, an empty List structure or a NULL value may be used for GTLV_Labels. This tag accepts a value of &amp;quot;~0&amp;quot; to detach the list from the listview, defined below. The default is NULL. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTLV_Top (UWORD)&lt;br /&gt;
: The ordinal number of the top item visible in the listview. The default is zero. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTLV_ReadOnly (BOOL)&lt;br /&gt;
: Set this to TRUE for a read-only listview, which the user can browse, but not select items from. A read-only listview can be recognized because the list area is recessed, not raised. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTLV_ScrollWidth (UWORD)&lt;br /&gt;
: The width of the scroller to be used in the listview. Any value specified must be reasonably bigger than zero. The default is 16. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTLV_ShowSelected (struct Gadget *)&lt;br /&gt;
: Use this tag to show the currently selected entry displayed underneath the listview. Set its value to NULL to get a read-only (TEXT_KIND) display of the currently selected entry or set it to a pointer to an already-created GadTools STRING_KIND gadget to allow the user to directly edit the current entry. By default, there is no display of the currently selected entry. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTLV_Selected (UWORD)&lt;br /&gt;
: Ordinal number of the item to be placed into the display of the current selection under the listview. This tag is ignored if GTLV_ShowSelected is not used. Set it to &amp;quot;~0&amp;quot; to have no current selection. The default is &amp;quot;~0&amp;quot;. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; LAYOUTA_Spacing (UWORD)&lt;br /&gt;
: Extra space, in pixels, to be placed between the entries in the listview. The default is zero. (Create only.)&lt;br /&gt;
&lt;br /&gt;
The program will only hear from a listview when the user selects an item from the list. The program will then receive an IDCMP_GADGETUP IntuiMessage. This message will contain the ordinal number of the item within the list that was selected in the Code field of the message. This number is independent of the displayed listview, it is the offset from the start of the list of items.&lt;br /&gt;
&lt;br /&gt;
If the program attaches a display gadget by using the TagItem {GTLV_ShowSelected, NULL}, then whenever the user clicks on an entry in the listview it will be copied into the display gadget.&lt;br /&gt;
&lt;br /&gt;
If the display gadget is to be editable, then the program must first create a GadTools STRING_KIND gadget whose width matches the width of the listview. The TagItem {GTLV_ShowSelected, stringgad} is used to install the editable gadget, where stringgad is the pointer returned by CreateGadget(). When the user selects any entry from the listview, it gets copied into the string gadget. The user can edit the string and the program will hear normal string gadget IDCMP_GADGETUP messages from the STRING_KIND gadget.&lt;br /&gt;
&lt;br /&gt;
The Exec List and its Node structures may not be modified while they are attached to the listview, since the list might be needed at any time. If the program has prepared an entire new list, including a new List structure and all new nodes, it may replace the currently displayed list in a single step by calling GT_SetGadgetAttrs() with the TagItem {GTLV_Labels, newlist}. If the program needs to operate on the list that has already been passed to the listview, it should detach the list by setting the GTLV_Labels attribute to &amp;quot;~0&amp;quot;. When done modifying the list, resubmit it by setting GTLV_Labels to once again point to it. This is better than first setting the labels to NULL and later back to the list, since setting GTLV_Labels to NULL will visually clear the listview. If the GTLV_Labels attribute is set to &amp;quot;~0&amp;quot;, the program is expected to set it back to something determinate, either a list or NULL, soon after.&lt;br /&gt;
&lt;br /&gt;
The height specified for the listview will determine the number of lines in the list area. When creating a listview, it will be no bigger than the size specified in the NewGadget structure. The size will include the current-display gadget, if any, that has been requested via the GTLV_ShowSelected tag. The listview may end up being less tall than the application asked for, since the calculated height assumes an integral number of lines in the list area.&lt;br /&gt;
&lt;br /&gt;
By default, the gadget label will be placed above the listview. This may be overridden using ng_Flags.&lt;br /&gt;
&lt;br /&gt;
==== Palette Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Palette gadgets (PALETTE_KIND) let the user pick a color from a set of several. A palette gadget consists of a number of colored squares, one for each color available. There may also be an optional indicator square which is filled with the currently selected color. To create a color editor, a palette gadget would be combined with some sliders to control red, green and blue components, for example.&lt;br /&gt;
&lt;br /&gt;
Palette gadgets use the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTPA_Depth (UWORD)&lt;br /&gt;
: The number of bitplanes that the palette represents. There will be 1 &amp;lt;&amp;lt; depth (2^depth) squares in the palette gadget. The default is one. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTPA_Color (UBYTE)&lt;br /&gt;
: The selected color of the palette. The default is one. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTPA_ColorOffset (UBYTE)&lt;br /&gt;
: The first color to use in the palette. For example, if GTPA_Depth is two and GTPA_ColorOffset is four, then the palette will have squares for colors four, five, six and seven. The default is zero. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTPA_IndicatorWidth (UWORD)&lt;br /&gt;
: The desired width of the current-color indicator. By specifying this tag, the application is asking for an indicator to be placed to the left of the color selection squares. The indicator will be as tall as the gadget itself. By default there is no indicator. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTPA_IndicatorHeight (UWORD)&lt;br /&gt;
: The desired height of the current-color indicator. By specifying this tag, the application is asking for an indicator to be placed above the color selection squares. The indicator will be as wide as the gadget itself. By default there is no indicator. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GA_Disabled (BOOL)&lt;br /&gt;
: Set this attribute to TRUE to disable the palette gadget, to FALSE otherwise. The default is FALSE. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
An IDCMP_GADGETUP IntuiMessage will be received when the user selects a color from the palette. The current-color indicator is recessed, indicating that clicking on it has no effect.&lt;br /&gt;
&lt;br /&gt;
If the palette is wide and not tall, use the GTPA_IndicatorWidth tag to put the indicator on the left. If the palette is tall and narrow, put the indicator on top using GTPA_IndicatorHeight.&lt;br /&gt;
&lt;br /&gt;
By default, the gadget&#039;s label will go above the palette gadget, unless GTPA_IndicatorWidth is specified, in which case the label will go on the left. In either case, the default may be overridden by setting the appropriate flag in the NewGadget&#039;s ng_Flags field.&lt;br /&gt;
&lt;br /&gt;
The size specified for the palette gadget will determine how the area is subdivided to make the individual color squares. The actual size of the palette gadget will be no bigger than the size given, but it can be smaller in order to make the color squares all exactly the same size.&lt;br /&gt;
&lt;br /&gt;
==== Text-Display and Numeric-Display Gadgets ====&lt;br /&gt;
&lt;br /&gt;
Text-display (TEXT_KIND) and numeric-display (NUMBER_KIND) gadgets are read-only displays of information. They are useful for displaying information that is not editable or selectable, while allowing the application to use the GadTools formatting and visuals. Conveniently, the visuals are automatically refreshed through normal GadTools gadget processing. The values displayed may be modified by the program in the same way other GadTools gadgets may be updated.&lt;br /&gt;
&lt;br /&gt;
Text-display and number-display gadgets consist of a fixed label (the one supplied as the NewGadget&#039;s ng_GadgetText), as well as a changeable string or number (GTTX_Text or GTNM_Number respectively). The fixed label is placed according to the PLACETEXT_ flag chosen in the NewGadget ng_Flags field. The variable part is aligned to the left-edge of the gadget.&lt;br /&gt;
&lt;br /&gt;
Text-display gadgets recognize the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTTX_Text (STRPTR)&lt;br /&gt;
: Pointer to the string to be displayed or NULL for no string. The default is NULL. (Create and set.)&lt;br /&gt;
&lt;br /&gt;
; GTTX_Border (BOOL)&lt;br /&gt;
: Set to TRUE to place a recessed border around the displayed string. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
; GTTX_CopyText (BOOL)&lt;br /&gt;
: This flag instructs the text-display gadget to copy the supplied GTTX_Text string instead of using only a pointer to the string. This only works for the value of GTTX_Text set at CreateGadget() time. If GTTX_Text is changed, the new text will be referenced by pointer, not copied. Do not use this tag without a non-NULL GTTX_Text. (Create only.)&lt;br /&gt;
&lt;br /&gt;
Number-display gadgets have the following tags:&lt;br /&gt;
&lt;br /&gt;
; GTNM_Number (LONG)&lt;br /&gt;
: The number to be displayed. The default is zero. (Create or set.)&lt;br /&gt;
&lt;br /&gt;
; GTNM_Border (BOOL)&lt;br /&gt;
: Set to TRUE to place a recessed border around the displayed number. The default is FALSE. (Create only.)&lt;br /&gt;
&lt;br /&gt;
Since they are not selectable, text-display and numeric-display gadgets never cause IntuiMessages to be sent to the application.&lt;br /&gt;
&lt;br /&gt;
==== Generic Gadgets ====&lt;br /&gt;
&lt;br /&gt;
If the application requires a specialized gadget which does not fit into any of the defined GadTools kinds but would still like to use the GadTools gadget creation and deletion functions, it may create a GadTools generic gadget and use it any way it sees fit. In fact, all of the kinds of GadTools gadgets are created out of GadTools GENERIC_KIND gadgets.&lt;br /&gt;
&lt;br /&gt;
The gadget that gets created will heed almost all the information contained in the NewGadget structure supplied.&lt;br /&gt;
&lt;br /&gt;
If ng_GadgetText is supplied, the gadget&#039;s GadgetText will point to an IntuiText structure with the provided string and font. However, do not specify any of the PLACETEXT ng_Flags, as they are currently ignored by GENERIC_KIND gadgets. PLACETEXT flags may be supported by generic GadTools gadgets in the future.&lt;br /&gt;
&lt;br /&gt;
It is up to the program to set the Flags, Activation, GadgetRender, SelectRender, MutualExclude and SpecialInfo fields of the Gadget structure.&lt;br /&gt;
&lt;br /&gt;
The application must also set the GadgetType field, but be certain to preserve the bits set by CreateGadget().&lt;br /&gt;
&lt;br /&gt;
For instance, to make a gadget boolean, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
gad-&amp;gt;GadgetType |= GTYP_BOOLGADGET;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and not&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
gad-&amp;gt;GadgetType = GTYP_BOOLGADGET;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using direct assignment, (the = operator), clears all other flags in the GadgetType field and the gadget may not be properly freed by FreeGadgets().&lt;br /&gt;
&lt;br /&gt;
=== Functions for Setting up GadTools Menus and Gadgets ===&lt;br /&gt;
&lt;br /&gt;
This section gives all the details on the functions used to set up GadTools menus and gadgets that were mentioned briefly earlier in this article.&lt;br /&gt;
&lt;br /&gt;
==== GetVisualInfo() and FreeVisualInfo() ====&lt;br /&gt;
&lt;br /&gt;
In order to ensure their best appearance, GadTools gadgets and menus need information about the screen on which they will appear. Before creating any GadTools gadgets or menus, the program must get this information using the GetVisualInfo() call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
APTR GetVisualInfoA( struct Screen *screen, struct TagItem *taglist );&lt;br /&gt;
APTR GetVisualInfo( struct Screen *screen, Tag tag1, ... );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the screen argument to a pointer to the screen you are using. The tag arguments, tag1 or taglist, are reserved for future extensions. Currently none are recognized, so only TAG_END should be used.&lt;br /&gt;
&lt;br /&gt;
The function returns an abstract handle called the VisualInfo. For GadTools gadgets, the ng_VisualInfo field of the NewGadget structure must be set to this handle before the gadget can be added to the window. GadTools menu layout and creation functions also require the VisualInfo handle as an argument.&lt;br /&gt;
&lt;br /&gt;
There are several ways to get the pointer to the screen on which the window will be opened. If the application has its own custom screen, this pointer is known from the call to OpenScreen() or OpenScreenTags(). If the application already has its window opened on the Workbench or some other public screen, the screen pointer can be found in Window.WScreen. Often the program will create its gadgets and menus before opening the window. In this case, use LockPubScreen() to get a pointer to the desired public screen, which also provides a lock on the screen to prevent it from closing. See [[Intuition_Screens|Intuition Screens]] and [[Intuition_Windows|Intuition Windows]] for more about public screens.&lt;br /&gt;
&lt;br /&gt;
The VisualInfo data must be freed after all the gadgets and menus have been freed but before releasing the screen. Custom screens are released by calling CloseScreen(), public screens are released by calling CloseWindow() or UnlockPubScreen(), depending on the technique used. Use FreeVisualInfo() to free the visual info data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID FreeVisualInfo( APTR vi );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function takes just one argument, the VisualInfo handle as returned by GetVisualInfo(). The sequence of events for using the VisualInfo handle could look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
void init()&lt;br /&gt;
{&lt;br /&gt;
myscreen = IIntuition-&amp;gt;LockPubScreen(NULL);&lt;br /&gt;
if (!myscreen)&lt;br /&gt;
    {&lt;br /&gt;
    cleanup(&amp;quot;Failed to lock default public screen&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
vi = IGadTools-&amp;gt;GetVisualInfo(myscreen);&lt;br /&gt;
if (!vi)&lt;br /&gt;
    {&lt;br /&gt;
    cleanup(&amp;quot;Failed to GetVisualInfo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
/* Create gadgets here */&lt;br /&gt;
ng.ng_VisualInfo = vi;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cleanup(STRPTR errorstr)&lt;br /&gt;
{&lt;br /&gt;
/* These functions may be safely called with a NULL parameter: */&lt;br /&gt;
IGadTools-&amp;gt;FreeGadgets(glist);&lt;br /&gt;
IGadTools-&amp;gt;FreeVisualInfo(vi);&lt;br /&gt;
&lt;br /&gt;
if (myscreen)&lt;br /&gt;
    IIntuition-&amp;gt;UnlockPubScreen(NULL, myscreen);&lt;br /&gt;
&lt;br /&gt;
IDOS-&amp;gt;Printf(errorstr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CreateContext() ====&lt;br /&gt;
&lt;br /&gt;
Use of GadTools gadgets requires some per-window context information. CreateContext() establishes a place for that information to go. This function must be called before any GadTools gadgets are created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Gadget *CreateContext( struct Gadget **glistptr );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The glistptr argument is a double-pointer to a Gadget structure. More specifically, this is a pointer to a NULL-initialized pointer to a Gadget structure.&lt;br /&gt;
&lt;br /&gt;
The return value of CreateContext() is a pointer to this gadget, which should be fed to the program&#039;s first call to CreateGadget(). This pointer to the Gadget structure returned by CreateContext(), may then serve as a handle to the list of gadgets as they are created. The code fragment listed in the next section shows how to use CreateContext() together with CreateGadget() to make a linked list of GadTools gadgets.&lt;br /&gt;
&lt;br /&gt;
=== Creating Gadget Lists ===&lt;br /&gt;
&lt;br /&gt;
In the discussion of CreateGadget() presented earlier, the examples showed only how to make a single gadget. For most applications that use GadTools, however, a whole list of gadgets will be needed. To do this, the application could use code such as this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct NewGadget *newgad1, *newgad2, *newgad3;&lt;br /&gt;
struct Gadget *glist = NULL;&lt;br /&gt;
struct Gadget *pgad;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
/* Initialize NewGadget structures */&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Note that CreateContext() requires a POINTER to a NULL-initialized&lt;br /&gt;
 * pointer to struct Gadget:&lt;br /&gt;
 */&lt;br /&gt;
pgad = IGadTools-&amp;gt;CreateContext(&amp;amp;amp;glist);&lt;br /&gt;
&lt;br /&gt;
pgad = IGadTools-&amp;gt;CreateGadget(BUTTON_KIND, pgad, newgad1, TAG_END);&lt;br /&gt;
pgad = IGadTools-&amp;gt;CreateGadget(STRING_KIND, pgad, newgad2, TAG_END);&lt;br /&gt;
pgad = IGadTools-&amp;gt;CreateGadget(MX_KIND,     pgad, newgad3, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (!pgad)&lt;br /&gt;
    {&lt;br /&gt;
    IGadTools-&amp;gt;FreeGadgets(glist);&lt;br /&gt;
    exit_error();&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    if ( mywin = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                              WA_Gadgets,   glist,&lt;br /&gt;
                              ...&lt;br /&gt;
                              /* Other tags... */&lt;br /&gt;
                              ...&lt;br /&gt;
                              TAG_END) )&lt;br /&gt;
        {&lt;br /&gt;
        /* Complete the rendering of the gadgets */&lt;br /&gt;
        IGadTools-&amp;gt;GT_RefreshWindow(win, NULL);&lt;br /&gt;
        ...&lt;br /&gt;
        /* and continue on... */&lt;br /&gt;
        ...&lt;br /&gt;
        IIntuition-&amp;gt;CloseWindow(mywin);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    IGadTools-&amp;gt;FreeGadgets(glist);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pointer to the previous gadget, pgad in the code fragment above, is used for three purposes. First, when CreateGadget() is called multiple times, each new gadget is automatically linked to the previous gadget&#039;s NextGadget field, thus creating a gadget list. Second, if one of the gadget creations fails (usually due to low memory, but other causes are possible), then for the next call to CreateGadget(), pgad will be NULL and CreateGadget() will fail immediately. This means that the program can perform several successive calls to CreateGadget() and only have to check for failure at the end.&lt;br /&gt;
&lt;br /&gt;
Finally, although this information is hidden in the implementation and not important to the application, certain calls to CreateGadget() actually cause several Intuition gadgets to be allocated and these are automatically linked together without program interaction, but only if a previous gadget pointer is supplied. If several gadgets are created by a single CreateGadget() call, they work together to provide the functionality of a single GadTools gadget. The application should always act as though the gadget pointer returned by CreateGadget() points to a single gadget instance. See &amp;quot;Documented Side-Effects&amp;quot; for a warning.&lt;br /&gt;
&lt;br /&gt;
There is one exception to the fact that a program only has to check for failure after the last CreateGadget() call and that is when the application depends on the successful creation of a gadget and caches or immediately uses the gadget pointer returned by CreateGadget().&lt;br /&gt;
&lt;br /&gt;
For instance, if the program wants to create a string gadget and save a pointer to the string buffer, it might do so as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(STRING_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                   GTST_String, &amp;quot;Hello World&amp;quot;,&lt;br /&gt;
                   TAG_END);&lt;br /&gt;
if (gad)&lt;br /&gt;
    {&lt;br /&gt;
    stringbuffer = ((struct StringInfo *)(gad-&amp;gt;SpecialInfo))-&amp;gt;Buffer;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Creation can continue here: */&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(..._KIND, gad, &amp;amp;ng2,&lt;br /&gt;
    ...&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A major benefit of having a reusable NewGadget structure is that often many fields do not change and some fields change incrementally. For example, the application can set just the NewGadget&#039;s ng_VisualInfo and ng_TextAttr only once and never have to modify them again even if the structure is reused to create many gadgets. A set of similar gadgets may share size and some positional information so that code such as the following might be used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* Assume that the NewGadget structure &#039;ng&#039; is fully&lt;br /&gt;
 * initialized here for a button labelled &amp;quot;OK&amp;quot;&lt;br /&gt;
 */&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(BUTTON_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                   TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Modify only those fields that need to change: */&lt;br /&gt;
ng.ng_GadgetID++;&lt;br /&gt;
ng.ng_LeftEdge += 80;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;Cancel&amp;quot;;&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(BUTTON_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                   TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=All gadgets created by GadTools currently have the GADTOOL_TYPE bit set in their GadgetType field. It is not correct to check for, set, clear or otherwise rely on this since it is subject to change.}}&lt;br /&gt;
&lt;br /&gt;
=== Gadget Refresh Functions ===&lt;br /&gt;
&lt;br /&gt;
Normally, GadTools gadgets are created and then attached to a window when the window is opened, either through the WA_FirstGadget tag or the NewWindow.FirstGadget field. Alternately, they may be added to a window after it is open by using the functions AddGList() and RefreshGList().&lt;br /&gt;
&lt;br /&gt;
Regardless of which way gadgets are attached to a window, the program must then call the GT_RefreshWindow() function to complete the rendering of GadTools gadgets. This function takes two arguments.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID GT_RefreshWindow( struct Window *win, struct Requester *req );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This win argument is a pointer to the window that contains the GadTools gadgets. The req argument is currently unused and should be set to NULL. This function should only be called immediately after adding GadTools gadgets to a window. Subsequent changes to GadTools gadget imagery made through calls to GT_SetGadgetAttrs() will be automatically performed by GadTools when the changes are made. (There is no need to call GT_RefreshWindow() in that case.)&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, applications must always ask for notification of window refresh events for any window that uses GadTools gadgets. When the application receives an IDCMP_REFRESHWINDOW message for a window, Intuition has already refreshed its gadgets. Normally, a program would then call Intuition&#039;s BeginRefresh(), perform its own custom rendering operations, and finally call EndRefresh(). But for a window that uses GadTools gadgets, the application must call GT_BeginRefresh() and GT_EndRefresh() in place of BeginRefresh() and EndRefresh(). This allows the the GadTools gadgets to be fully refreshed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID GT_BeginRefresh( struct Window *win );&lt;br /&gt;
VOID GT_EndRefresh ( struct Window *win, LONG complete );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For both functions, the win argument is a pointer to the window to be refreshed. For GT_EndRefresh(), set the complete argument to TRUE if refreshing is complete, set it to FALSE otherwise. See the discussion of BeginRefresh() and EndRefresh() in [[Intuition_Windows|Intuition Windows]] for more about window refreshing.&lt;br /&gt;
&lt;br /&gt;
When using GadTools gadgets, the program may not set the window&#039;s WFLG_NOCAREREFRESH flag. Even if there is no custom rendering to be performed, GadTools gadgets requires this minimum code to handle IDCMP_REFRESHWINDOW messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
case IDCMP_REFRESHWINDOW:&lt;br /&gt;
    IGadTools-&amp;gt;GT_BeginRefresh(win);&lt;br /&gt;
    /* custom rendering, if any, goes here */&lt;br /&gt;
    IGadTools-&amp;gt;GT_EndRefresh(win, TRUE);&lt;br /&gt;
    break;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other GadTools Functions ===&lt;br /&gt;
&lt;br /&gt;
This section discusses some additional support functions in the GadTools library that serve special needs.&lt;br /&gt;
&lt;br /&gt;
==== GT_FilterIMsg() and GT_PostFilterIMsg() ====&lt;br /&gt;
&lt;br /&gt;
For most GadTools programs, GT_GetIMsg() and GT_ReplyIMsg() work perfectly well. In rare cases an application may find they pose a bit of a problem. A typical case is when all messages are supposed to go through a centralized ReplyMsg() that cannot be converted to a GT_ReplyIMsg(). Since calls to GT_GetIMsg() and GT_ReplyIMsg() must be paired, there would be a problem.&lt;br /&gt;
&lt;br /&gt;
For such cases, the GT_FilterIMsg() and GT_PostFilterIMsg() functions are available. These functions allow GetMsg() and ReplyMsg() to be used in a way that is compatible with GadTools.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=These functions are for specialized use only and will not be used by the majority of applications. See GT_GetIMsg() and GT_ReplyIMsg() for standard message handling.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct IntuiMessage *GT_FilterIMsg( struct IntuiMessage *imsg );&lt;br /&gt;
struct IntuiMessage *GT_PostFilterIMsg( struct IntuiMessage *imsg );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The GT_FilterIMsg() function should be called right after GetMsg(). It takes a pointer to the original IntuiMessage and, if the message applies to a GadTools gadget, returns either a modified IntuiMessage or a NULL. A NULL return signifies that the message was consumed by a GadTools gadget (and not needed by the application).&lt;br /&gt;
&lt;br /&gt;
The GT_PostFilterIMsg() function should be called before replying to any message modified by GT_FilterIMsg(). It takes a pointer to the modified version of an IntuiMessage obtained with GT_FilterIMsg() and returns a pointer to the original IntuiMessage.&lt;br /&gt;
&lt;br /&gt;
The typical calling sequence for a program that uses these functions, is to call GetMsg() to get the IntuiMessage. Then, if the message applies to a window which contains GadTools gadgets, call GT_FilterIMsg(). Any message returned by GT_FilterIMsg() should be used like a message returned from GT_GetIMsg().&lt;br /&gt;
&lt;br /&gt;
When done with the message, the application must call GT_PostFilterIMsg() to perform any clean up necessitated by the previous call to GT_FilterIMsg(). In all cases, the application &#039;&#039;must&#039;&#039; then reply the original IntuiMessage using ReplyMsg(). This is true even for consumed messages as these are &#039;&#039;not&#039;&#039; replied by GadTools. For example, the application could use code such as this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* port is a message port receiving different messages */&lt;br /&gt;
/* gtwindow is the window that contains GadTools gadgets */&lt;br /&gt;
&lt;br /&gt;
imsg = IExec-&amp;gt;GetMsg(port);&lt;br /&gt;
&lt;br /&gt;
/* Is this the window with GadTools gadgets? */&lt;br /&gt;
if (imsg-&amp;gt;IDCMPWindow == gtwindow)&lt;br /&gt;
    {&lt;br /&gt;
    /* Filter the message and see if action is needed */&lt;br /&gt;
    if (gtimsg = IGadTools-&amp;gt;GT_FilterIMsg(imsg))&lt;br /&gt;
        {&lt;br /&gt;
        switch (gtimsg-&amp;gt;Class)&lt;br /&gt;
            {&lt;br /&gt;
            /* Act depending on the message */&lt;br /&gt;
            ...&lt;br /&gt;
            }&lt;br /&gt;
        /* Clean up the filtered message.  The return value is not needed */&lt;br /&gt;
        /* since we already have a pointer to the original message.       */&lt;br /&gt;
        IGadTools-&amp;gt;GT_PostFilterIMsg(gtimsg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
/* other stuff can go here */&lt;br /&gt;
IExec-&amp;gt;ReplyMsg(imsg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not make any assumptions about the contents of the unfiltered IntuiMessage (imsg in the above example). Only two things are guaranteed: the unfiltered IntuiMessage must be replied to and the unfiltered IntuiMessage (if it produces anything when passed through GT_FilterIMsg()) will produce a meaningful GadTools IntuiMessage like those described in the section on the different kinds of gadgets. The relationship between the unfiltered and filtered messages are expected to change in the future. See the section on documented side-effects for more information.&lt;br /&gt;
&lt;br /&gt;
==== DrawBevelBox() ====&lt;br /&gt;
&lt;br /&gt;
A key visual signature shared by most GadTools gadgets is the raised or recessed beveled box imagery. Since the program may wish to create its own boxes to match, GadTools provides the DrawBevelBox() and DrawBevelBoxA() functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
VOID DrawBevelBoxA( struct RastPort *rport, LONG left, LONG top, LONG width, LONG height,&lt;br /&gt;
                    struct TagItem *taglist );&lt;br /&gt;
VOID DrawBevelBox ( struct RastPort *rport, LONG left, LONG top, LONG width, LONG height,&lt;br /&gt;
                    Tag tag1, ... );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rport argument is a pointer to the RastPort into which the box is to be rendered. The left, top, width and height arguments specify the dimensions of the desired box.&lt;br /&gt;
&lt;br /&gt;
The tag arguments, tag1 or taglist, may be set as follows:&lt;br /&gt;
&lt;br /&gt;
; GT_VisualInfo (APTR)&lt;br /&gt;
: The VisualInfo handle as returned by a prior call to GetVisualInfo(). This value is required.&lt;br /&gt;
&lt;br /&gt;
; GTBB_Recessed (BOOL)&lt;br /&gt;
: A beveled box may either appear to be raised to signify an area of the window that is selectable or recessed to signify an area of the window in which clicking will have no effect. Set this boolean tag to TRUE to get a recessed box. Omit this tag entirely to get a raised box.&lt;br /&gt;
&lt;br /&gt;
DrawBevelBox() is a rendering operation, not a gadget. This means that the program must refresh any beveled boxes rendered through this function if the window gets damaged.&lt;br /&gt;
&lt;br /&gt;
=== Gadget Keyboard Equivalents ===&lt;br /&gt;
&lt;br /&gt;
Often, users find it convenient to control gadgets using the keyboard. The keyboard equivalent will be an underscored character in the gadget label, for easy identification. At the present time, however, the application is still responsible for implementing the reaction to each keypress.&lt;br /&gt;
&lt;br /&gt;
==== Denoting a Gadget&#039;s Keyboard Equivalent ====&lt;br /&gt;
&lt;br /&gt;
In order to denote the key equivalent, the application may add a marker-symbol to the gadget label. This is done by placing the marker-symbol immediately before the character to be underscored. This symbol can be any character that is not used in the label. The underscore character, &amp;quot;_&amp;quot; is the recommended marker-symbol. So, for example, to mark the letter &amp;quot;F&amp;quot; as the keyboard equivalent for a button labelled &amp;quot;Select Font ...&amp;quot;, create the gadget text:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;Select _Font ...&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To inform GadTools of the underscore in the label, pass the GA_Underscore tag to CreateGadget() or CreateGadgetA(). The data-value associated with this tag is a character, not a string, which is the marker-symbol used in the gadget label:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
GA_Underscore, &#039;_&#039;, /* Note &#039;_&#039;, not &amp;quot;_&amp;quot; !!! */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GadTools will create a gadget label which consists of the text supplied with the marker-symbol removed and the character following the marker-symbol underscored.&lt;br /&gt;
&lt;br /&gt;
The gadget&#039;s label would look something like:&lt;br /&gt;
&lt;br /&gt;
 Select &amp;lt;u&amp;gt;F&amp;lt;/u&amp;gt;ont ...&lt;br /&gt;
&lt;br /&gt;
==== Implementing a Gadget&#039;s Keyboard Equivalent Behavior ====&lt;br /&gt;
&lt;br /&gt;
Currently, GadTools does not process keyboard equivalents for gadgets. It is up to the application writer to implement the correct behavior, normally by calling GT_SetGadgetAttrs() on the appropriate gadget. For some kinds of gadget, the behavior should be the same regardless of whether the keyboard equivalent was pressed with or without the shift key. For other gadgets, shifted and unshifted keystrokes will have different, usually opposite, effects.&lt;br /&gt;
&lt;br /&gt;
Here is the correct behavior for keyboard equivalents for each kind of GadTools gadget:&lt;br /&gt;
&lt;br /&gt;
; Button Gadgets&lt;br /&gt;
: The keyboard equivalent should invoke the same function that clicking on the gadget does. There is currently no way to highlight the button visuals programmatically when accessing the button through a keyboard equivalent.&lt;br /&gt;
&lt;br /&gt;
; Text-Entry and Number-Entry Gadgets&lt;br /&gt;
: The keyboard equivalent should activate the gadget so the user may type into it. Use Intuition&#039;s ActivateGadget() call.&lt;br /&gt;
&lt;br /&gt;
; Checkbox Gadgets&lt;br /&gt;
: The keyboard equivalent should toggle the state of the checkbox. Use GT_SetGadgetAttrs() and the GTCB_Checked tag.&lt;br /&gt;
&lt;br /&gt;
; Mutually-Exclusive Gadgets&lt;br /&gt;
: The unshifted keystroke should activate the next choice, wrapping around from the last to the first. The shifted keystroke should activate the previous choice, wrapping around from the first to the last. Use GT_SetGadgetAttrs() and the GTMX_Active tag.&lt;br /&gt;
&lt;br /&gt;
; Cycle Gadgets&lt;br /&gt;
: The unshifted keystroke should activate the next choice, wrapping around from the last to the first. The shifted keystroke should activate the previous choice, wrapping around from the first to the last. Use GT_SetGadgetAttrs() and the GTCY_Active tag.&lt;br /&gt;
&lt;br /&gt;
; Slider Gadgets&lt;br /&gt;
: The unshifted keystroke should increase the slider&#039;s level by one, stopping at the maximum, while the shifted keystroke should decrease the level by one, stopping at the minimum. Use GT_SetGadgetAttrs() and the GTSL_Level tag.&lt;br /&gt;
&lt;br /&gt;
; Scroller Gadgets&lt;br /&gt;
: The unshifted keystroke should increase the scroller&#039;s top by one, stopping at the maximum, while the shifted keystroke should decrease the scroller&#039;s top by one, stopping at the minimum. Use GT_SetGadgetAttrs() and the GTSC_Top tag.&lt;br /&gt;
&lt;br /&gt;
; Listview Gadgets&lt;br /&gt;
: The unshifted keystroke should cause the next entry in the list to become the selected one, stopping at the last entry, while the shifted keystroke should cause the previous entry in the list to become the selected one, stopping at the first entry. Use GT_SetGadgetAttrs() and the GTLV_Top and GTLV_Selected tags.&lt;br /&gt;
&lt;br /&gt;
; Palette Gadgets&lt;br /&gt;
: The unshifted keystroke should select the next color, wrapping around from the last to the first. The shifted keystroke should activate the previous color, wrapping around from the first to the last. Use GT_SetGadgetAttrs() and the GTPA_Color tag.&lt;br /&gt;
&lt;br /&gt;
; Text-Display and Number-Display Gadgets&lt;br /&gt;
: These kinds of GadTools gadget have no keyboard equivalents since they are not selectable.&lt;br /&gt;
&lt;br /&gt;
; Generic Gadgets&lt;br /&gt;
: Define appropriate keyboard functions based on the kinds of keyboard behavior defined for other GadTools kinds.&lt;br /&gt;
&lt;br /&gt;
=== Complete GadTools Gadget Example ===&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a working example showing how to set up and use a linked list of GadTools gadgets complete with keyboard shortcuts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
/* gadtoolsgadgets.c&lt;br /&gt;
** Simple example of using a number of gadtools gadgets.&lt;br /&gt;
**&lt;br /&gt;
*/&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Gadget defines of our choosing, to be used as GadgetID&#039;s,&lt;br /&gt;
** also used as the index into the gadget array my_gads[].&lt;br /&gt;
*/&lt;br /&gt;
#define MYGAD_SLIDER    (0)&lt;br /&gt;
#define MYGAD_STRING1   (1)&lt;br /&gt;
#define MYGAD_STRING2   (2)&lt;br /&gt;
#define MYGAD_STRING3   (3)&lt;br /&gt;
#define MYGAD_BUTTON    (4)&lt;br /&gt;
&lt;br /&gt;
/* Range for the slider: */&lt;br /&gt;
#define SLIDER_MIN  (1)&lt;br /&gt;
#define SLIDER_MAX (20)&lt;br /&gt;
&lt;br /&gt;
struct TextAttr Topaz80 = { &amp;quot;topaz.font&amp;quot;, 8, 0, 0, };&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace  *IGraphics;&lt;br /&gt;
struct GadToolsIFace  *IGadTools;&lt;br /&gt;
&lt;br /&gt;
/* Print any error message.  We could do more fancy handling (like&lt;br /&gt;
** an EasyRequest()), but this is only a demo.&lt;br /&gt;
*/&lt;br /&gt;
void errorMessage(STRPTR error)&lt;br /&gt;
{&lt;br /&gt;
if (error)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Error: %s\n&amp;quot;, error);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Function to handle a GADGETUP or GADGETDOWN event.  For GadTools gadgets,&lt;br /&gt;
** it is possible to use this function to handle MOUSEMOVEs as well, with&lt;br /&gt;
** little or no work.&lt;br /&gt;
*/&lt;br /&gt;
VOID handleGadgetEvent(struct Window *win, struct Gadget *gad, UWORD code,&lt;br /&gt;
    WORD *slider_level, struct Gadget *my_gads[])&lt;br /&gt;
{&lt;br /&gt;
switch (gad-&amp;gt;GadgetID)&lt;br /&gt;
    {&lt;br /&gt;
    case MYGAD_SLIDER:&lt;br /&gt;
        /* Sliders report their level in the IntuiMessage Code field: */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Slider at level %ld\n&amp;quot;, code);&lt;br /&gt;
        *slider_level = code;&lt;br /&gt;
        break;&lt;br /&gt;
    case MYGAD_STRING1:&lt;br /&gt;
        /* String gadgets report GADGETUP&#039;s */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;String gadget 1: &#039;%s&#039;.\n&amp;quot;,&lt;br /&gt;
                ((struct StringInfo *)gad-&amp;gt;SpecialInfo)-&amp;gt;Buffer);&lt;br /&gt;
        break;&lt;br /&gt;
    case MYGAD_STRING2:&lt;br /&gt;
        /* String gadgets report GADGETUP&#039;s */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;String gadget 2: &#039;%s&#039;.\n&amp;quot;,&lt;br /&gt;
                ((struct StringInfo *)gad-&amp;gt;SpecialInfo)-&amp;gt;Buffer);&lt;br /&gt;
        break;&lt;br /&gt;
    case MYGAD_STRING3:&lt;br /&gt;
        /* String gadgets report GADGETUP&#039;s */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;String gadget 3: &#039;%s&#039;.\n&amp;quot;,&lt;br /&gt;
                ((struct StringInfo *)gad-&amp;gt;SpecialInfo)-&amp;gt;Buffer);&lt;br /&gt;
        break;&lt;br /&gt;
    case MYGAD_BUTTON:&lt;br /&gt;
        /* Buttons report GADGETUP&#039;s (button resets slider to 10) */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Button was pressed, slider reset to 10.\n&amp;quot;);&lt;br /&gt;
        *slider_level = 10;&lt;br /&gt;
        IGadTools-&amp;gt;GT_SetGadgetAttrs(my_gads[MYGAD_SLIDER], win, NULL,&lt;br /&gt;
                            GTSL_Level, *slider_level,&lt;br /&gt;
                            TAG_END);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Function to handle vanilla keys.&lt;br /&gt;
*/&lt;br /&gt;
VOID handleVanillaKey(struct Window *win, UWORD code,&lt;br /&gt;
    WORD *slider_level, struct Gadget *my_gads[])&lt;br /&gt;
{&lt;br /&gt;
switch (code)&lt;br /&gt;
    {&lt;br /&gt;
    case &#039;v&#039;:&lt;br /&gt;
        /* increase slider level, but not past maximum */&lt;br /&gt;
        if (++*slider_level &amp;gt; SLIDER_MAX)&lt;br /&gt;
            *slider_level = SLIDER_MAX;&lt;br /&gt;
        IGadTools-&amp;gt;GT_SetGadgetAttrs(my_gads[MYGAD_SLIDER], win, NULL,&lt;br /&gt;
                            GTSL_Level, *slider_level,&lt;br /&gt;
                            TAG_END);&lt;br /&gt;
        break;&lt;br /&gt;
    case &#039;V&#039;:&lt;br /&gt;
        /* decrease slider level, but not past minimum */&lt;br /&gt;
        if (--*slider_level &amp;lt; SLIDER_MIN)&lt;br /&gt;
            *slider_level = SLIDER_MIN;&lt;br /&gt;
        IGadTools-&amp;gt;GT_SetGadgetAttrs(my_gads[MYGAD_SLIDER], win, NULL,&lt;br /&gt;
                            GTSL_Level, *slider_level,&lt;br /&gt;
                            TAG_END);&lt;br /&gt;
        break;&lt;br /&gt;
    case &#039;c&#039;:&lt;br /&gt;
    case &#039;C&#039;:&lt;br /&gt;
        /* button resets slider to 10 */&lt;br /&gt;
        *slider_level = 10;&lt;br /&gt;
        IGadTools-&amp;gt;GT_SetGadgetAttrs(my_gads[MYGAD_SLIDER], win, NULL,&lt;br /&gt;
                            GTSL_Level, *slider_level,&lt;br /&gt;
                            TAG_END);&lt;br /&gt;
        break;&lt;br /&gt;
    case &#039;f&#039;:&lt;br /&gt;
    case &#039;F&#039;:&lt;br /&gt;
        IIntuition-&amp;gt;ActivateGadget(my_gads[MYGAD_STRING1], win, NULL);&lt;br /&gt;
        break;&lt;br /&gt;
    case &#039;s&#039;:&lt;br /&gt;
    case &#039;S&#039;:&lt;br /&gt;
        IIntuition-&amp;gt;ActivateGadget(my_gads[MYGAD_STRING2], win, NULL);&lt;br /&gt;
        break;&lt;br /&gt;
    case &#039;t&#039;:&lt;br /&gt;
    case &#039;T&#039;:&lt;br /&gt;
        IIntuition-&amp;gt;ActivateGadget(my_gads[MYGAD_STRING3], win, NULL);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Here is where all the initialization and creation of GadTools gadgets&lt;br /&gt;
** take place.  This function requires a pointer to a NULL-initialized&lt;br /&gt;
** gadget list pointer.  It returns a pointer to the last created gadget,&lt;br /&gt;
** which can be checked for success/failure.&lt;br /&gt;
*/&lt;br /&gt;
struct Gadget *createAllGadgets(struct Gadget **glistptr, void *vi,&lt;br /&gt;
    UWORD topborder, WORD slider_level, struct Gadget *my_gads[])&lt;br /&gt;
{&lt;br /&gt;
struct NewGadget ng;&lt;br /&gt;
struct Gadget *gad;&lt;br /&gt;
&lt;br /&gt;
/* All the gadget creation calls accept a pointer to the previous gadget, and&lt;br /&gt;
** link the new gadget to that gadget&#039;s NextGadget field.  Also, they exit&lt;br /&gt;
** gracefully, returning NULL, if any previous gadget was NULL.  This limits&lt;br /&gt;
** the amount of checking for failure that is needed.  You only need to check&lt;br /&gt;
** before you tweak any gadget structure or use any of its fields, and finally&lt;br /&gt;
** once at the end, before you add the gadgets.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/* The following operation is required of any program that uses GadTools.&lt;br /&gt;
** It gives the toolkit a place to stuff context data.&lt;br /&gt;
*/&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateContext(glistptr);&lt;br /&gt;
&lt;br /&gt;
/* Since the NewGadget structure is unmodified by any of the CreateGadget()&lt;br /&gt;
** calls, we need only change those fields which are different.&lt;br /&gt;
*/&lt;br /&gt;
ng.ng_LeftEdge   = 140;&lt;br /&gt;
ng.ng_TopEdge    = 20+topborder;&lt;br /&gt;
ng.ng_Width      = 200;&lt;br /&gt;
ng.ng_Height     = 12;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;_Volume:   &amp;quot;;&lt;br /&gt;
ng.ng_TextAttr   = &amp;amp;Topaz80;&lt;br /&gt;
ng.ng_VisualInfo = vi;&lt;br /&gt;
ng.ng_GadgetID   = MYGAD_SLIDER;&lt;br /&gt;
ng.ng_Flags      = NG_HIGHLABEL;&lt;br /&gt;
&lt;br /&gt;
my_gads[MYGAD_SLIDER] = gad = IGadTools-&amp;gt;CreateGadget(SLIDER_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                    GTSL_Min,         SLIDER_MIN,&lt;br /&gt;
                    GTSL_Max,         SLIDER_MAX,&lt;br /&gt;
                    GTSL_Level,       slider_level,&lt;br /&gt;
                    GTSL_LevelFormat, &amp;quot;%2ld&amp;quot;,&lt;br /&gt;
                    GTSL_MaxLevelLen, 2,&lt;br /&gt;
                    GT_Underscore,    &#039;_&#039;,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
&lt;br /&gt;
ng.ng_TopEdge   += 20;&lt;br /&gt;
ng.ng_Height     = 14;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;_First:&amp;quot;;&lt;br /&gt;
ng.ng_GadgetID   = MYGAD_STRING1;&lt;br /&gt;
my_gads[MYGAD_STRING1] = gad = IGadTools-&amp;gt;CreateGadget(STRING_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                    GTST_String,   &amp;quot;Try pressing&amp;quot;,&lt;br /&gt;
                    GTST_MaxChars, 50,&lt;br /&gt;
                    GT_Underscore, &#039;_&#039;,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
&lt;br /&gt;
ng.ng_TopEdge   += 20;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;_Second:&amp;quot;;&lt;br /&gt;
ng.ng_GadgetID   = MYGAD_STRING2;&lt;br /&gt;
my_gads[MYGAD_STRING2] = gad = IGadTools-&amp;gt;CreateGadget(STRING_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                    GTST_String,   &amp;quot;TAB or Shift-TAB&amp;quot;,&lt;br /&gt;
                    GTST_MaxChars, 50,&lt;br /&gt;
                    GT_Underscore, &#039;_&#039;,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
&lt;br /&gt;
ng.ng_TopEdge   += 20;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;_Third:&amp;quot;;&lt;br /&gt;
ng.ng_GadgetID   = MYGAD_STRING3;&lt;br /&gt;
my_gads[MYGAD_STRING3] = gad = IGadTools-&amp;gt;CreateGadget(STRING_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                    GTST_String,   &amp;quot;To see what happens!&amp;quot;,&lt;br /&gt;
                    GTST_MaxChars, 50,&lt;br /&gt;
                    GT_Underscore, &#039;_&#039;,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
&lt;br /&gt;
ng.ng_LeftEdge  += 50;&lt;br /&gt;
ng.ng_TopEdge   += 20;&lt;br /&gt;
ng.ng_Width      = 100;&lt;br /&gt;
ng.ng_Height     = 12;&lt;br /&gt;
ng.ng_GadgetText = &amp;quot;_Click Here&amp;quot;;&lt;br /&gt;
ng.ng_GadgetID   = MYGAD_BUTTON;&lt;br /&gt;
ng.ng_Flags      = 0;&lt;br /&gt;
gad = IGadTools-&amp;gt;CreateGadget(BUTTON_KIND, gad, &amp;amp;ng,&lt;br /&gt;
                    GT_Underscore, &#039;_&#039;,&lt;br /&gt;
                    TAG_END);&lt;br /&gt;
return(gad);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Standard message handling loop with GadTools message handling functions&lt;br /&gt;
** used (GT_GetIMsg() and GT_ReplyIMsg()).&lt;br /&gt;
*/&lt;br /&gt;
VOID process_window_events(struct Window *mywin,&lt;br /&gt;
    WORD *slider_level, struct Gadget *my_gads[])&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *imsg;&lt;br /&gt;
ULONG imsgClass;&lt;br /&gt;
UWORD imsgCode;&lt;br /&gt;
struct Gadget *gad;&lt;br /&gt;
BOOL terminated = FALSE;&lt;br /&gt;
&lt;br /&gt;
while (!terminated)&lt;br /&gt;
    {&lt;br /&gt;
    Wait (1 &amp;lt;&amp;lt; mywin-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
    /* GT_GetIMsg() returns an IntuiMessage with more friendly information for&lt;br /&gt;
    ** complex gadget classes.  Use it wherever you get IntuiMessages where&lt;br /&gt;
    ** using GadTools gadgets.&lt;br /&gt;
    */&lt;br /&gt;
    while ((!terminated) &amp;amp;&amp;amp;&lt;br /&gt;
           (imsg = IGadTools-&amp;gt;GT_GetIMsg(mywin-&amp;gt;UserPort)))&lt;br /&gt;
        {&lt;br /&gt;
        /* Presuming a gadget, of course, but no harm...&lt;br /&gt;
        ** Only dereference this value (gad) where the Class specifies&lt;br /&gt;
        ** that it is a gadget event.&lt;br /&gt;
        */&lt;br /&gt;
        gad = (struct Gadget *)imsg-&amp;gt;IAddress;&lt;br /&gt;
&lt;br /&gt;
        imsgClass = imsg-&amp;gt;Class;&lt;br /&gt;
        imsgCode = imsg-&amp;gt;Code;&lt;br /&gt;
&lt;br /&gt;
        /* Use the toolkit message-replying function here... */&lt;br /&gt;
        IGadTools-&amp;gt;GT_ReplyIMsg(imsg);&lt;br /&gt;
&lt;br /&gt;
        switch (imsgClass)&lt;br /&gt;
            {&lt;br /&gt;
            /*  --- WARNING --- WARNING --- WARNING --- WARNING --- WARNING ---&lt;br /&gt;
            ** GadTools puts the gadget address into IAddress of IDCMP_MOUSEMOVE&lt;br /&gt;
            ** messages.  This is NOT true for standard Intuition messages,&lt;br /&gt;
            ** but is an added feature of GadTools.&lt;br /&gt;
            */&lt;br /&gt;
            case IDCMP_GADGETDOWN:&lt;br /&gt;
            case IDCMP_MOUSEMOVE:&lt;br /&gt;
            case IDCMP_GADGETUP:&lt;br /&gt;
                handleGadgetEvent(mywin, gad, imsgCode, slider_level, my_gads);&lt;br /&gt;
                break;&lt;br /&gt;
            case IDCMP_VANILLAKEY:&lt;br /&gt;
                handleVanillaKey(mywin, imsgCode, slider_level, my_gads);&lt;br /&gt;
                break;&lt;br /&gt;
            case IDCMP_CLOSEWINDOW:&lt;br /&gt;
                terminated = TRUE;&lt;br /&gt;
                break;&lt;br /&gt;
            case IDCMP_REFRESHWINDOW:&lt;br /&gt;
                /* With GadTools, the application must use GT_BeginRefresh()&lt;br /&gt;
                ** where it would normally have used BeginRefresh()&lt;br /&gt;
                */&lt;br /&gt;
                IGadTools-&amp;gt;GT_BeginRefresh(mywin);&lt;br /&gt;
                IGadTools-&amp;gt;GT_EndRefresh(mywin, TRUE);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Prepare for using GadTools, set up gadgets and open window.&lt;br /&gt;
** Clean up and when done or on error.&lt;br /&gt;
*/&lt;br /&gt;
VOID gadtoolsWindow(VOID)&lt;br /&gt;
{&lt;br /&gt;
struct TextFont *font;&lt;br /&gt;
struct Screen   *mysc;&lt;br /&gt;
struct Window   *mywin;&lt;br /&gt;
struct Gadget   *glist, *my_gads[4];&lt;br /&gt;
void            *vi;&lt;br /&gt;
WORD            slider_level = 5;&lt;br /&gt;
UWORD           topborder;&lt;br /&gt;
&lt;br /&gt;
/* Open topaz 8 font, so we can be sure it&#039;s openable&lt;br /&gt;
** when we later set ng_TextAttr to &amp;amp;Topaz80:&lt;br /&gt;
*/&lt;br /&gt;
if (NULL == (font = IGraphics-&amp;gt;OpenFont(&amp;amp;Topaz80)))&lt;br /&gt;
    errorMessage( &amp;quot;Failed to open Topaz 80&amp;quot;);&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    if (NULL == (mysc = IIntuition-&amp;gt;LockPubScreen(NULL)))&lt;br /&gt;
        errorMessage( &amp;quot;Couldn&#039;t lock default public screen&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
        {&lt;br /&gt;
        if (NULL == (vi = IGadTools-&amp;gt;GetVisualInfo(mysc, TAG_END)))&lt;br /&gt;
            errorMessage( &amp;quot;GetVisualInfo() failed&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
            {&lt;br /&gt;
            /* Here is how we can figure out ahead of time how tall the  */&lt;br /&gt;
            /* window&#039;s title bar will be:                               */&lt;br /&gt;
            topborder = mysc-&amp;gt;WBorTop + (mysc-&amp;gt;Font-&amp;gt;ta_YSize + 1);&lt;br /&gt;
&lt;br /&gt;
            if (NULL == createAllGadgets(&amp;amp;glist, vi, topborder,&lt;br /&gt;
                                         slider_level, my_gads))&lt;br /&gt;
                errorMessage( &amp;quot;createAllGadgets() failed&amp;quot;);&lt;br /&gt;
            else&lt;br /&gt;
                {&lt;br /&gt;
                if (NULL == (mywin = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                        WA_Title,     &amp;quot;GadTools Gadget Demo&amp;quot;,&lt;br /&gt;
                        WA_Gadgets,   glist,      WA_AutoAdjust,    TRUE,&lt;br /&gt;
                        WA_Width,       400,      WA_MinWidth,        50,&lt;br /&gt;
                        WA_InnerHeight, 140,      WA_MinHeight,       50,&lt;br /&gt;
                        WA_DragBar,    TRUE,      WA_DepthGadget,   TRUE,&lt;br /&gt;
                        WA_Activate,   TRUE,      WA_CloseGadget,   TRUE,&lt;br /&gt;
                        WA_SizeGadget, TRUE,      WA_SimpleRefresh, TRUE,&lt;br /&gt;
                        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW |&lt;br /&gt;
                            IDCMP_VANILLAKEY | SLIDERIDCMP | STRINGIDCMP |&lt;br /&gt;
                            BUTTONIDCMP,&lt;br /&gt;
                        WA_PubScreen, mysc,&lt;br /&gt;
                        TAG_END)))&lt;br /&gt;
                    errorMessage( &amp;quot;OpenWindow() failed&amp;quot;);&lt;br /&gt;
                else&lt;br /&gt;
                    {&lt;br /&gt;
                    /* After window is open, gadgets must be refreshed with a&lt;br /&gt;
                    ** call to the GadTools refresh window function.&lt;br /&gt;
                    */&lt;br /&gt;
                    IGadTools-&amp;gt;GT_RefreshWindow(mywin, NULL);&lt;br /&gt;
&lt;br /&gt;
                    process_window_events(mywin, &amp;amp;slider_level, my_gads);&lt;br /&gt;
&lt;br /&gt;
                    IIntuition-&amp;gt;CloseWindow(mywin);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            /* FreeGadgets() even if createAllGadgets() fails, as some&lt;br /&gt;
            ** of the gadgets may have been created...If glist is NULL&lt;br /&gt;
            ** then FreeGadgets() will do nothing.&lt;br /&gt;
            */&lt;br /&gt;
            IGadTools-&amp;gt;FreeGadgets(glist);&lt;br /&gt;
            IGadTools-&amp;gt;FreeVisualInfo(vi);&lt;br /&gt;
            }&lt;br /&gt;
        IIntuition-&amp;gt;UnlockPubScreen(NULL, mysc);&lt;br /&gt;
        }&lt;br /&gt;
    IGraphics-&amp;gt;CloseFont(font);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Open all libraries and run.  Clean up when finished or on error..&lt;br /&gt;
*/&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  if (IIntuition == NULL)&lt;br /&gt;
    errorMessage( &amp;quot;Requires V50 intuition.library&amp;quot;);&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (IGraphics == NULL))&lt;br /&gt;
        errorMessage( &amp;quot;Requires V50 graphics.library&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      struct Library *GadToolsBase = IExec-&amp;gt;OpenLibrary(&amp;quot;gadtools.library&amp;quot;, 50);&lt;br /&gt;
      IGadTools = (struct GadToolsIFace*)IExec-&amp;gt;GetInterface(GadToolsBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
      if (IGadTools == NULL)&lt;br /&gt;
        errorMessage( &amp;quot;Requires V50 gadtools.library&amp;quot;);&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        gadtoolsWindow();&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IGadTools);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(GadToolsBase);&lt;br /&gt;
      }&lt;br /&gt;
      IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
      IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
    }&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
  }&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Restrictions on GadTools Gadgets ===&lt;br /&gt;
&lt;br /&gt;
There is a strict set of functions and operations that are permitted on GadTools gadgets. Even if a technique is discovered that works for a particular case, be warned that it cannot be guaranteed and should not be used. If the trick concocted only works most of the time, it may introduce subtle problems in the future.&lt;br /&gt;
&lt;br /&gt;
Never selectively or forcibly refresh gadgets. The only gadget refresh that should ever be performed is the initial GT_RefreshWindow() after a window is opened with GadTools gadgets attached. It is also possible to add gadgets after the window is opened by calling AddGlist() and RefreshGlist() followed by GT_RefreshWindow(). These refresh functions should not be called at any other time.&lt;br /&gt;
&lt;br /&gt;
GadTools gadgets may not overlap with each other, with other gadgets or with other imagery. Doing this to modify the gadget&#039;s appearance is not supported.&lt;br /&gt;
&lt;br /&gt;
GadTools gadgets may not be selectively added or removed from a window. This has to do with the number of Intuition gadgets that each call to CreateGadget() produces and with refresh constraints.&lt;br /&gt;
&lt;br /&gt;
Never use OnGadget() or OffGadget() or directly modify the GFLG_DISABLED Flags bit. The only approved way to disable or enable a gadget is to use GT_SetGadgetAttrs() and the GA_Disabled tag. Those kinds of GadTools gadgets that do not support GA_Disabled may not be disabled (for now).&lt;br /&gt;
&lt;br /&gt;
The application should never write into any of the fields of the Gadget structure or any of the structures that hang off it, with the exception noted earlier for GENERIC_KIND gadgets. Avoid making assumptions about the contents of these fields unless they are explicitly programmer fields (GadgetID and UserData, for example) or if they are guaranteed meaningful (Left, Top, Width, Height, Flags). On occasion, the program is specifically invited to read a field, for example the StringInfo-&amp;amp;gt;Buffer field.&lt;br /&gt;
&lt;br /&gt;
GadTools gadgets may not be made relative sized or relative positioned. This means that the gadget flags GFLG_RELWIDTH, GFLG_RELHEIGHT, GFLG_RELBOTTOM and GFLG_RELRIGHT may not be specified. The activation type of the gadget may not be modified (for example changing GACT_IMMEDIATE to GACT_RELVERIFY). The imagery or the highlighting method may not be changed.&lt;br /&gt;
&lt;br /&gt;
These restrictions are not imposed without reason; subtle or blatant problems may arise now or in future versions of GadTools for programs that violate these guidelines.&lt;br /&gt;
&lt;br /&gt;
=== Documented Side-Effects ===&lt;br /&gt;
&lt;br /&gt;
There are certain aspects of the behavior of GadTools gadgets that should not be depended on. This will help current applications remain compatible with future releases of the GadTools library.&lt;br /&gt;
&lt;br /&gt;
When using GT_FilterIMsg() and GT_PostFilterIMsg(), never make assumptions about the message before or after filtering. I.e., do not interpret the unfiltered message, assume that it will or will not result in certain kinds of filtered message or assume it will result in a consumed message (i.e., when GT_FilterIMsg() returns NULL).&lt;br /&gt;
&lt;br /&gt;
IDCMP_INTUITICKS messages are consumed when a scroller&#039;s arrows are repeating. That is, IDCMP_INTUITICKS will not be received while the user is pressing a scroller arrows. Do not depend or rely on this side effect, though, it will not necessarily remain so in the future.&lt;br /&gt;
&lt;br /&gt;
A single call to CreateGadget() may create one or more actual gadgets. These gadgets, along with the corresponding code in GadTools, define the behavior of the particular kind of GadTools gadget requested. Only the behavior of these gadgets is documented, the number or type of actual gadgets is subject to change. Always refer to the gadget pointer received from CreateGadget() when calling GT_SetGadgetAttrs(). Never refer to other gadgets created by GadTools, nor create code which depends on their number or form.&lt;br /&gt;
&lt;br /&gt;
For text-display gadgets, the GTTX_CopyText tag does not cause the text to be copied when the text is later changed with GTTX_Text.&lt;br /&gt;
&lt;br /&gt;
The PLACETEXT ng_Flags are currently ignored by GENERIC_KIND gadgets. However, this may not always be so.&lt;br /&gt;
&lt;br /&gt;
All GadTools gadgets set GADTOOL_TYPE in the gadget&#039;s GadgetType field. Do not use this flag to identify GadTools gadgets, as this flag is not guaranteed to be set in the future.&lt;br /&gt;
&lt;br /&gt;
The palette gadget subdivides its total area into the individual color squares. Do not assume that the subdivision algorithm won&#039;t change.&lt;/div&gt;</summary>
		<author><name>Costel Mincea</name></author>
	</entry>
</feed>