https://wiki.amigaos.net/w/api.php?action=feedcontributions&user=Alexandre+Balaban&feedformat=atom AmigaOS Documentation Wiki - User contributions [en] 2024-03-28T21:57:36Z User contributions MediaWiki 1.34.0 https://wiki.amigaos.net/w/index.php?title=AmigaOne_X1000&diff=7303 AmigaOne X1000 2014-03-11T11:37:17Z <p>Alexandre Balaban: /* X1000 fails to boot, &quot;Failed to load aigaboot.of: Insufficient memeory&quot; */ Typo correction</p> <hr /> <div>The AmigaOne X1000 is a powerful PowerPC-based high-end computer now available for AmigaOS.<br /> <br /> = Key Specifications =<br /> <br /> * ATX form factor<br /> <br /> * Dual core PWRficient PA6T-1682M 1.8 Ghz PowerISA v2.04+ CPU<br /> <br /> * Xena Co-processor running at 500 Mhz with a XCore XS1-L2 124 SDS<br /> <br /> * ATI Radeon HD 1GB 4650 Graphics Card<br /> <br /> * 7.1 channel HD Audio<br /> <br /> * Memory 4xDDR2 SDRAM Slots<br /> <br /> * 10 USB 2.0<br /> <br /> * 1x Gigabit Ethernet<br /> <br /> * A range of PCIe and PCI slots<br /> <br /> * A Xorro and Compact Flash slot<br /> <br /> * 2x RS-232 serial ports<br /> <br /> * 4x SATA2 connectors<br /> <br /> * 1x PATA connector<br /> <br /> * 1x JTAG connector<br /> <br /> = Manufacturer =<br /> <br /> [http://www.a-eon.com A-Eon Technology] is the manufacturer of the AmigaOne X1000 motherboard.<br /> <br /> [http://www.amigakit.com AmigaKit] is the worldwide distributor of the AmigaOne X1000 System.<br /> <br /> = Frequently Asked Questions =<br /> <br /> == When will Mixer be upgraded to support the X1000? ==<br /> <br /> The X1000's HDAudio chip does not support monitoring. As such, the Mixer will never work on the X1000 since the hardware does not exist.<br /> <br /> A software only solution may be possible to allow for monitoring but that will use up system resources and may or may not work with Mixer.<br /> <br /> = Troubleshooting Tips =<br /> <br /> == X1000 Won't Reboot Reliably ==<br /> <br /> # Turn off power to PSU.<br /> # Remove plug from PSU.<br /> # Remove power connector from the motherboard.<br /> # Take out one RAM card leaving only one to the right of the PA6T CPU (i.e. 3rd slot from the left).<br /> # While the RAM card is out check the slots 2 &amp; 3 for dust build up (the CPU fan blows dust onto them) and use a small clean brush to remove it.<br /> # Plug power connector back on to the motherboard.<br /> # Connect PSU power &amp; switch on PSU.<br /> # Turn on X1000.<br /> <br /> If the X1000 boots up to Workbench, press and hold the power button for 3-5 seconds &amp; it should shut off.<br /> <br /> ==X1000 CPU temperatures are rising==<br /> <br /> # Clean both fan filters on the front fan openings.<br /> # Clean the fan filter UNDER the power supply unit.<br /> <br /> ==X1000 fails to boot, &quot;Loading&quot; indicator goes full red and then stops==<br /> <br /> # This will happen when booting an older workbench with a newer graphics card.<br /> # You'll need to update your PCIGraphics.card and RadeonHD.chip, or revert to an HD4000 series graphics card.<br /> <br /> ==X1000 fails to boot==<br /> <br /> May also happen if non-bootable media is left in the CD/DVD drive.<br /> <br /> ==X1000 fails to boot, &quot;Failed to load amigaboot.of: Insufficient memory&quot;==<br /> <br /> # Relax your memory isn't fried.<br /> # Check the SATA cable to your DVDRW drive (top left on the motherboard)<br /> # Don't forget to check the power cable too.<br /> <br /> For some reason CFE reports insufficient memory errors when it can't find an optical drive.<br /> <br /> ==X1000 won't boot (black screen, no boot at all) with only 3 LEDs lighting on motherboard==<br /> <br /> Check the CMOS battery.<br /> <br /> == X1000 Keyboard does not work while booting==<br /> <br /> Plug the keyboard into the USB connector closest to the white audio jack.<br /> <br /> This is &quot;upper left&quot; as you look at the back of the case. (ref Sys:documentation/X1000_QuickstartGuide.pdf page 9)<br /> <br /> Do not plug a keyboard with a built in hub in that socket, CFE does not approve.<br /> <br /> ==Creating a new boot CD step by step==<br /> <br /> This is taken from a post on Amigaworld.net by Lyle Haze.<br /> <br /> If you have upgraded to a newer graphics card, any old boot <br /> CDs that don't have the new RadeonHD drivers will not boot. <br /> Here is a simple way to create a more useful CD.<br /> <br /> You'll need to go back to a compatible graphics card to get<br /> running long enough to do this. I hope your original video<br /> card is still available to you.<br /> <br /> Requirements:<br /> You have a Bootable Recovery CD, like &quot;AmigaOS 4.1 Update 5&quot;.<br /> Your current Workbench has been upgraded with the new RadeonHD drivers.<br /> You have a blank CDROM disk, and a drive capable of burning it.<br /> <br /> &lt;pre&gt;<br /> Step by step:<br /> Check Sys:Utilities for &quot;AmiDVD&quot;. If not there, insert the <br /> recovery CD and run the &quot;Extras Installer&quot;, then select AmiDVD<br /> and allow it to install.<br /> <br /> With the recoveryCD still in the drive, open a Shell and enter<br /> the following commands:<br /> makedir ram:BootVol<br /> copy CD0: ram:BootVol all clone<br /> copy sys:Kickstart/PCIGraphics.card ram:BootVol/System/Kickstart<br /> copy sys:Kickstart/RadeonHD.chip ram:BootVol/System/Kickstart<br /> <br /> then make a note of the exact volume name of CD0:. Mine was<br /> &quot;AmigaOS 4.1 Update 5&quot; for this example<br /> <br /> Now remove the recovery CD and replace it with a blank CDROM.<br /> <br /> Open Sys:Utilities/AmiDVD/AmiDVD<br /> From the &quot;CreateImage&quot; Tab, Set source to RAM:BootVol, then<br /> set VolumeName to &quot;AmigaOS 4.1 Update 5&quot;. Select Bootable AmigaOS4,<br /> and finally select &quot;Create Image&quot;.<br /> <br /> Now on to tab2, &quot;Burn Image&quot;, and click on &quot;Burn CD-R&quot;<br /> &lt;/pre&gt;<br /> That's it! The resulting disk should be identical to the original,<br /> but will boot properly with newer graphics cards.<br /> <br /> Remember, this new boot image is under the same agreement that the<br /> RadeonHD drivers were distributed under. it is NOT OK to share <br /> this disk with others.<br /> <br /> ==Intermittent power on/off problems==<br /> <br /> Check all PCI(e) cards are securely in their slots.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Intuition_Screens&diff=6697 Intuition Screens 2013-12-21T23:47:09Z <p>Alexandre Balaban: /* A Custom Screen Example */ Fixed missing '}' and bad indentation</p> <hr /> <div>{{WIP}}<br /> == Intuition Screens ==<br /> <br /> Intuition screens are the basis of any display Intuition can make. Screens determine the fundamental characteristics of the display such as the resolution and palette and they set up the environment for multiple, overlapping windows that makes it possible for each application to have its own separate visual context. This section shows how to use existing screens and how to create new screens.<br /> <br /> === Classic Intuition Screens ===<br /> <br /> Intuition screens have evolved since they were closely tied to the Classic Amiga's hardware chip set. See [[Classic_Intuition_Screens|Classic Intuition Screens]] for more information on the now obsolete API that was used to work with screens.<br /> <br /> == Types of Screens ==<br /> <br /> Screens are important because they determine the basic resolution and maximum number of colors in the display. Once a screen is set up, these attributes cannot be changed so any graphics work done on a given screen is restricted to that screen's resolution and number of colors. Hence, the type of screen used is a basic design decision.<br /> <br /> With Intuition screens, a video display can be created in any one of the many Amiga ''display modes''. The basic parameters of the video display such as resolution, total size, frame rate, genlock compatibility, support of screen movement and number of colors are defined by these modes.<br /> <br /> Many display modes are available. All these display modes, including the specialized modes, are integrated through the graphics library ''display database''. See [[Graphics_Primitives|Graphics Primitives]] for a complete list of all Amiga display modes.<br /> <br /> === Multiple Screens ===<br /> <br /> All Intuition display objects (such as windows and menus) take graphical characteristics from the screen. These objects are restricted to the same resolution and maximum number of colors as the screen they operate in. Other characteristics such as the palette, pens and fonts are inherited from the screen (but may be changed on a case by case basis).<br /> <br /> This is not too much of a restriction because the Amiga can maintain multiple screens in memory at the same time. In other words, one application can use a high resolution screen (with 16 colors) while another application uses a low resolution screen (with 32 colors) at the same time. Screens typically take up the entire viewing area so only one is usually visible. But screens can be moved up and down or rearranged allowing the user (or application) to move between screens easily.<br /> <br /> === Public Screens and Custom Screens ===<br /> <br /> An application may choose to use an existing screen or to create its own screen. For instance, the normal Amiga startup process opens the Workbench screen (Workbench is the Amiga's default user interface). Any application is free to use the Workbench screen instead of opening a new one. Screens that can be shared this way are called ''public'' screens.<br /> <br /> ''Public screens'' allow applications to open windows on them. Any screen may be set up as a public screen so that other applications may use it.<br /> <br /> The use of an existing public screen, like the Workbench screen, requires little effort by the application and does not use up any memory. However, using Workbench or another existing public screen means some flexibility is lost; the resolution, maximum number of colors and other attributes are already set. If the application cannot function under these limitations, it may open its own ''custom'' screen.<br /> <br /> ''Custom screens'' allow for complete control of the display space so an application can get exactly the kind of display it wants. However, since creating a new, custom screen uses up memory, they should only be used when there are no suitable public screens available.<br /> <br /> Owners of a custom screen can keep their screen private, or they may allow other applications to share their screen by registering the screen with the operating system as a public screen. See the section on &quot;Public Screen Functions&quot; below for more about public screens and Workbench.<br /> <br /> === Screen Components ===<br /> <br /> Screens have very little visual impact, they simply provide a resolution specific area to place other objects such as windows and menus. Screens have no borders. Only the title bar marks the screen limits (specifying the left and top edges, and the width of the screen), and the title bar may be hidden, or obscured by graphics or windows.<br /> <br /> The title bar also serves as the menu bar when the user presses the menu button on the mouse. The menu bar area is shared by all applications operating within the screen.<br /> <br /> Within the title bar, there are two gadgets: a screen drag gadget and a depth-arrangement gadget. The screen drag gadget allows the screen to be moved up and down. The depth-arrangement gadget allows the screen to be placed in front or behind all other screens.<br /> <br /> [[File:LibFig3-1.png|frame|center|An Intuition Screen (Workbench)]]<br /> <br /> Screens are always rectangular, and the areas at the sides and bottom of the display that are not within the screen's limits are filled with the background color. The area above all visible screens is filled with the background color of the highest screen. These areas surrounding the screen (normally unused) are known as the ''overscan'' area. The Amiga display system allows the overscan area to be used for graphics under special circumstances (see the section on &quot;Overscan and the Display Clip&quot; later in this chapter).<br /> <br /> == Custom Screen Functions ==<br /> <br /> All applications require a screen to work in. This can be an existing, public screen or a new, custom screen created by the application itself. To create a new, custom screen to work with, you call OpenScreenTags().<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Custom Screen Functions<br /> | OpenScreenTags() || rowspan=&quot;2&quot; | Create a new, custom screen from a tag list. Use either one of these to open any screen.<br /> |-<br /> | OpenScreenTagList()<br /> |-<br /> | CloseScreen() || Close a custom screen and free the memory it used.<br /> |}<br /> <br /> === Creating a new Custom Screen ===<br /> <br /> There are two functions you can use to open a custom screen: OpenScreenTags() or OpenScreenTagList().<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Screen *OpenScreenTagList( struct NewScreen * , struct TagItem *);<br /> struct Screen *OpenScreenTags( struct NewScreen *, ULONG, ULONG, ... );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The &quot;Screen Attributes&quot; section below contains a complete list of all the tag options available for setting up an Intuition screen. For a general description of tag items, see [[Utility_Library|Utility Library]].<br /> <br /> ==== A Custom Screen Example ====<br /> <br /> There are so many tag options available with screens it can be a bit overwhelming. Before discussing more details, it may be helpful to look at a simple example. The code below opens a new, custom screen using the OpenScreenTags() call. The example uses just two tag items (SA_Depth and SA_Pens) which provide the minimum attributes needed to make a screen that uses the 3D look of Intuition. (See the section on &quot;DrawInfo and the 3D Look&quot; below for more information.)<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* newlookscreen.c<br /> ** open a screen with the &quot;new look&quot;.<br /> */<br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;intuition/intuition.h&gt;<br /> #include &lt;intuition/screens.h&gt;<br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> #include &lt;proto/intuition.h&gt;<br /> <br /> /* Simple routine to demonstrate opening a screen with the new look.<br /> ** Simply supply the tag SA_Pens along with a minimal pen specification,<br /> ** Intuition will fill in all unspecified values with defaults.<br /> ** Since we are not supplying values, all are Intuition defaults.<br /> */<br /> int main(int argc, char **argv)<br /> {<br /> uint16 pens[] = { ~0 };<br /> <br /> struct Library *IntuitionBase = IExec-&gt;OpenLibrary(&quot;intuition.library&quot;, 50);<br /> struct IntuitionIFace *IIntuition = (struct IntuitionIFace*)IExec-&gt;GetInterface(IntuitionBase, &quot;main&quot;, 1, NULL);<br /> if (IIntuition != NULL)<br /> {<br /> /* The screen is opened two bitplanes deep so that the<br /> ** new look will show-up better.<br /> */<br /> struct Screen *my_screen = IIntuition-&gt;OpenScreenTags(NULL,<br /> SA_Pens, pens,<br /> SA_Depth, 2,<br /> TAG_END);<br /> if (my_screen != NULL)<br /> { <br /> /* screen successfully opened */<br /> IDOS-&gt;Delay(30); /* normally the program would be here */<br /> <br /> IIntuition-&gt;CloseScreen(my_screen);<br /> }<br /> <br /> IExec-&gt;DropInterface((struct Interface*)IIntuition);<br /> IExec-&gt;CloseLibrary(IntuitionBase);<br /> return 0;<br /> }<br /> }&lt;/syntaxhighlight&gt;<br /> <br /> ==== Return Values from OpenScreenTagList() ====<br /> <br /> OpenScreenTagList() and its variants return a pointer to a Screen structure on the successful creation of a new screen and NULL on failure. The call also supports extended error codes on failure. The error returns provide information on the type of failure, giving the application a greater chance of recovery. To get the extended error code, you need to use the SA_ErrorCode tag; the code itself will be placed into the LONG pointed to by the Item.ti_Data field. Here are the codes:<br /> <br /> ; OSERR_NOMONITOR<br /> : The monitor needed to display the requested mode is not available. An example of this error would be opening a Productivity mode screen on a system without a VGA or multisync monitor.<br /> <br /> ; OSERR_NOCHIPS<br /> : Newer custom chips are required for this screen mode. For instance, the ECS Denise is required for the productivity modes.<br /> <br /> ; OSERR_NOMEM<br /> : Could not allocate enough memory.<br /> <br /> ; OSERR_NOCHIPMEM<br /> : Could not allocate enough Chip memory.<br /> <br /> ; OSERR_PUBNOTUNIQUE<br /> : Could not create public screen'name already used. The system requires that public screen names be unique.<br /> <br /> ; OSERR_UNKNOWNMODE<br /> : Display mode requested was not recognized. The system does not understand the value specified with the SA_DisplayID tag.<br /> <br /> ==== Closing the Screen ====<br /> <br /> When an application has finished using a screen, the memory that the screen occupied should be returned to the system by calling CloseScreen(). Normally, an application should close ''only'' those screens that it created. CloseScreen() returns a boolean value, TRUE for success and FALSE for failure. CloseScreen() can fail if the screen is public and another task is still using the screen.<br /> <br /> === Screen Attributes ===<br /> <br /> The sections above discuss only the basic functions and screen types that Intuition programmers need to understand to create a custom screen. Intuition supports an astonishing number of additional display features and options. In this section and the sections to follow, the finer points of screen attributes and the functions that control them are presented.<br /> <br /> Screen attributes are specified using the tag item scheme described [[Utility_Library|Utility Library]]. Therefore, the screen attributes are listed here by tag values.<br /> <br /> ; SA_ErrorCode<br /> : Extended error code. Data is a pointer to a long which will contain the error code on return if OpenScreenTagList() returns NULL. The error codes are described above.<br /> <br /> ; SA_Left, SA_Top<br /> : Initial screen position (left edge and top edge). Data is a long, signed value. Offsets are relative to the text overscan rectangle.<br /> <br /> : If SA_Left is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Width is not specified or is specified as STDSCREENWIDTH, then the left edge of the screen will default to the left edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Width is specified, then the left edge defaults to zero (text overscan rectangle left edge). Likewise, the top edge may, independent of the left edge value, default to zero or to the top edge of the actual display clip. If SA_Top is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Height is not specified or specified as STDSCREENHEIGHT, then the top edge of the screen will default to the top edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Height is specified, then the top edge defaults to zero (text overscan rectangle top edge).<br /> <br /> : When opening a full sized overscan screen, SA_Left should be set to the MinX value of the display clip Rectangle used for the screen and SA_Top should be set to the MinY value of the display clip. This may be taken from the defaults, as explained above, or explicitly set by the application. See the section below on &quot;Overscan and the Display Clip&quot; and the OpenScreen() Autodoc for more details.<br /> <br /> : If your screen is larger than your display clip, you may wish to set the SA_Left and SA_Top to values less than your display clip MinX and MinY in order to center a large screen on a smaller display. For an example of how to open a centered overscan screen, see &quot;module/screen.c&quot; in the [[IFF_Source_Code|IFF Source Code]].<br /> <br /> ; SA_Width, SA_Height<br /> : Screen dimensions. Data is a long, unsigned value. These may be larger, smaller or the same as the dimensions of the display clip Rectangle. The use of STDSCREENWIDTH and STDSCREENHEIGHT will make the screen size equal to the display clip size.<br /> <br /> : To calculate the width of the display clip Rectangle, subtract the MinX value from the MaxX value plus one. Similarly, the height of the display clip may be calculated by subtracting the MinY value from the MaxY value plus one.<br /> <br /> ; SA_Depth<br /> : Screen bitmap depth. Data is a long, unsigned value. The depth of the screen determines the number of available colors. See the &quot;Graphics Primitives&quot; for more information on hardware limitations of the display. Do not set the depth to a value greater than that supported by the specific display mode. This information is available to the application through the graphics library display database. The default is one bitplane.<br /> <br /> ; SA_DisplayID<br /> : Extended display mode key for the screen. Data is a long, unsigned value. By using DisplayIDs and the display database, applications can open a screen in any display mode available on a user's system, including PAL and NTSC modes. See the discussion of the display database in the &quot;Graphics Primitives&quot; chapter and the include file &amp;lt;graphics/displayinfo.h&amp;gt; for more information.<br /> <br /> ; SA_Pens<br /> : Pen specification for the screen. Data is a pointer to a UWORD array terminated with ¬†0, as found in the DrawInfo structure. Specifying the SA_Pens tag informs the system that the application is prepared to handle a screen rendered with the new 3D look of Intuition. See the section below on &quot;DrawInfo and the 3D Look&quot;. Omitting this tag produces a screen with a flat look, but whose color usage is more backwards compatible.<br /> <br /> ; SA_DetailPen<br /> : Detail pen for the screen. Data is a long, unsigned value. Used for rendering details in the screen title bar and menus. Use SA_Pens beginning with V36 for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look of Intuition. Instead this value will be used as the detail pen.<br /> <br /> ; SA_BlockPen<br /> : Block pen for the screen. Data is a long, unsigned value. Used for rendering block fills in the screen title bar and menus. Use SA_Pens for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look and this value will be used as the block pen.<br /> <br /> ; SA_Title<br /> : Default screen title. Data is a pointer to a character string. This is the title displayed when the active window has no screen title or when no window is active on the screen.<br /> <br /> ; SA_Colors<br /> : Specifies initial screen palette colors. Data is a pointer to an array of ColorSpec structures, terminated by a ColorSpec structure with ColorIndex = -1. Screen colors may be changed after the screen is opened with the graphics library functions SetRGB4() and LoadRGB4(). ColorSpec colors are right-justified, four bits per gun.<br /> <br /> ; SA_FullPalette<br /> : Initialize color table to entire preferences palette (32 colors), rather than the subset from V34 and earlier, namely pens 0-3, 17-19, with remaining palette as returned by GetColorMap(). Data is a boolean value (use TRUE to set the flag). Defaults to FALSE.<br /> <br /> ; SA_Font<br /> : Data is a pointer to a TextAttr structure (defined in &amp;lt;graphics/text.h&amp;gt;) which specifies the font, size and style to use for the screen. Equivalent to NewScreen.Font.<br /> <br /> ; SA_SysFont<br /> : Alternative to SA_Font. Selects one of the preferences system fonts. Data is a long, unsigned value, with the following values defined:<br /> <br /> :{| class=&quot;wikitable&quot;<br /> | 0 || Open screen with the user's preferred fixed width font (the default).<br /> |-<br /> | 1 || Open screen with the user's preferred font, which may be proportional.<br /> |}<br /> <br /> : The Workbench screen is opened with {SA_SysFont, 1}. The following table summarizes how the font selected at OpenScreen() time effects subsequent text operations in screens and windows.<br /> <br /> : Intuition Font Selection Chart<br /> <br /> :{| class=&quot;wikitable&quot;<br /> !<br /> ! What you tell OpenScreen()<br /> ! Screen font<br /> ! Window.RPort font<br /> |-<br /> | A<br /> | NewScreen.Font = myfont<br /> | myfont<br /> | myfont<br /> |-<br /> | B<br /> | NewScreen.Font = NULL<br /> | GfxBase-&gt;DefaultFont<br /> | GfxBase-&gt;DefaultFont<br /> |-<br /> | C<br /> | {SA_Font, myfont}<br /> | myfont<br /> | myfont<br /> |-<br /> | D<br /> | {SA_SysFont, 0}<br /> | GfxBase-&amp;gt;DefaultFont<br /> | GfxBase-&amp;gt;DefaultFont<br /> |-<br /> | E<br /> | {SA_SysFont, 1}<br /> | Font Prefs Screen text<br /> | GfxBase-&amp;gt;DefaultFont<br /> |}<br /> <br /> : Notes:<br /> <br /> :* '''A''' and '''B''' are the options that existed in V34 and earlier OS versions.<br /> <br /> :* '''C''' and '''D''' are tags equivalent to '''A''' and '''B''' respectively.<br /> <br /> :* '''E''' is an option for V36 and higher. The Workbench screen uses this option.<br /> <br /> :* For myfont, any font may be used including a proportional one. This is true under all releases of the OS.<br /> <br /> :* GfxBase-&amp;gt;DefaultFont is always monospace. (This is the &quot;System Default Text&quot; from Font Preferences.)<br /> <br /> :* &quot;Font Prefs Screen&quot; text (the &quot;Screen Text&quot; choice from Font Preferences) can be monospace or proportional.<br /> <br /> : The screen's font may not legally be changed after a screen is opened. The menu bar, window titles, menu items, and the contents of a string gadget all use the screen's font. The font used for menu items can be overridden in the menu item's IntuiText structure. Under V36 and higher, the font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.<br /> <br /> : The Window.RPort font shown above is the initial font that Intuition sets in your window's RastPort. It is legal to change that subsequently with SetFont(). IntuiText rendered into a window (either through PrintIText() or as a gadget's GadgetText) defaults to the window's RastPort font, but can be overridden using its ITextFont field. Text rendered with the graphics library call Text() uses the window's RastPort font.<br /> <br /> ; SA_Type<br /> : Equivalent to the SCREENTYPE bits of the NewScreen.Type field. Data is a long, unsigned value which may be set to either CUSTOMSCREEN or PUBLICSCREEN (WBENCHSCREEN is reserved for system use). See the tags SA_BitMap, SA_Behind, SA_Quiet, SA_ShowTitle and SA_AutoScroll for the other attributes of the NewScreen.Type field.<br /> <br /> ; SA_BitMap<br /> : Use a custom bitmap for this screen. Data is a pointer to a BitMap structure. This tag is equivalent to NewScreen.CustomBitMap and implies the CUSTOMBITMAP flag of the NewScreen.Type field. The application is responsible for allocating and freeing the screen's bitmap.<br /> <br /> ; SA_Behind<br /> : Open this screen behind all other screens in the system. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENBEHIND flag of the NewScreen.Type field.<br /> <br /> ; SA_Quiet<br /> : Disable Intuition rendering into screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENQUIET flag of the NewScreen.Type field. The screen will have no visible title bar or gadgets, but dragging and depth arrangement still function. In order to completely prevent Intuition from rendering into the screen, menu operations must be disabled for each window in the screen using WFLG_RMBTRAP.<br /> <br /> ; SA_ShowTitle<br /> : Setting this flag places the screen's title bar in front of any backdrop windows that are opened on the screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SHOWTITLE flag of the NewScreen.Type field. The title bar of the screen is always displayed behind any non-backdrop windows on that screen. This attribute can be changed after the screen is open with the ShowTitle() function.<br /> <br /> ; SA_AutoScroll<br /> : Setting this flag will enable autoscroll for this screen when it is the active screen. (Currently, the screen may only be made active by activating a window in that screen either under user or application control.) Data is a boolean value (TRUE to set flag). This tag is equivalent to the AUTOSCROLL flag of the NewScreen.Type field.<br /> <br /> : Autoscroll means that screens larger than the visible display will automatically scroll when the user moves the mouse to the edge of the screen. Without this tag, the user moves the screen either by using the screen drag bar, or by pressing the mouse select button anywhere within the screen while holding down the left Amiga key and moving the mouse.<br /> <br /> ; SA_PubName<br /> : Presence of this tag means that the screen is to be a public screen. Data is a pointer to a string. The string is the name of the public screen which is used by other applications to find the screen. This tag is order dependent, specify ''before'' SA_PubSig and SA_PubTask.<br /> <br /> ; SA_PubSig, SA_PubTask<br /> : Task ID (returned by FindTask()) and signal for notification that the last window has closed on a public screen. Data for SA_PubSig is a long, unsigned value. Data for SA_PubTask is a pointer to a Task structure. These two tags are order dependent, and must be specified ''after'' the tag SA_PubName.<br /> <br /> ; SA_Overscan<br /> : Set to one of the OSCAN_ specifiers to use a system standard overscan display clip and screen dimensions (unless otherwise specified). Data is a long, unsigned value. Do not specify this tag and SA_DClip. SA_Overscan is used to get one of the standard overscan dimensions, while SA_DClip is for custom dimensions. If a display clip is not specified with either SA_Overscan or SA_DClip, the display clip defaults to OSCAN_TEXT. See the section below on &quot;Overscan and the Display Clip&quot; for more information.<br /> <br /> ; SA_DClip<br /> : Custom display clip specification. Data is a pointer to a Rectangle structure that defines the screen display clip region.<br /> <br /> == Public Screen Functions ==<br /> <br /> A public screen allows multiple applications to share a single screen thus saving memory. If your application opens a public screen, then other applications will be able to open their windows on your screen. In older versions of the operating system, only the Workbench screen could be shared so applications had to live within its limitations or use up Chip memory creating their own private, custom screens.<br /> <br /> Now the system allows any screen to be set up as a public screen so there may be many public screens in memory at once, not just Workbench. This permits the power user to set up different work environments that multiple applications can share in a way that is memory efficient (each one with a display mode appropriate to a particular job).<br /> <br /> Workbench is a special case public screen because it is the initial ''default public screen''. The ''default public screen'' is the screen applications will get when they ask for a public screen but don't specify a name. Under normal conditions, Workbench is the default public screen and is created by the system at startup time. However, keep in mind that the default public screen can be changed (it's not always guaranteed to be Workbench).<br /> <br /> {{Note|title=Screens for the Novice|text=If you're not sure what kind of screen to use, then use the default public screen. You can open a window on the default public screen without doing any screen set-up work. See the [[Intuition_Windows|Intuition Windows]] for more details.}}<br /> <br /> Generally, it is much easier to use an existing, public screen than to set up one of your own. Here are the basic functions you use to work with an existing public screen.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+Public Screen Functions<br /> |-<br /> | LockPubScreen() || Find Workbench or any other public screen; prevent it from closing while a window is opened or its attributes copied.<br /> |-<br /> | UnlockPubScreen() || Release the lock allowing the screen to later be closed.<br /> |-<br /> | SetDefaultPubScreen() || Establishes a given public screen as the default.<br /> |-<br /> | GetDefaultPubScreen() || Copies the name of the default screen to a user supplied buffer for use by the screen manager utility (the name is not needed by normal applications, use LockPubScreen(NULL) instead).<br /> |-<br /> | PubScreenStatus() || Converts a screen to private or public status.<br /> |-<br /> | SetPubScreenModes() || Controls the public screen global mode bits.<br /> |}<br /> <br /> By using an existing public screen, an application is no longer responsible for setting up the display, however, it also loses flexibility and control. It can no longer set the palette or depth, and it cannot write directly into screen memory without cooperation from the owner of the public screen. (If these limitations are too confining, the application can create a new screen instead.)<br /> <br /> === Accessing a Public Screen by Name ===<br /> <br /> The main calls for accessing an existing public screen are LockPubScreen() and UnlockPubScreen(). To use these functions you need to know the name of the public screen you want to access. If you do not know the name of the public screen or if you are not sure, you can lock the default public screen with LockPubScreen(NULL).<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Screen *LockPubScreen( UBYTE * );<br /> VOID UnlockPubScreen( UBYTE * , struct Screen *);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> These calls enable the application to determine that a public screen exists, and to ensure its continued existence while opening a window on it. This function also serves as an improvement over the old GetScreenData() function from V34 by returning a pointer to the Screen structure of the locked screen so that its attributes can be examined.<br /> <br /> Be sure to unlock the public screen when done with it. Note that once a window is open on the screen the program does not need to hold the screen lock, as the window acts as a lock on the screen. The pointer to the screen structure is valid as long as a lock on the screen is held by the application, or the application has a window open on the screen.<br /> <br /> Locks should not be held without reason. Holding unnecessary locks on screens may prevent the user from closing a public screen that has no apparent activity. Keep in mind that as long as you have a window open on a public screen, the window acts as a lock preventing the screen from closing.<br /> <br /> Shown here is a simple example of how to find the Workbench public screen using LockPubScreen() and UnlockPubScreen().<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* pubscreenbeep.c<br /> */<br /> <br /> #include &lt;exec/types.h&gt; /* Amiga data types. */<br /> #include &lt;exec/libraries.h&gt;<br /> #include &lt;intuition/intuition.h&gt; /* Lots of important Intuition */<br /> #include &lt;intuition/screens.h&gt; /* structures we will be using. */<br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/intuition.h&gt;<br /> <br /> struct IntuitionIFace *IIntuition = NULL;<br /> <br /> /* Simple example of how to find a public screen to work with.<br /> */<br /> <br /> int main(int argc, char **argv)<br /> {<br /> struct Screen *my_wbscreen_ptr; /* Pointer to the Workbench screen */<br /> <br /> /* Open the library before you call any functions */<br /> struct Library *IntuitionBase = IExec-&gt;OpenLibrary(&quot;intuition.library&quot;, 50);<br /> IIntuition = (struct IntuitionIFace*)IExec-&gt;GetInterface(IntuitionBase, &quot;main&quot;, 1, NULL);<br /> <br /> if (NULL != IIntuition)<br /> {<br /> if(NULL != (my_wbscreen_ptr = IIntuition-&gt;LockPubScreen(&quot;Workbench&quot;)))<br /> {<br /> /* OK found the Workbench screen. */<br /> /* Normally the program would be here. A window could */<br /> /* be opened or the attributes of the screen copied */<br /> IIntuition-&gt;DisplayBeep(my_wbscreen_ptr);<br /> <br /> IIntuition-&gt;UnlockPubScreen(NULL, my_wbscreen_ptr);<br /> }<br /> }<br /> <br /> IExec-&gt;DropInterface((struct Interface*)IIntuition);<br /> IExec-&gt;CloseLibrary(IntuitionBase);<br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> === The Default Public Screen and Workbench ===<br /> <br /> As mentioned earlier, Workbench is a special case public screen because it is the initial ''default public screen''. There are other reasons Workbench has a special status. Normally, it's the first thing the user sees because it is the default user interface on all Amiga computers. Many older applications written for V34 and earlier versions of the OS expect to run in the Workbench screen.<br /> <br /> Because of its close ties with the operating system, there are some extra functions available to manipulate the Workbench screen. One function which controls both Workbench and other public screens is SetPubScreenModes(). This function controls the global public screen mode bits, SHANGHAI and POPPUBSCREEN. If the SHANGHAI mode bit is set, older applications which expect to open on the Workbench screen will open instead on the default public screen (which may or may not be the Workbench screen). The POPPUBSCREEN bit controls whether public screens will be popped to the front when a window is opened. These modes are documented in the &quot;Intuition Windows&quot; chapter in the section on &quot;Windows and Screens&quot;.<br /> <br /> Other functions which control the Workbench screen are listed in the table below.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Workbench Public Screen Functions<br /> | WBenchToBack() || Move the Workbench screen behind all other screens.<br /> |-<br /> | WBenchToFront() || Move the Workbench screen in front of all other screens.<br /> |-<br /> | OpenWorkBench() || Open the Workbench screen. If the screen is already open, this call has no effect. This call will re-awaken the Workbench application if it was active when CloseWorkBench() was called.<br /> |-<br /> | CloseWorkBench() || Attempt to reclaim memory used for the Workbench screen. If successful, this call closes the screen and puts the Workbench application to sleep. This call fails if any application has windows open or locks on the Workbench screen.<br /> |}<br /> <br /> Programs can attempt to reclaim memory used by the Workbench screen by calling CloseWorkBench(). Programs that have closed Workbench, should call OpenWorkBench() as they exit or allow the user to re-open the screen through a menu or gadget.<br /> <br /> If Workbench is closed, any of the following events can re-open it: calling OpenWorkBench(); opening a window on the Workbench (including EasyRequests() such as the DOS &quot;Insert Disk&quot; requester); calling LockPubScreen(&amp;quot;Workbench&amp;quot;); calling LockPubScreen(NULL) when Workbench is the default public screen.<br /> <br /> === Taking a new Custom Screen Public ===<br /> <br /> Applications that open a new screen should consider taking the screen public. If the screen's characteristics are not very esoteric, making the screen public is useful because it allows other applications to share the working context. This makes an application more powerful and more attractive to the user because it allows the user to add supporting applications and utilities from other vendors to make a customized and integrated work environment.<br /> <br /> To make your own custom screen into a public screen that other applications may use, you give the screen a public name and then register the screen with Intuition. The screen must be declared as public in the OpenScreenTagList() call by specifying a public name string with the SA_PubName tag. The application's task ID and a signal bit may also be registered when the screen is opened with the SA_PubTask and SA_PubSig tags. If these tags are given, the system will signal your task when the last window on the screen closes.<br /> <br /> When a new public screen is opened, it starts out private so the application can perform any desired initialization (for instance, opening a backdrop window) before the screen is made public. Use the PubScreenStatus() function to make the screen public and available to other applications (or to take the screen private again, later). The screen may not be taken private or closed until all windows on the screen are closed and all locks on the screen are released. However, the screen does not need to be made private before closing it.<br /> <br /> CloseScreen() will fail if an attempt is made to close a public screen that still has visitor windows or locks on it. If the user selects close screen, but the screen will not close due to visitor windows, a requester should be displayed informing the user of the condition and instructing them to close any windows before closing the screen.<br /> <br /> === Searching the Public Screen List ===<br /> <br /> To access an existing public screen the application may take one of three approaches. To get a lock on the default public screen, either LockPubScreen(NULL) or {WA_PubScreenName, NULL} may be used.<br /> <br /> If the name of the screen is known, the application may use LockPubScreen(Name) to gain a lock on the screen as shown in the example above (or use OpenWindowTagList() with the WA_PubScreenName tag as described in the &quot;Intuition Windows&quot; chapter). Failure to lock the screen or open the window probably indicates that the screen does not exist.<br /> <br /> A third approach is to search the public screen list for a screen that meets the requirements of the application. These requirements may be related to the name or attributes of the screen. Here are the functions to use with the public screen list maintained by Intuition.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Public Screen List Functions<br /> | LockPubScreenList() || Lock the public screen list maintained by Intuition so that it may be quickly copied<br /> |-<br /> | UnlockPubScreenList() || Release the lock on the public screen list<br /> |-<br /> | NextPubScreen() || Find the next screen in the public screen list<br /> |}<br /> <br /> The main function used to access the public screen list is LockPubScreenList(). This function, intended for use by the public screen manager utility, locks the list to allow data from it to be quickly copied. The list is stored in an Exec List structure, with each node in the list being a PubScreenNode structure. See &amp;lt;intuition/screens.h&amp;gt; for details.<br /> <br /> Do not interpret the list while in a locked state, instead, copy any values required to local variables and release the lock. All required data must be copied, including the name of the screen which is not part of the structure. Pointers that reference the list or structures attached to the list are not valid after releasing the lock. Once the lock is released, the screen pointers in the list (psn_Screen) may be tested for equality against other screen pointers, but referencing any part of the screen structure from this pointer is strictly illegal. After the lock is released with UnlockPubScreenList(), the application may access the data in the screen structure by obtaining a lock on the screen using LockPubScreen() with the name of the screen.<br /> <br /> The application should only require accessing three fields in the PubScreenNode, these are ln_Name, psn_Screen and psn_Flags. The name of the public screen is maintained in the ln_Name field of the Node (psn_Node) structure. Access to other information on the screen may be done by getting a lock on this name and reading the data from the Screen structure. The screen pointer (psn_Screen) may only be used for testing against other screen pointers, never reference the screen structure from this value. Finally, the public screen flags are maintained in psn_Flags. Currently, only PSNF_PRIVATE is defined for this field. PSNF_PRIVATE indicates that the screen is not currently public.<br /> <br /> Remember that all information in the public screen list is transitory, that is, it may change at any time. Do not rely on the values in the list. The only way to ensure the existence or mode of a screen is to lock it, either directly with LockPubScreen() or by opening a window on the screen. To update the copy of the list, lock it and copy the data again. Don't forget to release the lock when finished.<br /> <br /> As an alternative to dealing with the public screen list, NextPubScreen() can be used. This call takes the name of a public screen as its argument and returns the name of the next screen in the public screen list. This helps an application move a window through the entire rotation of public screens. Repeated calls to NextPubScreen() could be used to get the names of all public screens one at a time. Keep in mind though that the list of public screens is subject to sudden change; the task that owns a public screen might close it after you obtain the name, but before you access the screen.<br /> <br /> Always use LockPubScreen() to access screen information after scanning the public screen list.<br /> <br /> === Cloning a Public Screen (Workbench) ===<br /> <br /> User preferences for screen attributes are generally reflected in the Workbench screen or in the default public screen. In some cases it may be useful to create a new screen with the same attributes.<br /> <br /> LockPubScreen() returns a pointer to the Screen structure of a specific screen. GetScreenDrawInfo() returns rendering information on the screen, such as the pen array and font used. QueryOverscan() returns the overscan information of a specific display mode (for more information, see the section on &quot;Overscan and the Display Clip&quot;).<br /> <br /> The example below shows how to use GetScreenDrawInfo() to examine the attributes of the Workbench screen so that a new screen with the same attributes can be created.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct DrawInfo *GetScreenDrawInfo( struct Screen * );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The attributes required to clone an existing screen are its width, height, depth, pens and mode. The pens and screen depth are available through the DrawInfo structure. The width and height may be obtained from the Screen structure. (The width and height may be larger than the overscan area if the screen is scrollable, and autoscroll may always be enabled as it does not effect displays smaller than or equal to the overscan area.)<br /> <br /> The screen's display mode can be obtained using the graphics library call GetVPModeID(). This call returns the display ID of an existing screen which can then be used as the data for the SA_DisplayID tag in OpenScreenTagList(). Note that the example assumes the screen should be open to the user's text overscan preference. If an exact copy of the display clip of the existing screen is required, use the VideoControl() command of the graphics library to access the ViewPortExtra structure.<br /> <br /> The colors of the screen may be copied using the graphics library calls GetRGB4(), SetRGB4(), SetRGB4CM() and LoadRGB4(). The example code does not copy the colors.<br /> <br /> The example copies the font from the cloned screen. A reasonable alternative would be to use the user's preference font, which may be accessed through the SA_SysFont tag.<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* clonescreen.c<br /> ** clone an existing public screen.<br /> */<br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;exec/memory.h&gt;<br /> #include &lt;intuition/intuition.h&gt;<br /> #include &lt;intuition/screens.h&gt;<br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> #include &lt;proto/graphics.h&gt;<br /> #include &lt;proto/intuition.h&gt;<br /> <br /> #include &lt;string.h&gt;<br /> <br /> VOID cloneScreen(CONST_STRPTR);<br /> <br /> struct IntuitionIFace *IIntuition = NULL;<br /> struct GraphicsIFace *IGraphics = NULL;<br /> <br /> /*<br /> ** Open all libraries for the cloneScreen() subroutine.<br /> */<br /> int main(int argc, char **argv)<br /> {<br /> CONST_STRPTR pub_screen_name = &quot;Workbench&quot;;<br /> <br /> struct Library *IntuitionBase = IExec-&gt;OpenLibrary(&quot;intuition.library&quot;, 50);<br /> IIntuition = (struct IntuitionIFace*)IExec-&gt;GetInterface(IntuitionBase, &quot;main&quot;, 1, NULL);<br /> <br /> struct Library *GfxBase = IExec-&gt;OpenLibrary(&quot;graphics.library&quot;, 50);<br /> IGraphics = (struct GraphicsIFace*)IExec-&gt;GetInterface(GfxBase, &quot;main&quot;, 1, NULL);<br /> <br /> if (IIntuition != NULL &amp;&amp; IGraphics != NULL)<br /> {<br /> cloneScreen(pub_screen_name);<br /> }<br /> <br /> IExec-&gt;DropInterface((struct Interface*)IGraphics);<br /> IExec-&gt;CloseLibrary(GfxBase);<br /> IExec-&gt;DropInterface((struct Interface*)IIntuition);<br /> IExec-&gt;CloseLibrary(IntuitionBase);<br /> return 0;<br /> }<br /> <br /> /* Clone a public screen whose name is passed to the routine.<br /> ** Width, Height, Depth, Pens, Font and DisplayID attributes are<br /> ** all copied from the screen.<br /> ** Overscan is assumed to be OSCAN_TEXT, as there is no easy way to<br /> ** find the overscan type of an existing screen.<br /> ** AutoScroll is turned on, as it does not hurt. Screens that are<br /> ** smaller than the display clip will not scroll.<br /> */<br /> <br /> VOID cloneScreen(CONST_STRPTR pub_screen_name)<br /> {<br /> struct Screen *my_screen;<br /> ULONG screen_modeID;<br /> UBYTE *pub_scr_font_name;<br /> UBYTE *font_name;<br /> ULONG font_name_size;<br /> struct TextAttr pub_screen_font;<br /> struct TextFont *opened_font;<br /> <br /> struct Screen *pub_screen = NULL;<br /> struct DrawInfo *screen_drawinfo = NULL;<br /> <br /> /* name is a pointer to the name of the public screen to clone */<br /> pub_screen = IIntuitino-&gt;LockPubScreen(pub_screen_name);<br /> if (pub_screen != NULL)<br /> {<br /> /* Get the DrawInfo structure from the locked screen<br /> ** This returns pen, depth and font info.<br /> */<br /> screen_drawinfo = IIntuition-&gt;GetScreenDrawInfo(pub_screen);<br /> if (screen_drawinfo != NULL)<br /> {<br /> screen_modeID = IGraphics-&gt;GetVPModeID(&amp;(pub_screen-&gt;ViewPort));<br /> if( screen_modeID != INVALID_ID )<br /> {<br /> /* Get a copy of the font<br /> ** The name of the font must be copied as the public screen may<br /> ** go away at any time after we unlock it.<br /> ** Allocate enough memory to copy the font name, create a<br /> ** TextAttr that matches the font, and open the font.<br /> */<br /> pub_scr_font_name = screen_drawinfo-&gt;dri_Font-&gt;tf_Message.mn_Node.ln_Name;<br /> font_name_size = 1 + strlen(pub_scr_font_name);<br /> font_name = IExec-&gt;AllocVecTags(font_name_size, AVT_ClearWithValue, 0, TAG_END);<br /> if (font_name != NULL)<br /> {<br /> strcpy(font_name, pub_scr_font_name);<br /> pub_screen_font.ta_Name = font_name;<br /> pub_screen_font.ta_YSize = screen_drawinfo-&gt;dri_Font-&gt;tf_YSize;<br /> pub_screen_font.ta_Style = screen_drawinfo-&gt;dri_Font-&gt;tf_Style;<br /> pub_screen_font.ta_Flags = screen_drawinfo-&gt;dri_Font-&gt;tf_Flags;<br /> <br /> opened_font = IGraphics-&gt;OpenFont(&amp;pub_screen_font);<br /> if (opened_font != NULL)<br /> {<br /> /* screen_modeID may now be used in a call to<br /> ** OpenScreenTagList() with the tag SA_DisplayID.<br /> */<br /> my_screen = IIntuition-&gt;OpenScreenTags(NULL,<br /> SA_Width, pub_screen-&gt;Width,<br /> SA_Height, pub_screen-&gt;Height,<br /> SA_Depth, screen_drawinfo-&gt;dri_Depth,<br /> SA_Overscan, OSCAN_TEXT,<br /> SA_AutoScroll, TRUE,<br /> SA_Pens, screen_drawinfo-&gt;dri_Pens,<br /> SA_Font, &amp;pub_screen_font,<br /> SA_DisplayID, screen_modeID,<br /> SA_Title, &quot;Cloned Screen&quot;,<br /> TAG_END);<br /> if (my_screen != NULL)<br /> {<br /> /* Free the drawinfo and public screen as we don't<br /> ** need them any more. We now have our own screen.<br /> */<br /> IIntuition-&gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);<br /> screen_drawinfo = NULL;<br /> IIntuition-&gt;UnlockPubScreen(pub_screen_name,pub_screen);<br /> pub_screen = NULL;<br /> <br /> IDOS-&gt;Delay(300); /* should be rest_of_program */<br /> <br /> IIntuition-&gt;CloseScreen(my_screen);<br /> }<br /> IGraphics-&gt;CloseFont(opened_font);<br /> }<br /> IExec-&gt;FreeVec(font_name);<br /> }<br /> }<br /> }<br /> }<br /> <br /> /* These are freed in the main loop if OpenScreenTagList() does<br /> ** not fail. If something goes wrong, free them here.<br /> */<br /> if (screen_drawinfo != NULL )<br /> IIntuition-&gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);<br /> if (pub_screen != NULL )<br /> IIntuition-&gt;UnlockPubScreen(pub_screen_name,pub_screen);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == DrawInfo and the 3D Look ==<br /> <br /> Whenever a new screen is created, Intuition also creates an auxiliary data structure called a DrawInfo. The DrawInfo structure provides information Intuition uses to support the 3D look and specifies graphical information for applications that use the screen. The information includes such items as aspect ratio (resolution), font, number of colors and drawing pens.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct DrawInfo<br /> {<br /> UWORD dri_Version; /* will be DRI_VERSION */<br /> UWORD dri_NumPens; /* guaranteed to be &amp;gt;= numDrIPens */<br /> UWORD *dri_Pens; /* pointer to pen array */<br /> <br /> struct TextFont *dri_Font; /* screen default font */<br /> UWORD dri_Depth; /* (initial) depth of screen bitmap */<br /> <br /> struct { /* from DisplayInfo database for initial display mode */<br /> UWORD X;<br /> UWORD Y;<br /> } dri_Resolution;<br /> <br /> ULONG dri_Flags; /* defined below */<br /> ULONG dri_Reserved[7]; /* avoid recompilation ;^) */<br /> };<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Before an application uses fields in the DrawInfo structure, it should check the version of the structure to ensure that all fields are available. If the field dri_Version is greater than or equal to the constant DRI_VERSION that the application was compiled with, it can be assured that all fields in DrawInfo that it knows about are being supported by Intuition.<br /> <br /> === The Pen Specification in DrawInfo ===<br /> <br /> The drawing pen specification in DrawInfo.dri_Pens allows applications to use appropriate colors for graphic operations such as drawing text, shading 3D objects and filling items selected by the user.<br /> <br /> Intuition has two default sets of pens, one for multi-bitplane screens and one for single bitplane screens. In addition, there is a special compatibility mode for screens that do not specify the SA_Pens tag.<br /> <br /> ; 3D Look<br /> : The is the full 3D look as found by default on the Workbench screen. Objects are drawn so that light appears to come from the upper left of the screen with shadows cast to the lower right giving them a three-dimensional look.<br /> <br /> ; Monochrome Look<br /> : It is impossible to produce the full 3D look in a single bitplane (two color) screen. Intuition provides a fallback pen specification that is used in monochrome screens with no loss of information.<br /> <br /> ; Compatible Look<br /> : Custom screens that do not provide the SA_Pens tag are assumed to have no knowledge of the pen array. They are rendered in a special version of the monochrome new look, which uses the screen's DetailPen and BlockPen to get its colors. This is provided for compatibility with V34 and older versions of the operating system.<br /> <br /> It is very easy for an application to use the default pen specification. Simply specify an empty pen specification (in C, {~0}), and Intuition will fill in all of the values with defaults appropriate for the screen. This technique is demonstrated in the first two examples listed earlier in this chapter.<br /> <br /> For certain applications, a custom pen specification is required. A custom pen specification is set up when the screen is opened by using the SA_Pens tag and a pointer to a pen array. Currently, Intuition uses nine pens to support the 3D look. The application can specify all of these, or only a few pens and Intuition will fill in the rest. Intuition will only fill in pens that are past the end of those specified by the application, there is no facility for using default values for &quot;leading&quot; pens (those at the beginning of the array) without using the defaults for the rest of the pens.<br /> <br /> Using the pen specification of an existing public screen is a bit more involved. First, the application must get a pointer to the screen structure of the public screen using the LockPubScreen() call. A copy of the screen's DrawInfo structure may then be obtained by calling GetScreenDrawInfo(). The DrawInfo structure contains a copy of the pen specification for the screen that can be used in the OpenScreenTagList() call with the SA_Pens tag. The pen array is copied to the data structures of the new screen (it is not kept as a pointer to the information passed), so the application may immediately call FreeScreenDrawInfo() and UnlockPubScreen() after the new screen is open.<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* publicscreen.c<br /> ** open a screen with the pens from a public screen.<br /> */<br /> #include &lt;exec/types.h&gt;<br /> #include &lt;intuition/intuition.h&gt;<br /> #include &lt;intuition/screens.h&gt;<br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> #include &lt;proto/intuition.h&gt;<br /> <br /> VOID usePubScreenPens(void);<br /> <br /> struct IntuitionIFace *IIntuition = NULL;<br /> <br /> /* main(): open libraries, clean up when done.<br /> */<br /> int main(int argc, char **argv)<br /> {<br /> struct Library *IntuitionBase = IExec-&gt;OpenLibrary(&quot;intuition.library&quot;, 50);<br /> IIntuition = (struct IntuitionIFace*)IExec-&gt;GetInterface(IntuitionBase, &quot;main&quot;, 1, NULL);<br /> if ( IIntuition != NULL )<br /> {<br /> usePubScreenPens();<br /> }<br /> IExec-&gt;DropInterface((struct Interface*)IIntuition);<br /> IExec-&gt;CloseLibrary(IntuitionBase);<br /> return 0;<br /> }<br /> <br /> /* Open a screen that uses the pens of an existing public screen<br /> ** (the Workbench screen in this case).<br /> */<br /> VOID usePubScreenPens(void)<br /> {<br /> struct Screen *my_screen;<br /> struct TagItem screen_tags[2];<br /> UBYTE *pubScreenName = &quot;Workbench&quot;;<br /> <br /> struct Screen *pub_screen = NULL;<br /> struct DrawInfo *screen_drawinfo = NULL;<br /> <br /> /* Get a lock on the Workbench screen */<br /> pub_screen = IIntuition-&gt;LockPubScreen(pubScreenName);<br /> if ( pub_screen != NULL )<br /> {<br /> /* get the DrawInfo structure from the locked screen */<br /> screen_drawinfo = IIntuition-&gt;GetScreenDrawInfo(pub_screen);<br /> if ( screen_drawinfo != NULL)<br /> {<br /> /* the pens are copied in the OpenScreenTagList() call,<br /> ** so we can simply use a pointer to the pens in the tag list.<br /> **<br /> ** This works better if the depth and colors of the new screen<br /> ** matches that of the public screen. Here we are forcing the<br /> ** workbench screen pens on a monochrome screen (which may not<br /> ** be a good idea). You could add the tag:<br /> ** (SA_Depth, screen_drawinfo-&gt;dri_Depth)<br /> */<br /> screen_tags[0].ti_Tag = SA_Pens;<br /> screen_tags[0].ti_Data = (ULONG)(screen_drawinfo-&gt;dri_Pens);<br /> screen_tags[1].ti_Tag = TAG_END;<br /> screen_tags[1].ti_Data = NULL;<br /> <br /> my_screen = IIntuition-&gt;OpenScreenTagList(NULL, screen_tags);<br /> if (my_screen != NULL)<br /> {<br /> /* We no longer need to hold the lock on the public screen<br /> ** or a copy of its DrawInfo structure as we now have our<br /> ** own screen. Release the screen.<br /> */<br /> IIntuition-&gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);<br /> screen_drawinfo = NULL;<br /> IIntuition-&gt;UnlockPubScreen(pubScreenName,pub_screen);<br /> pub_screen = NULL;<br /> <br /> IDOS-&gt;Delay(90); /* should be rest_of_program */<br /> <br /> IIntuition-&gt;CloseScreen(my_screen);<br /> }<br /> }<br /> }<br /> <br /> /* These are freed in the main loop if OpenScreenTagList() does<br /> ** not fail. If something goes wrong, free them here.<br /> */<br /> if ( screen_drawinfo != NULL )<br /> IIntuition-&gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);<br /> if ( pub_screen!= NULL )<br /> IIntuition-&gt;UnlockPubScreen(pubScreenName,pub_screen);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The pen specification for the Workbench screen happens to match the Intuition default specification, however, this is not required and may change in the future. To create a screen that uses the pens defined in the Workbench screen, the application must get a copy of the pen array from the Workbench screen and use this copy with the SA_Pens tag as described above.<br /> <br /> Here is a list of the pens that support the 3D look along with their uses. To read the value of a particular pen, use UWORD penvalue = myDrawInfo-&gt;dri_Pens[PENNAME], where myDrawInfo is a pointer to a DrawInfo structure and PENNAME is taken from the list below:<br /> <br /> ; DETAILPEN<br /> : Pen compatible with V34. Used to render text in the screen's title bar.<br /> <br /> ; BLOCKPEN<br /> : Pen compatible with V34. Used to fill the screen's title bar.<br /> <br /> ; TEXTPEN<br /> : Pen for regular text on BACKGROUNDPEN.<br /> <br /> ; SHINEPEN<br /> : Pen for the bright edge on 3D objects.<br /> <br /> ; SHADOWPEN<br /> : Pen for the dark edge on 3D objects.<br /> <br /> ; FILLPEN<br /> : Pen for filling the active window borders and selected gadgets.<br /> <br /> ; FILLTEXTPEN<br /> : Pen for text rendered over FILLPEN.<br /> <br /> ; BACKGROUNDPEN<br /> : Pen for the background color. Currently must be zero.<br /> <br /> ; HIGHLIGHTTEXTPEN<br /> : Pen for &quot;special color&quot; or highlighted text on BACKGROUNDPEN.<br /> <br /> === The Font Specification in DrawInfo ===<br /> <br /> Font information for a screen comes from a number of different places.<br /> <br /> The application may specify the font to be used in a screen by providing the SA_Font tag with a TextAttr structure. In this case, the font will be used by the screen and will be the default font for the RastPort of any window opening in the screen.<br /> <br /> If the application requests the user's preferred monospace font, it is taken from GfxBase-&amp;gt;DefaultFont. Any window's RastPorts are also initialized to use this same font.<br /> <br /> The screen font selected by the user from the Preferences font editor may be used for the screen by using the SA_SysFont tag. This font, the &quot;preferred screen font&quot;, may be proportional. For compatibility reasons, if this font is specified for the screen, the window's RastPort will be initialized to GfxBase-&amp;gt;DefaultFont (a non-proportional font).<br /> <br /> To access information on an open screen's font, the application may reference Screen.Font or DrawInfo.dri_Font. These fonts are identical, the DrawInfo structure simply provides an alternate method of accessing the information. Note that Screen.Font is a pointer to a TextAttr structure and that DrawInfo.dri_Font is a pointer to a TextFont structure. The application may use whichever form is best suited to its requirements.<br /> <br /> It is illegal to change the screen's font after the screen is opened. This means that the font specified in the Screen and DrawInfo structures is guaranteed to remain open as long is the screen is open.<br /> <br /> The menu bar, window titles, menu items, and the contents of a string gadget all use the screen's font. The font used for menu items can be overridden in the menu item's IntuiText structure. The font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.<br /> <br /> For more information on screen fonts, see the description of the SA_Font and SA_SysFont tags in the &quot;Screen Attributes&quot; section above.<br /> <br /> == Overscan and the Display Clip ==<br /> <br /> Screens may be larger or smaller than the defined display area (''overscan rectangle'' or ''display clip''). When a screen is smaller than the display area, the display clip acts as a &quot;container&quot; for the screen. The screen may be moved anywhere within the display clip. When a screen is larger than the display area, the display clip acts as a &quot;window&quot; into the screen. The screen may be moved so that different parts are visible. Each dimension of the screen is independent and may be larger than, the same as, or smaller than the dimensions of the display clip.<br /> <br /> The system is very flexible in its specification of screen size. Unless an application fixes its screen size with hard coded values, it should be prepared to handle the possibility that the user has changed the default overscan presets or the default monitor.<br /> <br /> Use the constants STDSCREENHEIGHT and STDSCREENWIDTH with the SA_Width and SA_Height tags to open a screen the same size as the display clip. These constants will work with any of the preset overscan values set with SA_Overscan, and with custom overscan values set with SA_DClip.<br /> <br /> === Preset Overscan Values ===<br /> <br /> Four preset overscan dimensions are provided. Applications that support overscan should use these preset values where possible since they will be tailored to each individual system. Avoid using custom values that happen to look good on a specific system. However, be aware that the size and positioning of overscan screens can be different on every system depending on how the user has set Overscan Preferences. These preset values are also dependent on the underlying display mode so keep in mind that both offset and size parameters will change under different screen modes. Overscan presets can be used, among other things, with the SA_Overscan tag to set the size of the screen's display clip or passed as an argument to QueryOverscan() to find their current overscan settings.<br /> <br /> ; OSCAN_TEXT<br /> : This overscan region is based on user preference settings and indicates a display that is completely within the visible bounds of the monitor. The View origin is set to the upper left corner of the text overscan rectangle which is the highest leftmost point known to be visible on the physical display. This position is set by the user through the Overscan Preferences editor. All screen positions and display clips are relative to this origin.<br /> <br /> ; OSCAN_STANDARD<br /> : The edges of OSCAN_STANDARD display are also based on user preferences and are set to be just outside the visible bounds of the monitor. OSCAN_STANDARD provides the smallest possible display that will fill the entire screen with no border around it. Parts of the display created with OSCAN_STANDARD may not be visible to the user.<br /> <br /> ; OSCAN_MAX<br /> : Create the largest display fully supported by Intuition and the graphics library. This is the largest size for which all enclosed sizes and positions are legal. Parts of the display created with OSCAN_MAX may not be visible to the user.<br /> <br /> ; OSCAN_VIDEO<br /> : Create the largest display, restricted by the hardware. This is the only legal size and position that is possibly (but not necessarily) larger than OSCAN_MAX. You must use the exact size and position specified. OSCAN_VIDEO does not support variable left edge, top edge positioning. Parts of the display created with OSCAN_VIDEO may not be visible to the user.<br /> <br /> If custom clipping is required, a display clip may be explicitly specified using the SA_DClip tag and a Rectangle structure specification. This custom rectangle must fit within the OSCAN_MAX rectangle, offset included. It is not permitted to specify custom rectangles whose values are in between OSCAN_MAX and OSCAN_VIDEO, nor is it permitted to specify rectangles larger than OSCAN_VIDEO. For an example of how to open a centered overscan screen based on user preferences, see the &quot;module/screen.c&quot; listing in the [[IFF_Source_Code|IFF Source Code]].<br /> <br /> Use the Graphics library call VideoControl() to find the true display clip of a screen. See the Graphics Autodocs and the chapter &quot;Graphics Primitives&quot; for more information on VideoControl(). The ViewPortExtra structure contains the display clip information.<br /> <br /> If any dimension of a screen is not equal to the equivalent display clip dimension, then the screen may be scrolled. If the screen's dimensions are smaller than the display clip, then the screen may be positioned within the display clip. If the screen is larger than the display clip, then it may be positioned such that any part of the screen is visible.<br /> <br /> AutoScroll may be activated by setting the tag SA_AutoScroll. Screens will only scroll when they are the active screen. Activate a window in the screen to make the screen active.<br /> <br /> {{Note|title=About the Default Display Clip|text=The default display clip for a screen is the entire screen, that is, the rectangle starting from the upper left corner of the screen and ending at the lower right corner of the screen. This display clip is only used if the application does not specify SA_Overscan or SA_DClip. When using this default display clip the screen will not scroll as the screen exactly fits into the clipping region.}}<br /> <br /> When opening a window in an overscanned screen, it is often useful to open it relative to the visible part of the screen rather than relative to the entire screen. Use QueryOverscan() to find the overscan region and where the screen is positioned relative to it.<br /> <br /> &lt;syntaxhighlight&gt;<br /> LONG QueryOverscan(ULONG displayID, struct Rectangle *rect, WORD overscanType );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> This example was taken from the chapter &quot;Intuition Windows&quot; in the section &quot;Visible Display Sized Window Example&quot;. The complete example is reproduced there.<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* this technique returns the text overscan rectangle of the screen that we<br /> ** are opening on. If you really need the actual value set into the display<br /> ** clip of the screen, use the VideoControl() command of the graphics library<br /> ** to return a copy of the ViewPortExtra structure. See the Graphics<br /> ** library chapter and Autodocs for more details.<br /> **<br /> ** GetVPModeID() is a graphics call...<br /> */<br /> <br /> screen_modeID = IGraphics-&gt;GetVPModeID(&amp;(pub_screen-&gt;ViewPort))))<br /> if (screen_modeID != INVALID_ID)<br /> {<br /> if ( IGraphics-&gt;QueryOverscan(screen_modeID, &amp;rect, OSCAN_TEXT) )<br /> {<br /> /* if this screen's origin is up or to the left of the */<br /> /* view origin then move the window down and to the right */<br /> left = max(0, -pub_screen-&gt;LeftEdge);<br /> top = max(0, -pub_screen-&gt;TopEdge);<br /> <br /> /* get width and height from size of display clip */<br /> width = rect.MaxX - rect.MinX + 1;<br /> height = rect.MaxY - rect.MinY + 1;<br /> <br /> /* adjust height for pulled-down screen (only show visible part) */<br /> if (pub_screen-&gt;TopEdge &gt; 0)<br /> height -= pub_screen-&gt;TopEdge;<br /> <br /> /* ensure that window fits on screen */<br /> height = min(height, pub_screen-&gt;Height);<br /> width = min(width, pub_screen-&gt;Width);<br /> <br /> /* make sure window is at least minimum size */<br /> width = max(width, MIN_WINDOW_WIDTH);<br /> height = max(height, MIN_WINDOW_HEIGHT);<br /> }<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Intuition Screens and the Graphics Library ==<br /> <br /> As previously mentioned, an Intuition screen is related to a number of underlying graphics library structures.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Graphics Data Structures Used with Screens<br /> ! Structure Name<br /> ! Description<br /> ! Include File<br /> |-<br /> | View || Root structure of the graphics display system || &amp;lt;graphics/view.h&amp;gt;<br /> |-<br /> | ViewPort || The graphics structure that corresponds to a screen || &amp;lt;graphics/view.h&amp;gt;<br /> |-<br /> | ColorMap || Contains size and pointer to the screen's color table || &amp;lt;graphics/view.h&amp;gt;<br /> |-<br /> | RastPort || Holds drawing, pen and font settings and the BitMap address || &amp;lt;graphics/rastport.h&amp;gt;<br /> |}<br /> <br /> These data structures are unified in Intuition's Screen structure (which also incorporates higher level Intuition constructs such as menus and windows). Here's a brief explanation of the graphics library structures used with Intuition.<br /> <br /> ; View<br /> : The View is the graphics structure that corresponds to the whole display, including all visible screens. The system has just one View; it's what you see on the monitor. The address of the View may be obtained from any screen by using ViewAddress().<br /> <br /> ; ViewPort<br /> : The ViewPort is the underlying graphics structure corresponding to a screen. Every screen has one ViewPort. To get the address of the ViewPort from the Screen structure, use (&amp;amp;my_screen-&amp;gt;ViewPort). From the ViewPort an application may obtain pointers to all the screen's bitplanes and to its color table.<br /> <br /> ; ColorMap<br /> : The ColorMap contains a pointer to the color table, an array of 32 WORDs for the hardware color registers. Use SetRGB4(), GetRGB4(), SetRGB4CM() and LoadRGB4() from the graphics library to access the color table. Do not read or write it directly.<br /> <br /> ; RastPort<br /> : A RastPort controls the graphics rendering to ''any'' display area (not just screens). Screens have a RastPort to allow direct rendering into the screen. Applications may find the RastPort address of a screen with (&amp;amp;my_screen-&amp;gt;RastPort). The screen's BitMap can also be found via the RastPort in the RastPort.BitMap field. This generally is not useful since applications normally render into windows.<br /> <br /> === Changing Screen Colors ===<br /> <br /> Screen colors are set at the time the screen is opened with the SA_Colors tag. If the colors need to be changed after the screen is opened, the graphics library function, LoadRGB4() should be used. To change a single entry in the color table, use SetRGB4() and SetRGB4CM(). See [[Graphics_Primitives|Graphics Primitives]] for more information on these functions.<br /> <br /> === Direct Screen Access ===<br /> <br /> Sometimes an application may want direct access to the custom screen's bitmap to use with low-level graphics library calls. This may be useful if the application needs to do custom manipulation of the display but also needs Intuition functionality. For instance, an application may want to use the graphics library primitives to perform double buffering then, when detecting user input, switch to Intuition control of the screen so that windows, gadgets and menus may be used to process the user input. If an application chooses to combine these techniques, it must take special care to avoid conflicts with Intuition rendered graphics. An example of how to do this is listed in the next section, &quot;Advanced Screen Programming&quot;.<br /> <br /> Application programs that open custom screens may use the screen's display memory in any way they choose. However, this memory is also used by Intuition for windows and other high level display components on the screen. Writing directly to the screen memory, whether through direct access or through graphics library calls that access the screen's RastPort, is not compatible with many Intuition constructs such as windows and menus.<br /> <br /> Techniques such as this require great care and understanding of the Amiga. If possible, the application should avoid these techniques and only use standard Intuition display and input processing. Directly accessing the screen's bitmap, while possible, is not recommended. A better way to access the screen display is through windows. Windows provide access to the screen through layers which perform clipping and arbitration between multiple drawing areas.<br /> <br /> Alternatives to writing directly to a screen, such as using a backdrop window, greatly limit the number of cases where an application must access screen memory. The ShowTitle() function allows the screen's title bar layer to be positioned in front of or behind any backdrop windows that are opened on the screen. Hence, a backdrop window may be created that uses the entire visible area of the monitor.<br /> <br /> Application programs that use existing public screens do not have the same freedom to access the screen's display memory as they do with custom screens. In general, public screens must be shared through the use of windows and menus rather than directly accessing the screen's display memory.<br /> <br /> {{Note|title=Use Direct Access Only On Screens You &quot;Own&quot;|text=An application may not steal the bitmap of a screen that it does not own. Stealing the Workbench screen's bitmap, or that of any other public screen, is strictly illegal. Accessing the underlying graphics structures of a screen may only be done on custom screens opened by the application itself.}}<br /> <br /> {{Note|title=Do Not Perform Layers Operations Directly|text=While layers are not part of the graphics library, it is appropriate to mention them here. Certain types of layers operations are not allowed with Intuition. You may not, for example, call SizeLayer() on a window (use SizeWindow() instead). To access layers library features with screens, use Intuition windows!}}<br /> <br /> A custom screen may be created to allow for modification of the screen's Copper list. The Copper is the display synchronized co-processor that handles the actual video display by directly affecting the hardware registers. See the ''Amiga Hardware Reference Manual'' or the graphics library chapters for more information on programming the Copper.<br /> <br /> === Limitations of the Graphics Subsystem ===<br /> <br /> If each of the visible screens does not have the same physical attributes, it may not be possible to display the data in its proper screen mode. ''Screen coercion'' is the technique that allows multiple screens with differing physical attributes to be displayed simultaneously. When a coerced screen is visible, its aspect ratio and colors may appear significantly changed. This is normal and the screen will be displayed correctly when it is the frontmost screen.<br /> <br /> Hardware restrictions prevent certain types of displays. For instance, screens always use the full width of the display, regardless of the width of the overscan rectangle. This prevents any changes in display mode within a video line. Other modes, such as the VGA modes, require specific revisions of the custom chips and may not be available on all machines. See [[Graphics_Primitives|Graphics Primitives]] and the ''Amiga Hardware Reference Manual'' for more information on Amiga display organization and limitations.<br /> <br /> == Advanced Screen Programming ==<br /> <br /> This section discusses how to perform double-buffering of Intuition screens and other advanced topics. Dual-playfield Intuition screens are covered in the [[Classic_Intuition_Screens|Classic Intuition Screens]] section.<br /> <br /> === Screen Locking ===<br /> <br /> Applications wishing to block all screen rendering so that they can do &quot;exceptional&quot; rendering (eg. dragging icons,<br /> pop-up menus, etc.) need to LockScreen(). By using LockScreen() and UnlockScreen(), such applications avoid the risk of deadlocking with Intuition. Intuition will behave much like it does when menus are down; that is to say that layer-related events such as window size, depth, or position changes are deferred until the screen is unfrozen.<br /> <br /> The application has the following obligations:<br /> # When UnlockScreen() is finally called, the screen's bitmap must be bit-for-bit the same as when you called LockScreen(), with the exception of rendering into your own windows' RastPorts. That is to say, you are responsible for doing non-destructive rendering during the time you have the screen frozen.<br /> # You may only make regular rendering calls (graphics calls, plus Intuition imagery functions such as PrintIText(), DrawImage(), DrawBorder()). Do not call any gadget refresh functions, OpenWindow(), Begin/EndRefresh(), etc. or any Intuition function that involves locking IntuitionBase (namely LockIBase()).<br /> # Don't freeze the screen on the user. Freeze it only in response to the user. Typically, if you don't unfreeze the screen when the user lets the mouse button go, you're freezing for too long.<br /> # Do not freeze a screen you don't own. It's OK to freeze a public screen (including the Workbench screen), or your own custom screen. If it's a public screen, be sure you hold a public-screen lock (see LockPubScreen()) or have a window open on it.<br /> <br /> The active window will still receive mouse-, keyboard- and timer-messages. So if you want your window to get the IDCMP messages, the following must be done:<br /> # make your window the active one, if it isn't (see ActivateWindow)<br /> # wait for IDCMP_ACTIVEWINDOW<br /> # then call LockScreen()<br /> # do your input processing. When receiving IDCMP_INACTIVEWINDOW abort and call UnlockScreen() (your window could have gone inactive before you had the chance to call LockScreen())<br /> # when done, call UnlockScreen()<br /> <br /> {{Note|BOOPSI class implementers must use the LockScreenGI() and UnlockScreenGI() functions.}}<br /> <br /> ==== Locking the Screen List ====<br /> <br /> Sometimes there is a need for applications to lock Intuition's entire screen list. For these situations use the LockScreenList() and UnlockScreenList functions. While the screen list is locked, Intuition is &quot;frozen&quot; from the user's point of view. It is best to copy any required information (e.g. screen titles) and then release the list.<br /> <br /> === Double Buffering ===<br /> <br /> Intuition supports double (or multiple) buffering inside an Intuition screen, with full support for menus, and support for certain<br /> kinds of gadget.<br /> <br /> The AllocScreenBuffer() call allows you to create other BitMap buffers for your screen. The SB_SCREEN_BITMAP flag is used to get a buffer handle for the BitMap currently in use in the screen. Subsequent buffers are allocated with the SB_COPY_BITMAP flag instead, which instructs Intuition to copy the current imagery (e.g., the screen title-bar and any of your rendering) into the alternate BitMap. Normally you let Intuition allocate these alternate BitMaps, but if your screen is CUSTOMBITMAP, you should allocate the alternate BitMaps yourself.<br /> <br /> To swap buffers, call the ChangeScreenBuffer() function, which attempts to install the new buffer. ChangeScreenBuffer() will fail if Intuition is temporarily unable to make the change (say while gadgets or menus are active). Intuition builds these functions on top of the graphics.library ChangeVPBitMap() function, so the signalling information that graphics provides is also available to the Intuition user.<br /> <br /> To clean up, call FreeScreenBuffer() for each screen buffer. It is not necessary to restore the original buffer before freeing things. Consult the autodocs for full details.<br /> <br /> When the user accesses the screen's menus, buffer-swapping will stop. The ChangeScreenBuffer() call will return failure during this time. When the user finishes his menu selection, buffer-swapping will be possible again. Only a small subset of gadgets are supportable in double-buffered screens. These gadgets are those whose imagery returns to the initial state when you release them (e.g., action buttons or the screen's depth gadget). To use other kinds of gadgets (such as sliders or string gadgets) you need to put them on a separate screen, which can be an attached screen.<br /> <br /> Windows with borders are not supportable on double-buffered screens. Double-buffered screens are expected to consist nearly entirely of custom rendering.<br /> <br /> An example program illustrating double-buffering under Intuition, with menu-lending and an attached screen to hold two slider gadgets is provided.<br /> <br /> ==== doublebuffer.c ====<br /> <br /> &lt;syntaxhighlight&gt;<br /> /*<br /> * doublebuffer.c - shows double-buffering, attached screens, menu lending<br /> *<br /> * Example shows use of double-buffering functions to achieve<br /> * 60 frame-per-second animation. Also shows use of menus in<br /> * double-buffered screens, and the use of attached screens with<br /> * menu-lending to achieve the appearance of slider gadgets in<br /> * double-buffered screens.<br /> *<br /> */<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;graphics/displayinfo.h&gt;<br /> #include &lt;graphics/videocontrol.h&gt;<br /> #include &lt;graphics/gfxbase.h&gt;<br /> #include &lt;intuition/intuitionbase.h&gt;<br /> #include &lt;intuition/gadgetclass.h&gt;<br /> #include &lt;libraries/gadtools.h&gt;<br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> #include &lt;proto/graphics.h&gt;<br /> #include &lt;proto/intuition.h&gt;<br /> #include &lt;proto/gadtools.h&gt;<br /> <br /> #include &lt;stdlib.h&gt;<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> STRPTR init_all( void );<br /> void error_exit( STRPTR errorstring );<br /> struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi );<br /> BOOL handleIntuiMessage( struct IntuiMessage *imsg );<br /> void handleDBufMessage( struct Message *dbmsg );<br /> ULONG handleBufferSwap( void );<br /> struct BitMap *makeImageBM( void );<br /> void CloseWindowSafely( struct Window *win );<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Some constants to handle the rendering of the animated face */<br /> #define BM_WIDTH 120<br /> #define BM_HEIGHT 60<br /> #define BM_DEPTH 2<br /> <br /> /* Odd numbers to give a non-repeating bounce */<br /> #define CONTROLSC_TOP 191<br /> #define SC_ID HIRES_KEY<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* User interface constants and variables */<br /> <br /> #define GAD_HORIZ 1<br /> #define GAD_VERT 2<br /> <br /> #define MENU_RUN 1<br /> #define MENU_STEP 2<br /> #define MENU_QUIT 3<br /> #define MENU_HSLOW 4<br /> #define MENU_HFAST 5<br /> #define MENU_VSLOW 6<br /> #define MENU_VFAST 7<br /> <br /> struct TextAttr Topaz80 =<br /> {<br /> &quot;topaz.font&quot;, /* Name */<br /> 8, /* YSize */<br /> FS_NORMAL, /* Style */<br /> FPF_ROMFONT|FPF_DESIGNED, /* Flags */<br /> };<br /> <br /> struct TagItem vctags[] =<br /> {<br /> VTAG_BORDERSPRITE_SET, TRUE,<br /> TAG_END, 0,<br /> };<br /> <br /> UWORD pens[] =<br /> {<br /> 0, /* DETAILPEN */<br /> 1, /* BLOCKPEN */<br /> 1, /* TEXTPEN */<br /> 2, /* SHINEPEN */<br /> 1, /* SHADOWPEN */<br /> 3, /* FILLPEN */<br /> 1, /* FILLTEXTPEN */<br /> 0, /* BACKGROUNDPEN */<br /> 2, /* HIGHLIGHTTEXTPEN */<br /> <br /> 1, /* BARDETAILPEN */<br /> 2, /* BARBLOCKPEN */<br /> 1, /* BARTRIMPEN */<br /> <br /> (UWORD)~0,<br /> };<br /> <br /> struct NewMenu demomenu[] =<br /> {<br /> { NM_TITLE, &quot;Project&quot;, 0 , 0, 0, 0, },<br /> { NM_ITEM, &quot;Run&quot;, &quot;R&quot;, 0, 0, ( APTR ) MENU_RUN, },<br /> { NM_ITEM, &quot;Step&quot;, &quot;S&quot;, 0, 0, ( APTR ) MENU_STEP, },<br /> { NM_ITEM, NM_BARLABEL, 0 , 0, 0, 0, },<br /> { NM_ITEM, &quot;Slower Horizontal&quot;, &quot;1&quot;, 0, 0, ( APTR ) MENU_HSLOW, },<br /> { NM_ITEM, &quot;Faster Horizontal&quot;, &quot;2&quot;, 0, 0, ( APTR ) MENU_HFAST, },<br /> { NM_ITEM, &quot;Slower Vertical&quot;, &quot;3&quot;, 0, 0, ( APTR ) MENU_VSLOW, },<br /> { NM_ITEM, &quot;Faster Vertical&quot;, &quot;4&quot;, 0, 0, ( APTR ) MENU_VFAST, },<br /> <br /> { NM_ITEM, NM_BARLABEL, 0 , 0, 0, 0, },<br /> { NM_ITEM, &quot;Quit&quot;, &quot;Q&quot;, 0, 0, ( APTR ) MENU_QUIT, },<br /> <br /> { NM_END, 0, 0 , 0, 0, 0, },<br /> };<br /> <br /> struct Screen *canvassc = NULL;<br /> struct Screen *controlsc = NULL;<br /> struct Window *controlwin = NULL;<br /> struct Window *canvaswin = NULL;<br /> struct Gadget *glist = NULL;<br /> struct Gadget *horizgad, *vertgad;<br /> struct Menu *menu = NULL;<br /> void *canvasvi = NULL;<br /> void *controlvi = NULL;<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> #define OK_REDRAW 1 /* Buffer fully detached, ready for redraw */<br /> #define OK_SWAPIN 2 /* Buffer redrawn, ready for swap-in */<br /> <br /> struct GraphicsIFace *IGraphics = NULL;<br /> struct IntuitionIFace *IIntuition = NULL;<br /> struct GadToolsIFace *IGadTools = NULL;<br /> <br /> struct MsgPort *dbufport = NULL;<br /> struct MsgPort *userport = NULL;<br /> <br /> struct ScreenBuffer *scbuf[] =<br /> {<br /> NULL,<br /> NULL,<br /> };<br /> struct RastPort rport[ 2 ];<br /> <br /> ULONG status[ 2 ];<br /> <br /> LONG prevx[ 2 ] =<br /> {<br /> 50, 50,<br /> };<br /> <br /> LONG prevy[ 2 ] =<br /> {<br /> 50, 50,<br /> };<br /> <br /> ULONG buf_current, buf_nextdraw, buf_nextswap;<br /> ULONG count;<br /> struct BitMap *face = NULL;<br /> LONG x, y, xstep, xdir, ystep, ydir;<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> int main()<br /> {<br /> STRPTR errorstring;<br /> ULONG sigs;<br /> BOOL terminated = FALSE;<br /> <br /> /* Let's get everything initialized */<br /> if ( errorstring = init_all() )<br /> {<br /> error_exit( errorstring );<br /> }<br /> <br /> count = 0;<br /> buf_current = 0;<br /> buf_nextdraw = 1;<br /> buf_nextswap = 1;<br /> sigs = 0;<br /> <br /> while ( !terminated )<br /> {<br /> /* Check for and handle any IntuiMessages */<br /> if ( sigs &amp; ( 1 &lt;&lt; userport-&gt;mp_SigBit ) )<br /> {<br /> struct IntuiMessage *imsg;<br /> <br /> while ( imsg = IGadTools-&gt;GT_GetIMsg( userport ) )<br /> {<br /> terminated |= handleIntuiMessage( imsg );<br /> IGadTools-&gt;GT_ReplyIMsg( imsg );<br /> }<br /> }<br /> <br /> /* Check for and handle any double-buffering messages.<br /> * Note that double-buffering messages are &quot;replied&quot; to<br /> * us, so we don't want to reply them to anyone.<br /> */<br /> if ( sigs &amp; ( 1 &lt;&lt; dbufport-&gt;mp_SigBit ) )<br /> {<br /> struct Message *dbmsg;<br /> while ( dbmsg = IExec-&gt;GetMsg( dbufport ) )<br /> {<br /> handleDBufMessage( dbmsg );<br /> }<br /> }<br /> <br /> <br /> if ( !terminated )<br /> {<br /> ULONG held_off = 0;<br /> /* Only handle swapping buffers if count is non-zero */<br /> if ( count )<br /> {<br /> held_off = handleBufferSwap();<br /> }<br /> if ( held_off )<br /> {<br /> /* If were held-off at ChangeScreenBuffer() time, then we<br /> * need to try ChangeScreenBuffer() again, without awaiting<br /> * a signal. We WaitTOF() to avoid busy-looping.<br /> */<br /> IGraphics-&gt;WaitTOF();<br /> }<br /> else<br /> {<br /> /* If we were not held-off, then we're all done<br /> * with what we have to do. We'll have no work to do<br /> * until some kind of signal arrives. This will normally<br /> * be the arrival of the dbi_SafeMessage from the ROM<br /> * double-buffering routines, but it might also be an<br /> * IntuiMessage.<br /> */<br /> sigs = IExec-&gt;Wait( ( 1 &lt;&lt; dbufport-&gt;mp_SigBit ) | ( 1 &lt;&lt; userport-&gt;mp_SigBit ) );<br /> }<br /> }<br /> }<br /> <br /> error_exit( NULL );<br /> }<br /> <br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Handle the rendering and swapping of the buffers */<br /> <br /> ULONG handleBufferSwap( void )<br /> {<br /> ULONG held_off = 0;<br /> /* 'buf_nextdraw' is the next buffer to draw into.<br /> * The buffer is ready for drawing when we've received the<br /> * dbi_SafeMessage for that buffer. Our routine to handle<br /> * messaging from the double-buffering functions sets the<br /> * OK_REDRAW flag when this message has appeared.<br /> *<br /> * Here, we set the OK_SWAPIN flag after we've redrawn<br /> * the imagery, since the buffer is ready to be swapped in.<br /> * We clear the OK_REDRAW flag, since we're done with redrawing<br /> */<br /> if ( status[ buf_nextdraw ] == OK_REDRAW )<br /> {<br /> x += xstep*xdir;<br /> if ( x &lt; 0 )<br /> {<br /> x = 0;<br /> xdir = 1;<br /> }<br /> else if ( x &gt; canvassc-&gt;Width - BM_WIDTH )<br /> {<br /> x = canvassc-&gt;Width - BM_WIDTH - 1;<br /> xdir = -1;<br /> }<br /> <br /> y += ystep*ydir;<br /> if ( y &lt; canvassc-&gt;BarLayer-&gt;Height )<br /> {<br /> y = canvassc-&gt;BarLayer-&gt;Height;<br /> ydir = 1;<br /> }<br /> else if ( y &gt;= CONTROLSC_TOP - BM_HEIGHT )<br /> {<br /> y = CONTROLSC_TOP - BM_HEIGHT - 1;<br /> ydir = -1;<br /> }<br /> <br /> IGraphics-&gt;SetAPen( &amp;rport[ buf_nextdraw ], 0 );<br /> IGraphics-&gt;RectFill( &amp;rport[ buf_nextdraw ],<br /> prevx[ buf_nextdraw ], prevy[ buf_nextdraw ],<br /> prevx[ buf_nextdraw ] + BM_WIDTH - 1, prevy[ buf_nextdraw ] + BM_HEIGHT - 1 );<br /> prevx[ buf_nextdraw ] = x;<br /> prevy[ buf_nextdraw ] = y;<br /> <br /> IGraphics-&gt;BltBitMapRastPort( face, 0, 0, &amp;rport[ buf_nextdraw ], x, y,<br /> BM_WIDTH, BM_HEIGHT, 0xc0 );<br /> <br /> IGraphics-&gt;WaitBlit(); /* Gots to let the BBMRP finish */<br /> <br /> status[ buf_nextdraw ] = OK_SWAPIN;<br /> <br /> /* Toggle which the next buffer to draw is.<br /> * If you're using multiple ( &gt;2 ) buffering, you<br /> * would use<br /> *<br /> * buf_nextdraw = ( buf_nextdraw+1 ) % NUMBUFFERS;<br /> *<br /> */<br /> buf_nextdraw ^= 1;<br /> }<br /> <br /> /* Let's make sure that the next frame is rendered before we swap...<br /> */<br /> if ( status[ buf_nextswap ] == OK_SWAPIN )<br /> {<br /> scbuf[ buf_nextswap ]-&gt;sb_DBufInfo-&gt;dbi_SafeMessage.mn_ReplyPort = dbufport;<br /> <br /> if ( IIntuition-&gt;ChangeScreenBuffer( canvassc, scbuf[ buf_nextswap ] ) )<br /> {<br /> status[ buf_nextswap ] = 0;<br /> <br /> buf_current = buf_nextswap;<br /> /* Toggle which the next buffer to swap in is.<br /> * If you're using multiple ( &gt;2 ) buffering, you<br /> * would use<br /> *<br /> * buf_nextswap = ( buf_nextswap+1 ) % NUMBUFFERS;<br /> *<br /> */<br /> buf_nextswap ^= 1;<br /> <br /> count--;<br /> }<br /> else<br /> {<br /> held_off = 1;<br /> }<br /> }<br /> return( held_off );<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Handle Intuition messages */<br /> <br /> BOOL handleIntuiMessage( struct IntuiMessage *imsg )<br /> {<br /> UWORD code = imsg-&gt;Code;<br /> BOOL terminated = FALSE;<br /> <br /> switch ( imsg-&gt;Class )<br /> {<br /> case IDCMP_GADGETDOWN:<br /> case IDCMP_GADGETUP:<br /> case IDCMP_MOUSEMOVE:<br /> switch ( ( ( struct Gadget * )imsg-&gt;IAddress )-&gt;GadgetID )<br /> {<br /> case GAD_HORIZ:<br /> xstep = code;<br /> break;<br /> <br /> case GAD_VERT:<br /> ystep = code;<br /> break;<br /> }<br /> break;<br /> <br /> case IDCMP_VANILLAKEY:<br /> switch ( code )<br /> {<br /> case 'S':<br /> case 's':<br /> count = 1;<br /> break;<br /> <br /> case 'R':<br /> case 'r':<br /> count = ~0;<br /> break;<br /> <br /> case 'Q':<br /> case 'q':<br /> count = 0;<br /> terminated = TRUE;<br /> break;<br /> }<br /> break;<br /> <br /> case IDCMP_MENUPICK:<br /> while ( code != MENUNULL )<br /> {<br /> struct MenuItem *item;<br /> <br /> item = IIntuition-&gt;ItemAddress( menu, code );<br /> switch ( ( ULONG ) GTMENUITEM_USERDATA( item ) )<br /> {<br /> case MENU_RUN:<br /> count = ~0;<br /> break;<br /> <br /> case MENU_STEP:<br /> count = 1;<br /> break;<br /> <br /> case MENU_QUIT:<br /> count = 0;<br /> terminated = TRUE;<br /> break;<br /> <br /> case MENU_HSLOW:<br /> if ( xstep &gt; 0 )<br /> {<br /> xstep--;<br /> }<br /> IGadTools-&gt;GT_SetGadgetAttrs( horizgad, controlwin, NULL,<br /> GTSL_Level, xstep,<br /> TAG_END );<br /> break;<br /> <br /> case MENU_HFAST:<br /> if ( xstep &lt; 9 )<br /> {<br /> xstep++;<br /> }<br /> IGadTools-&gt;GT_SetGadgetAttrs( horizgad, controlwin, NULL,<br /> GTSL_Level, xstep,<br /> TAG_END );<br /> break;<br /> <br /> case MENU_VSLOW:<br /> if ( ystep &gt; 0 )<br /> {<br /> ystep--;<br /> }<br /> IGadTools-&gt;GT_SetGadgetAttrs( vertgad, controlwin, NULL,<br /> GTSL_Level, ystep,<br /> TAG_END );<br /> break;<br /> <br /> case MENU_VFAST:<br /> if ( ystep &lt; 9 )<br /> {<br /> ystep++;<br /> }<br /> IGadTools-&gt;GT_SetGadgetAttrs( vertgad, controlwin, NULL,<br /> GTSL_Level, ystep,<br /> TAG_END );<br /> break;<br /> }<br /> code = item-&gt;NextSelect;<br /> }<br /> break;<br /> }<br /> return( terminated );<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> void handleDBufMessage( struct Message *dbmsg )<br /> {<br /> ULONG buffer;<br /> <br /> /* dbi_SafeMessage is followed by an APTR dbi_UserData1, which<br /> * contains the buffer number. This is an easy way to extract<br /> * it.<br /> * The dbi_SafeMessage tells us that it's OK to redraw the<br /> * in the previous buffer.<br /> */<br /> buffer = ( ULONG ) *( ( APTR ** ) ( dbmsg+1 ) );<br /> /* Mark the previous buffer as OK to redraw into.<br /> * If you're using multiple ( &gt;2 ) buffering, you<br /> * would use<br /> *<br /> * ( buffer + NUMBUFFERS - 1 ) % NUMBUFFERS<br /> *<br /> */<br /> status[ buffer^1 ] = OK_REDRAW;<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Get the resources and objects we need */<br /> <br /> STRPTR init_all( void )<br /> {<br /> struct Library *GfxBase = IExec-&gt;OpenLibrary(&quot;graphics.library&quot;, 50);<br /> IGraphics = (struct GraphicsIFace*)IExec-&gt;GetInterface(GfxBase, &quot;main&quot;, 1, NULL);<br /> if ( IGraphics == NULL )<br /> {<br /> return( &quot;Couldn't open Gfx V50\n&quot; );<br /> }<br /> <br /> struct Library *IntuitionBase = IExec-&gt;OpenLibrary(&quot;intuition.library&quot;, 50);<br /> IIntuition = (struct IntuitionIFace*)IExec-&gt;GetInterface(IntuitionBase, &quot;main&quot;, 1, NULL);<br /> if ( IIntuition == NULL )<br /> {<br /> return( &quot;Couldn't open Intuition V50\n&quot; );<br /> }<br /> <br /> struct Library *GadToolsBase = IExec-&gt;OpenLibrary(&quot;gadtools.library&quot;, 50);<br /> IGadTools = (struct GadToolsIFace*)IExec-&gt;GetInterface(GadToolsBase, &quot;main&quot;, 1, NULL);<br /> if ( IGadtools == NULL )<br /> {<br /> return( &quot;Couldn't open GadTools V50\n&quot; );<br /> }<br /> <br /> if ( !( dbufport = IExec-&gt;AllocSysObjectTags(ASOT_PORT, TAG_END) ) )<br /> {<br /> return( &quot;Failed to create port\n&quot; );<br /> }<br /> <br /> if ( !( userport = IExec-&gt;AllocSysObjectTags(ASOT_PORT, TAG_END) ) )<br /> {<br /> return( &quot;Failed to create port\n&quot; );<br /> }<br /> <br /> if ( !( canvassc = IIntuition-&gt;OpenScreenTags( NULL,<br /> SA_DisplayID, SC_ID,<br /> SA_Overscan, OSCAN_TEXT,<br /> SA_Depth, 2,<br /> SA_AutoScroll, 1,<br /> SA_Pens, pens,<br /> SA_ShowTitle, TRUE,<br /> SA_Title, &quot;Intuition double-buffering example&quot;,<br /> SA_VideoControl, vctags,<br /> SA_Font, &amp;Topaz80,<br /> TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't open screen\n&quot; );<br /> }<br /> <br /> if ( !( canvasvi = IGadTools-&gt;GetVisualInfo( canvassc, TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't get VisualInfo\n&quot; );<br /> }<br /> <br /> if ( !( canvaswin = IIntuition-&gt;OpenWindowTags( NULL,<br /> WA_NoCareRefresh, TRUE,<br /> WA_Activate, TRUE,<br /> WA_Borderless, TRUE,<br /> WA_Backdrop, TRUE,<br /> WA_CustomScreen, canvassc,<br /> WA_NewLookMenus, TRUE,<br /> TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't open window\n&quot; );<br /> }<br /> canvaswin-&gt;UserPort = userport;<br /> <br /> IIntuition-&gt;ModifyIDCMP( canvaswin, IDCMP_MENUPICK | IDCMP_VANILLAKEY );<br /> <br /> if ( !( controlsc = IIntuition-&gt;OpenScreenTags( NULL,<br /> SA_DisplayID, SC_ID,<br /> SA_Overscan, OSCAN_TEXT,<br /> SA_Depth, 2,<br /> SA_Pens, pens,<br /> SA_Top, CONTROLSC_TOP,<br /> SA_Height, 28,<br /> SA_Parent, canvassc,<br /> SA_ShowTitle, FALSE,<br /> SA_Draggable, FALSE,<br /> SA_VideoControl, vctags,<br /> SA_Quiet, TRUE,<br /> SA_Font, &amp;Topaz80,<br /> TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't open screen\n&quot; );<br /> }<br /> <br /> if ( !( controlvi = IGadTools-&gt;GetVisualInfo( controlsc, TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't get VisualInfo\n&quot; );<br /> }<br /> <br /> if ( !( menu = IGadTools-&gt;CreateMenus( demomenu, TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't create menus\n&quot; );<br /> }<br /> <br /> if ( !IGadTools-&gt;LayoutMenus( menu, canvasvi,<br /> GTMN_NewLookMenus, TRUE,<br /> TAG_END ) )<br /> {<br /> return( &quot;Couldn't layout menus\n&quot; );<br /> }<br /> <br /> if ( !createAllGadgets( &amp;glist, controlvi ) )<br /> {<br /> return( &quot;Couldn't create gadgets\n&quot; );<br /> }<br /> <br /> /* A borderless backdrop window so we can get input */<br /> if ( !( controlwin = IIntuition-&gt;OpenWindowTags( NULL,<br /> WA_NoCareRefresh, TRUE,<br /> WA_Activate, TRUE,<br /> WA_Borderless, TRUE,<br /> WA_Backdrop, TRUE,<br /> WA_CustomScreen, controlsc,<br /> WA_NewLookMenus, TRUE,<br /> WA_Gadgets, glist,<br /> TAG_END ) ) )<br /> {<br /> return( &quot;Couldn't open window\n&quot; );<br /> }<br /> <br /> controlwin-&gt;UserPort = userport;<br /> IIntuition-&gt;ModifyIDCMP( controlwin, SLIDERIDCMP | IDCMP_MENUPICK | IDCMP_VANILLAKEY );<br /> <br /> IGadTools-&gt;GT_RefreshWindow( controlwin, NULL );<br /> IIntuition-&gt;SetMenuStrip( canvaswin, menu );<br /> IIntuition-&gt;LendMenus( controlwin, canvaswin );<br /> <br /> if ( !( scbuf[ 0 ] = IIntuition-&gt;AllocScreenBuffer( canvassc, NULL, SB_SCREEN_BITMAP ) ) )<br /> {<br /> return( &quot;Couldn't allocate ScreenBuffer 1\n&quot; );<br /> }<br /> <br /> if ( !( scbuf[ 1 ] = IIntuition-&gt;AllocScreenBuffer( canvassc, NULL, SB_COPY_BITMAP ) ) )<br /> {<br /> return( &quot;Couldn't allocate ScreenBuffer 2\n&quot; );<br /> }<br /> <br /> /* Let's use the UserData to store the buffer number, for<br /> * easy identification when the message comes back.<br /> */<br /> scbuf[ 0 ]-&gt;sb_DBufInfo-&gt;dbi_UserData1 = ( APTR ) ( 0 );<br /> scbuf[ 1 ]-&gt;sb_DBufInfo-&gt;dbi_UserData1 = ( APTR ) ( 1 );<br /> status[ 0 ] = OK_REDRAW;<br /> status[ 1 ] = OK_REDRAW;<br /> <br /> if ( !( face = makeImageBM() ) )<br /> {<br /> return( &quot;Couldn't allocate image bitmap\n&quot; );<br /> }<br /> IGraphics-&gt;InitRastPort( &amp;rport[ 0 ] );<br /> IGraphics-&gt;InitRastPort( &amp;rport[ 1 ] );<br /> rport[ 0 ].BitMap = scbuf[ 0 ]-&gt;sb_BitMap;<br /> rport[ 1 ].BitMap = scbuf[ 1 ]-&gt;sb_BitMap;<br /> <br /> x = 50;<br /> y = 70;<br /> xstep = 1;<br /> xdir = 1;<br /> ystep = 1;<br /> ydir = -1;<br /> <br /> /* All is OK */<br /> return( 0 );<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Draw a crude &quot;face&quot; for animation */<br /> <br /> #define MAXVECTORS 10<br /> <br /> struct BitMap *makeImageBM( void )<br /> {<br /> struct BitMap *bm;<br /> struct RastPort rport;<br /> struct AreaInfo area;<br /> struct TmpRas tmpRas;<br /> PLANEPTR planePtr;<br /> <br /> BYTE areabuffer[ MAXVECTORS*5 ];<br /> <br /> if ( bm = ( struct BitMap * )IGraphics-&gt;AllocBitMap( BM_WIDTH, BM_HEIGHT,<br /> BM_DEPTH, BMF_CLEAR, NULL ) )<br /> {<br /> if ( planePtr = IGraphics-&gt;AllocRaster( BM_WIDTH, BM_HEIGHT ) )<br /> {<br /> IGraphics-&gt;InitRastPort( &amp;rport );<br /> rport.BitMap = bm;<br /> <br /> IGraphics-&gt;InitArea( &amp;area, areabuffer, MAXVECTORS );<br /> rport.AreaInfo = &amp;area;<br /> <br /> IGraphics-&gt;InitTmpRas( &amp;tmpRas, planePtr, RASSIZE( BM_WIDTH, BM_HEIGHT ) );<br /> rport.TmpRas = &amp;tmpRas;<br /> <br /> IGraphics-&gt;SetABPenDrMd( &amp;rport, 3, 0, JAM1 );<br /> IGraphics-&gt;AreaEllipse( &amp;rport, BM_WIDTH/2, BM_HEIGHT/2,<br /> BM_WIDTH/2-4, BM_HEIGHT/2-4 );<br /> IGraphics-&gt;AreaEnd( &amp;rport );<br /> <br /> IGraphics-&gt;SetAPen( &amp;rport, 2 );<br /> IGraphics-&gt;AreaEllipse( &amp;rport, 5*BM_WIDTH/16, BM_HEIGHT/4,<br /> BM_WIDTH/9, BM_HEIGHT/9 );<br /> IGraphics-&gt;AreaEllipse( &amp;rport, 11*BM_WIDTH/16, BM_HEIGHT/4,<br /> BM_WIDTH/9, BM_HEIGHT/9 );<br /> IGraphics-&gt;AreaEnd( &amp;rport );<br /> <br /> IGraphics-&gt;SetAPen( &amp;rport, 1 );<br /> IGraphics-&gt;AreaEllipse( &amp;rport, BM_WIDTH/2, 3*BM_HEIGHT/4,<br /> BM_WIDTH/3, BM_HEIGHT/9 );<br /> IGraphics-&gt;AreaEnd( &amp;rport );<br /> <br /> IGraphics-&gt;FreeRaster( planePtr, BM_WIDTH, BM_HEIGHT );<br /> }<br /> else<br /> {<br /> IGraphics-&gt;FreeBitMap( bm );<br /> bm = NULL;<br /> }<br /> return( bm );<br /> }<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Make a pair of slider gadgets to control horizontal and vertical<br /> * speed of motion.<br /> */<br /> struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi )<br /> {<br /> struct NewGadget ng;<br /> struct Gadget *gad;<br /> <br /> gad = IGadTools-&gt;CreateContext( glistptr );<br /> <br /> ng.ng_LeftEdge = 100;<br /> ng.ng_TopEdge = 1;<br /> ng.ng_Width = 100;<br /> ng.ng_Height = 12;<br /> ng.ng_GadgetText = &quot;Horiz: &quot;;<br /> ng.ng_TextAttr = &amp;Topaz80;<br /> ng.ng_VisualInfo = vi;<br /> ng.ng_GadgetID = GAD_HORIZ;<br /> ng.ng_Flags = 0;<br /> <br /> horizgad = gad = IGadTools-&gt;CreateGadget( SLIDER_KIND, gad, &amp;ng,<br /> GTSL_Min, 0,<br /> GTSL_Max, 9,<br /> GTSL_Level, 1,<br /> GTSL_MaxLevelLen, 1,<br /> GTSL_LevelFormat, &quot;%ld&quot;,<br /> TAG_END );<br /> <br /> ng.ng_LeftEdge += 200;<br /> ng.ng_GadgetID = GAD_VERT;<br /> ng.ng_GadgetText = &quot;Vert: &quot;;<br /> vertgad = gad = IGadTools-&gt;CreateGadget( SLIDER_KIND, gad, &amp;ng,<br /> GTSL_Min, 0,<br /> GTSL_Max, 9,<br /> GTSL_Level, 1,<br /> GTSL_MaxLevelLen, 1,<br /> GTSL_LevelFormat, &quot;%ld&quot;,<br /> TAG_END );<br /> <br /> return( gad );<br /> }<br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* Clean up everything and exit, printing the errorstring if any */<br /> void error_exit( STRPTR errorstring )<br /> {<br /> if ( controlwin )<br /> {<br /> IIntuition-&gt;ClearMenuStrip( controlwin );<br /> CloseWindowSafely( controlwin );<br /> }<br /> <br /> if ( canvaswin )<br /> {<br /> IIntuition-&gt;ClearMenuStrip( canvaswin );<br /> CloseWindowSafely( canvaswin );<br /> }<br /> <br /> if ( controlsc )<br /> {<br /> IIntuition-&gt;CloseScreen( controlsc );<br /> }<br /> <br /> if ( canvassc )<br /> {<br /> IIntuition-&gt;FreeScreenBuffer( canvassc, scbuf[ 1 ] );<br /> IIntuition-&gt;FreeScreenBuffer( canvassc, scbuf[ 0 ] );<br /> IIntuition-&gt;CloseScreen( canvassc );<br /> }<br /> <br /> if ( dbufport )<br /> {<br /> IExec-&gt;FreeSysObject(ASOT_PORT, dbufport );<br /> }<br /> <br /> if ( userport )<br /> {<br /> IExec-&gt;FreeSysObject(ASOT_PORT, userport );<br /> }<br /> <br /> if ( IGadTools )<br /> {<br /> IGadTools-&gt;FreeGadgets( glist );<br /> IGadTools-&gt;FreeMenus( menu );<br /> IGadTools-&gt;FreeVisualInfo( canvasvi );<br /> IGadTools-&gt;FreeVisualInfo( controlvi );<br /> struct Library *libBase = IGadTools-&gt;Data.LibBase;<br /> IExec-&gt;DropInterface((struct Interface*)IGadTools);<br /> IExec-&gt;CloseLibrary( libBase );<br /> }<br /> <br /> if ( IIntuition )<br /> {<br /> struct Library *libBase = IIntuition-&gt;Data.LibBase;<br /> IExec-&gt;DropInterface((struct Interface*)IIntuition);<br /> IExec-&gt;CloseLibrary( libBase );<br /> }<br /> <br /> if ( face )<br /> {<br /> IGraphics-&gt;FreeBitMap( face );<br /> }<br /> <br /> if ( IGraphics )<br /> {<br /> struct Library *libBase = IGraphics-&gt;Data.LibBase;<br /> IExec-&gt;DropInterface((struct Interface*)IGraphics);<br /> IExec-&gt;CloseLibrary( libBase );<br /> }<br /> <br /> if ( errorstring )<br /> {<br /> IDOS-&gt;Printf( errorstring );<br /> exit( 20 );<br /> }<br /> <br /> exit( 0 );<br /> }<br /> <br /> <br /> /*----------------------------------------------------------------------*/<br /> <br /> /* these functions close an Intuition window<br /> * that shares a port with other Intuition<br /> * windows or IPC customers.<br /> *<br /> * We are careful to set the UserPort to<br /> * null before closing, and to free<br /> * any messages that it might have been<br /> * sent.<br /> */<br /> #include &quot;exec/types.h&quot;<br /> #include &quot;exec/nodes.h&quot;<br /> #include &quot;exec/lists.h&quot;<br /> #include &quot;exec/ports.h&quot;<br /> #include &quot;intuition/intuition.h&quot;<br /> <br /> void<br /> CloseWindowSafely( struct Window *win )<br /> {<br /> /* we forbid here to keep out of race conditions with Intuition */<br /> IExec-&gt;Forbid();<br /> <br /> /* send back any messages for this window<br /> * that have not yet been processed<br /> */<br /> IIntuition-&gt;StripIntuiMessages( win-&gt;UserPort, win );<br /> <br /> /* clear UserPort so Intuition will not free it */<br /> win-&gt;UserPort = NULL;<br /> <br /> /* tell Intuition to stop sending more messages */<br /> IIntuition-&gt;ModifyIDCMP( win, 0L );<br /> <br /> /* turn multitasking back on */<br /> IExec-&gt;Permit();<br /> <br /> /* and really close the window */<br /> IIntuition-&gt;CloseWindow( win );<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Other Screen Functions ==<br /> <br /> Other screen functions provided by Intuition control screen depth arrangement, screen movement, the screen title bar and provide a visual &quot;error beep&quot;.<br /> <br /> === Screen Depth Arrangement ===<br /> <br /> ScreenToFront() and ScreenToBack() make a screen either the frontmost or the backmost screen. If an application needs to render into a screen before the screen becomes visible to the user, the screen may be opened behind all other screens and later moved to the front when ready with ScreenToFront().<br /> <br /> &lt;syntaxhighlight&gt;<br /> VOID ScreenToFront( struct Screen * );<br /> VOID ScreenToBack ( struct Screen * );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Depth control of screens is also available through the depth arrangement gadget in the screen's title bar or through keyboard shortcuts. The N key with the Left-Amiga qualifier moves the Workbench screen to front. The M key with the Left-Amiga qualifier moves the frontmost screen to back. Repeated selection of Left-Amiga-M will cycle through available screens. These keys are processed through the keymap and will retain their value even if the key location changes.<br /> <br /> === Screen Movement and Scrolling ===<br /> <br /> The MoveScreen() function moves the screen origin by the number of pixels specified in dx and dy.<br /> <br /> &lt;syntaxhighlight&gt;<br /> VOID MoveScreen( struct Screen *myscreen, WORD dx, WORD dy );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Calls to MoveScreen() are asynchronous; the screen is not necessarily moved upon return of this function. If the calls happen too quickly, there may be unexpected results. One way to pace these calls is to call the function one time for each IDCMP_INTUITICKS event.<br /> <br /> Screen movement is also available through the screen's drag gadget in the title bar and through a keyboard/mouse shortcut. Left-Amiga with the select button of the mouse anywhere within the screen will drag the screen (even if the title bar is totally concealed by a window). Dragging a screen down will reveal any screen(s) behind it. Screens are never revealed to the left, right or bottom of another screen.<br /> <br /> Additionally, oversized screens may be moved with the autoscroll feature. With autoscroll, the screen is automatically scrolled as the pointer reaches one of the edges of the display. Autoscroll only works on the active screen.<br /> <br /> Another screen movement feature is ''menu snap''. When a screen much larger than the viewing area is scrolled such that the upper left corner is not visible (scrolled down or to the right), menus may could be out of the visible portion of the screen. To prevent this, ''menu snap'' moves the screen to a position where the menus will be visible before rendering them. The screen appears to snap to the home position as the menus are selected, moving back when the operation is complete. If the Left-Amiga qualifier is held when the menus are selected then the screen will remain in the home position when the menu button is released.<br /> <br /> The Intuition preferences editor, IControl, allows the user to change a number of Intuition features. Some of these features include the ability to globally disable menu snap, and to change the select qualifier for dragging the screen. See the User's Manual for more information on Preferences editors.<br /> <br /> === Miscellaneous Screen Functions ===<br /> <br /> Three other functions used with screens are DisplayBeep(), ShowTitle() and GetScreenData(). DisplayBeep() flashes the screen colors to inform the user of an error or problem.<br /> <br /> &lt;syntaxhighlight&gt;<br /> VOID DisplayBeep( struct Screen *myscreen );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Since not all users will have speakers attached to the system, DisplayBeep() can be used to provide a visible bell. DisplayBeep() can beep any single screen or, if myscreen is set to NULL, all screens.<br /> <br /> ShowTitle() determines whether the screen's title bar will be displayed in front of or behind any backdrop windows on the screen.<br /> <br /> &lt;syntaxhighlight&gt;<br /> VOID ShowTitle( struct Screen *myscreen, BOOL infront );<br /> &lt;/syntaxhighlight&gt;<br /> <br /> By default, the screen's title bar is set to display in front of backdrop windows. Call this function with infront set to FALSE to put the screen title bar behind backdrop windows. This can also be set when the screen is opened with the SA_ShowTitle tag.<br /> <br /> == Function Reference ==<br /> <br /> The following are brief descriptions of the Intuition functions that relate to the use of Intuition screens. See the SDK for details on each function call.<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Function<br /> ! Description<br /> |-<br /> | OpenScreenTagList()<br /> | Open a screen.<br /> |-<br /> | OpenScreenTags()<br /> | Alternate calling sequence for OpenScreenTagList().<br /> |-<br /> | OpenScreen()<br /> | Pre-V36 open screen function.<br /> |-<br /> | CloseScreen()<br /> | Close an open screen.<br /> |-<br /> | MoveScreen()<br /> | Change the position of an open screen.<br /> |-<br /> | ScreenToBack()<br /> | Move a screen behind all other screens.<br /> |-<br /> | ScreenToFront()<br /> | Move a screen in front of all other screens.<br /> |-<br /> | ShowTitle()<br /> | Show the screen in front of through backdrop windows.<br /> |-<br /> | GetScreenDrawInfo()<br /> | Get the DrawInfo information for an open screen.<br /> |-<br /> | FreeScreenDrawInfo()<br /> | Free the DrawInfo information for a screen.<br /> |-<br /> | QueryOverscan()<br /> | Find overscan information for a specific display type.<br /> |-<br /> | LockPubScreen()<br /> | Obtain a lock on a public screen.<br /> |-<br /> | UnlockPubScreen()<br /> | Release a lock on a public screen.<br /> |-<br /> | NextPubScreen()<br /> | Return the name of the next public screen in the list.<br /> |-<br /> | PubScreenStatus()<br /> | Make a public screen private or private screen public.<br /> |-<br /> | LockPubScreenList()<br /> | Lock the public screen list (for a public screen utility).<br /> |-<br /> | UnlockPubScreenList()<br /> | Unlock the public screen list.<br /> |-<br /> | SetDefaultPubScreen()<br /> | Change the default public screen.<br /> |-<br /> | SetPubScreenModes()<br /> | Establish global public screen behavior.<br /> |-<br /> | GetDefaultPubScreen()<br /> | Copies the name of the default public screen to a buffer.<br /> |-<br /> | OpenWorkBench()<br /> | Open the Workbench screen, if closed.<br /> |-<br /> | CloseWorkBench()<br /> | Close the Workbench screen, if possible.<br /> |-<br /> | WBenchToBack()<br /> | Move the Workbench screen behind all other screens.<br /> |-<br /> | WBenchToFront()<br /> | Move the Workbench screen in front of all other screens.<br /> |-<br /> | GetScreenData()<br /> | Pre-V36 way to return information on an open screen.<br /> |-<br /> | GetScreenAttr()<br /> | Query a screen attribute.<br /> |-<br /> | GetScreenAttr()/GetScreenAttrsA()<br /> | Query screen attributes.<br /> |-<br /> | ViewAddress()<br /> | Return the address of a screen's View.<br /> |-<br /> | ViewPortAddress()<br /> | Use &amp;screen-&gt;ViewPort instead.<br /> |-<br /> | MakeScreen()<br /> | Low level screen handling-rebuild Copper list.<br /> |-<br /> | RethinkDisplay()<br /> | Low level screen handling-incorporate Copper list changes.<br /> |-<br /> | RemakeDisplay()<br /> | MakeScreen() for all screens, then RethinkDisplay().<br /> |}</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=AmigaDOS_Vector-Port&diff=6693 AmigaDOS Vector-Port 2013-11-15T10:59:44Z <p>Alexandre Balaban: Fixed a few typos found by Colin W.</p> <hr /> <div>{{WIP}}<br /> == Rationale ==<br /> <br /> During development of file-systems, developers often need to implement many parts to interface with DOS library rather than concentrate on implementing the actual function of the file-system. With this in mind, the new interface Vector-port simplify this by delegating most of this work to the DOS library, only the feature implementation itself is the file-system's developer charge.<br /> <br /> == Introduction ==<br /> In the past (up to version 53.77) DOS library used to communicate with file-systems and handlers using [[AmigaDOS_Packets|DOS packets]]. A DOS packet is merely an [[Exec_Messages_and_Ports#Messages|Exec Message]] with a structure appended in order to pass parameters one way and results in the other. The aim being to stay the most generic possible those parameters are abstracted - which gives more work to the developer when they have to handle the packet.<br /> Starting with version 53.77 DOS library offers the new API &quot;Vector-Port&quot; for file-systems (i.e. not for handlers). It is basically a [[Exec_Messages_and_Ports#Message_Ports|Message port]] with an associated function list (also called &quot;table of vectors&quot; thus the API name). This way we are freed from many legacy things - BSTRs notably - which let the developer know exactly what he is manipulating.<br /> <br /> == Legacy Support ==<br /> <br /> Moreover since version 53.95; DOS library provides a DOS packet emulation for legacy applications that used to send DOS packets directly to the file-system's Message Port, this would effectively cause these old applications to break on a complete Vector-Port based file-system. However, this special DOS packet emulation function allow these 'application-sent' DOS packets to be automatically converted to an equivalent Vector-Port call.<br /> <br /> == Existing file-system conversion ==<br /> <br /> DOS allows for a step by step conversion of existing file-systems to the Vector-Port API by falling back to sending a DOS packet when any Vector-Port function is NULL or the Vector-Port itself does not validate.<br /> This means that once can convert single functions at a time by simply adding or removing the function vector from the Vector-Port initialisation table.<br /> <br /> == Key points ==<br /> <br /> Formerly a file-system was required to allocate a Message Port, now it only needs to replace this creation by a call to DOS method &quot;AllocDosObject&quot; requesting creation of a Vector-Port and providing it the list of implemented functions. Everything else is automatically handled: for unimplemented functions DOS library will fallback to sending a DOS packet, for the other DOS library will call them directly. It is one of the advantage of this solution: as it uses direct function calls it's substantially faster than sending a message and waiting its handling by the file-system. It is also processed in the caller context thus we benefit from the OS multitasking feature to parallelize calls and there is no context switch anymore.<br /> <br /> Example for ACTION_LOCATE_OBJECT DOS packet and the corresponding &quot;Vector-Port&quot; function FSLock():<br /> <br /> &lt;syntaxhighlight&gt;<br /> INPUTS (DosPacket method)<br /> dp_Type - (int32) ACTION_LOCATE_OBJECT<br /> dp_Arg1 - (BPTR) lock.<br /> dp_Arg2 - (BSTR) object_name. (may also include path)<br /> dp_Arg3 - (int32) Mode. SHARED_LOCK or EXCLUSIVE_LOCK.<br /> dp_Arg4 - &lt;unused&gt; 0<br /> dp_Arg5 - (BSTR) nameformat - name format indicator value.<br /> only if(dp_Arg2==dp_Arg5) then BSTR's are guaranteed to be<br /> a nul-terminated string. (53.23)<br /> RESULT1 - (BPTR) Lock - (ZERO on failure.)<br /> RESULT2 - (int32) Failure code if RESULT1 == ZERO<br /> <br /> INPUTS (FileSystemVectorPort method)<br /> struct Lock * RESULT1 = FSLock(struct FileSystemVectorPort *fsvp, <br /> int32 *result2, struct Lock *rel,<br /> CONST_STRPTR name, int32 mode);<br /> fsvp - (struct FileSystemVectorPort *) Pointer to the vector port.<br /> result2 - (int32 *) Pointer to the storage area for RESULT2.<br /> rel_dir - (struct Lock *) Pointer to the 'name' reference dir lock.<br /> name - (CONST_STRPTR) Pointer to the object name.(no path component)<br /> mode - (int32) SHARED_LOCK, EXCLUSIVE_LOCK.<br /> RESULT1 - (struct Lock *) Pointer to the lock, NULL on failure.<br /> RESULT2 - (int32) Failure code if RESULT1 == NULL<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Developer tool ==<br /> <br /> To help with creation of new file-systems FSVPtool creates a ready to use skeleton. This tool should have find its way to the latest SDK but due to packaging time constraint before Amiwest it has not. It will be included in the next or may be published as a stand alone package.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=AmigaDOS_Vector-Port&diff=6692 AmigaDOS Vector-Port 2013-11-15T10:26:50Z <p>Alexandre Balaban: Added WIP logo</p> <hr /> <div>{{WIP}}<br /> == Rationale ==<br /> <br /> During development of file-systems developers often need to implement many parts to interface with DOS library rather than concentrate on implementing the actual function of the file-system. With this in mind the new interface Vector-port simplify this by delegating most of this work to the to the DOS library it self, only the feature implementation itself is the file-system's developer charge.<br /> <br /> == Introduction ==<br /> In the past (up to version 53.77) DOS library used to communicate with file-systems and handlers using [[AmigaDOS_Packets|DOS packets]]. A DOS packet is merely an [[Exec_Messages_and_Ports#Messages|Exec Message]] with a structure appended in order to pass parameters one way and results in the other. The aim being to stay the most generic possible those parameters are abstracted - which gives more work to the developer when they have to handle the packet.<br /> Starting with version 53.77 DOS library offers the new API &quot;Vector-Port&quot; for file-systems (i.e. not for handlers). It is basically a [[Exec_Messages_and_Ports#Message_Ports|Message port]] with an associated function list (also called &quot;table of vectors&quot; thus the API name). This way we are freed from many legacy things - BSTRs notably - which let the developer know exactly what he is manipulating.<br /> <br /> == Legacy Support ==<br /> <br /> Moreover since version 53.95; DOS library provides a DOS packet emulation for legacy applications that used to send DOS packets directly to the file-system's Message Port, this would effectively cause these old applications to break on a complete Vector-Port based file-system. However, this special DOS packet emulation function allow these 'application-sent' DOS packets to be automatically converted to an equivalent Vector-Port call.<br /> <br /> == Existing file-system conversion ==<br /> <br /> DOS allows for a step by step conversion of existing file-systems to the Vector-Port API by falling back to sending a DOS packet when any Vector-Port function is NULL or the Vector-Port itself does not validate.<br /> This means that once can convert single functions at a time by simply adding or removing the function vector from the Vector-Port initialisation table.<br /> <br /> == Key points ==<br /> <br /> Formerly a file-system was required to allocate a Message Port, now it only needs to replace this creation by a call to DOS method &quot;AllocDosObject&quot; requesting creation of a Vector-Port and providing it the list of implemented functions. Everything else is automatically handled: for unimplemented functions DOS library will fallback to sending a DOS packet, for the other DOS library will call them directly. It is one of the advantage of this solution: as it uses direct function calls it's substantially faster than sending a message and waiting its handling by the file-system. It is also processed in the caller context thus we benefit from the OS multitasking feature to parallelize calls and there is no context switch anymore.<br /> <br /> Example for ACTION_LOCATE_OBJECT DOS packet and the corresponding &quot;Vector-Port&quot; function FSLock():<br /> <br /> &lt;syntaxhighlight&gt;<br /> INPUTS (DosPacket method)<br /> dp_Type - (int32) ACTION_LOCATE_OBJECT<br /> dp_Arg1 - (BPTR) lock.<br /> dp_Arg2 - (BSTR) object_name. (may also include path)<br /> dp_Arg3 - (int32) Mode. SHARED_LOCK or EXCLUSIVE_LOCK.<br /> dp_Arg4 - &lt;unused&gt; 0<br /> dp_Arg5 - (BSTR) nameformat - name format indicator value.<br /> only if(dp_Arg2==dp_Arg5) then BSTR's are guaranteed to be<br /> a nul-terminated string. (53.23)<br /> RESULT1 - (BPTR) Lock - (ZERO on failure.)<br /> RESULT2 - (int32) Failure code if RESULT1 == ZERO<br /> <br /> INPUTS (FileSystemVectorPort method)<br /> struct Lock * RESULT1 = FSLock(struct FileSystemVectorPort *fsvp, <br /> int32 *result2, struct Lock *rel,<br /> CONST_STRPTR name, int32 mode);<br /> fsvp - (struct FileSystemVectorPort *) Pointer to the vector port.<br /> result2 - (int32 *) Pointer to the storage area for RESULT2.<br /> rel_dir - (struct Lock *) Pointer to the 'name' reference dir lock.<br /> name - (CONST_STRPTR) Pointer to the object name.(no path component)<br /> mode - (int32) SHARED_LOCK, EXCLUSIVE_LOCK.<br /> RESULT1 - (struct Lock *) Pointer to the lock, NULL on failure.<br /> RESULT2 - (int32) Failure code if RESULT1 == NULL<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Developer tool ==<br /> <br /> To help with creation of new file-systems FSVPtool creates a ready to use skeleton. This tool should have find its way to the latest SDK but due to packaging time constraint before Amiwest it has not. It will be included in the next or may be published as a stand alone package.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=AmigaDOS_Vector-Port&diff=6691 AmigaDOS Vector-Port 2013-11-15T10:22:19Z <p>Alexandre Balaban: First version</p> <hr /> <div>== Rationale ==<br /> <br /> During development of file-systems developers often need to implement many parts to interface with DOS library rather than concentrate on implementing the actual function of the file-system. With this in mind the new interface Vector-port simplify this by delegating most of this work to the to the DOS library it self, only the feature implementation itself is the file-system's developer charge.<br /> <br /> == Introduction ==<br /> In the past (up to version 53.77) DOS library used to communicate with file-systems and handlers using [[AmigaDOS_Packets|DOS packets]]. A DOS packet is merely an [[Exec_Messages_and_Ports#Messages|Exec Message]] with a structure appended in order to pass parameters one way and results in the other. The aim being to stay the most generic possible those parameters are abstracted - which gives more work to the developer when they have to handle the packet.<br /> Starting with version 53.77 DOS library offers the new API &quot;Vector-Port&quot; for file-systems (i.e. not for handlers). It is basically a [[Exec_Messages_and_Ports#Message_Ports|Message port]] with an associated function list (also called &quot;table of vectors&quot; thus the API name). This way we are freed from many legacy things - BSTRs notably - which let the developer know exactly what he is manipulating.<br /> <br /> == Legacy Support ==<br /> <br /> Moreover since version 53.95; DOS library provides a DOS packet emulation for legacy applications that used to send DOS packets directly to the file-system's Message Port, this would effectively cause these old applications to break on a complete Vector-Port based file-system. However, this special DOS packet emulation function allow these 'application-sent' DOS packets to be automatically converted to an equivalent Vector-Port call.<br /> <br /> == Existing file-system conversion ==<br /> <br /> DOS allows for a step by step conversion of existing file-systems to the Vector-Port API by falling back to sending a DOS packet when any Vector-Port function is NULL or the Vector-Port itself does not validate.<br /> This means that once can convert single functions at a time by simply adding or removing the function vector from the Vector-Port initialisation table.<br /> <br /> == Key points ==<br /> <br /> Formerly a file-system was required to allocate a Message Port, now it only needs to replace this creation by a call to DOS method &quot;AllocDosObject&quot; requesting creation of a Vector-Port and providing it the list of implemented functions. Everything else is automatically handled: for unimplemented functions DOS library will fallback to sending a DOS packet, for the other DOS library will call them directly. It is one of the advantage of this solution: as it uses direct function calls it's substantially faster than sending a message and waiting its handling by the file-system. It is also processed in the caller context thus we benefit from the OS multitasking feature to parallelize calls and there is no context switch anymore.<br /> <br /> Example for ACTION_LOCATE_OBJECT DOS packet and the corresponding &quot;Vector-Port&quot; function FSLock():<br /> <br /> &lt;syntaxhighlight&gt;<br /> INPUTS (DosPacket method)<br /> dp_Type - (int32) ACTION_LOCATE_OBJECT<br /> dp_Arg1 - (BPTR) lock.<br /> dp_Arg2 - (BSTR) object_name. (may also include path)<br /> dp_Arg3 - (int32) Mode. SHARED_LOCK or EXCLUSIVE_LOCK.<br /> dp_Arg4 - &lt;unused&gt; 0<br /> dp_Arg5 - (BSTR) nameformat - name format indicator value.<br /> only if(dp_Arg2==dp_Arg5) then BSTR's are guaranteed to be<br /> a nul-terminated string. (53.23)<br /> RESULT1 - (BPTR) Lock - (ZERO on failure.)<br /> RESULT2 - (int32) Failure code if RESULT1 == ZERO<br /> <br /> INPUTS (FileSystemVectorPort method)<br /> struct Lock * RESULT1 = FSLock(struct FileSystemVectorPort *fsvp, <br /> int32 *result2, struct Lock *rel,<br /> CONST_STRPTR name, int32 mode);<br /> fsvp - (struct FileSystemVectorPort *) Pointer to the vector port.<br /> result2 - (int32 *) Pointer to the storage area for RESULT2.<br /> rel_dir - (struct Lock *) Pointer to the 'name' reference dir lock.<br /> name - (CONST_STRPTR) Pointer to the object name.(no path component)<br /> mode - (int32) SHARED_LOCK, EXCLUSIVE_LOCK.<br /> RESULT1 - (struct Lock *) Pointer to the lock, NULL on failure.<br /> RESULT2 - (int32) Failure code if RESULT1 == NULL<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Developer tool ==<br /> <br /> To help with creation of new file-systems FSVPtool creates a ready to use skeleton. This tool should have find its way to the latest SDK but due to packaging time constraint before Amiwest it has not. It will be included in the next or may be published as a stand alone package.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Screens&diff=5389 UI Style Guide Screens 2013-04-26T09:49:01Z <p>Alexandre Balaban: /* Defining Screens */ Added Workbench screen figure</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Screens ==<br /> <br /> By organizing raw data into neat and friendly metaphors, GUIs make using a computer more intuitive and comprehensible. Still, sifting through the many possibilities in an effort to get to a desired task can often lead the user to wonder: &quot;Where am I?&quot; and &quot;What should I do next?&quot; The fact that the Amiga multitasks - that is, it allows the user to run a number of applications at the same time - only increases the need for strong context cues.<br /> <br /> When your application is run, it indicates the new context by either opening up in a window or on its own screen. Thus, screens and windows provide the main cues that tell the user where they are at any given moment.<br /> <br /> == Defining Screens ==<br /> <br /> Screens are unique to the Amiga. Other platforms have a single environment filling the monitor view or perhaps extending beyond that in height and width. On the Amiga, a user can have multiple screens, each an environment unto itself with its own palette, resolution and fonts - running at the same time.<br /> <br /> Typically, screens are at least as wide as the monitor display and have a single title bar at the top of the screen which is shared by all the applications that operate within that screen. It is possible, however, to have screens that are larger than the display area (known as virtual screens), or to have a screen that is not as tall as the monitor display. Applications will sometimes use the shorter screens as control panels in a different resolution than the display area. Workbench is the default screen a user is presented with upon booting the machine.<br /> <br /> Screens cannot be resized. New screens usually appear in front of existing screens. The user can access screens in the back by dragging the front screen down or flipping through the screens by using the screen depth gadget or keyboard combinations.<br /> <br /> [[File:SG7-1.png|frame|center|The workbench screen]]<br /> <br /> === Types of Screens ===<br /> <br /> Your application can open on one of three types of screens: the Workbench screen, a public custom screen that your application shares with other programs, or your own private custom screen.<br /> <br /> When your application uses the Workbench screen, it opens a window on the Workbench, using the palette, resolution and fonts that are defined in the Workbench Prefs.<br /> <br /> [[File:UG3-2.png|frame|center|A text editor open on the Workbench screen]]<br /> <br /> The Workbench screen is a public screen - that is, it can be used by any application. If your program needs a different resolution or palette than the user has chosen for his Workbench preferences, it should open on a public custom screen unless its requirements are restrictive enough to warrant a private custom screen.<br /> <br /> By keeping your custom screen public, you allow users to access other, perhaps supportive, applications without having to flip your application to the rear. Or, if the user is already running an application on a public screen with the proper palette and resolution requirements, he can open your program on that screen.<br /> <br /> [[File:UG3-3.png|frame|center|A text editor opened on an interlaced public custom screen]]<br /> <br /> A private custom screen is one that you set up to your specifications and which only your application may use. Private custom screens should be used only when your application has unusual rendering or resolution requirements, or when you need to be able to operate on the whole screen directly. An example of this would be an animation program that needs to switch viewports rapidly in order to get smooth motion.<br /> <br /> {{Note|title=Technical note|text=If your application opens a custom screen, make sure you redirect requesters to the custom screen - this applies to relevant DOS requesters as well as your application's requesters.}}<br /> <br /> == Respect User Choice ==<br /> <br /> Let the user decide, if possible, whether to open your application on Workbench or on another screen. If he decides to open it on a custom public screen, let him choose whether it will be a new screen or one that has already been created by a different application.<br /> <br /> Likewise, respect choices the user has already made. All custom screens, whether public or private, should default to the basic parameters<br /> established in Workbench's Preferences, unless your application has special requirements.<br /> <br /> If your application provides overscan capabilities you should respect the settings that the user has established in the Overscan editor found in Workbench's Prefs directory.<br /> <br /> == Screen Design ==<br /> <br /> Screens should have a depth gadget. If you have room, you should try not to obscure the screen depth gadget from view by opening windows that cover up the screen's depth gadget.<br /> <br /> The screen your application opens on should open in front of any other screens that are open.<br /> <br /> === Auto-scrolling ===<br /> <br /> Applications that open screens larger than the display area should provide the ability to auto-scroll. Moving the mouse to any of the display bounds should automatically scroll the screen to show more information in that area.<br /> <br /> {{Note|text=Screens that open larger than the display area should offer an auto-scrolling function.}}<br /> <br /> === Naming Your Public Screens ===<br /> <br /> Public screens are identified by their name. To name a public screen, use your application's basename (see [[User_Interface_Style_Guide#Defining_Your_Basename|Defining Your Basename]]) followed by an invocation count. The name should be in upper-case.<br /> <br /> For instance, a terminal package with the basename Axelterm that opens its own public screen should name the screen AXELTERM.1. On a system with a multi-serial port card, the user may decide to run a second copy of the package. That second public screen should be named AXELTERM.2.<br /> <br /> Some applications may use two screens during the same invocation of the program. For example, a paint program named VanGogh (with the basename VGOGH) may use one screen for the &quot;canvas&quot; and another screen with a different resolution for the control panel. In that case, the screens could be named VGOGHPAD.1 and VGOGHPANEL.1.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Glossary&diff=5388 UI Style Guide Glossary 2013-04-26T09:28:14Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> The purpose of this glossary is three-fold:<br /> <br /> * First, it is for you, the reader, if you are unsure as to the meaning of a term in this wiki.<br /> * Second, the technical writer in your organization can use it as a reference/style guide when writing documentation. For that reason, some terms are included in this glossary that haven't been discussed within the main body of this wiki. The capitalization, spelling and definitions given here agree (when applicable) with the User's Guide. If you follow this style, your manual will better agree with the main user manual for the Amiga system software. For instance, the definition for serial may be obvious to you, but by consulting this guide you can see that the user's manual did not capitalize it.<br /> * Third, it is for you, the developer, to let you know where certain functions are found in the system and what version of the system software contains that function. For instance, the concept of [[#AppIcon|AppIcon]]s first appeared with Release 2 of the system software and is supported by [[#workbench|workbench]].library. If you look at the glossary entry for [[#AppIcon|AppIcon]] you'll see it suffixed with ''Release 2 workbench.library''.<br /> <br /> Note: the codes (v) and (n) refer to verb and noun, respectively. A word may have different spellings and/or meanings depending on whether it is being used as a verb or a noun.<br /> <br /> ==A==<br /> ; {{Anchor|absolute pathname}}absolute pathname<br /> : The explicit identification of a file or [[#directory|directory]] - one that includes the [[#device|device]] or [[#partition|partition]] name and any directories that lead to the file.<br /> <br /> ; {{Anchor|action gadget|button}}action gadget<br /> : A box in a [[#window|window]] that lets you choose an operation to be performed by selecting the box. Common action gadgets are Continue, Save and Cancel. Also called ''buttons''.<br /> <br /> ; {{Anchor|active}}active<br /> : Currently selected; such as &quot;the active [[#window|window]]&quot;.<br /> <br /> ; {{Anchor|AmigaDOS|DOS}}AmigaDOS<br /> : The disk operating system (DOS) used by Amiga computers.<br /> <br /> ; {{Anchor|AppIcon}}AppIcon<br /> : An [[#icon|icon]] on [[#Workbench|Workbench]] that allows the user to pass arguments to an application. For instance, if a text processor has an [[#AppIcon|AppIcon]] on [[#Workbench|Workbench]], the user could drag the [[#icon|icon]] for a text file onto the [[#AppIcon|AppIcon]] and that file would be loaded into the application. ''Release 2 workbench.library'' <br /> <br /> ; {{Anchor|application gadget}}application gadget<br /> : Any of a number of programmed graphic images that appear within programs and can be manipulated with the mouse to perform a certain function. Under Release 2 of the operating system, standard [[#application gadget|application gadget]]s are available to the developer through [[#GadTools|GadTools]]. Previously, you would have to create [[#application gadget|application gadget]]s within your program.<br /> <br /> ; {{Anchor|AppMenu}}AppMenu<br /> : An AppMenu allows the user to add a custom [[#menu|menu]] item to the [[#Workbench|Workbench]] Tools [[#menu|menu]]. ''Release 2 workbench.library'' <br /> <br /> ; {{Anchor|AppWindow}}AppWindow<br /> : A [[#window|window]] on [[#Workbench|Workbench]] that allows the user to pass arguments to an application. For example, if a text processor has an [[#AppWindow|AppWindow]], the user can drag the [[#icon|icon]] for a text file into the [[#AppWindow|AppWindow]] and that file would be loaded into the application. ''Release 2 workbench.library'' <br /> <br /> ; {{Anchor|archive}}archive<br /> : 1. (n) A backup copy of a file or files.<br /> : 2. (v) To copy files to disk or tape for backup purposes.<br /> <br /> ; {{Anchor|ARexx}}ARexx<br /> : A text-based language that, along with [[#Workbench|Workbench]] and the [[#Shell|Shell]], serves as a built-in user interface for the Amiga. ARexx has two main uses. As a scripting language, it can operate internally with applications. It can also be used to operate two or more applications that may or may not be inherently compatible. In your documentation you should note that ARexx is a trademark of William S. Hawes. <br /> <br /> ; {{Anchor|argument}}argument<br /> : An additional item of information, such as a [[#filename|filename]], value or option, included along with a command. This information determines the exact action of the command. <br /> <br /> ; {{Anchor|argument passing}}argument passing<br /> : Specifying parameters for a program or command to follow. On the [[#Workbench|Workbench]], this can be handled through the Tool Types and Default Tool fields of an [[#icon|icon]], or through an [[#AppWindow|AppWindow]], [[#AppIcon|AppIcon]] or [[#AppMenu|AppMenu]]. More traditionally, [[#argument|argument]]s can be passed via a command line in the [[#Shell|Shell]]. <br /> <br /> ; {{Anchor|assign}}assign<br /> : To link a [[#directory|directory]] name to a logical [[#device|device]] name, with the ASSIGN command, so that programs which use that [[#directory|directory]] can look for one [[#device|device]] name rather than having to search through several [[#volume|volume]]s for the [[#directory|directory]]. For instance, the RAM:T [[#directory|directory]] is commonly assigned to the [[#device|device]] name T:. <br /> <br /> ; {{Anchor|attributes}}attributes<br /> : A series of flags stored with every file. Attributes indicate file type and control the file operations (read, write, delete, etc.) permissible on the file. Also called ''protection bits''. <br /> <br /> ; {{Anchor|autoscroll}}autoscroll<br /> : To automatically move a [[#screen|screen]] when the [[#pointer|pointer]] reaches the edges of the currently viewable area. <br /> <br /> ==B==<br /> ; {{Anchor|background process}}background process<br /> : A program that is started from the [[#Shell|Shell]] with the RUN command. The program does not take over the [[#Shell|Shell]] but is run in the &quot;background&quot;. <br /> <br /> ; {{Anchor|back up}}back up<br /> : (v) To make a backup copy.<br /> <br /> ; {{Anchor|backup}}backup<br /> : (n) A copy of a file on disk or tape used to replace lost data.<br /> <br /> ; {{Anchor|basename}}basename<br /> : A specific and non-conflicting name for an application that should be used by the developer when naming accompanying files and ports such as the application's [[#ARexx|ARexx]] port and the file where application-specific preferences are stored. In most cases, the basename should be the same as the name of the application's executable. <br /> <br /> ; {{Anchor|bitmap}}bitmap<br /> : An image made up of [[#pixel|pixel]]s.<br /> <br /> ; {{Anchor|boolean}}boolean<br /> : Having two possible states: on or off, true or false, yes or no.<br /> <br /> ; {{Anchor|Bridgeboard}}Bridgeboard<br /> : An expansion board made by Commodore that allowed the Amiga to emulate PC-compatible computers based on processors up to 486.<br /> <br /> ; {{Anchor|brush}}brush<br /> : An IFF graphics file; usually a section cut out from a full-sized picture.<br /> <br /> ; {{Anchor|buffer}}buffer<br /> : A temporary storage area.<br /> <br /> ==C==<br /> ; {{Anchor|check box|checkbox}}check box<br /> : An [[#application gadget|application gadget]] used to let a user turn an option on or off. When a check mark appears in the box, the selection is considered to be &quot;on&quot;. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|Chip RAM|Chip}}Chip RAM<br /> : The area of [[#RAM|RAM]] accessible to the Amiga's custom chip set used for graphics and sound data. <br /> <br /> ; {{Anchor|choose}}choose<br /> : In terms of using the mouse, choose is what the user does with [[#menu item|menu item]]s. ''Select'' is what he does with [[#icon|icon]]s. <br /> <br /> ; {{Anchor|clear}}clear<br /> : 1. To change a bit or flag to 0, off or disabled state. Opposite of ''set''.<br /> : 2. To erase a screen or [[#window|window]] display.<br /> <br /> ; {{Anchor|CLI}}CLI (Command Line Interface)<br /> : A means of communicating with a computer by issuing commands from the keyboard. The program that allows this on the Amiga is called the [[#Shell|Shell]] and, along with [[#Workbench|Workbench]] and [[#ARexx|ARexx]], is one of the three built-in interfaces. Before the [[#Shell|Shell]] was available, the program used was called the CLI. <br /> <br /> ; {{Anchor|click}}click<br /> : To press and release a mouse button.<br /> <br /> ; {{Anchor|close}}close<br /> : To remove a [[#window|window]] from the screen.<br /> <br /> ; {{Anchor|close gadget}}close gadget<br /> : A system gadget used to close [[#window|window]]s. It appears in the upper left corner of the [[#window|window]].<br /> <br /> ; {{Anchor|cold reboot}}cold reboot<br /> : To reset the Amiga by turning the power off, waiting 20 seconds, then restoring power. <br /> <br /> ; {{Anchor|color correction}}color correction<br /> : A printing option, selected through Prefs' PrinterGfx editor, that tries to better match the colors of a printout to the colors on the screen. <br /> <br /> ; {{Anchor|color selection gadget}}color selection gadget<br /> : A gadget from which you can choose one of several displayed colors. Also referred to as the palette gadget. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|command history}}command history<br /> : A feature of the [[#Shell|Shell]] that allows you to recall previously entered command lines by using the cursor keys or a vertical scrollbar if available.<br /> <br /> ; {{Anchor|command template|template}}command template<br /> : A line of text showing how a command and its [[#argument|argument]]s should be used. When a user types &quot;&lt;command&gt; ?&quot; in the [[#Shell|Shell]], he should be shown the template for that command. ''Release 2 dos.library ReadArgs()'' <br /> <br /> ; {{Anchor|Commodities Exchange|Exchange}}Commodities Exchange<br /> : A system that simplifies the process of writing programs which monitor the input handler system - these programs can respond to hot keys, take actions based on mouse action or inactivity, or even modify the input stream as it goes by. <br /> <br /> ; {{Anchor|condition flag}}condition flag<br /> : A variable that contains a return code value indicating the success or failure of command execution. <br /> <br /> ; {{Anchor|console window}}console window<br /> : A [[#window|window]] used for the input and output of text.<br /> <br /> ; {{Anchor|context cue}}context cue<br /> : A graphical hint to the user that where he is in the system or application has changed. When a user chooses New from a Project [[#menu|menu]], for example, a new [[#window|window]] opens. The new [[#window|window]] is a context cue. <br /> <br /> ; {{Anchor|contiguous}}contiguous<br /> : Continuous; consisting of a series of adjacent items. Contiguous memory is a block of memory. <br /> <br /> ; {{Anchor|Control-key combination}}Control-key combination<br /> : A key combination that performs a special function, entered by holding down Ctrl while pressing another key on the keyboard. Some Control-key combinations are executed as soon as they are pressed, such as when Ctrl-C is used to abort the execution of an [[#AmigaDOS|AmigaDOS]] command. Some produce a reversed character image and have no immediate effect. <br /> <br /> ; {{Anchor|cursor}}cursor<br /> : A highlighted rectangle used in the [[#Shell|Shell]] and some applications to indicate text position. <br /> <br /> ; {{Anchor|cursor keys}}cursor keys<br /> : The four keys with directional arrows on them found between the numpad and the rest of the keys on the keyboard.<br /> <br /> ; {{Anchor|custom icon}}custom icon<br /> : An application-specific gadget that can be moved and manipulated. Example: the note object in some music applications. <br /> <br /> ; {{Anchor|cycle gadget}}cycle gadget<br /> : An [[#application gadget|application gadget]] that allows the user to select one of several options. One option is displayed at a time and, as the gadget is selected, the other options become visible. The displayed option is the selected option. Similar to radio buttons but this takes up less space in a [[#window|window]] or requester. ''Release 2 GadTools'' <br /> <br /> ==D==<br /> ; {{Anchor|dead key}}dead key<br /> : A key, or key combination, that modifies the output of the next key to be pressed. For instance, on an American keyboard, Alt-H will superimpose a caret (^) symbol over the next applicable key to be pressed. <br /> <br /> ; {{Anchor|default}}default<br /> : A value or action assumed if the user has not specified anything else.<br /> <br /> ; {{Anchor|Default Tool}}Default Tool<br /> : An [[#argument passing|argument passing]] [[#device|device]] used with project [[#icon|icon]]s on the [[#Workbench|Workbench]]. When a project [[#icon|icon]] is double-clicked, the application specified in the Default Tool field of that [[#icon|icon]]'s Information [[#window|window]] will automatically load and run. <br /> <br /> ; {{Anchor|delete}}delete<br /> : To erase or discard a file, [[#buffer|buffer]] or other stored item.<br /> <br /> ; {{Anchor|delimiter}}delimiter<br /> : A character that marks the beginning and end of a string.<br /> <br /> ; {{Anchor|depth gadget}}depth gadget<br /> : A system gadget for moving a [[#window|window]] or screen in front of or behind other [[#window|window]]s or screens. It appears in the upper right corner of the [[#window|window]] or screen.<br /> <br /> ; {{Anchor|deselect}}deselect<br /> : This term was used in the Release 2 user documentation rather than ''unselect''. <br /> <br /> ; {{Anchor|destination}}destination<br /> : The [[#device|device]], [[#directory|directory]] or file that is receiving information. For instance, in EDIT, the file that the revised text is being sent to is the destination file. <br /> <br /> ; {{Anchor|device}}device<br /> : A physical mechanism, such as a printer or disk drive, or a software entity (logical device), such as CON: or NIL:, used as a source or destination for information. In the Release 2 user manual, hard disk and disk drive designations such as DF0: were capitalized. <br /> <br /> ; {{Anchor|device handler|handler}}device handler<br /> : Files that act as intermediate stages between [[#AmigaDOS|AmigaDOS]] and physical [[#device|device]]s, such as the Port-handler file in the L: [[#directory|directory]] which handles the interface for the PAR:, SER: and PRT: [[#device|device]]s. Its commonly abbreviated as just ''Handler''.<br /> <br /> ; {{Anchor|directory}}directory<br /> : A subdivision in a computer's filing system. Directories are represented on the [[#Workbench|Workbench]] as [[#drawer|drawer]] [[#icon|icon]]s. <br /> <br /> ; {{Anchor|disk}}disk<br /> : A medium for storage of computer data. This term was used in the Release 2 documentation rather than ''diskette''. <br /> <br /> ; {{Anchor|display box}}display box<br /> : An [[#application gadget|application gadget]], usually under a scroll gadget or next to a selection gadget, that displays the current selection but doesn't allow the user to edit it. <br /> <br /> ; {{Anchor|double-click}}double-click<br /> : To press and release the mouse's selection button twice.<br /> <br /> ; {{Anchor|drag}}drag<br /> : To move an [[#icon|icon]], [[#window|window]], gadget or screen across the display by pointing to the object, holding down the selection button and moving the mouse. <br /> <br /> ; {{Anchor|drag selection}}drag selection<br /> : The process of selecting several [[#icon|icon]]s at once by holding down the selection button and using the mouse to draw a dash-rule box (marquee) around the [[#icon|icon]]s to be selected. When the button is released, all the [[#icon|icon]]s in the marquee will be selected. <br /> <br /> ; {{Anchor|drawer}}drawer<br /> : A subdivision of a disk storage area. A drawer corresponds to an [[#AmigaDOS|AmigaDOS]] [[#directory|directory]]. Usually ''drawer'' is used in the context of the [[#Workbench|Workbench]], while ''[[#directory|directory]]'' will be preferred in the context of the [[#Shell|Shell]].<br /> <br /> ; {{Anchor|drive name}}drive name<br /> : The name assigned to a floppy disk drive or hard disk such as FH1: or DF0:. It's the same as the [[#device|device]] name. <br /> <br /> ; {{Anchor|dump}}dump<br /> : A printout of the image displayed on the screen.<br /> <br /> ==E==<br /> ; {{Anchor|ECS|Enhanced Chip Set}}ECS (Enhanced Chip Set)<br /> : The upgraded versions of the Amiga Classic's Agnus and Denise coprocessor chips. The ECS offered new display modes and expanded existing graphics capabilities. Many of the benefits of the ECS are available only in conjunction with Release 2 of the operating system. <br /> <br /> ; {{Anchor|ENV}}ENV&lt;nowiki&gt;:&lt;/nowiki&gt;<br /> : A [[#directory|directory]] where [[#environment variable|environment variable]]s and user preferences are temporarily stored. Short for &quot;''environment''&quot;. <br /> <br /> ; {{Anchor|ENVARC}}ENVARC&lt;nowiki&gt;:&lt;/nowiki&gt;<br /> : Similar to ENV: but it will survive a reboot. Short for &quot;''environment archive''&quot;. <br /> <br /> ; {{Anchor|environment variable}}environment variable<br /> : A variable used by [[#AmigaDOS|AmigaDOS]] to represent a string or a value.<br /> <br /> ; {{Anchor|error code}}error code<br /> : A number identifying an error that has occurred during execution of a command or program. <br /> <br /> ; {{Anchor|executable}}executable<br /> : The name for an application which the user types into the [[#Shell|Shell]] in order to run the program. The executable should be one word - short, but long enough to prevent it from conflicting with the executables of other programs. <br /> <br /> ; {{Anchor|extended selection}}extended selection<br /> : The process of selecting several [[#icon|icon]]s at once by holding down Shift while selecting each [[#icon|icon]] with the mouse. <br /> <br /> ==F==<br /> ; {{Anchor|FastFileSystem|FFS}}FastFileSystem (FFS)<br /> : An enhanced Amiga file system usable with both floppy and hard disks. A [[#volume|volume]] is formatted as either FFS or OldFileSystem (OFS). <br /> <br /> ; {{Anchor|Fast RAM}}Fast RAM<br /> : General memory used by programs and data; as opposed to ''[[#Chip RAM|Chip RAM]]''.<br /> <br /> ; {{Anchor|fatal error}}fatal error<br /> : Describes an error serious enough to halt the [[#process|process]] that caused it.<br /> <br /> ; {{Anchor|field}}field<br /> : 1. The screen area behind the text under a [[#Workbench|Workbench]] [[#icon|icon]]. The color of the field can be changed with the Font editor. <br /> : 2. An area in a requester where a text string can be inserted. A database, for example, will often have multi-field requesters.<br /> <br /> ; {{Anchor|filename}}filename<br /> : Use one word in your documentation. Note: use brackets when information should be substituted. In this example, &lt;filename&gt; is just a placeholder for the actual file name.<br /> <br /> ; {{Anchor|flag}}flag<br /> : A status indicator variable with a limited number of possible states.<br /> <br /> ; {{Anchor|format}}format<br /> : To prepare a disk for use with the Amiga. Use this term instead of ''initialize'' when referring to disks.<br /> <br /> ; {{Anchor|function keys}}function keys<br /> : Keys at the top of the Amiga keyboard, labelled F1 to F10, that can be programmed to perform special tasks. <br /> <br /> ==G==<br /> ; {{Anchor|gadget}}gadget<br /> : Any of various programmed graphic images that may appear in a [[#window|window]], requester or screen, that can be manipulated with the mouse to perform a certain function. <br /> <br /> ; {{Anchor|GadTools}}GadTools<br /> : A toolkit, available to developers under Release 2 of the operating system, that supplies pre-programmed, standard [[#application gadget|application gadget]]s for use within applications. <br /> <br /> ; {{Anchor|ghosting}}ghosting<br /> : Superimposing a pattern of dots in the shadow color over disabled [[#menu item|menu item]]s or gadgets. This gives the user a visual cue that the item is unavailable. [[#Intuition|Intuition]] will do this automatically to all standard [[#menu item|menu item]]s and gadgets that your program disables. <br /> <br /> ; {{Anchor|graphic user interface|GUI}}graphic user interface (GUI)<br /> : A visually oriented system allowing you to tell a computer what to do by manipulating graphic symbols rather than by typing in commands. Often, the GUI employs a metaphor for ease of understanding. The [[#Workbench|Workbench]] is the Amiga GUI. <br /> <br /> ==H==<br /> ; {{Anchor|hard disk}}hard disk<br /> : Use this term rather than ''hard drive''.<br /> <br /> ; {{Anchor|history buffer}}history buffer<br /> : A section of memory that stores the most recent commands for a given [[#Shell|Shell]].<br /> <br /> ; {{Anchor|hold down}}hold down<br /> : To continually press a mouse button until instructed to release it.<br /> <br /> ; {{Anchor|hot key}}hot key<br /> : A key or key combination used by Commodities Exchange programs to open a hidden [[#window|window]].<br /> <br /> ==I==<br /> ; {{Anchor|icon}}icon<br /> : An image appearing on the screen to represent a disk, [[#drawer|drawer]], project or tool. Icons can be moved and selected with the mouse to allow you to work with the items they represent. <br /> <br /> ; {{Anchor|icon drop box gadget}}icon drop box gadget<br /> : The drop box glyph in an [[#AppWindow|AppWindow]]. ''Release 2 workbench.library''<br /> <br /> ; {{Anchor|IFF|Interchange File Format}}IFF (Interchange File Format)<br /> : The standardized format in which the Amiga stores data for such things as text, graphics and sound. Such a standard is useful for data sharing between applications. ''iffparse.library (can be used with Release 1.3 and Release 2)'' <br /> <br /> ; {{Anchor|.info file}}.info file<br /> : A file containing the image and position data for an [[#icon|icon]].<br /> <br /> ; {{Anchor|Information window}}Information window<br /> : The [[#window|window]] presented on [[#Workbench|Workbench]] when a user selects ([[#click|click]]s once on) an [[#icon|icon]] and chooses &quot;Information...&quot; from the Icons [[#menu|menu]]. <br /> <br /> ; {{Anchor|Inter-Process Communication|IPC}}Inter-Process Communication (IPC)<br /> : The means by which two or more applications can operate in conjunction with one another regardless of whether they are inherently compatible. On the Amiga, this can be accomplished with [[#ARexx|ARexx]]. <br /> <br /> ; {{Anchor|Internal command}}Internal command<br /> : Refers to an [[#AmigaDOS|AmigaDOS]] command that is built into the [[#Shell|Shell]], rather than loaded from disk. <br /> <br /> ; {{Anchor|Intuition}}Intuition<br /> : The collective term for the [[#GUI|GUI]] toolkit and function libraries that contain the elements necessary for you to build graphic interfaces for your Amiga applications. <br /> <br /> ==K==<br /> ; K (Kilobyte)<br /> : 1024 bytes. The abbreviation used in the Release 2 user's manual; eg. &quot;512K&quot;. <br /> <br /> ; {{Anchor|keymap}}keymap<br /> : A file that determines the arrangement of characters on the keyboard and determines the meaning of each key. Different languages have different keymaps. <br /> <br /> ; {{Anchor|keystrokes}}keystrokes<br /> : In the Release 2 user's manual, alphabetical keys are specified with an upper-case letter and the word ''key'' is not used. Example: ''Press Q and then press Return.'' Non-alphanumeric keys appear in the manual as they do on the keyboard (Esc, Del, Alt, Ctrl, Caps Lock, Help) with the exceptions of Backspace, Return, Shift and the cursor keys. Key combinations are separated by a hyphen and upper-case letters are specified by the Shift combination. Example: ''Ctrl-O will move the cursor to the end of the line, but Ctrl-Shift-O will move the cursor to the end of the file.'' <br /> <br /> ; {{Anchor|keyword}}keyword<br /> : A word recognized by a command as identifying an [[#argument|argument]] or specifying an option. If the user needs to type the keyword on the command line along with its argument, &quot;/K&quot; should follow the keyword in the command template. <br /> <br /> ; {{Anchor|Kickstart}}Kickstart<br /> : Software that is read from disk and used to boot the Amiga. Also refers to the portion of the OS that is in [[#ROM|ROM]]. <br /> <br /> ==L==<br /> ; {{Anchor|library}}library<br /> : A related set of functions and collections of data that can be shared by various programs. For instance, the commodities.library in the LIBS: [[#directory|directory]] is used by all the Commodities Exchange programs. <br /> <br /> ; {{Anchor|link}}link<br /> : A file that is a pointer to another file. When the original file is called, the linked file will be used. <br /> <br /> ==M==<br /> ; {{Anchor|macro}}macro<br /> : A single command that represents a sequence of commands. [[#ARexx|ARexx]] should be the macro language used by applications on the Amiga. <br /> <br /> ; {{Anchor|MB|Megabyte}}MB (Megabyte)<br /> : The abbreviation for megabyte (1,048,576 bytes) used in the Release 2 System Software manual. <br /> <br /> ; {{Anchor|memory}}memory<br /> : The Classic Amigas had both [[#Chip RAM|Chip]] and [[#Fast RAM|Fast]] RAM, as well as 512K of [[#ROM|ROM]] memory. New Generation Amigas only have Fast RAM, Chip memory has been replaced by on-board memory of the respective dedicated card (Graphic and Audio cards).<br /> <br /> ; {{Anchor|menu}}menu<br /> : A list of on-screen options, displayed by using the [[#menu button|menu button]], from which users can choose commands that control a program. <br /> <br /> ; {{Anchor|menu bar}}menu bar<br /> : The list of headings that appears across the top of the screen when the [[#menu button|menu butto]] is held down. When the [[#menu button|menu button]] is not depressed, the visible bar is called the title bar. <br /> <br /> ; {{Anchor|menu button}}menu button<br /> : The right mouse button.<br /> <br /> ; {{Anchor|menu item}}menu item<br /> : An option that appears in a [[#menu|menu]].<br /> <br /> ; {{Anchor|modified project requester}}modified project requester<br /> : A requester that prompts the user to save the project before continuing. It should be presented when the user chooses an action (such as Quit Program) that would cause his currently unsaved work to be lost. <br /> <br /> ; {{Anchor|Moniterm Viking}}Moniterm Viking<br /> : A high-resolution monitor with a paper-white display which was especially useful with CAD or DTP applications. A similar monitor was sold by Commodore as the A2024. When used with the Amiga, these monitors worked in a special mode that tiles a number of high-resolution screens together to make one large, high-resolution display. <br /> <br /> ; {{Anchor|monospaced font}}monospaced font<br /> : A font in which each character takes up an equal amount of space.<br /> <br /> ; {{Anchor|MountList}}MountList<br /> : A text file in the DEVS: [[#directory|directory]] that contains information about [[#device|device]]s that have been attached to or installed in the Amiga. <br /> <br /> ; {{Anchor|multiscan}}multiscan<br /> : A type of video monitor that can accept several different scan rates.<br /> <br /> ; {{Anchor|multitasking}}multitasking<br /> : The ability to perform more than one operation, or task, at a time.<br /> <br /> ; {{Anchor|mutually exclusive}}mutually exclusive<br /> : Only one option can be chosen from a given group of options. For example, in a group of radio buttons, one and only one is always selected. When a user [[#click|click]]s on an unselected radio button, the one that had been highlighted becomes unselected. <br /> <br /> ==N==<br /> ; {{Anchor|notification}}notification<br /> : A means by which an application can receive a message whenever a specified file is modified. ''Release 2 dos.library'' <br /> <br /> ; {{Anchor|null string}}null string<br /> : An empty string. Null strings are commonly used in text editors to delete information. If the user replaces a word with a null string, the word is deleted. <br /> <br /> ==O==<br /> ; {{Anchor|offset}}offset<br /> : To shift or move over.<br /> <br /> ; {{Anchor|open}}open<br /> : To make the selected object available for use. When the user opens a disk or [[#drawer|drawer]] [[#icon|icon]], its contents are displayed. When the user opens a project or tool [[#icon|icon]], a program is started. <br /> <br /> ; {{Anchor|overscan area}}overscan area<br /> : The normally unused area surrounding a standard-size screen. The Overscan Preferences editor allows the user to expand his screen to fill this area. ''Release 2 intuition.library QueryOScan()'' <br /> <br /> ; {{Anchor|overwrite}}overwrite<br /> : To write information to a file or disk, replacing any information that previously was stored there. <br /> <br /> ==P==<br /> ; {{Anchor|parallel}}parallel<br /> : An interface port that transfers data one complete byte (8 bits) at a time, contrasted to a serial interface which sends a single bit at a time. The Amiga has an extended parallel port to which a printer is often connected. <br /> <br /> ; {{Anchor|parent}}parent<br /> : The [[#window|window]] or [[#directory|directory]] from which another [[#window|window]], [[#directory|directory]] or file was generated. <br /> <br /> ; {{Anchor|parsing}}parsing<br /> : When the system examines and interprets [[#argument|argument]]s so the appropriate operation can be performed. ''Release 2 dos.library ReadArgs()'' <br /> <br /> ; {{Anchor|partition}}partition<br /> : A distinct section of a hard disk.<br /> <br /> ; {{Anchor|path}}path<br /> : The series of [[#device|device]], [[#directory|directory]] and [[#subdirectory|subdirectory]] names that defines the location of a file. <br /> <br /> ; {{Anchor|pattern matching}}pattern matching<br /> : An [[#AmigaDOS|AmigaDOS]] feature that lets the user specify file and [[#directory|directory]] names by using [[#wildcard|wildcard]]s and other tokens. ''Release 2 dos.library MatchFirst() MatchNext() MatchPattern()'' <br /> <br /> ; {{Anchor|peripheral}}peripheral<br /> : An external hardware [[#device|device]].<br /> <br /> ; {{Anchor|pitch}}pitch<br /> : The number of characters printed in a horizontal inch.<br /> <br /> ; {{Anchor|pixels}}pixels<br /> : The dots of light that make up the Amiga screen display. A pixel is the smallest unit of display information on a given screen. <br /> <br /> ; {{Anchor|pointer}}pointer<br /> : An image on the screen that moves as the user moves the mouse. A default pointer, which can be redefined by the user, is included in the system. The pointer often changes to reflect [[#process|process]]es and tools. <br /> <br /> ; {{Anchor|Preferences|Prefs}}Preferences (Prefs)<br /> : A [[#Workbench|Workbench]] [[#drawer|drawer]] containing editors that let the user configure and customize his Amiga environment. <br /> <br /> ; {{Anchor|press}}press<br /> : The term used in Release 2 documentation when telling the user to hit a key. Example: ''Press F6 for your new macro.'' <br /> <br /> ; {{Anchor|priority}}priority<br /> : A variable determining the proportion of the Amiga's processing time that will be allotted to a given task. Each task has an independent priority. Task priority is set automatically but can be changed with the CHANGETASKPRI command. <br /> <br /> ; {{Anchor|process}}process<br /> : A task that can communicate with [[#AmigaDOS|AmigaDOS]]. Each process has a unique process number. [[#Shell|Shell]] process numbers are usually displayed as part of the [[#Shell|Shell]] prompt. <br /> <br /> ; {{Anchor|project}}project<br /> : A file in which information created or used by a tool is stored.<br /> <br /> ; {{Anchor|prompt}}prompt<br /> : A message or symbol, such as 1&gt;, that indicates that text input to the computer is possible. <br /> <br /> ; {{Anchor|protection bits}}protection bits<br /> : Use [[#attributes|''attributes'']] instead.<br /> <br /> ; {{Anchor|pseudo-icon}}pseudo-icon<br /> : An [[#icon|icon]] that is displayed, when the Show All Files item is chosen from [[#Workbench|Workbench]]'s [[#Window|Window]] [[#menu|menu]], for an object that does not have a .info file. <br /> <br /> ; {{Anchor|public screen}}public screen<br /> : A screen that can be used by any application. [[#Workbench|Workbench]] is a public screen. Release 2 allows an application to create its own custom public screen which can then be used by other applications. ''Release 2 intuition.library'' <br /> <br /> ; {{Anchor|pure}}pure<br /> : Describes a command or program that can be made resident.<br /> <br /> ==Q==<br /> ; {{Anchor|qualifier}}qualifier<br /> : A key, such as Shift, Ctrl or Alt, that changes the Amiga's interpretation of a simultaneous or subsequent keystroke or mouse [[#click|click]]. <br /> <br /> ==R==<br /> ; {{Anchor|radio buttons}}radio buttons<br /> : A group of circular gadgets used to offer mutually exclusive choices. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|RAM|Random Access Memory}}RAM (Random Access Memory)<br /> : Part of the Amiga's internal memory that can be used for data storage and is directly accessible by the CPU. Data in [[#RAM|RAM]] is lost when the Amiga is rebooted or powered down. <br /> <br /> ; {{Anchor|Ram Disk}}Ram Disk<br /> : A section of [[#RAM|RAM]] set aside to function as if it were a disk drive. Also known by its logical [[#device|device]] name of [[#RAM|RAM]]:. <br /> <br /> ; {{Anchor|RC}}RC<br /> : In [[#ARexx|ARexx]], the field where numerical returns are placed. String values are placed in the RESULT field. <br /> <br /> ; {{Anchor|read}}read<br /> : To retrieve stored information.<br /> <br /> ; {{Anchor|Read Only}}Read Only<br /> : If disk status is Read Only, the user can only look at the contents.<br /> <br /> ; {{Anchor|Read/Write}}Read/Write<br /> : If disk status is Read/Write, the user can look at and alter the contents.<br /> <br /> ; {{Anchor|reboot}}reboot<br /> : To reset the Amiga by pressing Ctrl, Left-Amiga and Right-Amiga simultaneously. Also called a ''warm boot''. <br /> <br /> ; {{Anchor|redirect}}redirect<br /> : To change the source or destination of a command's input or output from the default by using the special characters &lt; or &gt; [or &gt;&gt;]. <br /> <br /> ; {{Anchor|relative pathname}}relative pathname<br /> : The [[#path|path]] to a file or [[#directory|directory]] that does not include the [[#device|device]] or [[#partition|partition]] name that leads to the file. <br /> <br /> ; {{Anchor|Release 2}}Release 2<br /> : Use this term instead of ''Version 2.0'' or just ''2.0'', unless in the future you are referring to a specific version of Release 2 of the operating system. <br /> <br /> ; {{Anchor|requester}}requester<br /> : A [[#window|window]] that allows the user to control options, access files and confirm actions. Many requesters function just like other [[#window|window]]s but others, called modal requesters, block input to the system until the user responds to it. <br /> <br /> ; {{Anchor|resident}}resident<br /> : Describes a command or program that has been copied into memory, with the RESIDENT command, for quicker execution. Only pure files can be made resident. <br /> <br /> ; {{Anchor|resolution}}resolution<br /> : The number of [[#pixel|pixel]]s associated with a particular display mode. For example, a normal NTSC Hires screen has a resolution of 640 (horizontal) by 200 (vertical) [[#pixel|pixel]]s. <br /> <br /> ; {{Anchor|RESULT}}RESULT<br /> : In [[#ARexx|ARexx]], the field where string values are returned. Numerical values go in the RC field. <br /> <br /> ; {{Anchor|return code}}return code<br /> : A numerical value, generated upon execution of a command, to indicate its level of success. The number is 0 if the command was successful and usually 5, 10 or 20 if there was a problem in executing the command. The return code value is assigned to the condition flag. <br /> <br /> ; {{Anchor|RGB|Red-Green-Blue}}RGB (Red-Green-Blue)<br /> : A type of video signal in which the three primary color signals are sent separately. Standard Amiga output uses an RGB monitor. <br /> <br /> ; {{Anchor|ROM|Read Only Memory}}ROM (Read Only Memory)<br /> : Permanent memory that is pre-programmed with system instructions and does not change. The contents of ROM are not affected by user commands or program operation. <br /> <br /> ; {{Anchor|root directory}}root directory<br /> : The main [[#directory|directory]] on a [[#volume|volume]]. The root [[#directory|directory]] is at the top of the filing hierarchy and created when a [[#volume|volume]] is [[#format|formatted]]. The root [[#directory|directory]] is specified by the [[#volume name|volume name]] followed by a colon. <br /> <br /> ==S==<br /> ; {{Anchor|save}}save<br /> : To write the current version of a file to disk. In many settings requesters the user is given the choices Save, Use and Cancel. Save, in this case, would cause the program to use those settings each time it opens. <br /> <br /> ; {{Anchor|screen}}screen<br /> : An area of the display that shares the same video attributes, such as resolution and palette. <br /> <br /> ; {{Anchor|script}}script<br /> : A text file containing a series of commands that can be automatically executed to perform a complex or repetitive task. An example of a script is the Startup-sequence that is executed when the Amiga is booted. <br /> <br /> ; {{Anchor|scroll}}scroll<br /> : To move through the viewing area of a [[#window|window]]. <br /> <br /> ; {{Anchor|scroll arrows}}scroll arrows<br /> : Parts of a scroll gadget that can be used to move the viewing area continuously. <br /> <br /> ; {{Anchor|scroll bar}}scroll bar<br /> : The highlighted area within the scroll box that can be dragged to display the hidden contents of a [[#window|window]]. It changes its size to indicate the portion of the [[#window|window]] that is currently visible. <br /> <br /> ; {{Anchor|scroll box}}scroll box<br /> : The shaded area within which the scroll bar can be dragged. You can [[#click|click]] in the scroll box to move the scroll bar. <br /> <br /> ; {{Anchor|scroll gadget}}scroll gadget<br /> : A gadget that may appear in a [[#window|window]] to let the user move through the viewing area of a [[#window|window]]. A scroll gadget is made up of the scroll bar, scroll box and scroll arrows. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|scrolling list}}scrolling list<br /> : A gadget that allows the user to select an object from a variable list of objects. A scrolling list is made up of a scroll gadget, a view box and a text gadget. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|SCSI|Small Computer System Interface}}SCSI (Small Computer System Interface)<br /> : A standard interface protocol for connecting peripherals, usually mass storage [[#device|device]]s, to computer equipment. <br /> <br /> ; {{Anchor|select}}select<br /> : To choose an item to work with by pointing to it with the mouse, then pressing and releasing the selection button. <br /> <br /> ; {{Anchor|selection button}}selection button<br /> : The left mouse button.<br /> <br /> ; {{Anchor|serial}}serial<br /> : An interface port that transfers data one single bit at a time, contrasted to a parallel interface which sends one complex byte (eight bits) at a time. <br /> <br /> ; {{Anchor|set}}set<br /> : To change a bit or flag to its on or enabled state. Opposite of clear.<br /> <br /> ; {{Anchor|Shell}}Shell<br /> : The command line interface used to send typed commands to the Amiga. One of the three interfaces built into the Amiga. <br /> <br /> ; {{Anchor|sizing gadget}}sizing gadget<br /> : A system gadget that allows the user to enlarge or shrink the size of the [[#window|window]]. <br /> <br /> ; {{Anchor|slider gadget}}slider gadget<br /> : A gadget that allows the user to select a value by dragging a rectangle up or down in a vertical bar. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|slider value}}slider value<br /> : A number that appears next to a slider gadget to indicate the currently selected value. <br /> <br /> ; {{Anchor|snapshot}}snapshot<br /> : To save the positions of a [[#window|window]] and/or the [[#icon|icon]]s in it.<br /> <br /> ; {{Anchor|source}}source<br /> : A [[#device|device]], [[#directory|directory]] or file that is supplying information.<br /> <br /> ; {{Anchor|stack}}stack<br /> : A special area of [[#RAM|RAM]] reserved by a program for temporary storage.<br /> <br /> ; {{Anchor|Startup-sequence}}Startup-sequence<br /> : An [[#AmigaDOS|AmigaDOS]] script file, executed when the Amiga is booted, that helps set up the hardware and [[#directory|directory]] systems. <br /> <br /> ; {{Anchor|streaming tape}}streaming tape<br /> : A high-capacity, mass-storage [[#device|device]] that uses a magnetic tape cartridge to hold data; generally used to back up large hard disks. <br /> <br /> ; {{Anchor|string}}string<br /> : A piece of text treated as a single unit.<br /> <br /> ; {{Anchor|subdirectory}}subdirectory<br /> : A [[#directory|directory]] that is within another [[#directory|directory]].<br /> <br /> ; {{Anchor|submenu}}submenu<br /> : A secondary [[#menu|menu]] that appears when some [[#menu item|menu item]]s are highlighted. A [[#menu item|menu item]] that produces a submenu should have the symbol » at the far right. <br /> <br /> ; {{Anchor|switch}}switch<br /> : A command keyword that turns an option on or off. If the keyword is typed onto the command line, the option is considered to be on. <br /> <br /> ; {{Anchor|syntax}}syntax<br /> : The rules for the proper arrangement of commands, keywords and punctuation.<br /> <br /> ==T==<br /> ; {{Anchor|task}}task<br /> : A software function spawned by a [[#process|process]].<br /> <br /> ; {{Anchor|text gadget}}text gadget<br /> : A rectangular box in which the user can type information such as a [[#filename|filename]] or command. ''Release 2 GadTools'' <br /> <br /> ; {{Anchor|3-D look}}3-D look<br /> : The technique used by Release 2 of the operating system which uses simulated light and shadow to create the illusion of depth and simultaneously give the user context cues. ''Release 2 GadTools (provides 3-D gadgets)'' <br /> <br /> ; {{Anchor|title bar}}title bar<br /> : The top border of a screen or [[#window|window]] that commonly displays the name of the screen or [[#window|window]]. On a draggable [[#window|window]], this is sometimes referred to as the ''drag bar''. <br /> <br /> ; {{Anchor|tool}}tool<br /> : A program that creates, uses or displays data.<br /> <br /> ; {{Anchor|Tool Types}}Tool Types<br /> : A method for passing [[#argument|argument]]s used by the [[#GUI|GUI]]. Tool Types is a field in the Information [[#window|window]] of a project or tool [[#icon|icon]] where optional parameters can be entered. <br /> <br /> ; {{Anchor|Trashcan}}Trashcan<br /> : A [[#directory|directory]] for storing files the user wants to delete.<br /> <br /> ; {{Anchor|type}}type<br /> : In Release 2 documentation, this term was used instead of ''enter'' or ''key in''. Example: ''Type the macro name in the console.'' <br /> <br /> ==V==<br /> ; {{Anchor|version}}version<br /> : A number that identifies the edition of a program.<br /> <br /> ; {{Anchor|virtual screen}}virtual screen<br /> : A screen that is larger than the actual display area of the monitor.<br /> <br /> ; {{Anchor|volume}}volume<br /> : A floppy disk or hard disk [[#partition|partition]].<br /> <br /> ; {{Anchor|volume name}}volume name<br /> : The name given to a disk or [[#partition|partition]].<br /> <br /> ==W==<br /> ; {{Anchor|wait pointer}}wait pointer<br /> : An image that appears in place of the normal [[#pointer|pointer]] when an application is busy and cannot accept further input. The [[#Workbench|Workbench]]'s image of a stopwatch was not accessible by developers at the time this manual was published. <br /> <br /> ; {{Anchor|wildcard}}wildcard<br /> : A symbol used in pattern matching to represent a range of possible values.<br /> <br /> ; {{Anchor|window}}window<br /> : A rectangular screen area that can accept or display information.<br /> <br /> ; {{Anchor|Workbench|WB}}Workbench<br /> : The Amiga's [[#icon|icon]]-based [[#GUI|GUI]]. Its popular abbreviated form is ''WB''.<br /> <br /> ; {{Anchor|write}}write<br /> : To record data in memory or on a storage medium such as disk or tape.<br /> <br /> ; {{Anchor|write-enable}}write-enable<br /> : To allow information to be written onto a storage [[#device|device]].<br /> <br /> ; {{Anchor|write-protect}}write-protect<br /> : To prevent information from being written onto a storage [[#device|device]].<br /> <br /> ==Z==<br /> ; {{Anchor|zoom gadget}}zoom gadget<br /> : A gadget that may appear in the upper right corner of a [[#window|window]] which allows the [[#window|window]] to alternate between two sizes. <br /> <br /> ; {{Anchor|Zorro}}Zorro<br /> : The name for the proprietary expansion slot specification used by High-End Amiga Classic computers. Amiga 2000 family contains Zorro II slots while Amiga 3000 and 4000 families provided Zorro III slots.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Preferences&diff=5387 UI Style Guide Preferences 2013-04-26T09:28:04Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Preferences ==<br /> <br /> On the Amiga, the term &quot;Preferences&quot; refers to the set of programs and data used to configure the machine's working environment. They come in two basic varieties: system preferences (the basic ones in the operating system) and application preferences (any you choose to add).<br /> <br /> [[File:SG12-1.png|frame|center|Workbench's Prefs Directory]]<br /> <br /> A variety of editors in the Prefs drawer of Workbench allows the user to set various system defaults. These represent the system preferences. Some of these control settings for peripherals such as printers, while others control the look and feel of the system. The Palette editors, for instance, allows the user to set the default colors use by Workbench.<br /> <br /> == Prefs in Moderation ==<br /> <br /> Every additional selection in your application means additional complexity for the user. Before writing any preference controls for your application, consider these two warnings:<br /> <br /> Don't add a preferences item just because you couldn't reach a decision. It's very easy to settle a dispute about how something should work by adding yet another preferences item.<br /> <br /> Don't add a preferences item just because it's technically feasible to do it. Make sure it adds real value in the eyes of the user. (Note: we speak from experience here. Trust us on this one.)<br /> <br /> {{Note|text=Make sure that a preferences item adds real value in the eyes of the user.}}<br /> <br /> == The 90% Rule ==<br /> <br /> If 90% of your users prefer A, and all but the most obstinate of those who prefer B could live with A, don't bother making it a preferences item. Remember, it is far easier to add a control next version than it is to take one away.<br /> <br /> == Two Choices ==<br /> <br /> Two basic design decisions confront you when it comes to preferences.<br /> <br /> === Duplicate System Prefs? ===<br /> <br /> First, if one of the system preference editors contains an item relevant to your program, you must decide whether to duplicate the control in your program so that the user can override the system setting locally. For example, if you are writing a terminal package, you can use the baud rate the user has set with the Serial preferences editor or you can provide a control that allows the user to set the baud rate inside your application.<br /> <br /> Here's a guideline: if in the course of using your program, a user is likely to change an option, provide controls local to your application even if the control duplicates an item in a system preference editor. In that case, your controls should override the system preferences within the scope of your application.<br /> <br /> Whether you add controls local to your application or not, your program should look at the defaults the user has set up in the Workbench preferences editors and use them as the starting values for the preferences controls in your application.<br /> <br /> {{Note|text=Your application should look at the defaults the user has set up in the Workbench Prefs editors and use these as the starting values in your application.}}<br /> <br /> === Menu or Editor? ===<br /> <br /> The second design concern: you must decide where to put the controls for application-specific preferences. You can allow the user to access them through your application's Settings menu (see Chapter 6), or you can have an application preferences editor in your application's drawer; or you can provide both.<br /> <br /> In general, try to use your application's Settings menu rather than a stand-alone editor to set preferences. Any menu item could lead to submenus or a series of requesters for more complex settings.<br /> <br /> However, there are times when a preferences editor makes more sense than the Settings menu. For instance, your application might be a compiler that is used only from the Shell, or your application may have environment controls that need to be adjusted only when the user's hardware setup changes. In these cases, an application preferences editor may be better than using the Settings menu.<br /> <br /> ==== System-Wide Preferences ====<br /> <br /> If, for some reason, your preferences will control options system-wide, the editor can go in SYS:Prefs. One example of this is a stand-alone PostScript printer driver.<br /> <br /> == The System Preference Editors ==<br /> <br /> Here's a list of the system preference editors and the system defaults they control. You should know what the system controls provide before writing any controls of your own.<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Name<br /> ! Description<br /> |-<br /> | Font<br /> | Specification of default screen, system and Workbench fonts. The file format is IFF FORM PREF (FONT chunk). The filenames are screenfont.prefs, sysfont.prefs, and wbfont.prefs, respectively.<br /> |-<br /> | IControl<br /> | Intuition-specific control items, including verify timeout and command key definitions. The file format is IFF FORM PREF (ICTL chunk). The filename is icontrol.prefs.<br /> |-<br /> | Input<br /> | Mouse and keyboard control items. The file format is IFF FORM PREF (INPT chunk). The filename is input.prefs.<br /> |-<br /> | Overscan<br /> | Standard video and text overscan areas for the various modes supported by the system. The file format is IFF FORM PREF (OSCN chunk). The filename is oscan.prefs.<br /> |-<br /> | Palette<br /> | Color selections for the Workbench screen. The file format is IFF FORM ILBM (the main chunk is CMAP). The filename is palette.ilbm.<br /> |-<br /> | Pointer<br /> | Design of the mouse pointer image. The file format is IFF FORM ILBM. The filename is pointer.ilbm.<br /> |-<br /> | Printer<br /> | Printer text preferences and printer driver selection. The file format is IFF FORM PREF (PTXT chunk). The filename is printer.prefs.<br /> |-<br /> | PrinterGfx<br /> | Printer graphic preferences. The file format is IFF FORM PREF (PGFX chunk). The filename is printergfx.prefs.<br /> |-<br /> | ScreenMode<br /> | Display information such as the Workbench display mode and the raster dimensions. The file format is IFF FORM PREF (SCRM chunk). The filename is screenmode.prefs.<br /> |-<br /> | Serial<br /> | Serial port definitions including baud rate, handshaking and parity. The file format is IFF FORM PREF (SERL chunk). The filename is serial.prefs.<br /> |-<br /> | Time<br /> | System and real-time clock date and time. No file is used. Data is stored into the system and the battery-backed clock/calendar.<br /> |-<br /> | WBPattern<br /> | The backdrop patterns used in Workbench and its windows. The file format is custom. Names are wb.pat and win.pat respectively.<br /> |}<br /> <br /> === Storage of System Settings ===<br /> <br /> Users can change system-wide settings for their current session by clicking the Use gadget in an editor, or they can change the permanent settings by clicking the Save gadget.<br /> <br /> The system stores these settings files in four different locations. (Your application should follow these steps only if it installs a system-wide preferences editor in the [SYS:]Prefs drawer. Otherwise, you should follow the conventions outlined in the next section.)<br /> <br /> * Defaults are imbedded in the system preferences themselves.<br /> <br /> * If the user modifies a preference item and selects Save on the requester, the settings are stored in the preference archive ENVARC:sys/&lt;filename&gt; and ENV:sys/&lt;filename&gt; (see the list above for the filename).<br /> <br /> * If the user selects Use on the requester (instead of Save), the current settings are stored only in ENV:sys/&lt;filename&gt;. The archived settings in ENVARC:sys remain unchanged. ENV: does not survive a reboot.<br /> <br /> * Alternate settings called presets are stored in the Workbench:Prefs/Presets drawer or in other user-specified places.<br /> <br /> === Preference Presets ===<br /> <br /> Preferences presets are alternate versions of the recognized preferences files. Their purpose is to support a variety of user configurations that can be &quot;turned on&quot; at the user's discretion.<br /> <br /> For example, the default definition of the Amiga's mouse pointer is archived in ENVARC:sys/pointer.ilbm. However, the user may have an alternate pointer he favours when working with a paint program. An alternate image can be designed in the Pointer editor and saved in Workbench:Prefs/Presets.<br /> <br /> Once it's saved as a preset, the user can activate the alternate image at any time by double-clicking on its icon. (The icon's default tool is the Pointer preferences editor.) The preset can also be activated from the Shell or from the editor. The original version will still be available in the ENVARC:sys directory.<br /> <br /> == Conventions for Your Application's Prefs ==<br /> <br /> The general rules: whenever possible, your application should allow the user to save his preferred settings and should initialize on startup to the user's settings - whether they are the user's default settings or another settings file specified by the user in a startup argument.<br /> <br /> Also, whenever possible, the settings controls should be contained within your applications and accessed via the Settings menu. (See Chapter 6 for information about the Settings menu.)<br /> <br /> === Format ===<br /> <br /> There is no strict structure for preferences files. They may be ASCII, IFF, binary, or any form suggested by the preferences data therein. Many of the system files are made up of structured binary data and are saved with only a small amount of identifying header information. Others, such as pointer.ilbm, are actual IFF ILBM files.<br /> <br /> It is recommended that you use an IFF FORM for preferences files. If you wish to use a new or modified IFF FORM to contain your preference data, you should register the form at the [[IFF_FORM_and_Chunk_Registry|IFF FORM and Chunk Registry]].<br /> <br /> {{Note|text=Preferences files may be ASCII, IFF, binary or any form suggested by the data. Using an IFF FORM is recommended.}}<br /> <br /> === Accessing Settings Files ===<br /> <br /> Keeping the preferences operations simple yet viable for all types of users gets somewhat tricky for the application designer. To help with this, the following rules for accessing user preferences settings were devised.<br /> <br /> Upon startup, your application should look for its application preferences settings in the following places, and in the following order. You should use built-in defaults only if no user-specified settings are found.<br /> <br /> Note: keep in mind that allowing the user to save individual aspects of your program, rather than a general Save Settings, will change this scenario and the next one about saving settings files. Instead of looking for an individual file containing the settings, your program would be searching for a subdirectory of preferences settings. That directory would contain files such as pointer.ilbm or beepsound.8svx.<br /> <br /> 1. Look first in a file specified in startup arguments. Users should be able to specify a filename and path in either to icon's Tool Types field or on the command line in the Shell. The keyword in both cases would be SETTINGS. The specified file would be a settings file or directory saved during a previous session with your program.<br /> <br /> This will allow users without hard disks to save their settings wherever convenient - perhaps on a separate disk (some boot disks are too full already). It will also allow networked users to specify their individual settings files in project Tool Types or on the command line.<br /> <br /> 2. If no SETTINGS argument is specified, look for the file &lt;basename&gt;.prefs in your application's directory. Accessing and storing the settings file in your application's directory instead of the SYS: directory may benefit users without a hard disk whose boot disks may be full. In most cases this will effectively be the default since most users will not specify a settings file in Tool Types or [on] the command line.<br /> <br /> 3. If the settings aren't found in places 1 or 2, look for your application preferences settings file in ENV:&lt;basename&gt;/&lt;basename&gt;.prefs.<br /> <br /> 4. Use the built-in default settings.<br /> <br /> === Saving Settings Files ===<br /> <br /> The general rule here is to save any new settings chosen by the user to the place from which the settings were loaded - but allow the user to specify another place.<br /> <br /> However, this changes if the settings were loaded from your built-in defaults - which would be the case if this is the first time a user is using your program. In this case you should go through what are basically the same steps [as] for loading a settings file.<br /> <br /> Remember that if you allow individual aspects of your application to be saved, this changes from a search for a file to a search for a subdirectory and then a file or list of files.<br /> <br /> 1. As stated in the general rule above, save to the same path and file from which you loaded the settings.<br /> <br /> 2. If a file was not specified, save to the file called &lt;basename&gt;.prefs found in your application's directory. In most cases, this would be far enough to look and a good solution, but if your application is being used on a network, the network moderator would most likely set this file to be write-protected. He wouldn't want every user saving his settings on top of those optimized for the network.<br /> <br /> 3. If you are unable to write to the file in step 2, save the settings in ENVARC:&lt;basename&gt;/&lt;basename&gt;.prefs and in ENV:&lt;basename&gt;/&lt;basename&gt;.prefs.<br /> <br /> === Application Preference Editors ===<br /> <br /> The common way to handle user preferences is through a Settings menu item, but in more complex cases or when the settings will affect the overall system, you may want to use a stand-alone preferences editor.<br /> <br /> Complex cases include those where a number of invocations of your program are running concurrently or sequentially, and your application needs to know the settings from previous invocations.<br /> <br /> An example of a case where your program's settings affect the system is software for a multi-serial card.<br /> <br /> The design guidelines for preferences editors given here will ensure that each one presents a familiar and consistent user interface. Be sure to include all of the following standard operations in your application's preferences editors. See Chapter 2 as well for general design guidelines.<br /> <br /> ==== Editor Startup ====<br /> <br /> Every application preferences editor should have a set of reasonable default settings imbedded in the editor code. When the editor is started it should display the current internal settings unless a preset file name was given as an argument. If no preferences data is found, then revert to the default settings embedded in the code itself.<br /> <br /> ==== Editor Gadgets ====<br /> <br /> Every application preferences editor should open a window with at least three gadgets: Save, Use and Cancel. The gadgets should be near the bottom of the window (Save on the left, Use in the middle, Cancel on the right). Here are the functions the three gadgets should perform:<br /> <br /> ; Save<br /> : Use the new preferences settings the user has chosen and save the data to the assigned file. By default, it should be stored to the file ENVARC:&lt;basename&gt;/basename.prefs and ENV:&lt;basename&gt;/basename.prefs. The editor should terminate. Note that only system editors should store their data in the ENV:sys and ENVARC:sys directories.<br /> <br /> ; Use<br /> : Use the new preferences settings the user has chosen and save the data to ENV:&lt;basename&gt;/&lt;basename&gt;.prefs. The editor should terminate. Because no change is made to the copy in ENVARC:&lt;basename&gt;/&lt;basename&gt;.prefs, any changes the user makes this way will be lost if the system is reset.<br /> <br /> ; Cancel<br /> : Exit the editor. Any changes in preferences settings the user has chosen should be ignored.<br /> <br /> Note: applications with a stand-alone preferences editor shared by default should read in their settings from ENV:&lt;basename&gt;/&lt;basename&gt;.prefs and use file notification to watch for changes to that file.<br /> <br /> ==== Editor Menus ====<br /> <br /> Every application preferences editor should have at least three menus, a Project menu, an Edit menu and an Options menu. The Project menu should contain these menu items:<br /> <br /> ; Open<br /> : Load preferences data from a preset file. A requester should appear that allows the user to specify the file's name as well as its path. The command key is Right-Amiga-O.<br /> <br /> ; Save As...<br /> : Save preferences data in a preset file. A requester should appear that allows the user to specify the filename and path. The new file can be used as a preset. The command key is Right-Amiga-A.<br /> <br /> ; Quit<br /> : Exit the editor without saving. The command key is Right-Amiga-Q.<br /> <br /> The Edit menu should contain these items:<br /> <br /> ; Reset to Defaults<br /> : Reset the preferences editor to the default values embedded in the editor code.<br /> <br /> ; Last Saved<br /> : Reset the editor with the data from the preferences archive data file in ENVARC:&lt;basename&gt;/&lt;basename&gt;.prefs.<br /> <br /> ; Restore<br /> : Reset the editor to the settings that were in effect when it was started.<br /> <br /> ; Undo<br /> : Undo the most recent change the user made to the preferences settings.<br /> <br /> The Options menu should contain at least this one menu item:<br /> <br /> ; Create Icons?<br /> : Controls whether or not the editor will save a project icon with each preset saved.<br /> <br /> ==== Arguments ====<br /> <br /> Editors should also accept command line arguments when executed from the Shell. When started from the Shell, the editor will not always open its window. Instead, the editor should perform the action as detailed in the arguments.<br /> <br /> For both Shell usage and Tool Types usage, the command template should be of the form:<br /> <br /> FROM,EDIT/S,USE/S,SAVE/S,PUBSCREEN/K<br /> <br /> * FROM tells which preset file to use. If this argument is left out, use the archived preferences settings in &lt;programdirectory&gt;:&lt;basename&gt;.prefs or revert to the defaults coded into the editor itself.<br /> <br /> * EDIT opens the editor window and loads the preferences data in the file named in the FROM argument. This is the default switch. If no FROM is specified, the window should open with the defaults.<br /> <br /> * USE performs a &quot;Use&quot; on the data found in the data file named in the FROM argument. Do not open the editor window.<br /> <br /> * SAVE performs a &quot;Save&quot; on the data file named in the FROM argument. Do not open the editor window.<br /> <br /> * PUBSCREEN opens the window on the named public screen.<br /> <br /> Here's an example command line:<br /> <br /> 1&gt; Serial FROM SYS:ENVARC/Prefs/Presets/myserial.pre USE<br /> <br /> With this command, the serial device Preferences editor is run invisibly from the Shell. Without opening its window, the editor loads the serial preset file named myserial.pre, performs the Use function and then exits. If this command is placed in the Amiga's Startup-sequence, it will override normal default preferences with the preset named in the FROM argument.<br /> <br /> == Icons ==<br /> <br /> The user should also have ultimate control over what icons look like.<br /> <br /> Under past releases of the operating system, most applications hard coded their project icons into the program. You should name your default project icons in a standard manner and store them where the user can access them.<br /> <br /> === Naming Conventions ===<br /> <br /> {{Note|text=These rules for icon naming and storage are designed so the user can control the look of your program's default icons.}}<br /> <br /> Use the following naming convention:<br /> <br /> def_&lt;icon type&gt;.info<br /> <br /> The placeholder &lt;icon type&gt; indicates the type of file represented. For example, the types of icons in the system are: disk, drawer, tool, project and trashcan.<br /> <br /> The default icons for IFF files should use the FORM name as the &lt;icon type&gt;. For instance, a default icon for an IFF picture would be def_ILBM.info since ILBM is the FORM name. See Chapter 11 for more information on the IFF standard.<br /> <br /> === Storage and Access Conventions ===<br /> <br /> Store your default icons in ENV:&lt;basename&gt; and in ENVARC:&lt;basename&gt;. If the user wants to change the default icon he can find it in that directory.<br /> <br /> When your application needs an icon, direct it to look in ENV:&lt;basename&gt; first, then ENV:sys for the system default icon for that type of file. If it fails to find that, it should revert to the system's default project icon.<br /> <br /> Here's an example: when the user saves a text file in an application named MystrEd, the application will first look in ENV:mysed for an icon file named def_TXT.info. Finding none, it will then look in ENV:sys for a file named the same. None is found there, so it uses the system's default project icon.<br /> <br /> If, however, the MyStred user creates a default TXT icon in IconEdit and stores it as ENV:mysed/def_TXT.info, that icon should be used as the<br /> default.<br /> <br /> == Notification ==<br /> <br /> Notification is an Amiga facility whereby an application may be informed whenever a change is made to a file of interest. Through notification it is possible for an application to find out whenever the user has made a change to any preferences data file. This allows the application to make an adjustment to reflect the new preferences settings. See Chapter 11 for more information.<br /> <br /> Since an application has to handle its own preferences, there is not much point in getting notification about changes to application preferences files. However, changes to system preferences files may be relevant. For instance, a publishing program may want to be notified whenever the user changes the printer graphic setting from black-and-white to color.<br /> <br /> Under previous versions of the operating system, an application could ask Intuition to send it a message whenever system preferences data<br /> changed. This method still works for Preferences items that existed under Release 1.3.<br /> <br /> In either case, the important thing to understand is that your application is not expected to respond to every change made to system preferences. This is an extra feature you may add to your application if you believe it is warranted.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Data_Sharing&diff=5386 UI Style Guide Data Sharing 2013-04-26T09:27:59Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Data Sharing ==<br /> <br /> This chapter is a little different from the other chapters in this book because it does not directly consider the question of how to make the best human-machine interface for Amiga applications. Instead, it covers the best ways for programs to share data with each other.<br /> <br /> User interactions are not directly involved in data sharing, but the issue is crucial to the user. Without a data sharing standard, the user has to convert data to a different format every time it is used across applications. Worse still, a conversion tool may not be available. With a<br /> data sharing standard this problem is eliminated.<br /> <br /> A data sharing standard is also important to get the best advantage from multitasking. When applications on the Amiga follow a data standard, a unique synergy is created - specialized applications from different vendors can be combined seamlessly for a custom environment.<br /> <br /> For instance, you can create a background picture in a paint package, add three-dimensional text in a ray-tracing application, and add animation in an animation package, all without once running a conversion program. The applications can run together simultaneously as an integrated graphics package.<br /> <br /> The Amiga's standard for data sharing, Interchange File Format (IFF), is a widely accepted and simple set of rules that was well-defined and adopted very early in the Amiga's history.<br /> <br /> == A Practical Introduction to the IFF Standard ==<br /> <br /> This section provides a brief overview of the IFF standard. For more detail refer to [[IFF_Standard|Interchange File Format Specification]].<br /> <br /> An IFF data structure has two levels. The first level is a wrapper or envelope structure which is, technically, the syntax. It's easy to describe: the IFF wrapper is just a header containing an identifier and the file size. The identifier is four letters and the file size is a number taking up 32 bits of space. The whole IFF wrapper weighs in at eight bytes of disk space. That's it.<br /> <br /> {{Note|text=Perhaps the greatest strength of IFF is that it was well-defined and adopted very early in the Amiga's history.}}<br /> <br /> There are only three possible identifiers for the wrapper part of IFF files: FORM, CAT (a space after the T) and LIST.<br /> <br /> * FORM - This is by far the most common type, equivalent to a simple file.<br /> * CAT - A concatenation, equivalent to multiple files put together.<br /> * LIST - Also a concatenation but with some properties shared between the separate files to avoid redundancy.<br /> <br /> It is possible to build some fairly complex files using just these three types since a FORM may contain FORMs within it; a LIST can contain other FORMs, CATs or LISTs and so forth. (To help with the possible complexity of IFF syntax, the operating system contains a function library named iffparse.library.) In practice, however, the most common IFF file is the simple FORM containing a single set of data. There may be any type of data within the FORM wrapper.<br /> <br /> The second layer of an IFF file, underneath the wrapper, defines the particular file types. The file types consist of a four-letter identifier followed by a series of data chunks which contain the data natural to the type. Some examples are:<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Type ID<br /> ! Data Stored in this Type<br /> |-<br /> | ILBM<br /> | Graphics data in interleaved bitmap format. This is very widely used on the Amiga.<br /> |-<br /> | 8SVX<br /> | Audio data in 8-bit sample bytes. The most widely used audio file format on the Amiga.<br /> |-<br /> | FTXT<br /> | Formatted text. Recommended for sharing text data.<br /> |-<br /> | ANIM<br /> | A common way to store interchangeable animation files.<br /> |}<br /> <br /> Each IFF file type contains a number of data chunks specific to the type. These data chunks are the basic building blocks of IFF files and they come in a wide variety. They can be structured in any number of ways, but a chunk always starts with a four-letter ID followed by the chunk size which, as in the wrapper, is given as a 32-bit number.<br /> <br /> For example the 8SVX file type consists of two chunks: a VHDR chunk which contains information like the sampling speed, and the BODY chunk which contains the sample bytes.<br /> <br /> Here is a listing of typical 8SVX file to give you a better idea of how IFF files are structured:<br /> <br /> &lt;pre&gt;<br /> FORM IFF wrapper ID<br /> 10120 IFF file size as a 32-bit value<br /> <br /> 8SVX type ID (10120 bytes of 8SVX data)<br /> <br /> VHDR chunk ID<br /> 20 chunk size<br /> <br /> 00 02 3D 90 00 00 00 00 00 00 20 bytes of VHDR chunk data<br /> 00 00 21 4A 01 00 00 01 00 00<br /> <br /> BODY chunk ID<br /> 10080 chunk size<br /> <br /> 12 00 10 55 00 90 13 FF 12 00 10080 bytes of BODY chunk data<br /> 10 55 00 90 13 FF...<br /> &lt;/pre&gt;<br /> <br /> === The IFF Philosophy ===<br /> <br /> The goals of IFF are lofty but obviously within reach. IFF is intended to make data abstract enough to be stored independent of a particular program, compiler, device or machine architecture. With that achieved, IFF files could be shared not only between applications but also on and between platforms other than the Amiga.<br /> <br /> The key to this is making the data abstract. The IFF standard accomplishes this by building the files through chunks each with an ID and size. The ID is a reference by name to a given implementation of an access procedure. The access procedures can evolve and change independently of the software which uses them. And the size count enables applications to support object operations like &quot;copy&quot; and &quot;skip to next&quot; regardless of the object type or contents.<br /> <br /> IFF is meant to be easily extensible. The CAT and LIST wrappers make it easy to combine existing types to form new composite types. And if all else fails the IFF specification provides rules for creating new file types for special storage needs that are not handled by the existing IFF types.<br /> <br /> === IFF and Your Application ===<br /> <br /> If an IFF format exists for your program's genre, you only need to provide two features:<br /> * a way for users to save files in that IFF format;<br /> * a way for your program to import files that have been saved in that IFF format.<br /> <br /> In some cases the IFF format is also the best format for everyday file storage. Many applications have opted to use ILBM for those purposes. In other cases, the IFF format is not great as a normal storage format. In these cases, you can provide your own internal storage format, but you should also provide the means to save and import IFF.<br /> <br /> Note, however, that revisions to your application could mean changes in your internal data format. If you have been using your internal data format as your storage format as well, any files saved in the old format may not be readable by the new format. If you don't use IFF as the default storage format, at least try to provide for backwards compatibility in your own program.<br /> <br /> ==== If No IFF Format Exists ====<br /> <br /> If no IFF format exists for your program's genre, it would be a good idea to develop an IFF format and use that.<br /> <br /> If you need to create a new IFF format or expand upon an existing one, then you must register the change with the AmigaOS Development Team. The AmigaOS Development Team serves as a central clearinghouse to keep track of new developments in the IFF standard. The official registry of IFF form and chunk types is at the [[IFF_FORM_and_Chunk_Registry|IFF FORM and Chunk Registry]].<br /> <br /> == The Clipboard ==<br /> <br /> IFF provides a data sharing standard. In conjunction with that, the Amiga's clipboard device provides a place to store and retrieve that data. It does this by caching data to RAM and automatically spooling the data to disk if necessary.<br /> <br /> The clipboard is the recommended storage device for cut-and-paste operations and it's the best place to store data which is meant to be quickly moved between between two applications that are running simultaneously. It also provides a solid metaphor in keeping with the Workbench.<br /> <br /> {{Note|text=The clipboard allows the exchange of data dynamically between your application and another.}}<br /> <br /> === Use IFF ===<br /> <br /> All data written to the clipboard must be written in the IFF format. For most applications the data will be one of two types: graphics or text. For graphic clips use the ILBM form of IFF. For text clips use the FTXT form of IFF.<br /> <br /> When reading from the clipboard, never blindly read the data assuming, for instance, that the clip contains FTXT with a CHRS as the first and only chunk. Remember, the user may have been switching between many applications and may even have made a mistake in the cut-and-paste operation.<br /> <br /> ==== Storage Concerns ====<br /> <br /> When storing data in the clipboard, it is acceptable to write different representations of the same data. For instance, in a music application when the user cuts a bar of music, the application can write the information in three ways. The music itself could be saved in a SMUS form; the lyrics could be saved separately in an FTXT form; and a picture of the notation could be saved in an ILBM form. This type of multiple clip should be contained within a CAT &quot;wrapper&quot; form. Use CAT CLIP as the global form name.<br /> <br /> === Unit Selection ===<br /> <br /> The clipboard device allows for the selection of units ranging from zero to 255. By default, your application should always use clipboard unit zero for interactive cut, copy and paste operations. Provide a means, however, for the user to specify a different unit number.<br /> <br /> == Notification ==<br /> <br /> Notification is a means by which an application can receive a message whenever a certain file of interest is modified. By establishing &quot;hot links&quot; between applications, notification allows a change made to a file with one program to be automatically reflected in another application that is using the same file.<br /> <br /> {{Note|text=Hot links allow a change made to a file with one program to be automatically reflected in another application which is using the same file.}}<br /> <br /> An example would be a paint package in which the user could save a brush and, using notification, automatically have an image processor application modify the brush and return it in a separate file. The scenario would work like this:<br /> <br /> 1. PAINT PACKAGE<br /> * Writes brush to file named T:brush.<br /> <br /> 2. IMAGE PROCESSOR<br /> * Gets notification that T:brush has been updated.<br /> * Reads T:brush into work area.<br /> * Alters brush using an image processing feature.<br /> * Writes brush out to T:brush.mod.<br /> <br /> 3. PAINT PACKAGE<br /> * Gets notification that T:brush.mod has been updated.<br /> * Reads T:brush.mod into brush area.<br /> <br /> There is only one style standard to worry about with notification: keep RAM usage reasonable. Because RAM is a limited resource, applications that have very large amounts of data should not store directly in RAM:.<br /> <br /> Notification may not work in a network situation because it is not implemented with some file systems. It does work on RAM:, FFS and OFS.<br /> <br /> == Environment Variables ==<br /> <br /> With general availability of networking cards for the Amiga, the use of environment variables is sure to increase. Environment variables are<br /> basically places where information can be stored; they are similar to logical assignments in that a variable text string is given a generic label. For example, in the Shell, the user can type:<br /> <br /> echo $workbench<br /> <br /> and be presented with the current version in use. Environment variables are set by the user with the SetEnv command of AmigaDOS.<br /> <br /> Store your variables in a subdirectory such as ENV:&lt;basename&gt;/variables. Only user-defined and standard system variables should go in ENV:. ENV:Sys/ is reserved for system functions.<br /> <br /> Here are some recommended environment variable names currently in use in the system. Note: they are not case-sensitive.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | workbench<br /> | The version of Workbench<br /> |-<br /> | kickstart<br /> | The version of Kickstart<br /> |-<br /> | hostname<br /> | Contains the machine name<br /> |-<br /> | username<br /> | Contains the login name of the current user of the machine<br /> |-<br /> | editor<br /> | Sets the preferred text editor<br /> |}<br /> <br /> The following are local variables provided by the Shell. These are most relevant to applications that use DOS scripts. Of course you should avoid using these names for your variables since they are set automatically by the Shell.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | process<br /> | The process number<br /> |-<br /> | RC<br /> | The primary return code of the last command run<br /> |-<br /> | Result2<br /> | The secondary return code of the last command run<br /> |}</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&diff=5385 UI Style Guide Keyboard 2013-04-26T09:27:56Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == The Keyboard ==<br /> <br /> Your application should make gadget and menu items selectable with the keyboard as well as with the mouse. Often these keyboard equivalents will provide shortcuts and a smoother workflow that is appreciated by the user. Also, allowing the choice conforms to the Amiga credo: &quot;Power to the User&quot;.<br /> <br /> The set of conventions you should follow to implement this style rule are presented in this section.<br /> <br /> == Keyboard Conventions ==<br /> <br /> The arrangement of the Amiga's keyboard varies from model to model, but in general there are always three sets of keys:<br /> * the standard keyboard<br /> * special keys<br /> * modifier keys<br /> <br /> === The Standard Keyboard ===<br /> <br /> The standard keys consist of the familiar alphanumeric keys found on any standard typewriter. (In English, this is referred to as the QWERTY keyboard, but in German, since the standard keys are arranged differently, it's known as the QWERTZ keyboard. It's called other names in other languages.)<br /> <br /> Since the Amiga computer is sold in many different countries with different national keyboards, the arrangement of the standard keys can vary. To handle this, the Amiga uses a special facility known as a keymap that allows the user to change the way the keyboard input is mapped so it will correspond to his country's keyboard layout and characters. For example, to access all the standard German ASCII characters the user can type &quot;Setmap d&quot; in the Shell. Then German characters such as ü and ß will have the same key designations as they do on a standard German keyboard.<br /> <br /> Use the keymap facility to handle key assignments on the standard keyboard.<br /> <br /> === Special Keys ===<br /> <br /> The special keys include the ten function keys on the top row of the Amiga's keyboard, the Help key, the cursor (arrow) keys, the Del key, the backspace key and the Esc key.<br /> <br /> === Modifier Keys ===<br /> <br /> The modifier keys are the Ctrl, Shift, Alt and Amiga keys found on either side of the space bar. Modifier keys don't do anything by themselves; they alter the meaning of a key that is pressed at the same time. They are also used to modify the meaning of a mouse selection. For example, holding down the Shift key while clicking with the mouse is used for multiple object selection.<br /> <br /> ==== Dead Keys ====<br /> <br /> A &quot;dead&quot; key is a key, or combination, that does nothing immediately but modifies the output of the next key. For example, on an American keyboard, Alt-H will superimpose a caret (^) symbol over the next appropriate character.<br /> <br /> Although relatively unimportant on the American keyboard, dead keys are important in many languages. Keep in mind that you need to work these in manually if you're doing your own raw key mapping.<br /> <br /> == System Keyboard Shortcuts ==<br /> <br /> To provide mouse functions from the keyboard, only three features a needed: a way to press the left mouse button, a way to press the right mouse button, and a way to move the mouse pointer. These are provided by the system. They are listed here so you are aware of them and can avoid using these key combinations for any other purpose.<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Keyboard Shortcut<br /> ! Equivalent Mouse Activity<br /> |-<br /> | Either Amiga + Left-Alt<br /> | Same as clicking left mouse button<br /> |-<br /> | Either Amiga + Right-Alt<br /> | Same as clicking right mouse button<br /> |-<br /> | Either Amiga + Cursor Key<br /> | Moves mouse pointer<br /> |-<br /> | Either Amiga + Shift + Cursor Key<br /> | Moves mouse pointer in larger steps<br /> |}<br /> <br /> Five additional Amiga system functions have keyboard shortcuts:<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Default Keyboard Shortcut<br /> ! Equivalent System Function<br /> |-<br /> | Left-Amiga + N<br /> | Workbench screen to front<br /> |-<br /> | Left-Amiga + M<br /> | Front screen to back<br /> |-<br /> | Left-Amiga + B<br /> | Requester OK (leftmost gadget)<br /> |-<br /> | Left-Amiga + V<br /> | Requester Cancel (rightmost gadget)<br /> |-<br /> | Left-Amiga + selection button<br /> | Drags screen whether the pointer is on the title bar or not<br /> |}<br /> <br /> The first four functions listed above always use Left-Amiga in combination with another key. The second key in the combination can be changed by the user with the IControl Preferences editor in the Prefs drawer of Workbench. For the screen dragging shortcut, the user can change the modifier or combination of modifiers (Ctrl, Amiga, Shift, Alt) that need to be combined with the selection button.<br /> <br /> === The Left-Amiga Key ===<br /> <br /> Keep in mind that the Left-Amiga key is reserved at all times for system operations and should never be used as a qualifier for an application keyboard shortcut.<br /> <br /> == Application Keyboard Shortcuts ==<br /> <br /> Your application should provide a way for the user to bind any application function or ARexx macro to a key or combination of keys.<br /> <br /> === Gadgets ===<br /> <br /> Use a logical letter from the gadget label as the keyboard shortcut. For instance, a gadget labelled &quot;Smoothing&quot; could use the S key as its keyboard control, while a &quot;Left Offset&quot; gadget may use the O key.<br /> <br /> Place an underscore under the letter in the gadget label that activates the gadget. (Note: although the letter is capitalized on the label, the<br /> default action should react to the lower-case letter. Some gadgets have a different action for the shifted version of the letter. See the list on the next page.)<br /> <br /> ==== Three Rules ====<br /> <br /> * All action should occur on the down press of the key.<br /> * The same visual feedback should be given for keyboard activation as is given for mouse activation.<br /> * Avoid assigning the Enter key to a gadget.<br /> <br /> '''Feedback from Keystroke-Activated Gadgets'''<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Action button<br /> | On the down press of the key, the gadget should appear to be pressed in. On release of the key, the gadget should come back out. (Note: at the time this manual was published, GadTools did not support this function.)<br /> |-<br /> | Check box<br /> | Toggle the state of the check mark.<br /> |-<br /> | Scrolling list<br /> | Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.<br /> |-<br /> | Radio button<br /> | Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.<br /> |-<br /> | Cycle gadget<br /> | Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.<br /> |-<br /> | Selection gadget<br /> | Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.<br /> |-<br /> | Scroll gadget<br /> | Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.<br /> |-<br /> | Slider<br /> | Unshifted would increase the level by one unit. Shifted would decrease the level by one unit.<br /> |-<br /> | Text box<br /> | Activate the gadget for entry.<br /> |-<br /> | Numeric entry<br /> | Activate the gadget for entry.<br /> |}<br /> <br /> === Menus ===<br /> <br /> Use a Right-Amiga combination as the default keyboard shortcut for a menu item. Here is a list of the defaults in the English language for an application that has standard menus. This should be localized to the language at hand for non-English applications.<br /> <br /> Project Menu<br /> Right-Amiga + N New<br /> Right-Amiga + O Open...<br /> Right-Amiga + S Save<br /> Right-Amiga + A Save As...<br /> Right-Amiga + P Print<br /> Right-Amiga + Q Quit Program<br /> <br /> Edit Menu<br /> Right-Amiga + X Cut<br /> Right-Amiga + C Copy<br /> Right-Amiga + V Paste<br /> Right-Amiga + Z Undo<br /> <br /> == Use of the Special Keys ==<br /> <br /> As stated previously, the special keys are the ten function keys, the Del key, the Help key, the cursor (arrow) keys and the Esc key.<br /> <br /> === Cursor Keys ===<br /> <br /> Cursor keys are a convenient way to control the movement of the cursor inside an application. Here are some standard ways to use them:<br /> <br /> ==== Unmodified Cursor ====<br /> <br /> Move a small amount in the specified direction. Often this is one unit such as a character in a word processor or a pixel in a paint package, but it could be several pixels in an application where navigation is more important than fine control.<br /> <br /> ==== Shift + Cursor ====<br /> <br /> Move to the appropriate extreme of the window, or shift the view by one window full if you're already at that extreme.<br /> <br /> In applications such as a word processor where the two directions are not symmetrical, shifted cursor keys could take on different meanings. Shifted up and down cursors would page through windowfuls while the shifted left and right cursors would show more on the left and right if there is more to show. Of course it is often the case that the width of the document fits in the window, so the shifted left and right cursors could act as beginning-and end-of-line commands (or edge of window if the cursor isn't constrained to the form of the text).<br /> <br /> ==== Alt-Cursor ====<br /> <br /> This is used for application-specific functions. It's usually semantic units such as words in a text processor or fields in a spreadsheet.<br /> <br /> ==== Ctrl-Cursor ====<br /> <br /> Move to the appropriate extreme of the project (beginning, end, extreme left, extreme right).<br /> <br /> In the word processor example, the up and down cursor keys would take the cursor to the beginning and end of the file, respectively. But again, if the file fits within the width of the window, the left and right cursor keys combined with Alt would act like their shifted cousins.<br /> <br /> ==== Function Keys ====<br /> <br /> Function keys should generally be reserved for the user to define. If your application does make use of them, then it should at least allow the user to redefine or modify them.<br /> <br /> ==== Help Key ====<br /> <br /> If your application has built-in help, use the Help key to trigger it from the keyboard. Avoid giving the user that helpless feeling he gets when he presses the Help key and nothing happens.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_ARexx&diff=5384 UI Style Guide ARexx 2013-04-26T09:27:53Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == ARexx ==<br /> <br /> Previous chapters covered aspects of the GUI and the Shell. The third built-in Amiga interface is ARexx.<br /> <br /> Like the Shell, ARexx is a text-based facility for giving commands to the Amiga, but while the Shell operates externally to programs (copy, delete, etc.), ARexx can operate internally with programs.<br /> <br /> ARexx has two main uses: as a scripting language and for Inter-Process Communication (IPC). With the latter, ARexx acts as the standard hub for programs to communicate with each other by sending command and data messages.<br /> <br /> == Scripting and IPC ==<br /> <br /> The ability to handle macros, or scripts, is a powerful feature for any application. Whenever a user has a repetitive, well-defined job to do with application software, scripts allow him to automate.<br /> <br /> For example, many communications programs allow the user to set up a macro that will dial the phone number of a host system, log into it, check for messages and download those messages for later reading. The macro allows the user to do automatically what is usually done interactively.<br /> <br /> {{Note|text=ARexx provides Amiga users with a standard macro language.}}<br /> <br /> === Benefits of ARexx ===<br /> <br /> Unfortunately, if every application vendor creates his own macro language, the user will have to learn several macro languages instead of just one. Also, each developer would have to spend time creating a new macro language.<br /> <br /> With ARexx in place as a system-level macro language, there exists a consistent syntax and user interface for all applications. Part of the reason it can do this is its versatility. Although ARexx is an interpreted language with its own set of keywords, these keywords can be extended to include new words specific to your application.<br /> <br /> ARexx has another role, too. It is the system module that allows application software from different vendors to inter-operate, share data and communicate with one another in a multitasking environment. For instance, with ARexx a communications package could be set up to download data from a financial bulletin board and pass the data to a separate spreadsheet application for further analysis.<br /> <br /> {{Note|text=ARexx is based on REXX, variants of which are used across many platforms.}}<br /> <br /> When told about ARexx, most people imagine its use by power users - but the benefits of ARexx aren't strictly limited to power users. In a corporate environment, an information manager could take off-the-shelf software, throw in a mix of macros, batch commands and IPC, and come up with a system that truly meets the needs of his office. Once set up, the new, customized program could be used even by novices.<br /> <br /> Value-added resellers can use ARexx to design a custom front end that melds applications from different vendors. The result is a highly-specialized tool and an instant niche market for the application vendor.<br /> <br /> ARexx is like an open door on your application. Supporting it leaves the user free to combine your program with others - as a result, buyers of your program could end up using it in ways you never imagined.<br /> <br /> == Standards for ARexx ==<br /> <br /> In order for your program to work with ARexx, it must have an ARexx interface: a software structure that allows your application to send and receive messages from ARexx.<br /> <br /> If your application supports scripts, it should do so via ARexx. Even if your program doesn't use macros, you are urged to add an ARexx interface.<br /> <br /> Having said that, the purpose of the rest of this chapter is simple: to give a listing of standard ARexx commands so the same commands do the same thing from program to program.<br /> <br /> The fast-growing popularity of ARexx combined with its flexibility create, unfortunately, the likelihood of command name and function conflicts. It's frustrating to the user when the OPEN command works differently in his paint program than in his typesetting program.<br /> <br /> Your program should support at least a minimal subset of ARexx commands. It increases the power of your program, and as a powerful and seamless standard, it's good for the platform as a whole. Applications that don't support ARexx decrease the overall power and lure of the Amiga. Average users who decide to learn ARexx will almost certainly experience frustration when they run into the brick wall of the program that doesn't support ARexx.<br /> <br /> Once you learn how, incorporating ARexx support into your application requires about as much programming effort as supporting menus - without having to deal with the graphics. This chapter discusses which ARexx commands to support and the style in which they should be supported. For more details on the ARexx language itself refer to the Release 2 Users Manual ''Using the System Software''.<br /> <br /> === General Description and Guidelines ===<br /> <br /> ARexx is an interpreted programming language. As in BASIC, ARexx scripts can be written with any text editor and run without having to use a compiler. If an error occurs, ARexx outputs error messages indicating which line or lines are in error so the user can make changes and try again.<br /> <br /> ARexx is unique in that an ARexx script can contain commands that are specific to ARexx as well as commands that are specific to an application. ARexx provides commands typical of most programming languages (variable manipulation, arithmetic, conditional loops, string manipulation, etc.).<br /> <br /> Your application should provide, through ARexx, a subset, or even a superset, of the commands available through the GUI. This refers to menu or action gadget commands such as New, Save, Save As... and Quit.<br /> <br /> By combining ARexx commands and application-specific commands, the user can create simple or complex scripts to automate common tasks, create new functions, and integrate application software.<br /> <br /> For the sake of consistency, your application's ARexx commands should be similar in syntax to the AmigaDOS commands. They take the form:<br /> <br /> COMMAND [&lt;argument1&gt;, &lt;argument2&gt;...]<br /> <br /> where COMMAND is a keyword that your application recognizes followed by a list of arguments. In many cases the arguments will be optional and your application should be set to take some default action.<br /> <br /> As a general rule, your command set should:<br /> <br /> * accept arguments in the [[UI_Style_Guide_Shell#Standard_Form|standard AmigaDOS style]];<br /> <br /> * honor the AmigaDOS style of [[UI_Style_Guide_Shell#Pattern_Matching|pattern matching]] (e.g. the ''#?'' wildcard) when appropriate;<br /> <br /> * not be case-sensitive, and keywords should not contain spaces;<br /> <br /> * recognize and accept quoted arguments;<br /> <br /> * be verbose rather than terse (the user can make more sense of a keyword called OPEN versus O);<br /> <br /> * accept abbreviations for commonly used commands.<br /> <br /> === Errors ===<br /> <br /> ARexx scripts can be written by the everyday user or the professional programmer. Scripts must be able to handle error conditions and take some kind of action when an error occurs (i.e. notify the user, prompt the user, terminate, etc.). Therefore, it is important that your application return error codes when a command cannot be performed.<br /> <br /> The standard convention in ARexx is for application-specific commands to return error codes of zero (0) for no error, five (5) for warnings such as &quot;Aborted by user&quot;, ten (10) for errors such as &quot;&lt;file&gt; wrong type&quot;, and twenty (20) for failures such as &quot;Couldn't open the clipboard&quot;.<br /> <br /> Return codes are placed in ARexx's special variable called &quot;RC&quot; which can be checked for an manipulated in an ARexx script. For example, the user might try to save a file using the &quot;SAVEAS&quot; command. If the file could not be saved for any reason, it would be appropriate to return an error code of ten (10) so that the script could be terminated early, or some other action could be taken.<br /> <br /> You may choose to support a more comprehensive set of error codes. This is acceptable but your application should still return zero (0) for no error and use return codes of less than ten (10) for warnings.<br /> <br /> === Returning Data ===<br /> <br /> Some commands may return information back to ARexx. For example, a text editor or word processor might have a command that returns the string of text under the cursor. The string of text would be placed in a special ARexx variable called RESULT; the user, however, should be allowed to override this with a VAR or STEM switch in the command.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | VAR = return the value.<br /> |}<br /> <br /> {| class=&quot;wikitable&quot;<br /> | STEM =place the value in the specified variable.<br /> |}<br /> <br /> In order for result strings to be returned to ARexx, the script must use ARexx's OPTIONS RESULTS command. This command tells the application that it is OK to return result strings. Data placed in RESULT can then be copied and manipulated within an ARexx script.<br /> <br /> Here's an example: a text editor supports a command called GETLINE that returns the line of text under the cursor. The following is a sample ARexx script that uses the GETLINE command to get the line of text under the cursor and place it in a variable called &quot;line&quot;. Note that comments are surrounded by pairs of &quot;/*&quot; and<br /> &quot;*/&quot;.<br /> <br /> &lt;pre&gt;<br /> /* Example ARexx script */<br /> <br /> OPTIONS RESULTS<br /> <br /> 'GETLINE' /* Command telling the application to<br /> return the line of text under the<br /> cursor. */<br /> <br /> line=RESULT /* Set the variable called &quot;line&quot; equal<br /> to ARexx's special variable called<br /> &quot;RESULT&quot;. */<br /> &lt;/pre&gt;<br /> <br /> If the VAR (simple variable) or STEM (complex variable) switches are used, information should be placed into the named variable. For instance:<br /> <br /> &lt;pre&gt;<br /> MYCOMMAND STEM comresult<br /> &lt;/pre&gt;<br /> <br /> This would place the return result in the variable named ''comresult''. The VAR and STEM switches make it easier for applications from different vendors to operate on the same data under ARexx.<br /> <br /> Text strings that contain records with spaces should be returned with quotes around the record. A space should separate multiple records. For example, if you were trying to return the following list of records:<br /> <br /> &lt;pre&gt;<br /> Letter to John Doe<br /> Notes on Standards<br /> Addresses of the Stars<br /> &lt;/pre&gt;<br /> <br /> the information would be returned as:<br /> <br /> &lt;pre&gt;<br /> &quot;Letter to John Doe&quot; &quot;Notes on Standards&quot; &quot;Addresses of the Stars&quot;<br /> &lt;pre&gt;<br /> <br /> When the command returns multiple records, it should allow the user to specify a stem variable to place the contents in. For example, a command that would return the names of the loaded documents in a text editor would normally return information in the RESULT field, but if the user chooses to place the information in a stem variable, he could specify:<br /> <br /> &lt;pre&gt;<br /> GETATTRS DOCUMENTS STEM DocList.<br /> &lt;/pre&gt;<br /> <br /> which would return:<br /> <br /> &lt;pre&gt;<br /> DocList.count = 3<br /> DocList.0 = Letter to John Doe<br /> DocList.1 = Notes on Standards<br /> DocList.2 = Addresses of the Stars<br /> &lt;/pre&gt;<br /> <br /> In the above example, DocList.count contains the number of records in the array. Also note that the elements do not contain quotes around the information.<br /> <br /> === ARexx Port Naming ===<br /> <br /> Part of the job of supporting ARexx is adding an ARexx port to your application - this is a software structure through which ARexx communicates with your program. Each ARexx port must have a unique name and must be in upper-case. In ARexx, a port name is derived from:<br /> <br /> &lt;basename&gt;.&lt;slot #&gt;<br /> <br /> In the above line, &lt;basename&gt; is the same name your application's executable uses (see Chapter 2), and &lt;slot #&gt; is the next available ARexx port slot for that application.<br /> <br /> This ARexx port name should be displayed in the window brought up by the About menu item (along with the version of your application, etc.). The user should also have the ability to rename the port. Being able to specify the ARexx port name in the project icon's Tool Types field, or from the command line by using the PORTNAME keyword, is a good thing.<br /> <br /> Unique port names should be given to each project started within your application and for each instance of your application. For example, a fictional word processing package named GonzoWord with a basename of GWord should have an ARexx port of GWORD.1 when the first document is opened. A second document running concurrently should be given the port name of GWORD.2. If the user opened the GonzoWord program again without<br /> choosing QUIT in the first instance, that third document should have a port name of GWORD.3.<br /> <br /> === Command Shell ===<br /> <br /> Your application should allow the user to open a console window or command shell within your application's environment. There are two possible ways of handling this: you can provide a window that looks like a basic Shell window or one that looks like Workbench's Execute Command window.<br /> <br /> [[File:SG9-1.png|frame|center|Workbench's Execute Command window]]<br /> <br /> [[File:SG9-2.png|frame|center|A sample ARexx command shell]]<br /> <br /> Commands entered into this window would allow direct control of your application.<br /> <br /> As with any other type of window, your application should allow to user to snapshot the placement and size of the command shell.<br /> <br /> == Standard ARexx Commands ==<br /> <br /> The following commands are the minimum commands that your application should support. These commands are reserved across all applications - don't use these commands for any other functions.<br /> <br /> As listed here, the command template is named in bold on the first line. The definition for the command is on the following line, and that is followed by definitions of the options. If the options are self-explanatory, no definition is given.<br /> <br /> The commands are presented here in the same style as Shell commands discussed in Chapter 8. You should also use this style when you implement ARexx.<br /> <br /> {{Note|text=Your application should support at least these 15 ARexx commands.}}<br /> <br /> === Project-Related Commands ===<br /> <br /> ; NEW PORTNAME/K<br /> : NEW creates a new project and work area. This command should return the ARexx port name assigned to the project. PORTNAME is used to assign a specific port name to the project.<br /> <br /> ; CLEAR FORCE/S<br /> : This command clears the current project and its work area. FORCE suppresses the modified project requester.<br /> <br /> ; OPEN FILENAME/K,FORCE/S<br /> : This command opens the specified project into the current work area. If no FILENAME is given, prompt the user for a file name via a file requester. FORCE suppresses the modified project requester.<br /> <br /> ; SAVE ,<br /> : This command saves the current project to the current file name. If the project is unnamed, bring up the file requester so that the user can specify a file name.<br /> <br /> ; SAVEAS NAME/K<br /> : This command saves the current project to the specified file name. NAME specifies what the new project will be called. If no name is provided, it should bring up the file requester so that the user can specify the file name.<br /> <br /> ; CLOSE FORCE/S<br /> : This command closes the current project and window. FORCE suppresses the modified project requester.<br /> <br /> ; PRINT PROMPT/S<br /> : PRINT will print the specified object using the current settings. PROMPT provides a requester for use in setting print parameters.<br /> <br /> ; QUIT FORCE/S<br /> : QUIT stops the program. If the project was modified, the user should be prompted to save the work. FORCE suppresses the modified project requester.<br /> <br /> === Block-Related Commands ===<br /> <br /> ; CUT ,<br /> : CUT removes the currently selected block of information from the project and places it in the clipboard.<br /> <br /> ; COPY ,<br /> : COPY places a duplicate of the currently selected block of information into the clipboard.<br /> <br /> ; PASTE ,<br /> : PASTE puts the contents of the clipboard into the project - at the currently active point.<br /> <br /> ; ERASE FORCE/S<br /> : ERASE removes the currently selected block of information from the project. FORCE suppresses the &quot;Are you sure?&quot; requester.<br /> <br /> === Other Standard Commands ===<br /> <br /> ; HELP COMMAND,PROMPT/S<br /> : HELP provides access to information about your application. Information about things such as the supported functions and parameters required for functions should be readily available. When triggered from a non-graphical user interface, HELP should present the user with a text list of all the commands that the application supports. COMMAND presents the user with the list of options available for that command. PROMPT activates a graphical help system.<br /> <br /> ; FAULT /N<br /> : FAULT gives the user the text message assigned to the given error number. The text should be sent to the RESULT field. (See Returning Data section.)<br /> <br /> ; RX CONSOLE/S,ASYNC/S,COMMAND/F<br /> : RX allows the user to start an ARexx macro. CONSOLE indicates that a console (for default I/O) is needed. ASYNC indicates that the command should be run asynchronously. COMMAND sends whatever is typed on the rest of the line (usually a command) to ARexx for execution.<br /> <br /> === Other Common ARexx Commands ===<br /> <br /> These commands aren't applicable for every program, but please use them if your program provides this sort of functionality.<br /> <br /> ==== Cursor Positioning Commands ====<br /> <br /> ; GOTOLINE /N/A<br /> : This command moves the cursor to a specified line.<br /> <br /> ; GOTOCOLUMN /N/A<br /> : This command moves the cursor to a specified column.<br /> <br /> ; CURSOR UP/S/,DOWN/S,LEFT/S/RIGHT/S<br /> : CURSOR moves the cursor up, down, left or right a single line or column position.<br /> <br /> ; LINE /N/A<br /> : LINE accepts positive or negative arguments to move the cursor up or down relative to its current position.<br /> <br /> ; COLUMN /N/A<br /> : COLUMN accepts positive or negative arguments to move the cursor left or right relative to its current position.<br /> <br /> ; NEXT WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S<br /> : NEXT moves the cursor to the next word, sentence, paragraph or page.<br /> <br /> ; PREVIOUS WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S<br /> : PREVIOUS moves the cursor to the previous word, sentence, paragraph or page.<br /> <br /> ; SETBOOKMARK /N<br /> : This command is used to remember a place in text. If the program supports multiple bookmarks, a number can be used to differentiate them.<br /> <br /> ; GOTOBOOKMARK /N<br /> : Move cursor to a bookmark. If the program supports multiple bookmarks, a number can be used to differentiate them.<br /> <br /> ; POSITION SOF/S,EOF/S,SOL/S,EOL/S,SOW/S,EOW/S,SOV/S,EOV/S<br /> : POSITION moves the cursor to the position specified by the argument. SOF moves it to the beginning of the file. EOF moves it to the end of the file. SOL moves it to the beginning of the current line. EOL moves it to the end of the current line. SOW moves it to the start of the current word. EOW moves it to the end of the current word. SOV moves it to the top of the current view. EOV moves it to the bottom of the current view.<br /> <br /> ==== Find and Replace Commands ====<br /> <br /> ; FIND TEXT/F<br /> : FIND searches for text that matches the specified string. If no string is specified on the command line, a requester should be presented allowing the user to enter a search string.<br /> <br /> ; FINDCHANGE ALL/S,PROMPT/S,FIND/K,CHANGE/K<br /> : PROMPT brings up a requester. FIND searches for the string specified after the keyword. CHANGE replaces the specified FIND string with the string specified after the CHANGE keyword.<br /> <br /> ; FINDNEXT ,<br /> : This moves the cursor to the next occurrence of the current search string without displaying a requester.<br /> <br /> ==== Other Text-Related Commands ====<br /> <br /> ; TEXT TEXT/F<br /> : This command can be used in ARexx macros, allowing text to be entered via a command and honoring word wrap, insertion or any other current modes.<br /> <br /> ; UPPERCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> ; LOWERCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> ; SWAPCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> : These three commands can be used if your program has the ability to swap the case of a letter or letters. The default switch should be WORD.<br /> <br /> ; FONT NAME/S,SIZE/N,ITALIC/S,BOLD/S,PLAIN/S,UNDERLINED/S<br /> : Use this command if your application supports font selection. A combination of the text style arguments should be allowed following the SIZE argument<br /> <br /> ; UNDO /N<br /> : This command reverts back to the last state of the project. If your program supports multiple levels of undo, a number should be recognized as an argument. The default should be one level of undo.<br /> <br /> ; REDO ,<br /> : For applications with multiple levels of UNDO, REDO will allow the user to undo the UNDO command. See Chapter 6 for more information.<br /> <br /> ==== Window-Related Commands ====<br /> <br /> ; MOVEWINDOW LEFTEDGE/N,TOPEDGE/N<br /> : This command should be used to change the position of a window. An argument of -1 means no change or don't care. Your application should either do the best job it can to move the window to the specified position, or return an error code.<br /> <br /> ; SIZEWINDOW WIDTH/N,HEIGHT/N<br /> : This command should be used to change the size of a window. An argument of -1 means no change or don't care. Your application should either do the best job it can to size the window or return an error code.<br /> <br /> ; CHANGEWINDOW LEFTEDGE/N,TOPEDGE/N,WIDTH/N,HEIGHT/N<br /> : This command is similar to MOVEWINDOW and SIZEWINDOW but the application should move and size the window as one operation. This is important to the user because moving a window to a new size and position can require as many as three commands, depending on the original position and size as well as the target position and size.<br /> <br /> ; WINDOWTOFRONT ,<br /> : This command moves a window to the front.<br /> <br /> ; WINDOWTOBACK ,<br /> : This command moves a window behind all others.<br /> <br /> ; ACTIVATEWINDOW ,<br /> : This command activates a window.<br /> <br /> ; ZOOMWINDOW ,<br /> : If your program supports zoom gadgets on your windows, use this command to make a window small.<br /> <br /> ; UNZOOMWINDOWS ,<br /> : If your program supports zoom gadgets on your windows, use this command to unshrink it.<br /> <br /> ==== Telecommunications Commands ====<br /> <br /> ; BAUD /N<br /> : This command sets the baud rate.<br /> <br /> ; PARITY EVEN/S,ODD/S,NONE/S<br /> : Use this command to set the parity.<br /> <br /> ; STOPBITS 1/S,0/S<br /> : Use this to set the stopbits.<br /> <br /> ; DUPLEX FULL/S,HALF/S<br /> : Use this to set the duplex mode.<br /> <br /> ; PROTOCOL /K<br /> : Use this command to set the protocol.<br /> <br /> ; SENDFILE NAME/K<br /> : Use this to start a file send. If the file name is omitted, the user should be presented with a file requester.<br /> <br /> ; CAPTURE NAME/K<br /> : Use this to open a capture buffer. If the file name is omitted, the user should be presented with a file requester.<br /> <br /> ; DIAL NUM/F<br /> : Use this command to dial a phone number.<br /> <br /> ; REDIAL ,<br /> : Use this to redial the last phone number.<br /> <br /> ; SEND ,<br /> : Use this to send a text string.<br /> <br /> ; WAIT ,<br /> : Use this to wait for a text string.<br /> <br /> ; TIMEOUT /N<br /> : Use this to set the timeout value (in seconds) for waits.<br /> <br /> ==== Miscellaneous Commands ====<br /> <br /> ; TEXTPEN /N<br /> ; BACKGROUNDPEN /N<br /> : Text-oriented applications that support the ability to set pen and paper colors can use these two commands. Valid values should be bounded by the screen's maximum number of colors rather than by existing Amiga hardware.<br /> <br /> ; LOCKGUI ,<br /> : This inhibits the graphical user interface of the application.<br /> <br /> ; UNLOCKGUI ,<br /> : This undoes LOCKGUI, allowing GUI events to resume.<br /> <br /> ; GETATTR OBJECT/A,NAME,FIELD,STEM/K,VAR/K<br /> : GETATTR obtains information about the attributes of an object. OBJECT specifies the object type to obtain information on. NAME specifies the name of the object. FIELD specifies which field should be checked for information. STEM (keyword) specifies that the information is to be placed in the following stem variable rather than the RESULT field. VAR (keyword) specifies that the information is to be returned in a simple variable.<br /> <br /> : In the above OBJECT argument, the user could be seeking information on the attributes of any of the following items:<br /> :* APPLICATION<br /> :* PROJECT &lt;name&gt;<br /> :* WINDOW &lt;name&gt;<br /> :* PROJECTS<br /> :* WINDOWS<br /> <br /> : If the destination variable is a stem variable, the following fields are recommended:<br /> <br /> :; APPLICATION<br /> :: would consist of the following nodes (an aspect of APPLICATION that the user can seek sub-information about):<br /> ::* '''.VERSION''' Contains the current version information for the application.<br /> ::* '''.SCREEN''' Contains the name of the screen that the application resides in (if the screen is public).<br /> <br /> :; PROJECTS.COUNT<br /> :: would contain the number of projects;<br /> :: '''PROJECTS.0''' through '''PROJECTS.n''' would contain the handle for the project.<br /> <br /> :; PROJECT<br /> :: would consist of the following nodes. Other variables could be added based on the type of application. For example, a text editor could add a variable called .LINES that would contain the number of lines in the project's file.<br /> :: '''.AREXX''' Contains the ARexx port assigned to the project.<br /> :: '''.FILENAME''' Contains the complete filename of the project.<br /> :: '''.PATH''' Contains the path portion of the filename.<br /> :: '''.FILE''' Contains the file portion of the filename.<br /> :: '''.CHANGES''' Contains the number of changes made to the project (since last save).<br /> :: '''.PRIORITY''' Priority at which the project's process is running. Allows the user to set the priority. For example, a 3-D modelling application could be working on several different projects at one time. Using this field, the user would be able to give more time to the most important project.<br /> <br /> :; WINDOWS.COUNT<br /> :: would contain the number of windows;<br /> :: '''WINDOWS.0''' through '''WINDOWS.n''' would contain the handle for the window.<br /> <br /> :; WINDOW<br /> :: would consist of the following nodes:<br /> :: '''.LEFT''' Left edge of the window.<br /> :: '''.TOP''' Top edge of the window.<br /> :: '''.WIDTH''' Width of the window.<br /> :: '''.HEIGHT''' Height of the window.<br /> :: '''.TITLE''' Window title.<br /> :: '''.MIN.WIDTH''' Minimum width of the window.<br /> :: '''.MIN.HEIGHT''' Minimum height of the window.<br /> :: '''.MAX.WIDTH''' Maximum width of the window.<br /> :: '''.MAX.HEIGHT''' Maximum height of the window.<br /> :: '''.SCREEN''' Name of the screen in which the window resides.<br /> <br /> ; SETATTR OBJECT/A,NAME,FIELD,STEM/K,VAR/K<br /> : SETATTR manipulates the aspects of an object.<br /> :* OBJECT specifies the object type to manipulate.<br /> :* NAME specifies the name of the object.<br /> :* FIELD specifies which field should be modified.<br /> :* STEM specifies that the information is to be extracted from the following stem variable.<br /> :* VAR specifies that the information is to be extracted from the following simple variable.<br /> <br /> ; WINDOW NAMES/M,OPEN/S,CLOSE/S,SNAPSHOT/S,ACTIVATE/S,MIN/S,MAX/S,FRONT/S,BACK/S<br /> : WINDOW allows the user to manipulate several aspects of the window.<br /> :* NAMES specifies a list of windows to manipulate. This command should support wildcard expansion.<br /> :* OPEN is used to indicate that the window(s) should be opened.<br /> :* CLOSE is used to indicate that the window(s) should be closed.<br /> :* SNAPSHOT records the current position and size of the named window(s).<br /> :* ACTIVATE will make the window the input focal point.<br /> :* MIN sets the named window(s) to the minimum size.<br /> :* MAX sets the named window(s) to the maximum size.<br /> :* FRONT places the named window(s) in front of other windows.<br /> :* BACK places the named window(s) behind all others.<br /> <br /> ; CMDSHELL OPEN/S,CLOSE/S<br /> : CMDSHELL opens a command shell so the user can interact directly with the application at a command level. CMDSHELL allows the user quick access to infrequently used functions or macros that are not bound to a key, menu or button.<br /> <br /> ; ACTIVATE ,<br /> : ACTIVATE starts the GUI of an application. This involves &quot;de-iconifying&quot; it, bringing its screen to the front, unzooming the main window (ie. expanding it to its previously set size) and making that window active.<br /> <br /> ; DEACTIVATE ,<br /> : This command shuts down the GUI of an application. DEACTIVATE restores the GUI to the state it was in before receiving the ACTIVATE command.<br /> <br /> ; DISABLE NAMES/M<br /> : This command disables a command or list of commands. DISABLE should also disable any user interface items to which the command is bound. For example, if a command is bound to a gadget, then that gadget should be disabled as well. This command should support pattern expansion.<br /> <br /> ; ENABLE NAMES/M<br /> : ENABLE makes a command or a list of commands available to the user. ENABLE should also enable the GUI items to which they are bound (see DISABLE). This command should support pattern expansion.<br /> <br /> ; LEARN FILE/K,STOP/S<br /> : LEARN allows the application to build an ARexx macro consisting of the actions the user performs.<br /> :* FILE specifies the name of the file in which the user will save the commands.<br /> :* STOP tells the application to stop learning.<br /> <br /> ; ALIAS NAME/A,COMMAND/F<br /> : ALIAS assigns a user-definable name to a function and its options.<br /> :* NAME specifies what the new command will be called.<br /> :* COMMAND represents the rest of the line where the command and whatever options are to be bound to that command are entered.<br /> <br /> ; SELECT NAME/A,FROM/K,NEXT/S,PREVIOUS/S,TOP/S,BOTTOM/S<br /> : SELECT is used to select an object from a list of similar objects. The main use for this command is to allow the user to select the current project from a list of projects. Returns the name of the current project.<br /> :* FROM specifies the list from which the user can select. The default should be the main project list.<br /> :* NEXT specifies that the next object on the list should be selected.<br /> :* PREVIOUS specifies that the previous object on the list should be selected.<br /> :* TOP specifies that the first object on the list should be selected.<br /> :* BOTTOM specifies that the last object on the list should be selected.<br /> <br /> === Advanced ARexx Commands ===<br /> <br /> Increasingly, programmers are supporting ARexx commands that allow users to add their own ARexx scripts or even to modify the program's interface. If you would like your program to support any of the following functions, please use these standard commands:<br /> <br /> ; BUTTON NAME/A,LABEL/K,WINDOW/K,PROMPT/S,CMD/F<br /> : Some applications could set aside a number of action gadgets (buttons) to which the user can assign ARexx scripts. The BUTTON command would allow the user to modify these special gadgets.<br /> :* NAME specifies the name of the button to be edited.<br /> :* CMD specifies the command name assigned to the button.<br /> :* LABEL specifies the text label assigned to the button.<br /> :* WINDOW specifies the window in which the button belongs. This should default to the active or last active Intuition window for that application.<br /> :* PROMPT activates a window in which the user can graphically edit the button.<br /> <br /> ; KEYBOARD KEY/A,WINDOW/K,GLOBAL/S,HOTKEY/S,PROMPT/S,CMD/F<br /> : KEYBOARD allows commands to be tied to keystrokes.<br /> :* KEY specifies which keystroke to edit.<br /> :* CMD specifies what command will be assigned to that keystroke.<br /> :* WINDOW specifies which window the hotkey will perform in. It should default to the active or last active Intuition window for that application.<br /> :* GLOBAL indicates that the keystroke will operate when any of the application's windows are active.<br /> :* HOTKEY indicates that the keystroke will operate regardless of what window or application is active.<br /> :* PROMPT gives the user a window in which to edit the keystroke.<br /> <br /> ; MENU NAME/A,LABEL/K,MENU/K,WINDOW/K,PROMPT/K,CMD/F<br /> : MENU provides a means to add, edit or delete a menu item. MENU also allows access to the function that the menu item triggers.<br /> :* NAME specifies which menu item the user will edit.<br /> :* CMD speciifies what command will be assigned to the menu item.<br /> :* LABEL assigns a text label to the menu item.<br /> :* MENU specifies which menu strip the new or edited item will be added to.<br /> :* WINDOW specifies which window the new or edited menu belongs in. This should default to the active or last active Intuition window for that application.<br /> :* PROMPT (switch) activates a window in which the user can graphically edit the menu item.<br /> <br /> ; NOP ,<br /> : The NOP command is a special do nothing command (short for No OPeration) that is often extremely useful. For example, if your program supports custom keyboard<br /> definitions, it is often necessary to disable some keys when writing custom macros.<br /> <br /> ==== Programmable Requesters ====<br /> <br /> When a user writes an ARexx macro, it is often very useful to be able to bring up a file requester, or other type of requester, as part of the macro. If your program supports programmatic access to its requesters, the following commands should be used:<br /> <br /> ; REQUESTFILE TITLE/K,PATH/K,FILE/K,PATTERN/K<br /> : This command brings up a file requester. The user can specify the title, path and file name. If any of these arguments are omitted, they should be filled in with some defaults (eg. a default title and empty strings for path and/or file name).<br /> : The path and file name should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTSTRING PROMPT/K,DEFAULT/K<br /> : This command brings up a string entry requester. The user can specify a prompt string and a default string to be placed in the string editing gadget. Like the REQUESTFILE command, defaults should be provided if arguments are omitted.<br /> : The string entered should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTNUMBER PROMPT/K,DEFAULT/K<br /> : This command brings up a number entry requester. The user can specify a prompt string and a default number to be placed in the string editing gadget. Like the REQUESTFILE command, defaults should be provided if arguments are omitted.<br /> : The number entered should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTRESPONSE TITLE/K,PROMPT/K<br /> : This command bring up a query requester. The user can specify a prompt string and possibly text for the OK and CANCEL gadgets if your program wants to support these additional options.<br /> : A warning should be returned if the user selects CANCEL.<br /> <br /> ; REQUESTNOTIFY PROMPT/S<br /> : This command brings up a notification requester that can only be satisfied with an OK type of response.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Shell&diff=5383 UI Style Guide Shell 2013-04-26T09:27:49Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == The Shell ==<br /> <br /> One of the best things about the Amiga is its versatility. While the rest of the personal computing community continues to argue about the best type of interface, Amiga users have both - a graphic interface with Workbench and a text-based interface with the Shell.<br /> <br /> The Shell (also known as the Command Line Interface or CLI) preserves the best features of operating computers the &quot;old-fashioned way&quot;, that is, by typing commands at a console. Although that is in some ways more difficult than clicking on graphics with a mouse, it provides a finer level of control, less overhead and greater power than is possible through a GUI.<br /> <br /> Virtually anything that can be done through Workbench can also be done through the Shell. On Workbench, for instance, a user can move into a subdirectory by double-clicking on its drawer icon. The equivalent of this in the Shell is to type<br /> <br /> cd &lt;subdirectory name&gt;<br /> <br /> The command ''dir'' will then list the contents of the directory.<br /> <br /> The same example can be used to illustrate the greater power of the Shell. In addition to ''dir'', which gives a brief listing of files and subdirectories, the user can also use ''list'' which gives a more in-depth listing including the size of each file, information about the file and when it was last updated. Or you could list only a subset of files by using wildcards in the command. For example,<br /> <br /> list #?.info<br /> <br /> would list only those files ending with &quot;.info&quot;.<br /> <br /> [[File:SG8-1.png|frame|center|The Shell]]<br /> <br /> In addition to being able to do things with finer control than Workbench, the Shell can also do some things that Workbench cannot do at all. For instance, from the Shell you can create a script - a pre-recorded set of commands that helps to automate repetitive tasks.<br /> <br /> {{Note|text=Your application should support all three interfaces built into the Amiga: the GUI, the Shell and ARexx.}}<br /> <br /> == Parsing Commands ==<br /> <br /> You should use the command template method for parsing the command line:<br /> <br /> === Standard Form ===<br /> <br /> In general, Shell commands take the form:<br /> <br /> COMMAND [redirection-argument] [&lt;argument1&gt;,&lt;argument2&gt;, ...]<br /> <br /> where COMMAND is the name of an executable file, [redirection-argument] is a &quot;&lt;&quot;, &quot;&gt;&quot;, and/or &quot;&gt;&gt;&quot; symbol followed by an AmigaDOS device name, and [&lt;argument1&gt;, &lt;argument2&gt;, ...] is a list of arguments that will be passed to the executable file.<br /> <br /> Here's a sample Shell command:<br /> <br /> play myanim loops 20<br /> <br /> The command is ''play' (an executable program), there is no direction and three arguments are passed to the play program: ''myanim'' (the name of the data file), ''loops'' (a command option) and ''20'' (another command option).<br /> <br /> === Built-in Parsing ===<br /> <br /> The Amiga, like UNIX and many other systems, passes command line arguments to your application. The system automatically provides the list of arguments and counts them for the application. <br /> <br /> The system also needs to parse these arguments. Parsing refers to the job of examining the arguments to find out what they mean so the appropriate operation can be performed.<br /> <br /> When your application is started from the Shell, any arguments should be parsed using the command template method - the same method used to parse all the system-supplied Shell commands. This argument parsing method should also be used by your ARexx or scripting commands.<br /> <br /> By far, the greatest benefit of this method is that it allows you to use AmigaDOS routines to handle the chore of parsing the command line. The DOS routines will handle errors and give help messages so both your code and your development time can be shorter. Using standard argument parsing also makes the Shell interface more consistent and more comfortable for the user.<br /> <br /> {{Note|text=Using AmigaDOS routines to handle command line parsing can shorten both your code and your development time.}}<br /> <br /> === The Command Template ===<br /> <br /> In order for your application to use standard argument parsing, a command template must be constructed to describe the arguments that the command understands.<br /> <br /> In the command template, each argument is specified by a keyword (a preset argument that the program understands) followed by a modifier that describes the properties of that argument. Modifiers take the form &quot;/X&quot; where X is one of the characters from the table below. Each keyword can have none or many of the modifiers.<br /> <br /> Neither the keywords nor the modifiers are case-dependent. The format shown here corresponds to the way the system displays command templates in the Shell.<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Modifier<br /> ! Description<br /> |-<br /> | ,(comma) - No arguments<br /> | The comma indicates a null argument. It also separates arguments in the command template.<br /> |-<br /> | = - Equivalents<br /> | The equal sign indicates equivalent abbreviations for keywords, e.g. PS=PUBSCREEN/K.<br /> |-<br /> | A - Always required<br /> | The argument must be supplied in order for the command to be accepted.<br /> |-<br /> | F - Final argument<br /> | If this is specified, the entire rest of the line is taken together as a single argument, even if other keywords appear in it.<br /> |-<br /> | K - Keyword required<br /> | This means [that] the actual keyword must be typed on the command line along with its argument in order for the argument to be processed (often the keyword is optional). The argument will not be interpreted unless the keyword appears.<br /> For example, if the template is NAME/K, then unless Name=&lt;string&gt; or Name &lt;string&gt; appears in the command line, the command will be interpreted as having no Name argument at all.<br /> |-<br /> | M - Multiple argument<br /> | There can be a number of instances of this argument. For example, the AmigDOS Join command lets your merge together any number of files into a single file. The template is:<br /> FILE/M,AS=TO/K/A<br /> This allows commands such as:<br /> Join file1 file2 file3 as bigfile<br /> When the /M modifier is the first argument in a template, any number of file names may be specified by the user.<br /> <br /> If a command line has any leftover arguments, they will be interpreted as belonging to the /M argument. So only specify one /M per template. For example, in the command ''Join one two as bigfile three'', the word ''three'' is an extra argument. In this case, one, two and three will be merged together to form bigfile; the extra argument is tacked onto the /M arguments.<br /> <br /> The /M argument also interacts with the /A argument. If there aren't enough arguments given to fill all the /As, then part of the previous /M argument will be used to fill in the /As.<br /> |-<br /> | N - Number<br /> | This argument is a decimal number. If an invalid number is specified, an error message will be returned to the user. Unless the /N modifier is specified, all arguments are assumed to be strings.<br /> |-<br /> | S - Switch keyword<br /> | This modifier indicates a switch keyword argument. If the keyword is given, the switch is &quot;on&quot;. If the keyword isn't given, the switch is &quot;off&quot;<br /> |-<br /> | T - Toggle keyword<br /> | This is similar to /S but, when specified, it causes the switch value to toggle from &quot;on&quot; to &quot;off&quot; or vice-versa.<br /> |}<br /> <br /> {| class=&quot;wiktiable&quot;<br /> | Use the standard command template described here in your Shell and ARexx commands.<br /> |}<br /> <br /> === Displaying the Command Template ===<br /> <br /> When a user types a command name in the Shell followed by a space and a question mark, the command template should be shown to him. This acts as a sort of help message that gives the syntax of the command. For instance, in the above example of ''play myanim loops 20' the command is ''play''. When the user types ''play ?'' in the Shell he should see this:<br /> <br /> ANIM/A,LOOPS/K/N<br /> <br /> This means the command has two arguments: the Anim argument and the Loops argument. The Anim argument consists of the optional keyword ''ANIM'' followed by an animation file name. The /A modifier means this argument must always be given.<br /> <br /> In the Loops part of the argument, the /K means that the keyword ''LOOPS'' is required for the argument to be processed correctly, and the /N means that the ''LOOPS'' keyword should be followed by a decimal number. Notice that since the Loops argument does not have a /A modifier, it could be left out altogether.<br /> <br /> Other possible commands a user could come up with include:<br /> <br /> play myanimfile loops 20<br /> play myanimfile<br /> play anim myanimfile<br /> <br /> These commands, however, would be illegal:<br /> <br /> play myanimfile 20 (the required keyword LOOPS is missing)<br /> play anim (no file name is given)<br /> play anim myanimfile loops (the numeric value for LOOPS is missing)<br /> <br /> == Standard Arguments ==<br /> <br /> The following Shell arguments are standard within the system. Don't use these names for application-specific arguments with purposes different than those given below:<br /> <br /> FILES/M List of files to work with as projects<br /> PUBSCREEN/K Name of the public screen to open on<br /> PORTNAME/K Name to assign to the ARexx port<br /> STARTUP/K ARexx script to run at startup time<br /> NOGUI/S Indicates that no GUI is desired<br /> SETTINGS/K Name of preferences file to load at startup<br /> <br /> If you need a keyword for your application, try to find an applicable one that is already in use - and use it the same way. If you need to make a new keyword, don't make it the same as a common command name; e.g. &quot;list&quot;.<br /> <br /> == Pattern Matching ==<br /> <br /> When most people think of pattern matching, they think of using wilcards, but pattern matching is actually much more. It's a powerful feature that enables more efficient operations, especially on systems like the Amiga that have a hierarchical file system with few size limits.<br /> <br /> Pattern matching is part of the appeal of the Shell. It would be difficult to use icons to perform an operation on 50 files at once - not to mention the problem of fitting all the icons on a 12-inch monitor. Some complex operations are just easier to do with a text interface.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Standard Available Tokens<br /> ! Token<br /> ! Description<br /> |-<br /> | ?<br /> | Matches a single character. For instance ''a?'' matches any string with two characters that starts with the letter a.<br /> |-<br /> | #<br /> | Matches a subsequent expression 0 or more times. For instance, the pattern #? will match any string.<br /> |-<br /> | (ab&amp;#124;cd)<br /> | Matches any one of the items separated by &amp;#124;.<br /> |-<br /> | ~<br /> | Negates the following expression. For instance the pattern ~x? will match any two letter string except those starting with x. Another corollary of this is the pattern ''~(x?)'' which will match anything except two-letter strings beginning with x; ie. xaa or ab but not xa.<br /> |-<br /> | [abc]<br /> | Character class; matches any one of the characters in the brackets.<br /> |-<br /> | [a-z]<br /> | Character range (must be within character classes). You can also use [a-e,x-z] style.<br /> |-<br /> | %<br /> | Matches 0 characters always. For example, the pattern (foo&amp;#124;whiskey&amp;#124;%)bar matches foobar, whiskeybar and bar.<br /> |-<br /> | *<br /> | Synonym for #?. Not available by default but it is an option that can be turned on.<br /> |}<br /> <br /> Expression in this table means either a single token or character token (such as ''?'' or ''x''), or an alternation (such as ''(ab&amp;#124;cd&amp;#124;ef)''), or a character class (such as ''[a-z,A-Z]'').<br /> <br /> === The Amiga's Wildcard ===<br /> <br /> On the Amiga, the wilcard is #? - as opposed to * on other platforms. For example, the command<br /> <br /> delete #?.info<br /> <br /> would delete all the files in the current directory that end in .info.<br /> <br /> === Within Applications ===<br /> <br /> In addition to its usefulness on the filing system, pattern matching is also useful within applications. For example, the Find function within a text editor should be able to find strings according to a pattern.<br /> <br /> Your application should support pattern matching from the Shell and from functions within the application itself, through AmigaDOS system routines.<br /> <br /> == Embedded Version IDs ==<br /> <br /> Follow the standard method for indicating the version of your software.<br /> <br /> The Shell command VERSION provides support for version identification, but you have to follow a standard template.<br /> <br /> A text sequence that indicates the current version of your application should be embedded within your code. (It should also be put in your ARexx scripts and your configuration files.) The format of this text sequence is:<br /> <br /> $VER: &lt;name&gt; &lt;version&gt;.&lt;revision&gt; (&lt;d&gt;.&lt;m&gt;.&lt;y&gt;)<br /> <br /> &lt;name&gt; The name of your application<br /> &lt;version&gt; Major version number<br /> &lt;revision&gt; Minor revision number<br /> &lt;d&gt; Day created<br /> &lt;m&gt; Numeric month created<br /> &lt;y&gt; Year created<br /> <br /> If you follow this template, the user can find out what version of your software he is using with the Shell command VERSION.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Workbench&diff=5382 UI Style Guide Workbench 2013-04-26T09:27:45Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Workbench ==<br /> <br /> In a way, Workbench is just another program - but as the default interface on the Amiga, it's one program that many users will pass through on their way to your application. Although it has other facets and functions, it's the interface between your program and Workbench that is of concern in this chapter.<br /> <br /> [[File:SG7-1.png|frame|center|The workbench screen]]<br /> <br /> == Icons ==<br /> <br /> Icons are pictorial representations of directories, files, applications, objects or actions. Your program should have icons for anything the user can access including the main program itself, documentation files, and any tools that may accompany the program.<br /> <br /> === The .info File ===<br /> <br /> The icon imagery is found in a file bearing the suffix &quot;.info&quot;. For example, the icon for a data file called &quot;myletter&quot; would be found, along with some other information, in the file &quot;myletter.info&quot;.<br /> <br /> === Icon Design ===<br /> <br /> A good icon quickly communicates the function it represents. The Calculator icon and the NotePad icon are good examples.<br /> <br /> [[File:SG7-2.png|frame|center|The Calculator and NotePad icons]]<br /> <br /> ==== Size ====<br /> <br /> Icons should be small. The maximum recommended size for an icon is 80 pixels wide by 40 pixels high. Large icons take up valuable screen and disk space, make for an unprofessional Workbench look and, in general, can be just plain annoying.<br /> <br /> ==== The 3-D Look ====<br /> <br /> Icons should be designed with the light source coming from the upper left-hand corner. They should be viewable in one bitplane as well as two.<br /> <br /> ==== Text in Icons ====<br /> <br /> Before you use words in an icon, think about how extensive a distribution you would like to see your product achieve. It's better to have an icon communicate through symbolism than language.<br /> <br /> {{Note|text=Before using words in an icon, think about how wide a distribution you envision for you product.}}<br /> <br /> === Icon Types ===<br /> <br /> There are five types of icons on the Workbench: disk icons, drawer icons, trashcan icons, tool icons and project icons. Of these, only tool icons and project icons bear discussion here.<br /> <br /> ==== Tool Icons ====<br /> <br /> A tool icon represents an executable file such as an application. A double-click on a tool icon will run the application. The look of tool icons varies from application to application.<br /> <br /> ==== Project Icons ====<br /> <br /> A project icon represents a data file. Typically, a double-click on this icon will cause the application that created the data to run and automatically load this data.<br /> <br /> The look of a project icon is usually determined by the application that created it and by the type of data it represents. However there is also a default project icon included in the system.<br /> <br /> [[File:SG7-3.png|frame|center|The default project icon]]<br /> <br /> ==== Create Icons? ====<br /> <br /> When your application creates a data file, it should, by default, create a .info file to go with it. Many users are accustomed to accessing their projects via an icon.<br /> <br /> Create the icon after the user successfully saves the project. This will prevent the possibility of project-less icons in the system.<br /> <br /> Your application, however, should allow the user the option of saving files without creating icons. The recommended way of doing this is to have an item in the Settings menu called Create Icons?. This item should be enabled by default. See [[UI Style Guide Menus]] for more information.<br /> <br /> ==== Positioning ====<br /> <br /> New project icons should never be saved in a specific position. Rather they should be positioned algorithmically by Workbench (see the Workbench flag NO_ICON_POSITION). In the same vein, if the imagery of an existing icon is changed (e.g. the application creates a reduced version of the project for use as the icon imagery) the icon should be saved without a specific position.<br /> <br /> == Argument Passing ==<br /> <br /> To make the common operation of starting up programs more powerful, the Amiga provides ways of passing along more specific information to the application. This process of providing additional information to a program that's about to run is known as argument passing.<br /> <br /> When using the Shell, the system is straightforward. The user runs an application by typing its name along with additional information such as the name of a file to operate on and any command options. The entire command line is then passed to the program as an argument.<br /> <br /> The Workbench also provides means of passing arguments - but it is tied to icons instead of lines of type.<br /> <br /> === Tool Types and Default Tool ===<br /> <br /> Applications started from Workbench receive startup information from the system in the form of a WBStartup message. The WBStartup message can be used to get various Workbench arguments including those found in the Tool Types and Default Tool fields of the icon the user clicked on.<br /> <br /> ==== Tool Types ====<br /> <br /> If you click once on any tool icon and choose &quot;Information...&quot; from the Workbench's Icon menu, you should see a requester with a field for Tool Types.<br /> <br /> Fig 7.4: The Information requester for a tool icon.<br /> <br /> Arguments placed in this field take the form KEYWORD=value. KEYWORD is an argument name specified by your application, and value is a number or string that should be assigned to that argument.<br /> <br /> For instance, the Tool Types field of a text display program might be set to LINES=20 to indicate how many lines to display at once.<br /> <br /> Normally, the Tool Types information is filled in when the .info file is created; subsequent operations only read this information.<br /> <br /> ==== Default Tool ====<br /> <br /> The Information requester for a project icon is different than that of a tool icon - it has both a Default Tool field and a Tool Types field. The Default Tool field tells the system which application is used to edit the project. When a user double-clicks on a project icon, the application specified in the Default Tool field gets run and the data is passed to that application via the WBStartup message.<br /> <br /> Fig 7.5: An Information requester for a project icon.<br /> <br /> ==== Altering Icons ====<br /> <br /> If your application needs to alter an icon for some reason, change only the things you intend to change and preserve the rest. If your application needs to change the Tool Types field, for instance, leave the imagery, position and Default Tool field alone.<br /> <br /> More important, change only the Tool Types entries relevant to your application - do not rewrite Tool Types from scratch for an existing project. If the icon has a Tool Types entry that your application does not recognize, that entry should be preserved.<br /> <br /> ==== Tool Types and Networks ====<br /> <br /> You should support project-specific Tool Types arguments. With the advent of networked Amigas sharing the same applications, this becomes increasingly important. Tool Types arguments written to specific projects will allow networked users to override Tool Types arguments written to the application's tool icon - arguments that have probably been set for the least common denominator.<br /> <br /> ==== Standard Tool Types Arguments ====<br /> <br /> Some Tool Types arguments are already used in the system. If they apply to your application, support them; if not, take care that your Tool Types arguments don't conflict with these. Listed below are some of the more common Tool Types arguments found in the system:<br /> <br /> {{Note|text=Take care that your Tool Types arguments don't conflict with those listed here.}}<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Tool Type<br /> ! Notes<br /> |-<br /> | WINDOW=CON:&lt;window spec&gt;<br /> |<br /> |-<br /> | DONOTWAIT<br /> | Don't wait for return; used by wbstartup<br /> |-<br /> | TOOLPRI=&lt;priority&gt;<br /> STARTPRI=&lt;priority (-127 to 128)<br /> | Used to set your program's priority<br /> |-<br /> | PUBSCREEN=&lt;name&gt;<br /> | The name of the public screen to open on<br /> |-<br /> | STARTUP=&lt;name&gt;<br /> | ARexx script to run at startup time<br /> |-<br /> | PORTNAME=&lt;name&gt;<br /> | Name to assign to your application's ARexx port; overrides default naming system<br /> |-<br /> | SETTINGS=&lt;name&gt;<br /> | Allows a user to specify a settings file<br /> |-<br /> | UNIT=&lt;number&gt;<br /> | Device or unit number<br /> |-<br /> | DEVICE=&lt;parallel/serial&gt;<br /> | The name of the device to use<br /> |-<br /> | FILE=&lt;file pathname&gt;<br /> |<br /> |-<br /> | WAIT=&lt;number (of seconds)&gt;<br /> |<br /> |-<br /> | PREFS=&lt;prefstype&gt;<br /> |<br /> |-<br /> | CX_POPUP=&lt;yes/no&gt;<br /> |<br /> |-<br /> | CX_POPKEY=&lt;CX key specifier&gt;<br /> |<br /> |-<br /> | CX_PRIORITY=&lt;CX priority level&gt;<br /> |<br /> |-<br /> | &lt;CX fkey spec&gt;=&lt;CX string spec&gt;<br /> |<br /> |}<br /> <br /> === The Apps ===<br /> <br /> Other facilities also exist in Workbench that allow an application to get more information while the application is already running. These are known as AppWindow, AppIcon and AppMenu.<br /> <br /> For example, a text editor's windows may function as AppWindows. A user would be able to drag the icon of a file into the window and that file would be loaded automatically.<br /> <br /> AppWindows, AppIcons and AppMenus are aimed at the user. By using techniques that are totally graphic-oriented (Tool Types arguments still eventually come down to a line of type), the Apps bring the power of argument passing more into line with the &quot;point-and-click&quot; metaphor.<br /> <br /> ==== AppWindows ====<br /> <br /> An AppWindow is a special kind of Workbench window that allows the user to drag icons into it. It's basically a graphical alternative to a file requester.<br /> <br /> Applications that set up an AppWindow will receive a message from Workbench whenever the user moves an icon into that AppWindow. The message contains the name of the file or directory that the icon represents.<br /> <br /> For instance, Workbench's IconEdit is an AppWindow with three different areas which users can drag icons into - each with a different purpose. When the user drags an icon into the large box, the icon is loaded as a project. When an icon is dragged into the box labelled &quot;Normal&quot;, its image is used for the normal (not selected) image. Likewise for an icon that is dragged into the box labelled &quot;Selected&quot; - its image becomes the activated image for the icon.<br /> <br /> Fig 7.6: IconEdit is an example of an AppWindow. It has three different drop areas which do three different things.<br /> <br /> An AppWindow will often use icon drop box gadgets to indicate the active area where the user may drop an icon. Note: IconEdit did not follow this convention because it was more important to indicate the function of each area by its imagery (e.g. the &quot;normal&quot; area can be clicked on, etc.).<br /> <br /> The window should activate when an icon is dragged into it.<br /> <br /> AppWindows only work when your application is running on the Workbench screen. This makes sense because you need to be able to drag icons from Workbench to the AppWindow and draggable objects can't be dragged across screens. If the user opts to run your application on a screen other than Workbench, set your AppWindows so they will revert to AppMenus (see below).<br /> <br /> As a general rule, AppWindows are appropriate when your application needs to have a window anyway.<br /> <br /> ==== AppIcons ====<br /> <br /> An AppIcon is similar an AppWindow - it allows the user to pass a graphical argument to a running application. The only difference is that AppWindows use a window to accept the argument and AppIcons use an icon.<br /> <br /> The image for an AppIcon should give some indication what operations it supports; ie. whether it represents an iconized application or supports dropped projects. Avoid vague imagery.<br /> <br /> AppIcons are useful for programs that operate in the background with little other user interaction - a print spooler is a good example.<br /> <br /> Double-clicking on an AppIcon should normally open a window offering information and controls. For example, a print spooler could open a status window in which the user could rename, abort or re-order the things he sent to be printed.<br /> <br /> ==== AppMenus ====<br /> <br /> An AppMenu allows your application to add a custom menu item to the Tools menu on Workbench. An application that sets up an AppMenu item will receive a message from Workbench whenever the user picks that item from the Workbench menu.<br /> <br /> === Preferences ===<br /> <br /> Applications can also get arguments from Preferences. Via Preferences, the user can graphically set up the system defaults to suit his taste and needs.<br /> <br /> Through Preferences, the user can control such things as screen fonts, colors and other global information that your application should respect.<br /> <br /> You can create a preference editor to handle the defaults used by your application. See [[UI Style Guide Preferences]] for more information.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Menus&diff=5381 UI Style Guide Menus 2013-04-26T09:27:41Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Menus ==<br /> <br /> Menus are used either to invoke an action from a fixed list of choices or to set an option within an application.<br /> <br /> The Amiga's menu system is a very appealing part of the user interface for a number of reasons:<br /> <br /> * Even novice users can quickly understand how a menu works.<br /> <br /> * Menus allow the user to browse through the set of possible actions that can be performed. This gives an outline-like overview of what functions are offered by a program.<br /> <br /> * They give the user familiar landmarks that can always be brought into view by pressing the menu button.<br /> <br /> * The menu system keeps the Amiga's GUI from being too cluttered since menus stay neatly tucked away until the user wants to see them.<br /> <br /> == Menu Anatomy ==<br /> <br /> The menu system consists of the menu bar which shows the name of each menu, a drop-down menu under each name showing the list of choices (also known as menu items), and an optional group of secondary choices called a submenu, which are attached to a menu item. Submenus should appear to the right of the menu.<br /> <br /> [[File:SG6-1.png|frame|center|Elements of the menu system]]<br /> <br /> == General Rules ==<br /> <br /> Throughout your design of a menu system, keep the user in mind. Think of what the user will have to go through to choose a menu item. Working with a mouse is simple for simple tasks, but when menus get long or are complicated and illogically arranged, working with a mouse can be very frustrating indeed.<br /> <br /> === Strive for Fixed Menus ===<br /> <br /> When possible, your menus should offer a fixed set of visible choices. A large, variable set of choices such as fonts can make a menu unwieldy.<br /> <br /> Usually, a variable set of choices should be presented in either a requester or a scrolling list gadget. There are rare exceptions such as the [[#Standard_Menus|standard User menu]] described later.<br /> <br /> === Multiple Selection ===<br /> <br /> The user should be able to select a number of menu items at one time by clicking the multiple items with the selection button before releasing the menu button.<br /> <br /> Anticipate conflicting choices that can occur during multiple selection. If the user chooses an item and then chooses one that conflicts with the first, the second item should override the first.<br /> <br /> === Let Intuition work for You ===<br /> <br /> Many of the functions described here can be handled for you automatically through Intuition. Let Intuition do the work for you whenever possible rather than coding your own menus.<br /> <br /> == Menu Design ==<br /> <br /> Use the same criteria for designing the menu system as for other elements of the GUI: design it on a 640x200 screen with the Topaz 8 font. At run-time, your application should test to see if the menu will fit in the user's preferred font and screen size. If not, revert to the Topaz 8 font.<br /> <br /> === Colors ===<br /> <br /> When possible, menus should use dark text on a light background. Note: the examples shown in this chapter don't follow this rule because the version of Release 2 they were taken from had color constraints that prevented this rule from being obeyed.<br /> <br /> === Font ===<br /> <br /> A uniform font is recommended for all the menu items within an application. A change in font style could be appropriate, however, if it symbolizes a change in the style of text in your application. For example, in a word processor, a menu called Style may list the type styles Bold, Italic and Underline. Those menu items may be rendered in bold, italic and underline text, respectively.<br /> <br /> === Toggle Items ===<br /> <br /> Some menu items turn an option on or off. To show this, use a check mark that toggles visible and invisible in a space in front of the item. Don't add a submenu with items such as &quot;on&quot; and &quot;off&quot;.<br /> <br /> Toggle items should be indented to allow room for the check mark. This will give the user a visual cue that it is a toggle item.<br /> <br /> [[File:SG6-2.png|frame|center|A toggle menu item shown in both states]]<br /> <br /> === Organization ===<br /> <br /> Within a menu, items should be grouped according to ''function''. Distinct groups should be separated with a separator bar. &quot;Function&quot; can be defined by these three rules:<br /> <br /> Separate the toggle items from the non-toggle items. This will help the user quickly distinguish one type from the other. It will also look better if the left-hand side of the menu text doesn't indent more than once.<br /> <br /> Group similar choices together. For example, if a menu includes the items<br /> * Save as ASCII<br /> * Save as MystrEd doc<br /> * Quit<br /> * New<br /> * Save as IFF clip<br /> <br /> the three Save as... items should be grouped together.<br /> <br /> Frequently used items should be placed towards the top of the menu. When ordering the items, make sure to separate commonly used items from dangerous ones. It's relatively easy for a user to choose the wrong menu item by mistake - try to anticipate such mistakes and their possible outcomes and arrange your menus to avoid them.<br /> <br /> {{Note|text=When ordering menu items, make sure to separate commonly used items from dangerous ones.}}<br /> <br /> ==== Limit the Size ====<br /> <br /> Try to limit the number of items in a menu to about a dozen. Submenus should have about a half dozen items at most. The utility of a menu decreases as its length increases.<br /> <br /> ==== On the Menu Bar ====<br /> <br /> When it comes to ordering the menus themselves on the menu bar, remember that the user can most easily access the outside menus. Put menus that are used less towards the middle.<br /> <br /> Follow the order given in [[#Standard_Menus|Standard Menus]] for any standard menus.<br /> <br /> === Ghosting ===<br /> <br /> Whenever a menu or menu item is inappropriate or unavailable for selection, it should be ghosted. Never allow the user to select something that does nothing in response.<br /> <br /> [[File:SG6-3.png|frame|center|A menu with ghosted items]]<br /> <br /> === Labeling ===<br /> <br /> Menu and menu items labels should be terse - preferably one to three words. Use the capitalization rules that apply in the language at hand.<br /> <br /> Menu items that represent an action should reflect that action in the item's label. For instance, &quot;Print&quot; is better than &quot;Printer&quot;. Try, also, to keep the terminology user-friendly and non-technical.<br /> <br /> Don't repeat a menu's title on every item's label. For instance, in a Macros menu use the label &quot;Load...&quot; rather than &quot;Load Macros...&quot;. Repeat the menu title, however, if user comprehension will suffer.<br /> <br /> === Toggle Items ===<br /> <br /> As discussed earlier, indenting toggle items lets the user know that they represent a choice. Another cue you can give to the user is to end the label in a question mark. In some Settings menus, for example, an item labelled &quot;Create Icons?&quot; is preceded by a check mark (or placeholder) and ended with a question mark. Both are cues to the user that this is a toggle item.<br /> <br /> === Ellipsis ===<br /> <br /> When a menu item brings up a window or requester, an ellipsis (three dots) should be appended to the menu item's label.<br /> <br /> [[File:SG6-4.png|frame|center|A menu item that brings up a requester or another window]]<br /> <br /> ==== Submenus ====<br /> <br /> When an item has a submenu, the symbol » should be placed on the far right side of the menu item. These should be flush right (see the next section).<br /> <br /> [[File:SG6-5.png|frame|center|A menu item that brings up a submenu]]<br /> <br /> ==== Lining up on the Right ====<br /> <br /> If you have any symbols or text that you want to line up on the right side of the menu item, make sure it is flush right (ie. right justified) or it won't line up properly when used with a proportional font.<br /> <br /> == Standard Menus ==<br /> <br /> Some menus and menu items appear so often in applications that their use and design can be standardized. Using standard menus will enhance the feeling of consistency across applications and help the user feel more comfortable with your program.<br /> <br /> Listed below are standard menus that your application should use whenever appropriate. The order in which the menus and their items are presented is an important part of the specification.<br /> <br /> === Project Menu ===<br /> <br /> Applications that create, edit and save data should have a Project menu. The Project menu should be the first menu on the left side of the menu bar and should contain as many of these items as appropriate - in the order shown.<br /> <br /> [[File:SG6-6.png|frame|center|The Project menu]]<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Label<br /> ! Shortcut<br /> ! Description<br /> |-<br /> | New<br /> | Right-Amiga-N<br /> | New should open a blank, untitled project.<br /> |-<br /> | Open...<br /> | Right-Amiga-O<br /> | Open... should bring up a file requester. If an unsaved project currently exists, the modified project requester (Save project?) should be brought up when Open... is selected - unless your application supports multiple projects.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Save<br /> | Right-Amiga-S<br /> | If the project had been saved previously, Save should copy the current version over the old version. If the project has never been saved, Save should bring up a file requester.<br /> |-<br /> | Save As...<br /> | Right-Amiga-A<br /> | Save As... should bring up a file requester to prompt the user for a name, and then save the file.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Print<br /> | Right-Amiga-P<br /> | Print should send the project to the printer using the current settings. The current settings may either be those found in Workbench's Prefs directory or individual program settings previously saved by the user.<br /> |-<br /> | Print As...<br /> |<br /> | Print As... brings up a requester that can be used to set your progam's print options.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Hide<br /> |<br /> | This menu item is for programs that can handle multiple projects at one time. Hide should remove a project's window from the screen. If the user selects Quit Program before returning to a hidden project that has unsaved changes, the modified project requester should come up asking if he wants to save the project first. A separate modified project requester should be presented for each unsaved, hidden project.<br /> |-<br /> | Reveal...<br /> |<br /> | Reveal... is for programs that can handle multiple projects at one time. Reveal... should bring up a requester with a scrolling list gadget containing all opened projects whether hidden or not. Although its true purpose is to return hidden projects to the screen, it is more user-friendly to allow the user to view all open projects from this item. The chosen project's window should open an jump in fron of any other windows. If no projects are hidden, this item should be ghosted.<br /> |-<br /> | Close<br /> |<br /> | This menu item is for programs that can handle multiple projects at one time. Close should remove the current project's window from the screen. If there are unsaved changes, a modified project requester should come up first.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | About...<br /> |<br /> | About... should bring up a window with information about your program and the current project. What information is included is up to you, but it should include at least the version number of your software. Some suggested information to provide: ARexx port name, project size and tool name.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Exit [Level]<br /> |<br /> | Exit [Level] should allow the user to leave the current level, such as the slide editing level in an electronic slide show program, and return to the next highest level. The word [Level] on the label can be replaced with the name of the current level.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Quit [Program]...<br /> | Right-Amiga-Q<br /> | Quit [Program]... should exit the entire program. The word [Program] can be replaced by the name of your application. A separate modified project requester should come up for any open or hidden projects with unsaved changes. Quit [Program]... should not be modal; i.e. it should be available at any level.<br /> |}<br /> <br /> Note:<br /> * Print and Print As... are optional items for applications that support printing.<br /> * Hide, Reveal... and Close... are optional items for multi-project applications. Exit is an option for multi-level programs.<br /> <br /> === Edit Menu ===<br /> <br /> Any application that can perform editing functions should have the Edit menu in the second position. The following items represent the suggested list with Redo as an optional item. A basic rule is that any menu item that operates on blocks of data should go in the Edit menu.<br /> <br /> You should support the Clipboard as your buffer for cut-and-paste operations. This will allow the user to share the clips between applications. For more information on the Clipboard see [[UI Style Guide Data Sharing]].<br /> <br /> [[File:SG6-7.png|frame|center|The Edit menu]]<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Label<br /> ! Shortcut<br /> ! Description<br /> |-<br /> | Cut<br /> | Right-Amiga-X<br /> | Cut should remove the highlighted text or current object and place it in the buffer.<br /> |-<br /> | Copy<br /> | Right-Amiga-C<br /> | Copy should retain the highlighted text or current object in the project while also placing a duplicate of it in the buffer. The text should then be unhighlighted to show that the copy took place.<br /> |-<br /> | Paste<br /> | Right-Amiga-V<br /> | Paste should take whatever is in the buffer and place it at the insertion point in the project window.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Erase<br /> |<br /> | Erase should remove the highlighted text or current object.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Undo<br /> | Right-Amiga-Z<br /> | Undo should restore the project to the state it was in before the user's last action. If, for instance, the user imported a text clip by mistake in a word processor, Undo should automatically take it out. Some programs support multiple steps of Undo. Using the example in the previous paragraph, say the user had deleted a paragraph before inserting the text clip. Selecting Undo twice should take out the mistaken text clip and return his original paragraph.<br /> |-<br /> | Redo<br /> |<br /> | Redo is an option for programs supporting multiple steps of Undo. In a program that doesn't support multiple steps of Undo, hitting Undo twice would take the user back to where he was before he selected Undo - it's cyclical. He imports a text clip, selects Undo and the text clip is gone. Select Undo again and the text clip is back again. In this program, Undo undoes Undo. But in applications with multiple steps of Undo, the user can undo anything except Undo. He just keeps going back another step instead of going through cyclical actions. So Redo is there to undo any mistaken Undos.<br /> |}<br /> <br /> === Macros Menu ===<br /> <br /> If your application supports macros, it should use ARexx, the Amiga's built-in scripting language, rather than its own internal scripting language. For more information see [[UI Style Guide ARexx]].<br /> <br /> The Macros menu should allow the user to create and assign macros. Optionally, you can include the ability to load and save macros as well.<br /> <br /> [[File:SG6-8.png|frame|center|The Macros menu]]<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Label<br /> ! Shortcut<br /> ! Description<br /> |-<br /> | Start Learning<br /> |<br /> | When Start Learning is selected, all subsequent user actions should be written in ARexx script form to an internal buffer - until Stop Learning is selected. Feedback to the user is essential, so it would be a good idea to let the user know that the Start Learning command was accepted. The indicated can be subtle but should be noticeable if the user wants to look for it. One idea is to put a small glyph, letter or word in the project's title bar.<br /> |-<br /> | Stop Learning<br /> |<br /> | When Stop Learning is selected, your program should stop writing user actions to the internal buffer created by Start Learning. Any visual feedback that originated with Start Learning should go away.<br /> |-<br /> | Assign Macro...<br /> |<br /> | Assign Macro... should bring up a requester through which the user can assign the script created by the previous two commands. Macros can be implemented [in] a number of ways. One longtime favorite is to assign the macro to the function keys, the ten keys across the top of the<br /> keyboard, or to some other keyboard combination. Or the Macro can be assigned to the User menu (see section later in this chapter). Or the macro can be given a name and saved to disk; from there it can be implemented via an ARexx command console. Some programs may choose to support one, some or all of these methods. Your Assign Macro... requester should reflect all the possible choices your program supports.<br /> |-<br /> | Load...<br /> |<br /> | Load... should bring up a requester listing the macros that can be loaded into memory. This, along with Save..., forms an optional set of items.<br /> |-<br /> | Save...<br /> |<br /> | The macros design as discussed above assumes that key- and menu-activated macros will be assigned and used on a per-session basis. The next time the user returns to your program, the macros he had assigned to keys and menus will no longer exist. Save... should allow the user to name a macro and save it to disk. At another session, the macro could be loaded and then assigned to a key or menu. This, along with Load..., forms an optional set of items. Otherwise, if the user wishes to save the macros and their assigned positions, he should do it through the Save Settings item found in the Settings menu.<br /> |}<br /> <br /> === Settings Menu ===<br /> <br /> Provide a Settings menu if you allow the user to change and save aspects of your program's environment. This menu should be located in the second position from the right.<br /> <br /> Consult [[UI Style Guide Preferences]] for more information about preferences in general.<br /> <br /> Two items should be accessible through this menu (if your program supports them): a toggle item asking if icons should be created and an overall Save Settings item. They may be directly in the menu or in a requester brought up by a menu item.<br /> <br /> [[File:SG6-9.png|frame|center|The Settings menu]]<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Label<br /> ! Shortcut<br /> ! Description<br /> |-<br /> | [options]<br /> |<br /> | Listed in this menu should be any specific options you wish to handle through a menu item, such as what screen resolution to operate in or baud rate for a terminal package. Or you could have an option labelled Set Settings... that would bring up a control panel which could be used to set all the program's options. [options] can be toggle items, or they can bring up submenus or requesters - it all depends on your<br /> application's needs. Whatever these options are, they would change the user's preferences for that session only. To save them as defaults the user would have to use one of the Save Settings items. Note: the word [option] shown here is only a placeholder to be replaced with a descriptive menu label.<br /> |-<br /> | Create Icons?<br /> |<br /> | Create Icons? is a toggle item that should allow the user to choose whether a .info file will be created with each project. A .info file contains a graphic image for the file's icon. User's who don't use the GUI as a filing system may find .info files to be extraneous and a waste of storage space. Your program should provide this menu item.<br /> |-<br /> | colspan=&quot;3&quot; | ---<br /> |-<br /> | Load Settings...<br /> |<br /> | Load Settings... is an optional item that should allow the user to change from his current settings to a new set of previously-saved settings. Load Settings... should be included if you think your users will want or need multiple settings files.<br /> |-<br /> | Save Settings<br /> |<br /> | Save Settings should save the user's settings as your application's default preferences. Each time your application is opened thereafter, it should use these user-specified settings. By default, Save Settings should save the new settings in the current settings file. See [[UI Style Guide Preferences]] for more information on saving settings files.<br /> |-<br /> | Save Settings As...<br /> |<br /> | Save Settings As... is an optional item that brings up a file requester. Save Settings As... will be used when the user wishes to save multiple settings files. There are a couple of reasons why this option may be helpful to users. A user may save different settings for different types of projects; e.g. settings.vid for projects being output to video and settings.prt for projects being output to a printer. Also, if your application is being used on a network, users may wish to save personal settings.<br /> |}<br /> <br /> === User Menu ===<br /> <br /> The User menu is a variable-length menu that contains user-defined macros and any other optional features a user may want to add as a menu item. This should be located at the far right.<br /> <br /> [[File:SG6-10.png|frame|center|A sample User menu]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Gadgets&diff=5380 UI Style Guide Gadgets 2013-04-26T09:27:37Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Gadgets ==<br /> <br /> Gadgets are graphic symbols that represent a specific action or control. They are usually contained in a window or requester.<br /> <br /> There are two basic types of gadgets on the Amiga: system gadgets and application gadgets<br /> <br /> == System Gadgets ==<br /> <br /> System gadgets control aspects of the GUI environment like window size and screen positioning. System gadgets can be thought of basically as environment maintenance tools. This screen's in the way - click it to the back. This window needs to be bigger - click and drag on the<br /> sizing gadget.<br /> <br /> [[File:SG5-1.png|frame|center|System gadgets]]<br /> <br /> System gadgets are highly standardized in when and how they are used. Intuition will handle most of their functions. The table that follows lists the gadgets and how they operate. This is mainly for quick reference. Since the gadgets are tied closely to them, consult [[UI Style Guide Screens]] and [[UI Style Guide Windows and Requesters]] for more detailed information.<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Name<br /> ! Description<br /> |-<br /> | Close [[File:SG-CloseGad.png]]<br /> | Located on a window's upper left corner, the close gadget removes the window from the screen and quits whatever program or file the window was running<br /> |-<br /> | Depth [[File:SG-DepthGad.png]]<br /> | Located on a window or screen's upper right corner, the depth gadget adjusts which window or screen is in front of all the others. For example, if a window is in front of all the other windows on that screen, clicking once on the depth gadget will put it behind all the other windows. Clicking again on that window's depth gadget will bring that window in front of all the other windows.<br /> |-<br /> | Sizing [[File:SG-SizeGad.png]]<br /> | Located on a window's lower right corner, the sizing gadget allows the user to resize the window by clicking on the gadget and dragging. Not all windows have or need a sizing gadget although its use and support is strongly encouraged.<br /> |-<br /> | Zoom [[File:SG-ZoomGad.png]]<br /> | Located on windows next to the depth gadget, the zoom gadget allows users to quickly reduce a window to its minimum size to temporarily get it out of the way. When it is needed again, another click on the zoom gadget will bring the window back to the size it had been. Not all windows have or need a zoom gadget although its use and support is strongly encouraged.<br /> |}<br /> <br /> {{Note|text=This is only a reference listing of system gadgets. For more information about these see [[UI Style Guide Screens]] and [[UI Style Guide Windows and Requesters]].}}<br /> <br /> == Application Gadgets ==<br /> <br /> Application gadgets represent choices the user can make and thus proceed with his task. As a developer, you will have to make more decisions about how and when to use application gadgets than you will with system gadgets.<br /> <br /> === Design ===<br /> <br /> Like other aspects of the GUI, gadget layout and size should be based on a 640x200 screen resolution with the Topaz 8 font. See @{&quot;Resolutions&quot; link Basi2} for more information.<br /> <br /> At run-time, your application should check the size of the font and the screen resolution to determine if the gadgets used will fit in the window with the user's preferred settings. If not, revert to the Topaz 8 font.<br /> <br /> === Labeling ===<br /> <br /> Put a label on gadgets whose purpose is not immediately obvious. Labels should be terse without being obscure - preferably one to three words. Ponder on these a while and test them if possible before incorporating them into your program. Capitalization should follow good grammatical sense for the language being used. If in doubt, refer to a writing or journalism style guide for your language. The [[UI_Style_Guide_Glossary|Glossary]] in the back of this manual gives the English capitalization of many Amiga terms.<br /> <br /> === Ghosting ===<br /> <br /> As with other elements of the GUI, a gadget that is temporarily unavailable for selection should be obviously disabled. Don't allow the user to select something that does nothing in response. Follow the ghosting standard shown in [[UI_Style_Guide_Basics#Ghosting|Ghosting]].<br /> <br /> Fig 5.2: A ghosted text gadget.<br /> <br /> === Keyboard Equivalents ===<br /> <br /> As a convenience, a key may be bound to each gadget so that its actions can be controlled from the keyboard as well as through the mouse.<br /> <br /> Use a logical letter from the gadget label as the key control for the gadget. For instance, a gadget labelled &quot;Spacing&quot; could use &quot;S&quot; as its keyboard control, whereas a gadget labelled &quot;Get Fonts&quot; may use &quot;F&quot;. The letter you use should be underlined on the gadget.<br /> <br /> [[File:SG5-3.png|frame|center|Gadgets with keyboard equivalents]]<br /> <br /> ==== Restrictions ====<br /> <br /> Never provide keyboard equivalents for asynchronous requesters. An asynchronous requester is one that occurs due to an action of the application rather than an action of the user. In a hard disk backup program, for example, asynchronous requesters appear asking for a new floppy disk when the current disk is full.<br /> <br /> Because asynchronous requesters can appear when the user isn't expecting them, keyboard equivalents can lead to the user choosing an unwanted action. Backup programs, to use that example again, don't require a lot of user attention, so there's a good chance the user will go to another application. If the backup program's requester comes up asking for a new floppy disk while the user is typing in a word processor, unwanted actions could easily occur.<br /> <br /> An asynchronous requester will have two default however, that are built into the system. Left-Amiga-V and Left-Amiga-B will, respectively, activate the extreme left and extreme right gadgets at the bottom of a requester.<br /> <br /> Another restriction on keyboard equivalents for gadgets: don't use the Return key to activate the OK gadget. This applies to both synchronous and asynchronous requesters.<br /> <br /> {{Note|text=Left-Amiga-V and Left-Amiga-B will, respectively, activate the extreme left and extreme right gadgets in a requester.}}<br /> <br /> ==== Visual Feedback ====<br /> <br /> Visual feedback should be given when a keyboard equivalent is used. This feedback should be the same as the feedback given when the mouse is used. The specific feedback for each gadget is listed with the gadget description later in this chapter.<br /> <br /> === Grouping ===<br /> <br /> Gadgets should be grouped logically on your requesters or support windows. Some general premises about grouping follow. Use these premises and your own common sense when grouping elements.<br /> <br /> The general rule is that gadgets should be grouped according to function. On a print requester, for example, gadgets controlling text would go in one area, while those affecting graphics would go in another.<br /> <br /> Another rule is to place commonly used functions within easy reach, especially on a crowded control panel.<br /> <br /> Don't put dangerous controls, such as Delete or Format, near commonly used controls.<br /> <br /> Think of the user's work flow when you order the gadgets. Try to emulate a logical and intuitive order. If there is an order of events, start in the upper left and work to the lower right (depending on local language, of course).<br /> <br /> [[File:SG5-4.png|frame|center|A requester that has grouped gadgets according to function]]<br /> <br /> == Application Gadgets by Type ==<br /> <br /> Each of the Amiga's application gadgets has a particular use and limitation. This section gives an overview of each type. Each type of gadget can be recognized by its appearance. Do not make gadgets that look like these standard gadgets but act differently.<br /> <br /> === Action Gadgets ===<br /> <br /> Action gadgets (often referred to as buttons) are graphic rectangles usually containing a few words. Clicking on an action gadget should perform the activity named on the action gadget.<br /> <br /> [[File:SG5-5.png|frame|center|An action gadget]]<br /> <br /> ==== Labels ====<br /> <br /> Make the label descriptive. &quot;OK&quot; and &quot;Cancel&quot; may not always be the best choice. Use friendly, less technical terms (i.e. &quot;Stop&quot;, rather than &quot;Abort&quot;).<br /> <br /> The user shouldn't have to read an entire body of text before deciding which action gadget to press. Instead, a carefully selected label should tell the user what the gadget does in one to three words.<br /> <br /> ==== Triggering the Action ====<br /> <br /> The action should be triggered on the release of the mouse's select button - not the down press. This gives the user a chance to &quot;roll off&quot; the gadget before activating it.<br /> <br /> ==== More Choices ====<br /> <br /> When an action gadget brings up another window or requester, the label should end in an ellipsis (three periods).<br /> <br /> [[File:SG5-6.png|frame|center|A gadget which brings up another window or requester should have a label ending in an ellipsis]]<br /> <br /> ==== Use of Cancel ====<br /> <br /> An action gadget labelled &quot;Cancel&quot; should only be used if it actually allows the user to back out, leaving the application exactly as it was before the requester appeared. For example, &quot;Cancel&quot; is not appropriate for a gadget on a requester that is displayed while printing is occurring. &quot;Stop&quot; would be a better label.<br /> <br /> ==== Gadget Placement ====<br /> <br /> The positive choice, or continuation of the requested action, should be displayed on the lower left side of the requester/window, while the negative choice or discontinuation of the action should be displayed on the lower right.<br /> <br /> [[File:SG5-7.png|frame|center|The positive choice should be placed in the lower left, while the negative choice should go in the lower right corner]]<br /> <br /> === Check Box ===<br /> <br /> A check box is a small square that toggles from being blank to having a check mark in it. Check boxes are appropriate whenever you need to present an option that may be turned off or on.<br /> <br /> [[File:SG5-8.png|frame|center|Check box gadgets]]<br /> <br /> ==== Keystroke Activation ====<br /> <br /> If activated through a keystroke, the state of the check mark should toggle within the box.<br /> <br /> === Scroll Gadget ===<br /> <br /> Scroll gadgets are used to adjust the position of a view. By themselves, scroll gadgets are used to adjust a large display area within a window's view, such as a text file that won't all fit within one window's view. Scroll gadgets are also a component of a scrolling list gadget (see next section).<br /> <br /> A scroll gadget is comprised of the scroll bar, scroll box and scroll arrows.<br /> <br /> [[File:SG5-9.png|frame|center|A scroll gadget]]<br /> <br /> Any window that displays only a portion of the file's entire contents should have scroll gadgets. If your application allows the editing of files that are wider than the window, it would be good to have a horizontal scroll gadget as well.<br /> <br /> ==== Moving Through in Steps ====<br /> <br /> The display area should be updated immediately as the scroll bar is dragged.<br /> <br /> When the user clicks in the scroll box (the rectangle containing the scroll bar) but not directly on the scroll bar, the display should move in viewfuls. For instance, if the user clicks in the scroll box above the scroll bar, the user's view of the list should move up so that the line which was first is now at the bottom of the view. The inverse applies where the user clicks below the scroll bar. This lets the user walk through the list in steps without missing anything on the list. Leaving one line from the previous view assures the user that he hasn't missed anything.<br /> <br /> === Scrolling List ===<br /> <br /> A scrolling list features a view box showing textual names of files or objects accompanied by a scroll gadget to the right of the view box. If the list is longer than can be accommodated by the view box, the user can move through the list using the scroll gadget.<br /> <br /> A scrolling list should be used whenever you need to present the user with a variable list of objects. Probably the most common example is found in the requester presented when a user chooses to open or save a file.<br /> <br /> [[File:SG5-10.png|frame|center|A scrolling list]]<br /> <br /> ==== Custom Scrolling Gadgets ====<br /> <br /> On the Amiga, only one of the items in the system-supplied scrolling list may be selected at a time. Using custom code, it is possible to create a scrolling list gadget that supports multiple selection. If implemented, that gadget should still follow the multiple selection guidelines for text covered in [[User_Interface_Style_Guide#Four_Ways_to_Highlight_Text|Highlighting Text]].<br /> <br /> ==== Keystroke Activation ====<br /> <br /> The scrolling list gadget reacts differently to shifted and unshifted keystrokes. An unshifted keystroke should cause the list to scroll forwards through the choices. A shifted keystroke should cause the list to scroll backwards through the choices.<br /> <br /> Note: Using the Shift key in tandem with another key should never be the only way to do things since it is usually a choice that is not immediately apparent to the user. In the above case, it is backed up by the mouse.<br /> <br /> === Radio Buttons ===<br /> <br /> Radio buttons are a group of mutually exclusive gadgets - one and no more than one is always selected. Use this when the user must choose one option from a short list of possibilities.<br /> <br /> Fig 5.11: Radio buttons.<br /> <br /> Radio buttons are similar to cycle gadgets in functionality. Each has its benefits and drawbacks. See later for a discussion on this subject.<br /> <br /> ==== Keystroke Activation ====<br /> <br /> Radio buttons react differently to shifted and unshifted keystrokes. An unshifted keystroke should cause the highlighted button to cycle in one direction. A shifted keystroke should cause the highlighted button to cycle in the opposite direction.<br /> <br /> === Cycle Gadgets ===<br /> <br /> Like radio buttons, cycle gadgets allow the user to choose one option from several but only the selected option is visible.<br /> <br /> [[File:SG5-12.png|frame|center|A cycle gadget]]<br /> <br /> Cycle gadgets should be used to set attributes, not trigger actions. Never use a cycle gadget for an on/off choice - use a check box instead.<br /> <br /> ==== Keystroke Activation ====<br /> <br /> The cycle gadget reacts differently to shifted and unshifted keystrokes. An unshifted keystroke should cause the gadget to cycle forwards through the choices. A shifted keystroke should cause the gadget to cycle backwards through the choices.<br /> <br /> ==== Cycle Gadgets vs. Radio Buttons vs. Scrolling Lists ====<br /> <br /> If the user needs to choose one option from a choice of three or more, cycle gadgets, radio buttons and scrolling lists are viable options. Which one you choose to implement depends on your application, your preference and common sense. Consider the following points:<br /> <br /> * A cycle gadget presents a cleaner interface. The selected choice is quickly evident and the unwanted choices are hidden away. Theoretically it's also able to handle a larger number of choices, although users will probably have trouble remembering a really large number of choices. Large numbers of choices may work well in a cycle gadget if they are ordered choices, such as the months of the year.<br /> <br /> * In general though, options with more than about a dozen choices should use a scrolling list.<br /> <br /> * Radio buttons present a clear choice to the user - the possible choices are always visible. Radio buttons work well with a small number of options. They are probably also slightly more intuitive than cycle gadgets.<br /> <br /> === Color Selection Gadget ===<br /> <br /> The color selection gadget (sometimes referred to as the palette gadget) allows the user to pick a color from a set palette.<br /> <br /> [[File:SG5-13.png|frame|center|A color selection gadget]]<br /> <br /> ==== Keystroke Activation ====<br /> <br /> The color selection gadget reacts differently to shifted and unshifted keystrokes. An unshifted keystroke should cause the gadget to cycle forwards through the choices. A shifted keystroke should cause the gadget to cycle backwards through the choices.<br /> <br /> === Slider ===<br /> <br /> Sliders are used to choose a value in a given range. Usually, this value represents a level or an intensity such as volume or color.<br /> <br /> Sliders look similar to scrollers without arrows, but when the user clicks in the slider box above or below the slider bar, the slider value should change in single increments rather than entire views.<br /> <br /> Fig 5.14: Slider gadgets.<br /> <br /> ==== Keystroke Activation ====<br /> <br /> The slider reacts differently to shifted and unshifted keystrokes. An unshifted keystroke should cause the horizontal bar in the slider to move up. A shifted keystroke should cause the horizontal bar to move down. When the horizontal bar reaches either the top or bottom it should change directions automatically. Don't tie the direction solely to a shifted keystroke.<br /> <br /> === Text Gadget ===<br /> <br /> Text gadgets (sometimes referred to as string gadgets) are rectangular boxes used to accept keyboard input for alphanumeric fields.<br /> <br /> [[File:SG5-15.png|frame|center|A text gadget]]<br /> <br /> ==== Activating Text Gadgets ====<br /> <br /> When a window or requester appears containing a text gadget, have the gadget immediately activated and ready for keyboard input if:<br /> <br /> * there are no gadgets on the requester that can be activated by the keyboard;<br /> <br /> * the window or requester appeared in direct response to user activity (i.e. it is not asynchronous).<br /> <br /> If the window or requester has other gadgets that can be activated from the keyboard, the user should also be able to activate the text gadget from the keyboard. In this case, it should not be activated by default.<br /> <br /> The user should not have to select the text gadget with the mouse before typing an entry (if he chose an action that directly called that requester up). If may seem like a small delay to go from keyboard to mouse to keyboard, but all breaks in the flow of your program should be minimized.<br /> <br /> ==== Ordering of Text Gadgets ====<br /> <br /> When a window contains a series of text gadgets, activate the gadget in the far upper left region of the window first (depending on the scanning direction of the local language).<br /> <br /> ==== Moving Through Fields ====<br /> <br /> Let the user move through fields with the Tab key. When the user presses Tab, activate the next text or number gadget in the series. When the user reaches the end of the series and presses Tab, the cursor should return to the first entry gadget. This function is supported by Intuition.<br /> <br /> Shift-Tab should activate the previous gadget in the series.<br /> <br /> {{Note|text=Let the user move through fields with the Tab key.}}<br /> <br /> === Display Box ===<br /> <br /> A display box is a rectangle that shows non-editable textual or numeric information.<br /> <br /> Display boxes look similar to text gadgets, but since they are read-only, they appear recessed.<br /> <br /> [[File:SG5-16.png|frame|center|A display gadget]]<br /> <br /> === Icon Drop Box ===<br /> <br /> Icon drop boxes are used for manipulation of icons. If a user drags an icon to an icon drop box, the image of the icon is copied into the box. Depending on the function assigned to that icon drop box, the icon imagery may then be redrawn or used to represent other files.<br /> <br /> Fig 5.17: An icon drop box.<br /> <br /> === Custom Gadgets ===<br /> <br /> Sometimes a system-supported control does not provide the exact function you need. In that case, you may choose to create a custom gadget.<br /> <br /> If a custom gadget is an extension to an existing gadget type, then you should try to emulate the features of the existing gadget.<br /> <br /> For example, a custom multi-line text gadget should support the same keyboard functions that the standard single-line text gadget does. Right-Amiga-X should erase the text and Right-Amiga-Q should undo changes. Refer to [[#Keyboard|Keyboard]] for more information on the keyboard conventions used in text gadgets.<br /> <br /> All custom gadgets should support a minimum of three images: normal, selected and disabled. The gadget images must be usable on a monochrome screen.<br /> <br /> === Custom Icons ===<br /> <br /> Custom icons are application-specific gadgets that can be moved and manipulated. The note object in a music package is one example.<br /> <br /> Take care when using custom icons alongside action gadgets that have graphical labels. Since it is not clear which symbols trigger an action when clicked and which symbols move when clicked, users could be triggering unwanted actions. If you use both in the same application, be sure the user can tell at a glance which is which. Grouping them spatially is one possible solution. Color cues may provide a solution. Boxing them may be a third solution.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Screens&diff=5379 UI Style Guide Screens 2013-04-26T09:27:33Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Screens ==<br /> <br /> By organizing raw data into neat and friendly metaphors, GUIs make using a computer more intuitive and comprehensible. Still, sifting through the many possibilities in an effort to get to a desired task can often lead the user to wonder: &quot;Where am I?&quot; and &quot;What should I do next?&quot; The fact that the Amiga multitasks - that is, it allows the user to run a number of applications at the same time - only increases the need for strong context cues.<br /> <br /> When your application is run, it indicates the new context by either opening up in a window or on its own screen. Thus, screens and windows provide the main cues that tell the user where they are at any given moment.<br /> <br /> == Defining Screens ==<br /> <br /> Screens are unique to the Amiga. Other platforms have a single environment filling the monitor view or perhaps extending beyond that in height and width. On the Amiga, a user can have multiple screens, each an environment unto itself with its own palette, resolution and fonts - running at the same time.<br /> <br /> Typically, screens are at least as wide as the monitor display and have a single title bar at the top of the screen which is shared by all the applications that operate within that screen. It is possible, however, to have screens that are larger than the display area (known as virtual screens), or to have a screen that is not as tall as the monitor display. Applications will sometimes use the shorter screens as control panels in a different resolution than the display area. Workbench is the default screen a user is presented with upon booting the machine.<br /> <br /> Screens cannot be resized. New screens usually appear in front of existing screens. The user can access screens in the back by dragging the front screen down or flipping through the screens by using the screen depth gadget or keyboard combinations.<br /> <br /> Fig 3.1: The Workbench Screen.<br /> <br /> === Types of Screens ===<br /> <br /> Your application can open on one of three types of screens: the Workbench screen, a public custom screen that your application shares with other programs, or your own private custom screen.<br /> <br /> When your application uses the Workbench screen, it opens a window on the Workbench, using the palette, resolution and fonts that are defined in the Workbench Prefs.<br /> <br /> [[File:UG3-2.png|frame|center|A text editor open on the Workbench screen]]<br /> <br /> The Workbench screen is a public screen - that is, it can be used by any application. If your program needs a different resolution or palette than the user has chosen for his Workbench preferences, it should open on a public custom screen unless its requirements are restrictive enough to warrant a private custom screen.<br /> <br /> By keeping your custom screen public, you allow users to access other, perhaps supportive, applications without having to flip your application to the rear. Or, if the user is already running an application on a public screen with the proper palette and resolution requirements, he can open your program on that screen.<br /> <br /> [[File:UG3-3.png|frame|center|A text editor opened on an interlaced public custom screen]]<br /> <br /> A private custom screen is one that you set up to your specifications and which only your application may use. Private custom screens should be used only when your application has unusual rendering or resolution requirements, or when you need to be able to operate on the whole screen directly. An example of this would be an animation program that needs to switch viewports rapidly in order to get smooth motion.<br /> <br /> {{Note|title=Technical note|text=If your application opens a custom screen, make sure you redirect requesters to the custom screen - this applies to relevant DOS requesters as well as your application's requesters.}}<br /> <br /> == Respect User Choice ==<br /> <br /> Let the user decide, if possible, whether to open your application on Workbench or on another screen. If he decides to open it on a custom public screen, let him choose whether it will be a new screen or one that has already been created by a different application.<br /> <br /> Likewise, respect choices the user has already made. All custom screens, whether public or private, should default to the basic parameters<br /> established in Workbench's Preferences, unless your application has special requirements.<br /> <br /> If your application provides overscan capabilities you should respect the settings that the user has established in the Overscan editor found in Workbench's Prefs directory.<br /> <br /> == Screen Design ==<br /> <br /> Screens should have a depth gadget. If you have room, you should try not to obscure the screen depth gadget from view by opening windows that cover up the screen's depth gadget.<br /> <br /> The screen your application opens on should open in front of any other screens that are open.<br /> <br /> === Auto-scrolling ===<br /> <br /> Applications that open screens larger than the display area should provide the ability to auto-scroll. Moving the mouse to any of the display bounds should automatically scroll the screen to show more information in that area.<br /> <br /> {{Note|text=Screens that open larger than the display area should offer an auto-scrolling function.}}<br /> <br /> === Naming Your Public Screens ===<br /> <br /> Public screens are identified by their name. To name a public screen, use your application's basename (see [[User_Interface_Style_Guide#Defining_Your_Basename|Defining Your Basename]]) followed by an invocation count. The name should be in upper-case.<br /> <br /> For instance, a terminal package with the basename Axelterm that opens its own public screen should name the screen AXELTERM.1. On a system with a multi-serial port card, the user may decide to run a second copy of the package. That second public screen should be named AXELTERM.2.<br /> <br /> Some applications may use two screens during the same invocation of the program. For example, a paint program named VanGogh (with the basename VGOGH) may use one screen for the &quot;canvas&quot; and another screen with a different resolution for the control panel. In that case, the screens could be named VGOGHPAD.1 and VGOGHPANEL.1.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Basics&diff=5378 UI Style Guide Basics 2013-04-26T09:25:30Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Some Basics ==<br /> <br /> The Amiga is a true multitasking system with three built-in interfaces:<br /> <br /> * a graphic user interface (GUI);<br /> <br /> * a command line interface [CLI] (the Shell);<br /> <br /> * a scripting language that can handle inter-process communication (ARexx).<br /> <br /> Of these three interfaces, the GUI dominates. By default, the Amiga presents the user with a graphic interface, Workbench, upon startup. Likewise, even a simple Amiga application will generally present a graphic interface of some sort; text editors, for instance, usually include mouse-driven gadgets and menus.<br /> <br /> In addition to the GUI, users can control the Amiga through the Shell. The Shell is a text-based interface that preserves the best features of the &quot;old way&quot; of operating computers - by typing in commands. The Shell trades the GUI's ease of use for a finer level of control and greater power.<br /> <br /> {{Note|text=The GUI is the Amiga's default interface.}}<br /> <br /> With Release 2 of the operating system came a third way of interfacing with the Amiga: inter-process communication (IPC) via ARexx. Simply put, ARexx is a scripting language, but it also acts as a central hub which applications can use to send data and commands to each other. ARexx allows software created by different companies to interface, letting the user create custom applications by integrating off-the-shelf software products. For example, with ARexx it is possible to set up a telecommunications package to dial an electronic bulletin board, download financial data, and then pass that data to a separate spreadsheet package for statistical analysis - without user intrevention other than the original scripting. Arexx is based on REXX, an IPC language used in various forms across many platforms.<br /> <br /> Many users consider the choice of interface to be one of the best features of the Amiga. Not only does it offer the user the freedom to choose his favorite means of interacting with the Amiga and with your program, it's also an effective way to provide the right tool for the job and user's level of expertise. For these reasons, your application should support all three interfaces in the manner described in this manual.<br /> <br /> {{Note|text=Support all three of the Amiga's interfaces.}}<br /> <br /> == Basics of the Operating System ==<br /> <br /> The following are basic concepts to keep in mind throughout your design work on the Amiga:<br /> <br /> === Design for the Novice ===<br /> <br /> Designing for the lowest common denominator does not mean your application is doomed to mediocrity. In most cases it just means pondering a little longer on how to solve a problem or present a procedure so that almost anyone can grasp it easily. Even power users appreciate it when you give them simple solutions for such things as hard disk installation or navigation through procedural steps. Conserve your user's energy for creative tasks.<br /> <br /> {{Note|text=Conserve the user's energy for creative tasks.}}<br /> <br /> === Let the System Work for You ===<br /> <br /> In general, if the system will do something for you, let it. For example, GadTools provides pre-programmed gadgets for use in your application. If one of these gadgets suits your needs, use it instead of coding your own. Two other examples of using this built-in support are:<br /> <br /> * using Intuition to provide many functions of your application's GUI;<br /> <br /> * using DOS ReadArgs() to provide support for argument passing.<br /> <br /> {{Note|text=If the system will do something for you... let it.}}<br /> <br /> === The Amiga's Resources ===<br /> <br /> Remember that the Amiga is a multitasking system. Any time you allocate a part of the system for your use, you prevent other tasks from using it.<br /> <br /> Try your best to economize your use of shared resources. If possible, only obtain a resource when needed and free it as soon as you are done. This applies to just about every computing activity your application can perform. There are only limited amounts of RAM, serial ports, printers and disk drives for all applications to share.<br /> <br /> Your application should provide controls that allow the user to temporarily suspend usage of any non-shareable resource it may use, such as the serial or parallel device. For example, a MIDI application should allow the user to suspend serial port usage so he can switch to a terminal package and download a new sample without having to exit the MIDI application.<br /> <br /> === Speech and Sound ===<br /> <br /> Speech and sound can be excellent ways to provide feedback to the user, especially if the user is visually impaired or if your application includes lengthy processes during which the user might walk away from the system.<br /> <br /> However, a significant number of users may be hearing impaired, so don't use sound and speech as the only cue. Use it in conjunction with other cues such as position, size and rendering.<br /> <br /> Sounds can be annoying. Provide a means for the user to turn off sound [and/or] speech.<br /> <br /> {{Note|text=Provide a means for the user to turn off sounds and/or speech.}}<br /> <br /> ==== Controls ====<br /> <br /> It may be a good idea to provide additional controls so the user can modify the speech or sound in your program. Consider this if speech/sound is integral to the application. If they are secondary, you may just be over-complicating the controls in your program.<br /> <br /> === Closing Down the Workbench ===<br /> <br /> If your application needs more memory than is available, it may attempt to close Workbench.<br /> <br /> If your application does this, it should allow the user to re-enable Workbench from a menu item if more memory becomes available. A preference setting should also be made available so the user can specify that Workbench should always be closed (or not) on entry to the application.<br /> <br /> If your application closes Workbench, it must reopen it again before exiting.<br /> <br /> === Defining Your Basename ===<br /> <br /> A basename is a short, one-word name that you assign to your application. It's usually (and preferably) the same word as the name of your program's executable - the word a user types into the Shell in order to run your program.<br /> <br /> All user-accessible names from your program should be built from the basename. These include such things as ARexx ports, public screen names, and configuration settings names.<br /> <br /> For example, a database program called MondoBase may have the basename of MBase. Users running the program from the Shell would type MBase on the command line. The program may open on a public screen named ''MBase.1'' with an ARexx port of the same name.<br /> <br /> Basenames should be logical to the user, easily remembered and short. Making it too short, however, could raise the likelihood of conflicts with other programs. If MondoBase's basename were simply M, it could conflict with MusicBoard's basename.<br /> <br /> == Basics of the GUI ==<br /> <br /> For a system with three interfaces, a relatively large section of this book is devoted to the GUI. This isn't surprising when you think about the popularity of GUIs and the fact that a GUI designer needs to worry as much about form as function.<br /> <br /> The Amiga GUI has two parts: Intuition and Workbench. As a developer, you will be more concerned with Intuition. It consists of the function libraries and tools you will use to create and run GUIs for your application.<br /> <br /> Before talking specifically about the elements of the Amiga GUI (in the next five chapters), some basic elements and techniques need to be discussed.<br /> <br /> === Metaphor ===<br /> <br /> One of the reasons for the success of GUIs is that they greatly reduce the need to memorize commands and command structures. One technique used to accomplish this is the use of metaphor - GUIs try to mimic the traditional work world in some way.<br /> <br /> By presenting what can be frighteningly complex workings as familiar, real-world objects and activities, even novice users can understand and use a computer quickly. Hierarchical filing systems become drawers. RAM caches that spool to disk become clipboards. Pulling an ASCII clip from a buffer into a document becomes pasting.<br /> <br /> To employ a metaphor in your application, look for some common physical system that is analogous to the behavior of the application.<br /> <br /> For example, a 3-D modelling package could be designed to work on wire-frame objects as though they were actually physical objects. The user could rotate the object by &quot;grabbing&quot; a corner of the object and pulling just as one would grab the corner of a real object.<br /> <br /> Of course, it is not always necessary or even desirable for every interface to be modeled after some physical object.<br /> <br /> === Object-Action ===<br /> <br /> Most of the popular GUIs today encourage the use of object-action methodology. This simply means that first the user selects the thing he wants something done to, then he selects what he wants to do with it. For example, first he clicks on the object he wants deleted then he chooses the delete function, rather than choosing delete then clicking on the objects to be deleted.<br /> <br /> This is done mainly to prevent modality. Modes are generally avoided in GUIs because they can easily become restrictive and confusing to the user. If the user were to select delete before selecting the object(s), he would be in delete mode and could accidentally delete something he wanted to save - especially if he were to go away and come back later. There is also the problem of how to indicate the last object to delete. In general, any mode is limiting and should be avoided if possible.<br /> <br /> === Focus ===<br /> <br /> When a user is looking at a computer screen, he is most likely to concentrate his attention on one particular spot. In a word processor, he will most likely be looking at the cursor. Or in a CAD program, the focus may be the currently selected line or polygon. Very often, the pointer itself is the focus.<br /> <br /> It's important to identify the user's focus because this can be a very effective channel of information. For example, you can animate or change the appearance of the focus object to indicate some change in the program's state. The wait or busy pointer is an example of this. If the wait pointer's clock imagery appeared in the title bar instead of on the pointer, it would be far less effective.<br /> <br /> === Feedback ===<br /> <br /> Users expect to see an immediate reaction to every action. Even if the action will take some time, they expect to see some indication of &quot;I'm working on it&quot; such as the normal pointer changing to a wait pointer.<br /> <br /> Try to provide feedback through as many appropriate media as you can. For example, when a user drags a note in a music package, he could hear the pitch change as well as see the note move. After all, the visual graphic of the note is only a representation of the sound of the note - and that's what the user is really interested in.<br /> <br /> It's usually easy to provide instantaneous feedback to simple actions, but for more complex or time-consuming actions, a surrogate feedback may be necessary. A familiar example of this is the way windows are dragged on the Amiga - you don't drag a window, you drag an outline of it. Another example of feedback on time-consuming activities is the word processor that refreshes just the line being worked on as the user is typing, and the whole display whenever there is some processing time to spare. This gives the illusion of responsiveness, which from the user's point of view is as good as the real thing.<br /> <br /> {{Note|text=When the user attempts an action, provide feedback.}}<br /> <br /> === Color ===<br /> <br /> Color is a very powerful medium of communication. Be conscious of what message you are communicating.<br /> <br /> Keep in mind that simple images with fewer colors usually work better than complicated colorful ones. When choosing default colors for an application, do not use intense combinations like blue/yellow, red/green, red/blue or green/blue. Use subdued colors instead of fully saturated colors. Color can set the mood of your program and that mood should be one that the user can live with for extended periods of time.<br /> <br /> ==== Give Controls ====<br /> <br /> For all the screens you set up within your application, provide the user with the ability to load, save and edit the colour palette. When the user makes a change to the color palette, examine the new palette to assign the appropriate colors for rendering.<br /> <br /> ==== Color as a Cue ====<br /> <br /> Be consistent when using color to indicate meaning. Window backgrounds should be in the background color, text in the text color and headings in highlighted text.<br /> <br /> Beware of using color alone to convey information - especially in small areas of the display. Some users may work on a monochrome display. Keep in mind also that many people are color blind. For these reasons, try to reserve the use of color only as an extra cue, used in conjunction with other cues such as position, size and rendering.<br /> <br /> {{Note|text=Don't use color as the sole cue - some users work in monochrome.}}<br /> <br /> ==== Check Visibility ====<br /> <br /> The Amiga's Preferences editors allow users to run their applications in two colors. Certain high-resolution monitors have only grey-scale displays. Because of these cases, if your application can open on Workbench or a public custom screen, you should check that your graphics and text can be read on a monochrome or grey-scale display.<br /> <br /> Elements should be visible, should continue to provide visual feedback, and should be recognizable as the same element whether in color or monochrome. Basically, you should make sure that all your functionally significant colors contrast with adjacent functionally significant colors. It's more a matter of the color's brightness or luminance than the actual color itself.<br /> <br /> Commodore's high-resolution grey-scale monitor (A2024), for instance, uses patterns made up of its four basic greys to make more shades when running in medium and low resolutions. At these resolutions, things look best if you choose a color that maps to a solid, not patterned, grey. But these colors are not obvious.<br /> <br /> One palette that produces solid greys follows:<br /> Color 0: Light Grey, RGB=10,8,6<br /> Color 1: Black, RGB=0,0,2<br /> Color 2: White, RGB=15,15,15<br /> Color 3: Dark Grey, RGB=7,7,9<br /> <br /> === Fonts ===<br /> <br /> Users can specify which font the system should use for the Workbench icon font, the screen font (the preferred system font) and the system default font (a default monospaced font). These are selected with the Font Preference editor in the Workbench's Prefs drawer.<br /> <br /> Your application should try to adjust itself to whatever size the user may select. Menus, windows, requesters and gadgets must all be adjusted to allow the various text sizes to fit.<br /> <br /> Keep in mind that the chosen font may be monospaced or proportionally spaced, and not every application can handle a proportional font. A spreadsheet, for example, depends on the font being monospaced to properly handle the columnar data it manipulates. If your application<br /> needs a monospaced font it should honor whatever the user has chosen to be the default system font - that font is guaranteed to be monospaced.<br /> <br /> If your application can handle a proportional font, however, it should honor whatever the user has chosen to be the screen font.<br /> <br /> === Internationalization ===<br /> <br /> Since the Amiga is sold in a world market, internationalization is an important design factor in your application. One of the most important and fundamental issues to consider when thinking about internationalization is the difference in length of the text between English and any other language.<br /> <br /> English is one of the most terse language, and when converting English to, say, German the text the text is bound to grow 30-50% in size.<br /> <br /> Another thing to keep in mind is the desirability of building a text table that is centrally located so that it can be adjusted by the user. Do not scatter embedded text throughout your application; consider putting it in a file instead. This way, the text can be easily localized by updating one central table, or even by loading a new text table from disk.<br /> <br /> Don't make every text item adjustable. For instance, if your application incorporates a procedural language or ARexx keywords, do not allow these special words to be localized. To do so would severely hurt the ability to share scripts between international users.<br /> <br /> If your application provides database-like operations that involve money, numbers, telephone numbers and addresses, remember that these sort of fields vary in usage from country to country. The table below lists some of the differences.<br /> <br /> International Formats<br /> {| class=&quot;wikitable&quot;<br /> ! Language<br /> ! Date<br /> ! Time<br /> ! Hours<br /> ! Decimal<br /> ! Thousands Separator<br /> |-<br /> | American English || mm/dd/yy || hh:mm:ss || 12 hr || period || comma<br /> |-<br /> | Australian English || dd/mm/yy || hh:mm:ss || 12 hr || period || comma<br /> |-<br /> | British English || dd/mm/yy || hh:mm:ss || 12 hr || period || comma<br /> |-<br /> | Canadian French || dd/mm/yy || hh:mm:ss || 24 hr || comma || space<br /> |-<br /> | Danish || dd/mm/yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | Dutch || dd-mm-yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | Finnish || d.m.yyyy || hh.mm.ss || 24 hr || comma || space<br /> |-<br /> | Flemish || dd-mm-yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | French || dd.mm.yy || hh:mm:ss || 24 hr || comma || space<br /> |-<br /> | German || dd.mm.yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | Italian || dd-mm-yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | Norwegian || dd-mm-yy || hh:mm:ss || 24 hr || comma || space<br /> |-<br /> | Portuguese || yy/mm/dd || hh:mm:ss || 24 hr || comma || space<br /> |-<br /> | Spanish || dd/mm/yy || hh:mm:ss || 24 hr || comma || period<br /> |-<br /> | Swedish || yy-mm-dd || hh.mm.ss || 24 hr || comma || space<br /> |-<br /> | Swiss French || dd.mm.yy || hh:mm:ss || 24 hr || period || apostrophe<br /> |-<br /> | Swiss German || dd.mm.yy || hh:mm:ss || 24 hr || period || apostrophe<br /> |}<br /> <br /> {{Note|text=It is best to build internationalization into your application from the start.}}<br /> <br /> === The 3-D Look ===<br /> <br /> Release 2 of the Amiga operating system first introduced a three-dimensional look to its GUI. Whenever possible, elements are drawn so that light appears to come from the upper left of the display with shadows cast to the lower right. This gives the illusion of depth.<br /> <br /> The outside border of a raised object has thin lines forming the top and left sides; dark lines for the bottom and right sides. Reverse these colors for a recessed image.<br /> <br /> Using this simulated depth, you can make images appear to be standing out or recessed in the display, thus giving the user a context cue at the same time.<br /> <br /> Fig 2.1: Raised and recessed images.<br /> <br /> Fig 2.2: Closeup view of 3-D box corner with a pixel grid superimposed.<br /> <br /> If an icon or gadget appears raised, this means it is available for use or modifiable. If its default appearance is recessed, this means it is unmodifiable or for display purposes only.<br /> <br /> Another context cue: when a user clicks on an icon or a button-like gadget, it should change from a raised image to a recessed, highlighted image. This goes along the basic idea of providing feedback that the attempted action (clicking on an action gadget, for example) has worked.<br /> <br /> Objects that highlight should show distinctly different imagery between highlighted and non-highlighted states. One convention used by Workbench is to display the image in complementary colors.<br /> <br /> ==== Ghosting ====<br /> <br /> When a control is unavailable for selection, it should be obviously unselectable. Do not allow the user to select something that does nothing in response.<br /> <br /> [[File:SGFig2-3a.png|frame|center]] [[File:SGFig2-3b.png|frame|center]]<br /> &lt;center&gt;Fig 2.3: Examples of non-highlighted and highlighted gadgets.&lt;/center&gt;<br /> <br /> Intuition will automatically display a ghosting pattern - a grid of dots in the &quot;shadow&quot; color - over an unavailable control. If, however, you need to do it manually, follow the example shown below:<br /> <br /> [[File:SGFig2-4.png|frame|center|Fig 2.4: A normal and enlarged view of a ghosted gadget]]<br /> <br /> ==== GadTools ====<br /> <br /> If you are developing for the Release 2 environment, you don't need to worry about incorporating the 3-D look into your gadgets - GadTools will do it for you. GadTools is a toolkit that supplies pre-programmed, standard application gadgets for use in your application.<br /> <br /> Whenever possible you should let GadTools work for you. Even beyond the standardization of function it provides, this tool frees you to spend more time and effort on the crux of your application.<br /> <br /> ==== The System as your Guide ====<br /> <br /> In the absence of any system-provided support, or if the special needs of your application require you to build your own elements, use existing system elements as a general guide to style and function.<br /> <br /> === The Mouse ===<br /> <br /> Having graphics on the screen that represent something to the user is good, but you need a way for the user to interact with those graphics. Although a number of devices can be used to do this on the Amiga (including a touch screen, a joystick or even the keyboard), most often the user will interface with the GUI via a two-button mouse.<br /> <br /> Using a mouse can become a seamless, unconscious act for most users in the same way that a typist can type the letter &quot;k&quot; without consciously thinking: &quot;Let's see...right hand, third finger from the right.&quot; Typists are able to do that because typewriter manufacturers make their keyboards consistent. Likewise, it is important that your software follows rules of mouse usage so that the GUI is &quot;mechanically&quot; consistent.<br /> <br /> The standard Amiga mouse has two buttons: the selection button (left) and the menu button (right). A scroll wheel acts as a middle button as well as vertical scrolling.<br /> <br /> [[File:Style2.5.png|center]]<br /> <br /> The selection button (the left mouse button) selects or operates gadgets, icons, windows and screens. It can also be used to select special objects unique to your application (eg. the note object in a music package) or to set the pen &quot;down&quot; in a drawing operation.<br /> <br /> The menu button (the right mouse button) actives the menu system and allows the user to choose a menu item. The menu button may also be used to abort a selection button operation. For example, the user is dragging a window around by holding down the selection button and moving the mouse. If he decides that this action was a mistake and wants the window back where it was originally, he can press the menu button and the window should snap back to its original position.<br /> <br /> {{Note|text=Follow basic rules for the mouse so that its use becomes instinctive.}}<br /> <br /> ==== Activating Tools ====<br /> <br /> Clicking on a tool gadget, such as the Fill tool in a paint package, with the selection button should activate the tool. If appropriate, a double-click could activate a settings editor for the tool so that the user can fine-tune the way the tool works.<br /> <br /> ==== Triggering a Process ====<br /> <br /> When the selection of something will trigger a process, the action should occur on the release of the mouse button - not the down press. This way the user can change his mind about the operation and move the mouse away from the object before he releases the button.<br /> <br /> {{Note|text=Actions should be triggered on the release of the selection button.}}<br /> <br /> ==== Current Objects ====<br /> <br /> When an object is selected, that object is considered to be the current object. Some examples are: the highlighted section in a CAD program, a note in a music program, or the character under the cursor in a word processor. When the user chooses an action (via a menu item, for example) that action is carried out on the current object. For example, in a desktop publishing program the user can click on a text box and make that the current object. If the user selects an action such as &quot;erase&quot; or &quot;copy&quot;, that action is performed on that text box.<br /> <br /> ==== Shift Selecting ====<br /> <br /> In some instances, a user can select more than one item at once by using shift selection. The user clicks on an item with the selection button. While holding the Shift key down, the user can then select any number of objects. The result is called a @{i}selected group@{ui}. Some<br /> examples of objects that could be grouped are: columnar text boxes in a DTP program, notes in a music program and text items in a list.<br /> <br /> Some actions that can be carried out on current objects will still apply to a group, such as copy or delete, but others may not. For example, you can have a requester showing the attributes of a current object but it would be difficult to have such a requester for a group of dissimilar objects.<br /> <br /> ==== Dragging ====<br /> <br /> Objects can also be grouped by dragging. Dragging, a common mouse operation on many platforms, involves holding the selection button down while moving the mouse. There are two types of dragging: contiguous and non-contiguous.<br /> <br /> A good example of contiguous dragging can be found in a word processor. The user clicks on a letter then, without releasing the selection button, moves the cursor somewhere else in the text. The first letter he clicked on becomes the &quot;anchor&quot; point and all the text between the anchor point and the point where the selection button was released should be highlighted. The highlighted text is now a selected group.<br /> <br /> Objects can also be grouped contiguously by the use of the marquee. When the mouse is dragged on Workbench, for example, an animated dotted line, the marquee, marks the area covered by the mouse. All the icons within the marquee will be selected.<br /> <br /> Fig 2.6: Grouping by using the marquee.<br /> <br /> Less common is non-contiguous dragging. One example of this is a program that allows the user to drag select a section of text and then deselect individual lines by clicking on them. The deselected line can be in between two selected lines.<br /> <br /> In both styles of dragging, the display should scroll automatically if the mouse goes beyond the boundaries of the display region.<br /> <br /> ==== Four Ways to Highlight Text ====<br /> <br /> There are four methods you can use in your program to highlight text: dragging (see the previous section), extended selection, multiple-clicking and selecting all.<br /> <br /> With extended selection, the user clicks the cursor in the text (this becomes the anchor point), then shift-clicks the cursor at another place in the text. The area between the anchor point and where he shift-clicked will be highlighted. Note that the user can adjust the window's scroll bar between clicks and thus highlight a very wide range.<br /> <br /> If you employ the multiple-click method, the user can select a word by double-clicking on it, or select a paragraph by triple-clicking on it. (Note: these are not timed clicks.) A &quot;word&quot; can be defined simply as anything with spaces around it, or you can use a more complex definition. A fourth click should revert back to a normal cursor.<br /> <br /> Selecting all would feature a menu item that would select all the characters in the document. This action shouldn't select the document as well; that is, any actions chosen for the selected group should not act upon the document itself. For example, if the user chooses select all and then erase, all the characters should vanish from the document's window but the window should remain.<br /> <br /> ==== Current Objects vs. Selected Groups ====<br /> <br /> Current objects and selected groups should not co-exist in the same application. In other words, no matter how many projects your application may run simultaneously, there should be only one current object ''or'' one selected group at any one time. If the user has a current object, then creates a group through shift selection, there should no longer be a current object.<br /> <br /> {{Note|text=There should only be one current object or one selected group at one time.}}<br /> <br /> ==== Selection Context ====<br /> <br /> Confine the user's ability to select to a single context or &quot;level&quot;. For example, don't let the user select documents and characters at the same time. You can allow him to select any numbers of characters in a document, or any number of documents, but not both at once.<br /> <br /> === The Pointer ===<br /> <br /> The Amiga uses a pointer that the user can redesign.<br /> <br /> The default pointer looks like this:<br /> <br /> Fig 2.7: The default pointer with a superimposed pixel grid.<br /> <br /> Your application can use a custom pointer instead of the standard. The arrangement of color intensity in your custom pointer should be consistent with that of the standard pointer - especially since two different applications can share the same screen.<br /> <br /> Technical note: here's how the colors should be set up:<br /> Color 0 Transparent<br /> Color 1 Medium intensity<br /> Color 2 Darkest<br /> Color 3 Brightest<br /> <br /> The pointer should be framed by either color 1 or color 3 to ensure its contrast on most screens.<br /> <br /> ==== Waiting ====<br /> <br /> When your application is busy with a task and temporarily cannot accept input, another pointer, known as a wait pointer, appears.<br /> <br /> Currently the wait pointer used by Workbench is not accessible by developers. Shown here is a pixel-by-pixel view of Workbench's wait pointer if you want to make yours look similar.<br /> <br /> Fig 2.8: Workbench's wait pointer with a superimposed pixel grid.<br /> <br /> If the wait is for a measured amount of work, such as in a ray-tracing program, your application should provide a requester that shows the progress of the activity. The progress requester should have a gadget on it allowing the user to stop the activity.<br /> <br /> [[File:SGFig2-9.png|frame|center|Fig 2.9: A sample progress requester indicating the progress of a time-consuming activity]]<br /> <br /> Use a progress requester like the one shown on the previous page instead of an animated pointer to indicate that the application is busy. Here's why: A man is rendering a simple object with a ray-tracing package. While waiting for that to finish, he decides to write a letter in a word processing package that opens on the same screen but in a different window. If the ray-tracing program displayed a progress requester,<br /> he could see when the rendering was done. But if it used an animated pointer, that pointer would disappear when he clicked in the word processing window.<br /> <br /> Don't tie a wait pointer to the progress requester (i.e. when the user clicks on the progress the pointer changes to a wait pointer). If you do, the user may think he can't activate the &quot;Stop&quot; gadget.<br /> <br /> Don't show multiple cycles in the same progress bar. For example, if the progress bar reads &quot;Calculating...&quot;, fills up and then reads<br /> &quot;Rendering...&quot;, you've falsely built up the hopes of the user. Instead, use multiple bars if knowing the progress of each task is important to the user, or a single bar that is filled only once if knowing when the job is done is all that is important.<br /> <br /> ==== Pointers with Purpose ====<br /> <br /> Pointers can be used to give the user a context cue.<br /> <br /> For instance, if your application supports different tools, such as the sketch tool or the fill tool in a paint program, the pointer imagery can reflect the currently selected tool.<br /> <br /> Or, if your window is divided into distinct areas with different uses, it might be more appropriate to have the pointer image reflect its current purpose based on its position. The pointer for a CAD program, for example, might be a cross-hair while it is over the drawing area, but turn into the standard arrow when it is over the control panel area.<br /> <br /> === Resolutions ===<br /> <br /> The Amiga hardware and software offers a variety of resolutions for the user to choose from. The resolution refers to the number of pixels in that mode. For instance, a common Amiga resolution of 640x200 refers to the number of pixels in height and width, respectively.<br /> <br /> If you allow the user to choose resolutions, make sure that your GUI fits comfortably on whatever screen the user has chosen. A good way to do this is by checking it on a least common denominator resolution - a non-interlaced, non-overscan NTSC screen (640x200) with a Topaz 8 font. You don't need to set these as defaults; just make sure that what you create will look and function normally when set to these settings.<br /> <br /> If your original target audience will most likely be using PAL, it's still a good idea to design with a 640x200 NTSC screen in mind so your distribution isn't limited later.<br /> <br /> Respect the choices the user has made in his Preferences setup for resolution and how large a display should be set up.<br /> <br /> {{Note|text=Design your GUI on a 640x200 screen with the Topaz 8 font.}}<br /> <br /> Technical note: If your application is text-oriented, make sure you use the text overscan setting. If it's graphic-oriented, use the &quot;standard&quot; overscan setting. The user can set both of these by using Workbench's Overscan Preferences editor.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Introduction&diff=5377 UI Style Guide Introduction 2013-04-26T09:24:24Z <p>Alexandre Balaban: Categorized into User Interface Style Guide</p> <hr /> <div>[[Category:User Interface Style Guide]]<br /> == Introduction ==<br /> <br /> The purpose of this book is to describe how the user interface of a standard Amiga application should look and operate. It is intended to be read by current developers as well as developers who are considering writing and/or designing application software for the Amiga.<br /> <br /> This book assumes some familiarity with computers and their interfaces in general, and with the Amiga's graphic user interface (GUI) in particular, but, for the most part, you do not have to be a programmer to understand the material.<br /> <br /> Only the behavioral guidelines for an Amiga application are presented here. The details of how to implement them are covered in other volumes of the Amiga [Technical] Reference Series.<br /> <br /> == What's In This Guide ==<br /> <br /> This document provides the following information:<br /> <br /> * the benefits of a standard user interface;<br /> <br /> * an overview of the components of the Amiga user interface;<br /> <br /> * specifications showing how to use the components of the Amiga user interface to create a standard Amiga application.<br /> <br /> The Amiga hardware and system software provide the basic building blocks of the user interface: a mouse and pointer, windows and icons, menus and requesters and more. But it is your software that combines these elements and ultimately determines how the machine will be used.<br /> <br /> === Non-Stifling Standards ===<br /> <br /> In one sense, this style guide can be considered a book of rules for you to follow when designing application software for the Amiga. It describes the best ways to combine and use elements of the Amiga system software to communicate with the user.<br /> <br /> Unlike rule books such as a state's driving code or a company's employee handbook, the style guide's originators don't suggest penalties for violators. In fact, penalties of that sort would be counterproductive. The aim of this book is to establish standards for Amiga applications without stifling creativity. New versions of the Amiga and new types of applications will probably require refinement and expansion of these standards in the future.<br /> <br /> That's not to say no penalties exist. In a free, competitive market the only real penalties are financial and self-inflicted. This book has been created under this premise: standardized software will be better for reasons described later in this chapter, and thus, in a competitive situation it should sell better.<br /> <br /> In short, these standards were devised to improve your program and the Amiga platform in the eyes of the user.<br /> <br /> === A Point on the Horizon ===<br /> <br /> No one expects every application to conform to every one of the rules and guidelines in this manual.<br /> <br /> These rules have not been created in a vacuum. Many of the standards discussed in this manual have been culled from experience - experience gained through the multitudes of Amiga applications released since the Amiga's inception. The writers of this document first looked at what has worked best in specific applications and then tried to transform that raw experience into a standard, efficient and accepted way to do things on the Amiga. The trick was to come up with ideas that worked well ''and'' would work well in a variety of situations.<br /> <br /> Clearly there will always be exceptions to these rules. Even applications created by the AmigaOS development team may not comply with every idea in this book.<br /> <br /> The hope is that the idealized application described in this manual will be like a point on the horizon you keep in sight throughout the development of your very real program.<br /> <br /> {{Note|text=The rules in this manual describe an idealized application not yet created.}}<br /> <br /> == Some Perspective ==<br /> <br /> The user is the most important part of any application. In the past, improving the speed or increasing the capacity of application software has been a major focus of computer programmers. The idea of emphasizing the user interface of a computer system over its performance and capacity is a relatively recent innovation in computer history.<br /> <br /> Historically, most users of computer systems were technical people who could tolerate the exacting requirements of expensive computer hardware and their &quot;unfriendly&quot; application designs. Complex commands had to be remembered and typed into a terminal in the proper sequence and thus required these early computers to have a professional staff in order to run them. This made sense because the computing hardware of that era was much more expensive than human labor. The user was there to serve the needs of these expensive machines.<br /> <br /> The advent of microprocessors and the long-term trend to lower-priced computers has changed all that. Today's computer user is trying to get work done. The user probably does not know much about computers and may not even know how to type. The human-machine interface has been reversed, and now computers must serve the needs of the user.<br /> <br /> === The User's Needs ===<br /> <br /> The user's needs are simple:<br /> <br /> * the operation of the software should be predictable;<br /> <br /> * learning the software should be easy; the software's functions should be easily scannable;<br /> <br /> * the software's operations should be consistent throughout tools, applications and similar objects;<br /> <br /> * feedback should be provided to the user, so he knows what has happened and what he can do;<br /> <br /> * the software should adapt to the user's level of experience.<br /> <br /> {{Note|text=The user needs: predictability, intuitiveness, accessibility, consistency, feedback, adaptability and simplicity.}}<br /> <br /> === Consistency ===<br /> <br /> The user's needs listed above really all combine into one: consistency. If your interface is consistent with the model, it becomes predictable; if it can be scanned easily, it provides feedback; if it provides feedback in a reasonable manner, the user always feels comfortable and can master increasing levels of complexity.<br /> <br /> Consistency in a user interface allows the user to apply previously learned knowledge to each new application. The user will spend less time figuring out how to get work done, and can therefore be more productive. Learning a new application is much easier if it works just like one the user already understands.<br /> <br /> The Amiga is a multitasking computer that allows the user to run more than one application at a time. This makes consistency even more important because the user can easily switch from one application to another. Consistency between applications allows the user to make this switch without having to make a &quot;mental jump&quot; between one way of working and another.<br /> <br /> Following standards also makes new applications more familiar; thus the user will probably be less afraid of inadvertently wiping out data or making other non-recoverable mistakes.<br /> <br /> {{Note|text=Once again: predictability, intuitiveness, accessibility, consistency, feedback, adaptability and simplicity.}}<br /> <br /> === Filling the Needs ===<br /> <br /> A graphic user interface (GUI) is current the best method available for simplifying the user interface and meeting the needs of the user.<br /> <br /> Amongst the first computers to use GUIs were the Xerox Star and Apple Lisa. Based on pioneering research performed at the Xerox Palo Alto Research Centre in the 1970s, these computers allow the user to issue commands with a mouse and pointer. Resources represented by graphic icons can be activated by pointing to them with the mouse, and actions can be performed through mouse-activated menus.<br /> <br /> GUIs provide immediate feedback and scanning ability, so users can tell what their options are and don't have to memorize commands. They allow for enormous growth and adaptability of an application, because levels of functions and commands can be buried yet still be graphically accessible. In short, they provide a user interface that lets the user operate, without learning, the computer - in much the same way that the average driver doesn't need to be versed in internal combustion.<br /> <br /> By utilizing standardized tools and objects provided in the GUI, programmers are less likely to invent needlessly different ways of doing things. The interface becomes predictable and consistent.<br /> <br /> === The Amiga ===<br /> <br /> The Amiga is a further refinement of this philosophy that puts the needs of the user foremost. Like many other systems, the Amiga has a GUI with a mouse, pointer, windows and menus which makes it easier for users - especially beginners.<br /> <br /> But the Amiga also offers two other built-in interfaces: a text-oriented interface, the Shell, an an inter-process scripting language, ARexx.<br /> <br /> Together, these three interfaces provide a powerful and flexible environment for both the novice and the expert. In fact a philosophy for the design of the Amiga user interface might be &quot;Power to the User&quot;.<br /> <br /> == Developer-Oriented Benefits to Standards ==<br /> <br /> The obvious beneficiary of a good user interface design is the user. He can pick up a new application and learn how to use it, scan its functions to see what it does, apply old knowledge to cut the learning curve, and so on.<br /> <br /> But a disciplined approach to designing and building an application also has enormous benefits for the software developer.<br /> <br /> === Market Acceptance ===<br /> <br /> Marketing is simplified when a product &quot;belongs&quot; in a comfortable environment, sharing features and tools with other popular applications. A user trying out a demo version of your software and your competitor's software in a store rarely has the time to learn a new way of doing things. A familiar environment and controls that work in a predictable manner will allow him to experience what you are really selling - how well your application does its job.<br /> <br /> {{Note|text=Some real market-oriented reasons exist for standards too.}}<br /> <br /> === Coexistence ===<br /> <br /> By following the recommended guidelines, applications acquire the ability to inter-operate, thereby increasing each application's value. When applications can inter-operate and share data as well as they can on the Amiga, users can combine in off-the-shelf software packages to create custom solutions for the work they want to do with the Amiga.<br /> <br /> Secondly, it is important that applications behave well in a multitasking environment. Sticking to the standards assures peaceful coexistence.<br /> <br /> === Easier Creation and Maintenance ===<br /> <br /> Design is also simplified when you follow standards.<br /> <br /> The operating system features toolkits such as GadTools which provide you with pre-coded elements of the GUI. Even when elements aren't provided &quot;prefab&quot;, standards allow you to spend less time devising and designing environments and more time working on the actual operations of your program. It also allows you more time for testing. For instance, if you are designing a CAD program, chances are that it is computer-aided design which really interests you. Instead of spending a lot of time working on gadgets to control your application, you could be spending more time working on the actual CAD operations.<br /> <br /> Also in the absence of any standards, progress is more difficult because each application will require an individual upgrade plan.<br /> <br /> == Power to the User ==<br /> <br /> Clearly, this book was created to sell the idea of standards as much as to simply present those standards. But the reasons for those standards goes beyond an innate need for order originating from some corporate boardroom. The reasons set forth here have been set forth honestly in an attempt to provide more power to the user. This book intends to make clear what features of the user interface will help to make the interface simple - and predictable - and consistent - and adaptable - and intuitive. This in turn should improve the Amiga market for all of us.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Help:Editing&diff=4855 Help:Editing 2013-02-20T14:16:55Z <p>Alexandre Balaban: Page creation</p> <hr /> <div>== Rules ==<br /> === Category ===<br /> Categories help users finding information more quickly by grouping together article related to the same subject. An article can be related to any number of categories. Adding an article to the category ''MyCategory'' is simple just add somewhere in the article (preferably at the beginning) the wiki code &lt;pre&gt;[[Category:MyCategory]]&lt;/pre&gt;.<br /> All categories associated with an article are displayed at the bottom of the article. Each category name is clickable and brings the user to a page listing every article related to this category in alphabetical order. A category page is a wiki page like any other so it's more effective to edit and fill it with useful text instead of letting it empty with just the article listing. See for example the page for the [[:Category:Debug]].<br /> <br /> == Formatting ==<br /> === Tables ===<br /> To be useful and ensure consistency tables must follow the rules:<br /> * use table caption instead of separate formatted text. This is done by adding the wiki code ''&lt;nowiki&gt;|+&lt;/nowiki&gt;'' followed by the caption text.<br /> * activate table border and cell colors. This is done by adding the wiki code ''&lt;nowiki&gt;class=&quot;wikitable&quot;&lt;/nowiki&gt;'' on the same line as the table start.<br /> * use table headers instead of separate formatted text. This is done by using the wiki code ''&lt;nowiki&gt;!&lt;/nowiki&gt;''. In case of row headers it's possible to use the ''&lt;nowiki&gt;scope=row|col&lt;/nowiki&gt;'' attribute to define the scope. For this includes the attribute after the ''&lt;nowiki&gt;!&lt;/nowiki&gt;'' but before the header text and separate it from the text by a ''&lt;nowiki&gt;|&lt;/nowiki&gt;''<br /> <br /> Example:<br /> {| width=&quot;100%&quot;<br /> ! style=&quot;width: 50%;&quot;|Wiki code<br /> ! style=&quot;width: 50%;&quot;|Preview<br /> |-<br /> | style=&quot;padding: 5px;&quot;|<br /> &lt;pre&gt;<br /> {| class=&quot;wikitable&quot;<br /> |+ Sample caption<br /> |-<br /> ! scope=&quot;col&quot;| Col header 1<br /> ! scope=&quot;col&quot;| Col header 2<br /> ! scope=&quot;col&quot;| Col header 3<br /> |-<br /> ! scope=&quot;row&quot;| Row header 1<br /> | 1st cell row 1<br /> | 2nd cell row 1<br /> |-<br /> ! scope=&quot;row&quot;| Row header 2<br /> | 1st cell row 2<br /> | 2nd cell row 2<br /> |-<br /> ! scope=&quot;row&quot; colspan=&quot;2&quot;| Row header 3 with colspan<br /> | 1st cell row 3<br /> |}<br /> &lt;/pre&gt;<br /> | style=&quot;padding: 5px;&quot;|<br /> {| class=&quot;wikitable&quot;<br /> |+ Sample caption<br /> |-<br /> ! scope=&quot;col&quot;| Col header 1<br /> ! scope=&quot;col&quot;| Col header 2<br /> ! scope=&quot;col&quot;| Col header 3<br /> |-<br /> ! scope=&quot;row&quot;| Row header 1<br /> | 1st cell row 1<br /> | 2nd cell row 1<br /> |-<br /> ! scope=&quot;row&quot;| Row header 2<br /> | 1st cell row 2<br /> | 2nd cell row 2<br /> |-<br /> ! scope=&quot;row&quot; colspan=&quot;2&quot;| Row header 3 with colspan<br /> | 1st cell row 3<br /> |}<br /> |}<br /> <br /> === Syntax highlighting ===<br /> It's often required to quote source code. The common way of doing it is to use embraces the code between &lt;nowiki&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/nowiki&gt; but the drawback of such solution is that the code in not too easy to read. Instead you can use the &lt;nowiki&gt;&lt;syntaxhighlight&gt;&lt;/syntaxhighlight&gt;&lt;/nowiki&gt; this will give readers syntax highlighting.<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ Code sample with and without syntax highlighting<br /> ! scope=&quot;col&quot;| Method<br /> ! scope=&quot;col&quot;| Wiki code<br /> ! scope=&quot;col&quot;| Preview<br /> |-<br /> ! scope=&quot;row&quot;| Without<br /> |<br /> &lt;pre&gt;<br /> &lt;pre&gt;<br /> #include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &amp;lt;/pre&gt;<br /> &lt;/pre&gt;<br /> |<br /> &lt;pre&gt;#include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/pre&gt;<br /> |-<br /> ! scope=&quot;row&quot;| With<br /> |<br /> &lt;pre&gt;&lt;syntaxhighlight&gt;#include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;&lt;/pre&gt;<br /> |<br /> &lt;syntaxhighlight&gt;#include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }&lt;/syntaxhighlight&gt;<br /> |}<br /> <br /> === Additional Help ===<br /> <br /> You can find useful interesting additional help on the official [[http://www.mediawiki.org/wiki/Help:Contents Mediawiki]] website, more specifically in the [http://www.mediawiki.org/wiki/Help:Formatting Mediawiki:Formatting] help page</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=GDB_for_Beginners&diff=4854 GDB for Beginners 2013-02-20T13:25:48Z <p>Alexandre Balaban: /* Breakpoints */ Rework layout</p> <hr /> <div>[[Category:Debug]]<br /> = Author =<br /> <br /> Roman Kargin&lt;br/&gt;<br /> Copyright (c) 2012 Roman Kargin&lt;br/&gt;<br /> Used by permission.<br /> <br /> = Introduction =<br /> <br /> On the Internet you can find a lot of GDB tutorials; ranging from the simple ones, which give you a basic reference for GDB, up to the big tutorials and documentation (including the official ones). Of course, none of these are written in the context of AmigaOS, because for the rest of word AmigaOS is very obscure, and as result all those GDB tutorials are oriented towards x86/UNIX, or occasionally PPC/Sparc/Alpha based Unixes (although those still look pretty much the same, as it is still UNIX). In the case of AmigaOS, the SDK documentation just includes the official GDB docs, which cover just the GDB itself without taking AmigaOS into account.<br /> <br /> Some of you may remember that GDB on AmigaOS worked reasonably well on the early versions of the OS, prior to the first releases of AmigaOS 4.1, which brought a lot of changes, some of which caused GDB to stop working properly. But with the release of AmigaOS 4.1 Update 3, GDB once again started to work more-or-less as expected, and lately (with release of Update 4) it is usable again. As result, all of the information in this article are based on the GDB from latest SDK 53.20 (GDB version 6.3, 2005.07.19 build) and AmigaOS 4.1 Update 4.<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -v<br /> GNU gdb 6.3 (AmigaOS build 20050719)<br /> Copyright 2004 Free Software Foundation, Inc.<br /> GDB is free software, covered by the GNU General Public License, and you are<br /> welcome to change it and/or distribute copies of it under certain conditions.<br /> Type &quot;show copying&quot; to see the conditions.<br /> There is absolutely no warranty for GDB. Type &quot;show warranty&quot; for details.<br /> This GDB was configured as &quot;ppc-amigaos&quot;.<br /> <br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> = What is GDB? =<br /> <br /> GDB is not just a simple debugger or disassembler, but a full debug environment that gives you the ability to debug your program in real time, disassemble your code, dump or disassemble memory areas, attach to running processes (e.g. a commodity that is already running), step through source code or assembler instructions, set breakpoints on labels or addresses, display variable values, and just about anything else a serious programmer may need.<br /> <br /> So of what use is GDB to a beginner programmer? Why have GDB on Amiga OS at all? Put simply, GDB enables you to determine exactly what is happening inside your program, and help you figure out why it runs into trouble. For example, if you wrote a program, but it doesn't work how you expected it to, and/or it crashes. You can use GDB to set a breakpoint somewhere before the problem, and launch the program. The program will run until it reaches the breakpoint, and then you can check the state of the program, its variables, dump memory buffers, inspect the stack and see exactly what is going on. Then, after you have investigated everything you need to look at, you can continue execution of the program, or step over the code (either at source code or machine code level), to see what is happening.<br /> <br /> GDB on AmigaOS is also good for any research of work, for example you can find out at which address AmigaOS loads a relocated ELF, which addresses it uses for kernel areas, what happens when programs calls the system parts of OS and so on. Of course, it is entirely possible to do some non-interactive debugging without GDB, like plain disassembly (with &quot;objdump&quot; for example), but static disassembly is not real time debugging, so you can't check what happens exactly at a given moment of a program's execution.<br /> <br /> Sometimes it is useful to be able to use a debugger when you have no documentation to hand, and don't know how some functions behave, or what they return and where. Also, it can sometimes be the case that even the documentation is wrong and the debugger can help you confirm exactly what is wrong to fill out a proper bug-report or for creating a patch/fix.<br /> <br /> When using GDB, it is necessary to include debugging information in the binary. This information will you to reference functions and variables by their names, and set breakpoints at named functions, like at &quot;main()&quot; to debug from the start of your program, or function &quot;foo()&quot;. It is possible to debug binaries which have no debugging information at all, but then you will not be able to follow the high-level source code (usually C/C++), and will only be able to examine the assembly code. Without debug information you are usually forced to start at the &quot;_start&quot; label, which is where the all real code start (which is in the C/C++ library startup code for C/C++ programs).<br /> <br /> GCC has many flags to specify the kind of debugging information to be included in the binary, including -g, -ggdb, -gstabs and -gstabs+. The only flag which is recommended for use on AmigaOS is -gstabs. So, whenever you wish to create a binary with debugging information, you should add &quot;-gstabs&quot;. Other ones may or may not work, but to avoid any issues just use &quot;-gstabs&quot;. The same is true if you want to just use &quot;addr2line&quot; to find the address of a crash from a stack trace.<br /> <br /> = First Steps =<br /> <br /> Lets start with the simple Hello World example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type hello.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gcc -gstabs hello.c -o hello<br /> <br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) quit<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> In the above example, we compiled a binary with debug information, and ran the binary inside the debugger. The &quot;-q&quot; (or &quot;--quiet&quot;) option on the command line just tells GDB not to print version information on startup.<br /> <br /> For basic debugging, you need to know how to set a breakpoint, how to step over the code, and how to view code, variable and memory contents. The most useful commands for beginners are: run, break, disassemble, next, step, stepi and continue. For all the GDB commands you always have some short names, like b for break, r for run, disas for disassembly, si for stepi and so on. Lets cover the most common scenarios:<br /> <br /> = Breakpoints =<br /> <br /> Just running a program in the debugger isn't very useful - we need to pause execution and examine the state of the code at a particular point. For that, we use the &quot;breakpoint&quot; command (&quot;break&quot; or &quot;b&quot; for short). For example:<br /> <br /> &lt;pre&gt;<br /> break _start : break at beginning of the _start() function<br /> break 10 : break at line 10 of the current file<br /> break *0xXXXXXXX : break at the address<br /> &lt;/pre&gt;<br /> <br /> Lets see it in action: <br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) info break<br /> Num Type Disp Enb Address What<br /> 1 breakpoint keep y 0x7f974208 in main at hello.c:3<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> BS 653c1a68<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Here we have set a breakpoint at beginning of program, displayed the current breakpoints (there is only the one we just set), then ran the program. The program execution stopped at the beginning of the main function (this is called &quot;hitting a breakpoint&quot;), and returned to the GDB prompt. Once at the GDB prompt, you can disable or enable any breakpoint (via &quot;enable&quot; and &quot;disable&quot; commands), as well as removing it with the &quot;clear&quot; command.<br /> <br /> = Step by Step =<br /> <br /> Once you have hit a breakpoint, you can have fine control over the execution of the program, using the following commands:<br /> <br /> &lt;pre&gt;<br /> next (n) - Execute current statement and move to the next one in the function.<br /> <br /> step (s) - The same as next, but with difference that if you are at a function call and you hit next, then the function will execute and return. But if you hit step, then you will step into the first line of the called function.<br /> <br /> stepi (si) - Stepping a single assembly instruction at a time. This is only for experienced developers with knowledge of assembly language.<br /> <br /> continue (c) - If you are finished with manual stepping, then you can just type &quot;c&quot; and the program will continue execution until the next breakpoint is reached, or the program reaches the end.<br /> &lt;/pre&gt;<br /> <br /> Here is a more in-depth example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) list<br /> 1 #include &lt;stdio.h&gt;<br /> 2 main()<br /> 3 {<br /> 4 printf(&quot;just hello&quot;);<br /> 5 return 0;<br /> 6 }<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 63afafd8<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) step<br /> main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) step<br /> 0x7f97424c in printf ()<br /> (gdb) step<br /> 0x7f974254 in __NewlibCall ()<br /> (gdb) step<br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> In that example we examine the source of the program via the &quot;list&quot; command, set break at main(), and then step over the main function. As you can see, printf() also calls the __Newlibcall() function (which is part of the newlib libc.a). You could also debug to see what exactly it does, which argument it takes and what happens after.<br /> <br /> By the way; when you are stepping with stepi (i.e. per instruction) and you reach a &quot;bctrl&quot; instruction, then, via stepi you will jump into the kernel, which is probably not what you want, so to jump over that kind of instruction, and to avoid entering to kernel, you can just do:<br /> <br /> &lt;pre&gt;<br /> (gdb) break _start<br /> (gdb) r<br /> (gdb) disas<br /> .....<br /> (gdb) break *0xaddress_after_bctrl<br /> (gdb) c<br /> &lt;/pre&gt;<br /> <br /> and then again continue with stepi.<br /> <br /> = Disassembling =<br /> <br /> When you reach a point in your program where a crash happens, you might want to see how your C function looks &quot;for real&quot;, in assembly language. For this, you use the &quot;disassemble&quot; command (or &quot;disas&quot; for short):<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 6624f370<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Of course for real work you should know at least the basics of PowerPC assembly. You might want to read a book on this (the best one is called the &quot;green book&quot;, check the links section), but even without deep knowledge you can see that there some magic happens, then a jump to &quot;printf&quot;, and then the rest of the code performs a clean exit (return 0). Those strange values at the &lt;main+20&gt; and &lt;main+24&gt; are offsets to our &quot;hello world&quot; buffer.<br /> <br /> = Get More Info =<br /> <br /> As you should know, assembly language is a set of instructions that the CPU understands directly. The PowerPC architecture is designed to manipulate data inside CPU registers. It has 32 general registers, 32 floating point registers, and some special ones. So, with every single &quot;stepi&quot;, some registers will change their state, and that can be seen by the:<br /> <br /> &lt;pre&gt;<br /> -- info reg - show all the registers at current moment<br /> -- info reg X - show only register X<br /> &lt;/pre&gt;<br /> <br /> With this information, you can get to know exactly which data is placed in the registers, which memory regions they point to or what values they have.<br /> <br /> You can also dump any memory to which your program has access, and to do this you use the &quot;x&quot; command (short for &quot;examine&quot;). The options for this command are the size and format of data to dump and an address where the data you are interested in resides. This can be pretty helpful when you want to know what kind of data you have in the stack, or an array. Lets take a look at that example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 657d7430<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) break *0x7f974224<br /> Breakpoint 2 at 0x7f974224: file hello.c, line 4.<br /> (gdb) c<br /> Continuing.<br /> Current action: 0<br /> BS 63b03568<br /> Current action: 2<br /> <br /> Breakpoint 2, 0x7f974224 in main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) info reg r3<br /> r3 0x6579a038 1702469688<br /> (gdb) x/1s 0x6579a038<br /> 0x6579a038 &lt;_SDA_BASE_+28756968&gt;: &quot;just hello&quot;<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> We set a breakpoint at main(), just to have ability to run and disassemble the main function. We then set a breakpoint at the &quot;crclr&quot; instruction before the jump to printf, and continued execution. We then hit the breakpoint, which means that the CPU stopped just before executing the instruction at 0x7f974224. The instructions just before that (at addresses 0x7f97421c and 0x7f974220) calculated an address and stored it in register 3 (r3). We viewed the contents of r3 with the &quot;info reg r3&quot; command, and saw that it contained &quot;0x6579a038&quot;. We then examined the memory at that address as a string, and we can see that it is our &quot;just hello&quot; string, so we can see that the first argument to printf is placed in r3.<br /> <br /> = Final Words =<br /> <br /> So now, even at this point, you can already start to do some real-time debugging on AmigaOS. Of course, this article only covers a few commands and examples. GDB has hundreds of commands with all kinds of features and possibilities, but hopefully this small guide will help to get you started.<br /> <br /> Sure, to use GDB fully, you should know PowerPC assembly language, C, and how it all works at low-level, but you don't have to know all that for GDB to be useful. Hopefully this has helped you get started.<br /> <br /> If that there is demand, I will write more articles to explain more aspects of our GDB, its different features and abilities.<br /> <br /> Thanks for reading, and for Peter Gordon for proof-reading and helping with corrections.<br /> <br /> = Quick reference =<br /> <br /> == Basics ==<br /> <br /> &lt;pre&gt;<br /> amiga shell:&gt; gdb filename - start gdb with loaded binary to it<br /> (gdb) file filename - load file when you are already in GDB<br /> (gdb) run - run :)<br /> (gdb) run arg1 arg2 etc - run program with command line args<br /> (gdb) quit - quit :)<br /> (gdb) help command - get help for a certain command<br /> &lt;/pre&gt;<br /> <br /> == Breakpoints ==<br /> <br /> &lt;pre&gt;<br /> (gdb) break foo() - set break at function<br /> (gdb) break 5 - set break at line 5 of source code<br /> (gdb) break *0xXXXXXXXX - set break at address<br /> (gdb) info break - display breakpoints<br /> (gdb) delete X - delete breakpoint X (where X is a breakpoint number from &quot;info break&quot;)<br /> (gdb) enable X - enable breakpoint X<br /> (gdb) disable X - disable breakpoint X<br /> &lt;/pre&gt;<br /> <br /> == Disassembling ==<br /> <br /> &lt;pre&gt;<br /> (gdb) disas - disassemble current function<br /> (gdb) disas 0xXXXXXXX - disassemble by address (unlike setting a breakpoint by address, you don't need a '*')<br /> (gdb) x/[num]i 0xXXXXXXX - disassemble by &quot;examine&quot; any amount of instructions ([num]) at any address in memory<br /> &lt;/pre&gt;<br /> <br /> == Stepping ==<br /> <br /> &lt;pre&gt;<br /> (gdb) step - step into the function on this line (if possible)<br /> (gdb) stepi - step by assembly instructions<br /> (gdb) next - run to the next line of this function<br /> (gdb) continue - continue to next breakpoint (or till end)<br /> <br /> for all the stepping functions you can specify how many steps to do, e.g. stepi 100, or next 20.<br /> &lt;/pre&gt;<br /> <br /> == Registers ==<br /> <br /> &lt;pre&gt;<br /> (gdb) info registers - show integer registers only (the most usable)<br /> (gdb) info all-registers - show all registers (including floating point ones, special ones etc, not used so often)<br /> (gdb) info reg X - show only register X <br /> &lt;/pre&gt;<br /> <br /> == Misc == <br /> <br /> &lt;pre&gt;<br /> (gdb) list - examine the source of the program<br /> (gdb) bt - alias of &quot;backtrace&quot;: show the current stack<br /> (gdb) RETURN - if you hit &quot;enter&quot; while you in gdb, it will repeat the last command<br /> &lt;/pre&gt;<br /> <br /> = Links =<br /> <br /> [1] [http://www.freescale.com/files/product/doc/MPCFPE32B.pdf Green Book] - the best for ppc-assembly.&lt;br/&gt;<br /> [2] [http://devpit.org/wiki/GDB Article on DevPit] - GDB relates, and was originally written from a 32bit PowerPC architecture perspective and register information will vary across architectures.&lt;br/&gt;<br /> [3] [http://www.gnu.org/s/gdb/documentation/ Original GDB docs]&lt;br/&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=GDB_for_Beginners&diff=4853 GDB for Beginners 2013-02-20T13:25:15Z <p>Alexandre Balaban: /* Registers */ Removed extra space on first line</p> <hr /> <div>[[Category:Debug]]<br /> = Author =<br /> <br /> Roman Kargin&lt;br/&gt;<br /> Copyright (c) 2012 Roman Kargin&lt;br/&gt;<br /> Used by permission.<br /> <br /> = Introduction =<br /> <br /> On the Internet you can find a lot of GDB tutorials; ranging from the simple ones, which give you a basic reference for GDB, up to the big tutorials and documentation (including the official ones). Of course, none of these are written in the context of AmigaOS, because for the rest of word AmigaOS is very obscure, and as result all those GDB tutorials are oriented towards x86/UNIX, or occasionally PPC/Sparc/Alpha based Unixes (although those still look pretty much the same, as it is still UNIX). In the case of AmigaOS, the SDK documentation just includes the official GDB docs, which cover just the GDB itself without taking AmigaOS into account.<br /> <br /> Some of you may remember that GDB on AmigaOS worked reasonably well on the early versions of the OS, prior to the first releases of AmigaOS 4.1, which brought a lot of changes, some of which caused GDB to stop working properly. But with the release of AmigaOS 4.1 Update 3, GDB once again started to work more-or-less as expected, and lately (with release of Update 4) it is usable again. As result, all of the information in this article are based on the GDB from latest SDK 53.20 (GDB version 6.3, 2005.07.19 build) and AmigaOS 4.1 Update 4.<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -v<br /> GNU gdb 6.3 (AmigaOS build 20050719)<br /> Copyright 2004 Free Software Foundation, Inc.<br /> GDB is free software, covered by the GNU General Public License, and you are<br /> welcome to change it and/or distribute copies of it under certain conditions.<br /> Type &quot;show copying&quot; to see the conditions.<br /> There is absolutely no warranty for GDB. Type &quot;show warranty&quot; for details.<br /> This GDB was configured as &quot;ppc-amigaos&quot;.<br /> <br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> = What is GDB? =<br /> <br /> GDB is not just a simple debugger or disassembler, but a full debug environment that gives you the ability to debug your program in real time, disassemble your code, dump or disassemble memory areas, attach to running processes (e.g. a commodity that is already running), step through source code or assembler instructions, set breakpoints on labels or addresses, display variable values, and just about anything else a serious programmer may need.<br /> <br /> So of what use is GDB to a beginner programmer? Why have GDB on Amiga OS at all? Put simply, GDB enables you to determine exactly what is happening inside your program, and help you figure out why it runs into trouble. For example, if you wrote a program, but it doesn't work how you expected it to, and/or it crashes. You can use GDB to set a breakpoint somewhere before the problem, and launch the program. The program will run until it reaches the breakpoint, and then you can check the state of the program, its variables, dump memory buffers, inspect the stack and see exactly what is going on. Then, after you have investigated everything you need to look at, you can continue execution of the program, or step over the code (either at source code or machine code level), to see what is happening.<br /> <br /> GDB on AmigaOS is also good for any research of work, for example you can find out at which address AmigaOS loads a relocated ELF, which addresses it uses for kernel areas, what happens when programs calls the system parts of OS and so on. Of course, it is entirely possible to do some non-interactive debugging without GDB, like plain disassembly (with &quot;objdump&quot; for example), but static disassembly is not real time debugging, so you can't check what happens exactly at a given moment of a program's execution.<br /> <br /> Sometimes it is useful to be able to use a debugger when you have no documentation to hand, and don't know how some functions behave, or what they return and where. Also, it can sometimes be the case that even the documentation is wrong and the debugger can help you confirm exactly what is wrong to fill out a proper bug-report or for creating a patch/fix.<br /> <br /> When using GDB, it is necessary to include debugging information in the binary. This information will you to reference functions and variables by their names, and set breakpoints at named functions, like at &quot;main()&quot; to debug from the start of your program, or function &quot;foo()&quot;. It is possible to debug binaries which have no debugging information at all, but then you will not be able to follow the high-level source code (usually C/C++), and will only be able to examine the assembly code. Without debug information you are usually forced to start at the &quot;_start&quot; label, which is where the all real code start (which is in the C/C++ library startup code for C/C++ programs).<br /> <br /> GCC has many flags to specify the kind of debugging information to be included in the binary, including -g, -ggdb, -gstabs and -gstabs+. The only flag which is recommended for use on AmigaOS is -gstabs. So, whenever you wish to create a binary with debugging information, you should add &quot;-gstabs&quot;. Other ones may or may not work, but to avoid any issues just use &quot;-gstabs&quot;. The same is true if you want to just use &quot;addr2line&quot; to find the address of a crash from a stack trace.<br /> <br /> = First Steps =<br /> <br /> Lets start with the simple Hello World example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type hello.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gcc -gstabs hello.c -o hello<br /> <br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) quit<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> In the above example, we compiled a binary with debug information, and ran the binary inside the debugger. The &quot;-q&quot; (or &quot;--quiet&quot;) option on the command line just tells GDB not to print version information on startup.<br /> <br /> For basic debugging, you need to know how to set a breakpoint, how to step over the code, and how to view code, variable and memory contents. The most useful commands for beginners are: run, break, disassemble, next, step, stepi and continue. For all the GDB commands you always have some short names, like b for break, r for run, disas for disassembly, si for stepi and so on. Lets cover the most common scenarios:<br /> <br /> = Breakpoints =<br /> <br /> Just running a program in the debugger isn't very useful - we need to pause execution and examine the state of the code at a particular point. For that, we use the &quot;breakpoint&quot; command (&quot;break&quot; or &quot;b&quot; for short). For example:<br /> <br /> &lt;pre&gt;<br /> break _start : break at beginning of the _start() function<br /> break 10 : break at line 10 of the current file<br /> break *0xXXXXXXX : break at the address<br /> &lt;/pre&gt;<br /> <br /> Lets see it in action: <br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) info break<br /> Num Type Disp Enb Address What<br /> 1 breakpoint keep y 0x7f974208 in main at hello.c:3<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> BS 653c1a68<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Here we have set a breakpoint at beginning of program, displayed the current breakpoints (there is only the one we just set), then ran the program. The program execution stopped at the beginning of the main function (this is called &quot;hitting a breakpoint&quot;), and returned to the GDB prompt. Once at the GDB prompt, you can disable or enable any breakpoint (via &quot;enable&quot; and &quot;disable&quot; commands), as well as removing it with the &quot;clear&quot; command.<br /> <br /> = Step by Step =<br /> <br /> Once you have hit a breakpoint, you can have fine control over the execution of the program, using the following commands:<br /> <br /> &lt;pre&gt;<br /> next (n) - Execute current statement and move to the next one in the function.<br /> <br /> step (s) - The same as next, but with difference that if you are at a function call and you hit next, then the function will execute and return. But if you hit step, then you will step into the first line of the called function.<br /> <br /> stepi (si) - Stepping a single assembly instruction at a time. This is only for experienced developers with knowledge of assembly language.<br /> <br /> continue (c) - If you are finished with manual stepping, then you can just type &quot;c&quot; and the program will continue execution until the next breakpoint is reached, or the program reaches the end.<br /> &lt;/pre&gt;<br /> <br /> Here is a more in-depth example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) list<br /> 1 #include &lt;stdio.h&gt;<br /> 2 main()<br /> 3 {<br /> 4 printf(&quot;just hello&quot;);<br /> 5 return 0;<br /> 6 }<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 63afafd8<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) step<br /> main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) step<br /> 0x7f97424c in printf ()<br /> (gdb) step<br /> 0x7f974254 in __NewlibCall ()<br /> (gdb) step<br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> In that example we examine the source of the program via the &quot;list&quot; command, set break at main(), and then step over the main function. As you can see, printf() also calls the __Newlibcall() function (which is part of the newlib libc.a). You could also debug to see what exactly it does, which argument it takes and what happens after.<br /> <br /> By the way; when you are stepping with stepi (i.e. per instruction) and you reach a &quot;bctrl&quot; instruction, then, via stepi you will jump into the kernel, which is probably not what you want, so to jump over that kind of instruction, and to avoid entering to kernel, you can just do:<br /> <br /> &lt;pre&gt;<br /> (gdb) break _start<br /> (gdb) r<br /> (gdb) disas<br /> .....<br /> (gdb) break *0xaddress_after_bctrl<br /> (gdb) c<br /> &lt;/pre&gt;<br /> <br /> and then again continue with stepi.<br /> <br /> = Disassembling =<br /> <br /> When you reach a point in your program where a crash happens, you might want to see how your C function looks &quot;for real&quot;, in assembly language. For this, you use the &quot;disassemble&quot; command (or &quot;disas&quot; for short):<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 6624f370<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Of course for real work you should know at least the basics of PowerPC assembly. You might want to read a book on this (the best one is called the &quot;green book&quot;, check the links section), but even without deep knowledge you can see that there some magic happens, then a jump to &quot;printf&quot;, and then the rest of the code performs a clean exit (return 0). Those strange values at the &lt;main+20&gt; and &lt;main+24&gt; are offsets to our &quot;hello world&quot; buffer.<br /> <br /> = Get More Info =<br /> <br /> As you should know, assembly language is a set of instructions that the CPU understands directly. The PowerPC architecture is designed to manipulate data inside CPU registers. It has 32 general registers, 32 floating point registers, and some special ones. So, with every single &quot;stepi&quot;, some registers will change their state, and that can be seen by the:<br /> <br /> &lt;pre&gt;<br /> -- info reg - show all the registers at current moment<br /> -- info reg X - show only register X<br /> &lt;/pre&gt;<br /> <br /> With this information, you can get to know exactly which data is placed in the registers, which memory regions they point to or what values they have.<br /> <br /> You can also dump any memory to which your program has access, and to do this you use the &quot;x&quot; command (short for &quot;examine&quot;). The options for this command are the size and format of data to dump and an address where the data you are interested in resides. This can be pretty helpful when you want to know what kind of data you have in the stack, or an array. Lets take a look at that example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 657d7430<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) break *0x7f974224<br /> Breakpoint 2 at 0x7f974224: file hello.c, line 4.<br /> (gdb) c<br /> Continuing.<br /> Current action: 0<br /> BS 63b03568<br /> Current action: 2<br /> <br /> Breakpoint 2, 0x7f974224 in main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) info reg r3<br /> r3 0x6579a038 1702469688<br /> (gdb) x/1s 0x6579a038<br /> 0x6579a038 &lt;_SDA_BASE_+28756968&gt;: &quot;just hello&quot;<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> We set a breakpoint at main(), just to have ability to run and disassemble the main function. We then set a breakpoint at the &quot;crclr&quot; instruction before the jump to printf, and continued execution. We then hit the breakpoint, which means that the CPU stopped just before executing the instruction at 0x7f974224. The instructions just before that (at addresses 0x7f97421c and 0x7f974220) calculated an address and stored it in register 3 (r3). We viewed the contents of r3 with the &quot;info reg r3&quot; command, and saw that it contained &quot;0x6579a038&quot;. We then examined the memory at that address as a string, and we can see that it is our &quot;just hello&quot; string, so we can see that the first argument to printf is placed in r3.<br /> <br /> = Final Words =<br /> <br /> So now, even at this point, you can already start to do some real-time debugging on AmigaOS. Of course, this article only covers a few commands and examples. GDB has hundreds of commands with all kinds of features and possibilities, but hopefully this small guide will help to get you started.<br /> <br /> Sure, to use GDB fully, you should know PowerPC assembly language, C, and how it all works at low-level, but you don't have to know all that for GDB to be useful. Hopefully this has helped you get started.<br /> <br /> If that there is demand, I will write more articles to explain more aspects of our GDB, its different features and abilities.<br /> <br /> Thanks for reading, and for Peter Gordon for proof-reading and helping with corrections.<br /> <br /> = Quick reference =<br /> <br /> == Basics ==<br /> <br /> &lt;pre&gt;<br /> amiga shell:&gt; gdb filename - start gdb with loaded binary to it<br /> (gdb) file filename - load file when you are already in GDB<br /> (gdb) run - run :)<br /> (gdb) run arg1 arg2 etc - run program with command line args<br /> (gdb) quit - quit :)<br /> (gdb) help command - get help for a certain command<br /> &lt;/pre&gt;<br /> <br /> == Breakpoints ==<br /> <br /> &lt;pre&gt;<br /> (gdb) break foo() - set break at function<br /> (gdb) break 5 - set break at line 5 of source code<br /> (gdb) break *0xXXXXXXXX - set break at address<br /> (gdb) info break - display breakpoints<br /> (gdb) delete X - delete breakpoint X (where X is a breakpoint number from &quot;info break&quot;)<br /> (gdb) enable X - enable breakpoint X<br /> (gdb) disable X - disable breakpoint X<br /> &lt;/pre&gt;<br /> <br /> == Disassembling ==<br /> <br /> &lt;pre&gt;<br /> (gdb) disas - disassemble current function<br /> (gdb) disas 0xXXXXXXX - disassemble by address (unlike setting a breakpoint by address, you don't need a '*')<br /> (gdb) x/[num]i 0xXXXXXXX - disassemble by &quot;examine&quot; any amount of instructions ([num]) at any address in memory<br /> &lt;/pre&gt;<br /> <br /> == Stepping ==<br /> <br /> &lt;pre&gt;<br /> (gdb) step - step into the function on this line (if possible)<br /> (gdb) stepi - step by assembly instructions<br /> (gdb) next - run to the next line of this function<br /> (gdb) continue - continue to next breakpoint (or till end)<br /> <br /> for all the stepping functions you can specify how many steps to do, e.g. stepi 100, or next 20.<br /> &lt;/pre&gt;<br /> <br /> == Registers ==<br /> <br /> &lt;pre&gt;<br /> (gdb) info registers - show integer registers only (the most usable)<br /> (gdb) info all-registers - show all registers (including floating point ones, special ones etc, not used so often)<br /> (gdb) info reg X - show only register X <br /> &lt;/pre&gt;<br /> <br /> == Misc == <br /> <br /> &lt;pre&gt;<br /> (gdb) list - examine the source of the program<br /> (gdb) bt - alias of &quot;backtrace&quot;: show the current stack<br /> (gdb) RETURN - if you hit &quot;enter&quot; while you in gdb, it will repeat the last command<br /> &lt;/pre&gt;<br /> <br /> = Links =<br /> <br /> [1] [http://www.freescale.com/files/product/doc/MPCFPE32B.pdf Green Book] - the best for ppc-assembly.&lt;br/&gt;<br /> [2] [http://devpit.org/wiki/GDB Article on DevPit] - GDB relates, and was originally written from a 32bit PowerPC architecture perspective and register information will vary across architectures.&lt;br/&gt;<br /> [3] [http://www.gnu.org/s/gdb/documentation/ Original GDB docs]&lt;br/&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=GDB_for_Beginners&diff=4852 GDB for Beginners 2013-02-20T13:24:39Z <p>Alexandre Balaban: /* Breakpoints */ Missing part added</p> <hr /> <div>[[Category:Debug]]<br /> = Author =<br /> <br /> Roman Kargin&lt;br/&gt;<br /> Copyright (c) 2012 Roman Kargin&lt;br/&gt;<br /> Used by permission.<br /> <br /> = Introduction =<br /> <br /> On the Internet you can find a lot of GDB tutorials; ranging from the simple ones, which give you a basic reference for GDB, up to the big tutorials and documentation (including the official ones). Of course, none of these are written in the context of AmigaOS, because for the rest of word AmigaOS is very obscure, and as result all those GDB tutorials are oriented towards x86/UNIX, or occasionally PPC/Sparc/Alpha based Unixes (although those still look pretty much the same, as it is still UNIX). In the case of AmigaOS, the SDK documentation just includes the official GDB docs, which cover just the GDB itself without taking AmigaOS into account.<br /> <br /> Some of you may remember that GDB on AmigaOS worked reasonably well on the early versions of the OS, prior to the first releases of AmigaOS 4.1, which brought a lot of changes, some of which caused GDB to stop working properly. But with the release of AmigaOS 4.1 Update 3, GDB once again started to work more-or-less as expected, and lately (with release of Update 4) it is usable again. As result, all of the information in this article are based on the GDB from latest SDK 53.20 (GDB version 6.3, 2005.07.19 build) and AmigaOS 4.1 Update 4.<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -v<br /> GNU gdb 6.3 (AmigaOS build 20050719)<br /> Copyright 2004 Free Software Foundation, Inc.<br /> GDB is free software, covered by the GNU General Public License, and you are<br /> welcome to change it and/or distribute copies of it under certain conditions.<br /> Type &quot;show copying&quot; to see the conditions.<br /> There is absolutely no warranty for GDB. Type &quot;show warranty&quot; for details.<br /> This GDB was configured as &quot;ppc-amigaos&quot;.<br /> <br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> = What is GDB? =<br /> <br /> GDB is not just a simple debugger or disassembler, but a full debug environment that gives you the ability to debug your program in real time, disassemble your code, dump or disassemble memory areas, attach to running processes (e.g. a commodity that is already running), step through source code or assembler instructions, set breakpoints on labels or addresses, display variable values, and just about anything else a serious programmer may need.<br /> <br /> So of what use is GDB to a beginner programmer? Why have GDB on Amiga OS at all? Put simply, GDB enables you to determine exactly what is happening inside your program, and help you figure out why it runs into trouble. For example, if you wrote a program, but it doesn't work how you expected it to, and/or it crashes. You can use GDB to set a breakpoint somewhere before the problem, and launch the program. The program will run until it reaches the breakpoint, and then you can check the state of the program, its variables, dump memory buffers, inspect the stack and see exactly what is going on. Then, after you have investigated everything you need to look at, you can continue execution of the program, or step over the code (either at source code or machine code level), to see what is happening.<br /> <br /> GDB on AmigaOS is also good for any research of work, for example you can find out at which address AmigaOS loads a relocated ELF, which addresses it uses for kernel areas, what happens when programs calls the system parts of OS and so on. Of course, it is entirely possible to do some non-interactive debugging without GDB, like plain disassembly (with &quot;objdump&quot; for example), but static disassembly is not real time debugging, so you can't check what happens exactly at a given moment of a program's execution.<br /> <br /> Sometimes it is useful to be able to use a debugger when you have no documentation to hand, and don't know how some functions behave, or what they return and where. Also, it can sometimes be the case that even the documentation is wrong and the debugger can help you confirm exactly what is wrong to fill out a proper bug-report or for creating a patch/fix.<br /> <br /> When using GDB, it is necessary to include debugging information in the binary. This information will you to reference functions and variables by their names, and set breakpoints at named functions, like at &quot;main()&quot; to debug from the start of your program, or function &quot;foo()&quot;. It is possible to debug binaries which have no debugging information at all, but then you will not be able to follow the high-level source code (usually C/C++), and will only be able to examine the assembly code. Without debug information you are usually forced to start at the &quot;_start&quot; label, which is where the all real code start (which is in the C/C++ library startup code for C/C++ programs).<br /> <br /> GCC has many flags to specify the kind of debugging information to be included in the binary, including -g, -ggdb, -gstabs and -gstabs+. The only flag which is recommended for use on AmigaOS is -gstabs. So, whenever you wish to create a binary with debugging information, you should add &quot;-gstabs&quot;. Other ones may or may not work, but to avoid any issues just use &quot;-gstabs&quot;. The same is true if you want to just use &quot;addr2line&quot; to find the address of a crash from a stack trace.<br /> <br /> = First Steps =<br /> <br /> Lets start with the simple Hello World example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type hello.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gcc -gstabs hello.c -o hello<br /> <br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) quit<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> In the above example, we compiled a binary with debug information, and ran the binary inside the debugger. The &quot;-q&quot; (or &quot;--quiet&quot;) option on the command line just tells GDB not to print version information on startup.<br /> <br /> For basic debugging, you need to know how to set a breakpoint, how to step over the code, and how to view code, variable and memory contents. The most useful commands for beginners are: run, break, disassemble, next, step, stepi and continue. For all the GDB commands you always have some short names, like b for break, r for run, disas for disassembly, si for stepi and so on. Lets cover the most common scenarios:<br /> <br /> = Breakpoints =<br /> <br /> Just running a program in the debugger isn't very useful - we need to pause execution and examine the state of the code at a particular point. For that, we use the &quot;breakpoint&quot; command (&quot;break&quot; or &quot;b&quot; for short). For example:<br /> <br /> &lt;pre&gt;<br /> break _start : break at beginning of the _start() function<br /> break 10 : break at line 10 of the current file<br /> break *0xXXXXXXX : break at the address<br /> &lt;/pre&gt;<br /> <br /> Lets see it in action: <br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) info break<br /> Num Type Disp Enb Address What<br /> 1 breakpoint keep y 0x7f974208 in main at hello.c:3<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> BS 653c1a68<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Here we have set a breakpoint at beginning of program, displayed the current breakpoints (there is only the one we just set), then ran the program. The program execution stopped at the beginning of the main function (this is called &quot;hitting a breakpoint&quot;), and returned to the GDB prompt. Once at the GDB prompt, you can disable or enable any breakpoint (via &quot;enable&quot; and &quot;disable&quot; commands), as well as removing it with the &quot;clear&quot; command.<br /> <br /> = Step by Step =<br /> <br /> Once you have hit a breakpoint, you can have fine control over the execution of the program, using the following commands:<br /> <br /> &lt;pre&gt;<br /> next (n) - Execute current statement and move to the next one in the function.<br /> <br /> step (s) - The same as next, but with difference that if you are at a function call and you hit next, then the function will execute and return. But if you hit step, then you will step into the first line of the called function.<br /> <br /> stepi (si) - Stepping a single assembly instruction at a time. This is only for experienced developers with knowledge of assembly language.<br /> <br /> continue (c) - If you are finished with manual stepping, then you can just type &quot;c&quot; and the program will continue execution until the next breakpoint is reached, or the program reaches the end.<br /> &lt;/pre&gt;<br /> <br /> Here is a more in-depth example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) list<br /> 1 #include &lt;stdio.h&gt;<br /> 2 main()<br /> 3 {<br /> 4 printf(&quot;just hello&quot;);<br /> 5 return 0;<br /> 6 }<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 63afafd8<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) step<br /> main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) step<br /> 0x7f97424c in printf ()<br /> (gdb) step<br /> 0x7f974254 in __NewlibCall ()<br /> (gdb) step<br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> In that example we examine the source of the program via the &quot;list&quot; command, set break at main(), and then step over the main function. As you can see, printf() also calls the __Newlibcall() function (which is part of the newlib libc.a). You could also debug to see what exactly it does, which argument it takes and what happens after.<br /> <br /> By the way; when you are stepping with stepi (i.e. per instruction) and you reach a &quot;bctrl&quot; instruction, then, via stepi you will jump into the kernel, which is probably not what you want, so to jump over that kind of instruction, and to avoid entering to kernel, you can just do:<br /> <br /> &lt;pre&gt;<br /> (gdb) break _start<br /> (gdb) r<br /> (gdb) disas<br /> .....<br /> (gdb) break *0xaddress_after_bctrl<br /> (gdb) c<br /> &lt;/pre&gt;<br /> <br /> and then again continue with stepi.<br /> <br /> = Disassembling =<br /> <br /> When you reach a point in your program where a crash happens, you might want to see how your C function looks &quot;for real&quot;, in assembly language. For this, you use the &quot;disassemble&quot; command (or &quot;disas&quot; for short):<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 6624f370<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Of course for real work you should know at least the basics of PowerPC assembly. You might want to read a book on this (the best one is called the &quot;green book&quot;, check the links section), but even without deep knowledge you can see that there some magic happens, then a jump to &quot;printf&quot;, and then the rest of the code performs a clean exit (return 0). Those strange values at the &lt;main+20&gt; and &lt;main+24&gt; are offsets to our &quot;hello world&quot; buffer.<br /> <br /> = Get More Info =<br /> <br /> As you should know, assembly language is a set of instructions that the CPU understands directly. The PowerPC architecture is designed to manipulate data inside CPU registers. It has 32 general registers, 32 floating point registers, and some special ones. So, with every single &quot;stepi&quot;, some registers will change their state, and that can be seen by the:<br /> <br /> &lt;pre&gt;<br /> -- info reg - show all the registers at current moment<br /> -- info reg X - show only register X<br /> &lt;/pre&gt;<br /> <br /> With this information, you can get to know exactly which data is placed in the registers, which memory regions they point to or what values they have.<br /> <br /> You can also dump any memory to which your program has access, and to do this you use the &quot;x&quot; command (short for &quot;examine&quot;). The options for this command are the size and format of data to dump and an address where the data you are interested in resides. This can be pretty helpful when you want to know what kind of data you have in the stack, or an array. Lets take a look at that example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 657d7430<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) break *0x7f974224<br /> Breakpoint 2 at 0x7f974224: file hello.c, line 4.<br /> (gdb) c<br /> Continuing.<br /> Current action: 0<br /> BS 63b03568<br /> Current action: 2<br /> <br /> Breakpoint 2, 0x7f974224 in main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) info reg r3<br /> r3 0x6579a038 1702469688<br /> (gdb) x/1s 0x6579a038<br /> 0x6579a038 &lt;_SDA_BASE_+28756968&gt;: &quot;just hello&quot;<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> We set a breakpoint at main(), just to have ability to run and disassemble the main function. We then set a breakpoint at the &quot;crclr&quot; instruction before the jump to printf, and continued execution. We then hit the breakpoint, which means that the CPU stopped just before executing the instruction at 0x7f974224. The instructions just before that (at addresses 0x7f97421c and 0x7f974220) calculated an address and stored it in register 3 (r3). We viewed the contents of r3 with the &quot;info reg r3&quot; command, and saw that it contained &quot;0x6579a038&quot;. We then examined the memory at that address as a string, and we can see that it is our &quot;just hello&quot; string, so we can see that the first argument to printf is placed in r3.<br /> <br /> = Final Words =<br /> <br /> So now, even at this point, you can already start to do some real-time debugging on AmigaOS. Of course, this article only covers a few commands and examples. GDB has hundreds of commands with all kinds of features and possibilities, but hopefully this small guide will help to get you started.<br /> <br /> Sure, to use GDB fully, you should know PowerPC assembly language, C, and how it all works at low-level, but you don't have to know all that for GDB to be useful. Hopefully this has helped you get started.<br /> <br /> If that there is demand, I will write more articles to explain more aspects of our GDB, its different features and abilities.<br /> <br /> Thanks for reading, and for Peter Gordon for proof-reading and helping with corrections.<br /> <br /> = Quick reference =<br /> <br /> == Basics ==<br /> <br /> &lt;pre&gt;<br /> amiga shell:&gt; gdb filename - start gdb with loaded binary to it<br /> (gdb) file filename - load file when you are already in GDB<br /> (gdb) run - run :)<br /> (gdb) run arg1 arg2 etc - run program with command line args<br /> (gdb) quit - quit :)<br /> (gdb) help command - get help for a certain command<br /> &lt;/pre&gt;<br /> <br /> == Breakpoints ==<br /> <br /> &lt;pre&gt;<br /> (gdb) break foo() - set break at function<br /> (gdb) break 5 - set break at line 5 of source code<br /> (gdb) break *0xXXXXXXXX - set break at address<br /> (gdb) info break - display breakpoints<br /> (gdb) delete X - delete breakpoint X (where X is a breakpoint number from &quot;info break&quot;)<br /> (gdb) enable X - enable breakpoint X<br /> (gdb) disable X - disable breakpoint X<br /> &lt;/pre&gt;<br /> <br /> == Disassembling ==<br /> <br /> &lt;pre&gt;<br /> (gdb) disas - disassemble current function<br /> (gdb) disas 0xXXXXXXX - disassemble by address (unlike setting a breakpoint by address, you don't need a '*')<br /> (gdb) x/[num]i 0xXXXXXXX - disassemble by &quot;examine&quot; any amount of instructions ([num]) at any address in memory<br /> &lt;/pre&gt;<br /> <br /> == Stepping ==<br /> <br /> &lt;pre&gt;<br /> (gdb) step - step into the function on this line (if possible)<br /> (gdb) stepi - step by assembly instructions<br /> (gdb) next - run to the next line of this function<br /> (gdb) continue - continue to next breakpoint (or till end)<br /> <br /> for all the stepping functions you can specify how many steps to do, e.g. stepi 100, or next 20.<br /> &lt;/pre&gt;<br /> <br /> == Registers ==<br /> <br /> &lt;pre&gt;<br /> (gdb) info registers - show integer registers only (the most usable)<br /> (gdb) info all-registers - show all registers (including floating point ones, special ones etc, not used so often)<br /> (gdb) info reg X - show only register X <br /> &lt;/pre&gt;<br /> <br /> == Misc == <br /> <br /> &lt;pre&gt;<br /> (gdb) list - examine the source of the program<br /> (gdb) bt - alias of &quot;backtrace&quot;: show the current stack<br /> (gdb) RETURN - if you hit &quot;enter&quot; while you in gdb, it will repeat the last command<br /> &lt;/pre&gt;<br /> <br /> = Links =<br /> <br /> [1] [http://www.freescale.com/files/product/doc/MPCFPE32B.pdf Green Book] - the best for ppc-assembly.&lt;br/&gt;<br /> [2] [http://devpit.org/wiki/GDB Article on DevPit] - GDB relates, and was originally written from a 32bit PowerPC architecture perspective and register information will vary across architectures.&lt;br/&gt;<br /> [3] [http://www.gnu.org/s/gdb/documentation/ Original GDB docs]&lt;br/&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Debug_Kernel&diff=4851 Debug Kernel 2013-02-20T13:23:30Z <p>Alexandre Balaban: /* What is &quot;debug kernel&quot; exactly? */ Removed vague statement</p> <hr /> <div>== Introduction ==<br /> <br /> When you program AmigaOS, sooner or later you will need tools to help you debugging your applications (and not only yours) and find out why a program crashes or runs erratically. For that you have debuggers ([[GDB_for_Beginners|GDB]] and db101), small tools to operate with debug outputs and level of debugging (like KDebug, DumpDebugBuffer, etc.), memory trackers (like MemGuard and MemGuardian) and bunch of other small utilities and tricks which help you to do debugging. One of those tricks, which is little known, is the fact that a debug version of the standard AmigaOS kernel is publicly released ready to use for third party developers (or advanced users willing to help debugging applications).<br /> <br /> == What is &quot;debug kernel&quot; exactly? ==<br /> <br /> Debug kernel is the same kernel as the standard one, but with added code to help catching some of the most common programming errors. This additional code adds a slightly (yet non negligible) amount of bytes to the kernel size (around half a megabyte) and is slower. Thus it has been removed from the standard kernel for performance and optimization purposes and is generally not useful to the standard average user. Yet this debug kernel can prove itself to be very useful to programmers, especially it provides the following functionality:<br /> <br /> # Clobbering of succ/pred pointers when a Node is removed from a list.&lt;BR&gt;One common mistake when dealing with lists is to access successor and/or previous pointers from a removed node. While at first sight it may seems harmless it may reveal itself as a dreadful bug to spot because once a node has been removed from a list nothing guarantees you that the list has not be reworked/copied/reallocated or anything else. This will give you a bug that is very hard to reproduce or even non reproducible at all. As such one should NEVER access Succ/Prev pointers from a removed Node, instead if he needs to, he should access them prior the call to Remove(). To help spotting such errors the debug kernel will modify the ln_Succ/ln_Prev pointer to a given fixed invalid value thus the bug automatically becomes always reproducible. This will also help you spot the fact one is trying to free a Node twice.<br /> # &quot;Munge&quot; of memory. With this option the debug kernel will do additional action/tests on memory pointers. It will help spotting what really happen under the hood just by observing the DAR (or DEAR on 440ep and 460ex CPUs) value in the crash log:<br /> #* If the value is 0x00000000 (or thereabouts), then you have accessed a null pointer (and you will see 0xABADCAFE in one or more registers);<br /> #* If the value is 0xDEADBEEF (or thereabouts), then you have accessed some memory after it has been freed;<br /> #* If the value is 0xCCCCCCCC (or thereabouts), then you have tried to free a Node a second time.<br /> <br /> So, in other words, when you want to catch all those problems which you can't catch by just simply running MemGuard on a &quot;plain&quot; kernel, you should use the debug kernel. I.e. to say it more clear, when you develop anything you should be just on debug kernel.<br /> <br /> == How to set it up ==<br /> <br /> The kernel.debug file is already placed in the Kickstart directory with all the other kernel modules. By default this directory is &quot;SYS:Kickstart/&quot;.<br /> <br /> The preferred way to use the debug kernel is to edit your file kicklayout, duplicate the current layout and in this duplicate modify the line pointing to &quot;kernel&quot; so it points to &quot;kernel.debug&quot;, do not forget to change the label associated with this new layout by adding 'debug' or something. This way you will be able to choose which configuration you want to boot at any time.<br /> <br /> Once you have setup the use of the debug kernel, you must also add some commandline options to the kernel. Basically in order to activate the munge option of the debug kernel you must set the variable &quot;os4_commandline&quot; adds the keyword &quot;munge&quot;. This part depends on your platform firmware, please report to your documentation to know how to do it, but you have to do something like os4_commandline='munge', if os4_commandline is already set with another value you can concatenate using space e.g. os4_commandline='serial munge' to have the debug output to serial port and the munge option activated.<br /> <br /> The following kernel command line parameters are available:<br /> ; serial<br /> : Output all debug via the serial port.<br /> <br /> ; baudrate [N]<br /> : Sets the serial port baud rate to N.<br /> <br /> ; debuglevel [N]<br /> : Set the debuglevel where N is a value from 0 to 10.<br /> <br /> ; nofpuemu<br /> : Sets the AttnFlags in ExecBase to a 68020 without an FPU.<br /> <br /> ; munge<br /> : Munges freed memory so that memory trashing can be found more easily when using a debug kernel. Does nothing on a release kernel.<br /> <br /> {{Note|text=It is strongly recommended that you do not rename the kernel.debug file.}}<br /> <br /> == FAQ ==<br /> <br /> None at the moment<br /> <br /> [[Category:Debug]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Category:Debug&diff=4850 Category:Debug 2013-02-20T09:28:18Z <p>Alexandre Balaban: &quot;How to open and use the exec debug interface&quot; moved from basics to expert</p> <hr /> <div>This page gather all articles from this wiki that are related to debugging on AmigaOS. This includes Tutorials and HowTos as well as more general instructions to effective debugging.<br /> <br /> Debug basics:<br /> * [[Debug Kernel]]<br /> * [[Troubleshooting Your Software]]<br /> <br /> Advanced debugging:<br /> * [[Using Crash-Logs for Debugging]]<br /> * [[Redirecting Debug Output to the Serial Port on Startup]]<br /> * [[Debug Logging on AmigaOS]]<br /> <br /> Expert debugging:<br /> * [[How to open and use the exec debug interface]]<br /> * [[How to install a hardware interrupt]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Category:Debug&diff=4849 Category:Debug 2013-02-20T08:58:40Z <p>Alexandre Balaban: First draft of category text</p> <hr /> <div>This page gather all articles in this wiki that are related to debugging on AmigaOS. This includes Tutorials and HowTos as well as more general instructions to effective debugging.<br /> <br /> Debug basics:<br /> * [[Debug Kernel]]<br /> * [[Troubleshooting Your Software]]<br /> * [[How to open and use the exec debug interface]]<br /> <br /> Advanced debugging:<br /> * [[Using Crash-Logs for Debugging]]<br /> * [[Redirecting Debug Output to the Serial Port on Startup]]<br /> * [[Debug Logging on AmigaOS]]<br /> <br /> Expert debugging:<br /> * [[How to install a hardware interrupt]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Troubleshooting_Your_Software&diff=4848 Troubleshooting Your Software 2013-02-18T15:43:31Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> == Troubleshooting Your Software ==<br /> <br /> Many Amiga programming errors have classic symptoms. This guide will help you to eliminate or avoid these problems in your software.<br /> <br /> == Typical Problems ==<br /> <br /> === Audio: Corrupted Samples ===<br /> <br /> On Classic Amiga hardware, the bit data for audio samples ''must'' be in Chip RAM. Check your compiler manual for directives or flags which will place your audio sample data in Chip RAM. Or dynamically allocate Chip RAM and copy or load the audio sample there.<br /> <br /> === Character Input/Output Problems ===<br /> <br /> RAWKEY users must be aware that RAWKEY codes can be different letters or symbols on national keyboards. If you need to use RAWKEY, run the codes through RawKeyConvert() (see the [[Intuition_Mouse_and_Keyboard|Intuition Mouse and Keyboard]]) to get proper translation to correct ASCII codes. Improper display or processing of high-ASCII international characters can be caused by incorrect tolower()/toupper(), or by sign extension of character values when switched on or assigned into larger size variables. Use unsigned variables such as UBYTE (not char) for strings and characters whenever possible. Internationally correct string functions are provided in the utility.library.<br /> <br /> === CLI Error Message Problems ===<br /> <br /> Improper error messages are caused by calling exit(n) with an invalid or missing return value ''n''. Assembler programmers using startup code should jump to the startup code's _exit with a valid return value on the stack. Programs without startup code should return with a valid value in D0. Valid return values such as RETURN_OK, RETURN_WARN, RETURN_FAIL are defined in &amp;lt;dos/dos.h&amp;gt; and &amp;lt;dos/dos.i&amp;gt;. Values outside of these ranges (-1 for instance) can cause invalid CLI error messages such as &quot;not an object module&quot;. Useful hint - if your program is called from a script, your valid return value can be conditionally branched on in the script (i.e., call program, then perform actions based on IF WARN or IF NOT WARN). RETURN_FAIL will cause the script to stop if a normal FAILAT value is being used in script.<br /> <br /> === CLI Won't Close on RUN ===<br /> <br /> A CLI can't close if a program has a Lock() on the CLI input or output stream (&amp;quot;*&amp;quot;). If your program is &quot;RUN &amp;gt;NIL:&quot; from a CLI, that CLI should be able to close unless your code or your compiler's startup code explicitly opens &amp;quot;*&amp;quot;.<br /> <br /> === Crashes and Memory Corruption ===<br /> <br /> Memory corruption, address errors, and illegal instruction errors are generally caused by use of an uninitialized, incorrectly initialized, or already freed/closed pointer or memory. You may be using the pointer directly, or it may be one that you placed (or forgot to place) in a structure passed to system calls. Or you may be overwriting one of your arrays, or accidentally modifying or incrementing a pointer later used in a free/close. Be sure to test the return of all open/allocation type functions before using the result, and only close/free things that you successfully opened/allocated. Use watchdog/torture utilities such as ''MemGuard'' to catch use of uninitialized pointers or freed memory, and other memory misuse problems. Use the debugging tool ''TNT'' to get additional debugging information instead of a Software Error requester. You may also be overflowing your stack - your compiler's stack checking option may be able to catch this. Cut stack usage by dynamically allocating large structures, buffers, and arrays which are currently defined inside your functions.<br /> <br /> Corruption or crashes can also be caused by passing wrong or missing arguments to a system call (for example SetAPen(3) or SetAPen(win,3), instead of SetAPen(rp,3)). C programmers should use function prototypes to catch such errors. If using short integers be sure to explicitly type long constants as long (e.g., 42L). (For example, with short ints, 1 &amp;lt;&amp;lt; 17 may become zero). If corruption is occurring during exit, use printf() (or kprintf(), etc.) with Delay(n) to slow down your cleanup and broadcast each step. See &amp;lt;exec/alerts.h&amp;gt; for Amiga-specific alert numbers. Also see &quot;Crashes - After Exit&quot; below.<br /> <br /> ==== After Exit ====<br /> <br /> If this only happens when you start your program from Workbench, then you are probably UnLocking() one of the WBStartup message wa_Locks, or UnLocking() the Lock() returned from an initial CurrentDir() call. If you CurrentDir(), save the lock returned initially, and CurrentDir() back to it before you exit. Only UnLock() locks that ''you'' created.<br /> <br /> If you are crashing from both Workbench and CLI, and you are only crashing ''after'' exit, then you are probably either freeing/closing something twice, or freeing/closing something your did not actually allocate/open, or you may be leaving an outstanding device I/O request or other wakeup request. You must abort and WaitIO() any outstanding I/O requests before you free things and exit (see the Autodocs for your device, and for Exec AbortIO() and WaitIO()). Similar problems can be caused by deleting a subtask that might be in a WaitTOF(). Only delete subtasks when you are sure they are in a safe state such as Wait(0L).<br /> <br /> ==== Subtasks, Interrupts ====<br /> <br /> If part of your code runs on a different stack or the system stack, you must turn off compiler stack-checking options. If part of your code is called directly by the system or by other tasks, you must use long code/long data or use special compiler flags or options to assure that the correct base registers are set up for your subtask or interrupt code.<br /> <br /> ==== Window Related ====<br /> <br /> Be careful not to CloseWindow() a window during a while(msg=GetMsg(...)) loop on that window's port (next GetMsg() would be on freed pointer). Also, use ModifyIDCMP(NULL) with care, especially if using one port with multiple windows. Be sure to ClearMenuStrip() any menus before closing a window, and do not free items such as dynamically allocated gadgets and menus while they are attached to a window. Do not reference an IntuiMessage's IAddress field as a structure pointer of any kind before determining it ''is'' a structure pointer (this depends on the Class of the IntuiMessage). If a crash or problem only occurs when opening a window after extended use of your program, check to make sure that your program is properly freeing up signals allocated indirectly by CreatePort(), OpenWindow() or ModifyIDCMP().<br /> <br /> ==== Workbench Only ====<br /> <br /> If you are crashing near the first DOS call, either your stack is too small or your startup code did not GetMsg() the WBStartup message from the process message port. If your program crashes during execution or during your exit procedure only when started from Workbench, and your startup opens no stdio window or &quot;NIL:&quot; file handles for WB programs, then make sure you are not writing anything to stdout (printf(), etc.) when started from WB (argc==0). See also &quot;Crashes - After Exit&quot;.<br /> <br /> === Device-related Problems ===<br /> <br /> Device-related problems may caused by: improperly initialized port or I/O request structures (use CreatePort() and CreateExtIO()); use of a too-small I/O request (see the device's &amp;lt;.h&amp;gt; files and Autodocs for information on the required type of I/O request); re-use of an I/O request before it has returned from the device (use the debugging tool ''IO_Torture'' to catch this); failure to abort and wait for an outstanding device request before exiting; waiting on a signal/port/message allocated by a different task.<br /> <br /> === Disk Icon Won't Go Away ===<br /> <br /> This occurs when a program leaves a Lock() on one or more of a disk's files or directories. A memory loss of exactly 24 bytes is usually Lock() which has not been UnLocked().<br /> <br /> === DOS-related Problems ===<br /> <br /> In general, any dos.library function which fills in a structure for you (for example, Examine()), requires that the structure be longword aligned. In most cases, the only way to insure longword alignment in C is to dynamically allocate the structure. Unless documented otherwise, dos.library functions may only be called from a process, not from a task. Also note that a process's pr_MsgPort is intended for the exclusive use of dos.library. (The port may be used to receive a WbStartup message as long as the message is GetMsg()'d from the port before DOS is used.<br /> <br /> === Fails only on machines with Fast RAM ===<br /> <br /> Data and buffers which will be accessed directly by the custom chips ''must'' be in Chip RAM. This includes bitplanes (use OpenScreen() or AllocRaster()), audio samples, trackdisk buffers, and the graphic image data for sprites, pointers, bobs, images, gadgets, etc. Use compiler or linker flags to force Chip RAM loading of any initialized data needing to be in Chip RAM, or dynamically allocate Chip RAM and copy any initialization data there.<br /> <br /> === Graphics: Corrupted Images ===<br /> <br /> On classic Amiga hardware, the bit data for graphic images such as sprites, pointers, bobs, and gadgets ''must'' be in Chip RAM. Check your compiler manual for directives or flags which will place your graphic image data in Chip RAM. Or dynamically allocate Chip RAM and copy them there.<br /> <br /> === Hangs ===<br /> <br /> ==== One Program Only ====<br /> <br /> Program hangs are generally caused by Wait()ing on the wrong signal bits, on the wrong port, on the wrong message, or on some other event that will never occur. This can occur if the event you are waiting on is not coming, or if one task tries to Wait(), WaitPort(), or WaitIO() on a signal, port, or window that was created by a different task. Both WaitIO() and WaitPort() can call Wait(), and you cannot Wait() on another task's signals. Hangs can also be caused by verify deadlocks. Be sure to turn off all Intuition verify messages (such as MENUVERIFY) before calling AutoRequest() or doing disk access.<br /> <br /> ==== Whole System ====<br /> <br /> This is generally caused by a Disable() without a corresponding Enable(). It can also be caused by memory corruption, especially corruption of low memory. See &quot;Crashes and Memory Corruption&quot;.<br /> <br /> === Memory Loss ===<br /> <br /> First determine that your program is actually causing a memory loss. It is important to boot with a standard Workbench because a number of third party items such as some background utilities, shells, and network handlers dynamically allocate and free pieces of memory. Open a Shell for memory checking, and a Shell or Workbench drawer for starting your program. Arrange windows so that all are accessible, and so that no window rearrangement will be needed to run your program.<br /> <br /> In the Shell, type Avail FLUSH&amp;lt;RET&amp;gt; several times (2.0 option). This will flush all non-open disk-loaded fonts, devices, etc., from memory. Note the amount of free memory. Now without rearranging any windows, start your program and use all of your program features. Exit your program, wait a few seconds, then type Avail FLUSH&amp;lt;RET&amp;gt; several times. Note the amount of free memory. If this matches the first value you noted, your program is fine, and is not causing a memory loss.<br /> <br /> If memory was actually lost, and your program can be run from CLI or Workbench, then try the above procedure with both methods of starting your program. Note that under 2.0, there will be a slight permanent (until reboot) memory usage of about 672 bytes when the audio.device or narrator.device is first opened. See &quot;Memory Loss - CLI Only&quot; and &quot;Memory Loss - WB Only&quot; if appropriate. If you lose memory from both WB and CLI, then check all of the open/alloc/get/create/lock type calls in your code, and make sure that there is a matching close/free/delete/unlock type call for each of them (note - there are a few system calls that have or require no corresponding free - check the Autodocs). Generally, the close/free/delete/unlock calls should be in opposite order of the allocations.<br /> <br /> If you are losing a fixed small amount of memory, look for a structure of that size in the Structure Offsets listing in the SDK. For example, a loss of exactly 24 bytes is probably a Lock() which has not been UnLocked(). If you are using ScrollRaster(), be aware that ScrollRaster() left or right in a Superbitmap window with no TmpRas will lose memory under 1.3 (workaround - attach a TmpRas). If you lose much more memory when started from Workbench, make sure your program is not using Exit(n). This would bypass startup code cleanups and prevent a Workbench-loaded program from being unloaded. Use exit(n) instead.<br /> <br /> ==== CLI Only ====<br /> <br /> Make sure you are testing in a standard environment. Some third-party shells dynamically allocate history buffers, or cause other memory fluctuations. Also, if your program executes different code when started from CLI, check that code and its cleanup. And check your startup.asm if you wrote your own.<br /> <br /> ==== CTRL-C Exit Only ====<br /> <br /> You have Amiga-specific resources opened or allocated and you have not disabled your compiler's automatic Ctrl-C handling (causing all of ''your'' program cleanups to be skipped). Disable the compiler‚Äôs Ctrl-C handling and handle Ctrl-C (SIGBREAKF_CTRL_C) yourself.<br /> <br /> ==== During Execution ====<br /> <br /> A continuing memory loss during execution can be caused by failure to keep up with voluminous IDCMP messages such as MOUSEMOVE messages. Intuition cannot re-use IDCMP message blocks until you ReplyMsg() them. If your window's allotted message blocks are all in use, new sets will be allocated and not freed till the window is closed. Continuing memory losses can also be caused by a program loop containing an allocation-type call without a corresponding free.<br /> <br /> ==== Workbench Only ====<br /> <br /> Commonly, this is caused by a failure of your code to unload after you exit. Make sure that your code is being linked with a standard correct startup module, and do ''not'' use the Exit(n) function to exit your program. This function will bypass your startup code's cleanup, including its ReplyMsg() of the WBStartup message (which would signal Workbench to unload your program from memory). You should exit via either exit(n) where ''n'' is a valid DOS error code such as RETURN_OK (&amp;lt;dos/libraries.h&amp;gt;), or via final &quot;}&quot; or return. Assembler programmers using startup code can JMP to _exit with a long return value on stack, or use the RTS instruction.<br /> <br /> === Menu Problems ===<br /> <br /> A flickering menu is caused by leaving a pixel or more space between menu subitems when designing your menu. Crashing after browsing a menu (looking at menu without selecting any items) is caused by not properly handling MENUNULL select messages. Multiple selection not working is caused by not handling NextSelect properly. See the [[Intuition_Menus|Intuition Menus]] chapter.<br /> <br /> === Out-of-Sync Response to Input ===<br /> <br /> Caused by failing to handle all received signals or all possible messages after a Wait() or WaitPort() call. More than one event or message may have caused your program to awakened. Check the signals returned by Wait() and act on every one that is set. At ports which may have more than one message (for instance, a window's IDCMP port), you must handle the messages in a while(msg=GetMsg(...)) loop.<br /> <br /> === Performance Loss in Other Processes ===<br /> <br /> This is often caused by a one program doing one or more of the following: busy waiting or polling; running at a higher priority; doing lengthy Forbids(), Disables(), or interrupts.<br /> <br /> === Windows ===<br /> <br /> ==== Borders Flicker after Resize ====<br /> <br /> Set the NOCAREREFESH flag. Even SMART_REFRESH windows may generate refresh events if there is a sizing gadget. If you don't have specific code to handle this, you must set the NOCAREREFRESH flag. If you do have refresh code, be sure to use the Begin()/EndRefresh() calls. Failure to do one or the other will leave Intuition in an intermediate state, and slow down operation for all windows on the screen.<br /> <br /> ==== Visual Problems ====<br /> <br /> Many visual problems in windows can be caused by improper font specification or improper setting of gadget flags. See &quot;Release 4 Compatibility&quot; for detailed information on common problems.<br /> <br /> == General Debugging Techniques ==<br /> <br /> === Narrow the search ===<br /> <br /> Use methodical testing procedures, and debugging messages if necessary, to locate the problem area. Low level code can be debugged using IExec-&gt;DebugPrintf() serial messages. Check the initial values, allocation, use, and freeing of all pointers and structures used in the problem area. Check that all of your system and internal function calls pass correct initialized arguments, and that all possible error returns are checked for and handled.<br /> <br /> === Isolate the problem ===<br /> <br /> If errors cannot be found, simplify your code to the smallest possible example that still functions. Often you will find that this smallest example will not have the problem. If so, add back the other features of your code until the problem reappears, then debug that section.<br /> <br /> === Use debugging tools ===<br /> <br /> A variety of debugging tools are available to help locate faulty code. Some of these are source level and other debuggers (e.g. [[GDB_for_Beginners|GDB]] or [http://os4depot.net/index.php?function=showfile&amp;file=development/debug/db101.lha db101]), crash interceptors, vital watchdog and memory invalidation tools like [http://os4depot.net/index.php?function=showfile&amp;file=development/debug/memguard.lha ''MemGuard''].<br /> <br /> AmigaOS4's [[Debug Kernel]] is probably the first step of practical debugging.<br /> <br /> == A Final Word About Testing ==<br /> <br /> Test your program with memory watchdog and invalidation tools on a wide variety of systems and configurations. Programs with coding errors may appear to work properly on one or more configurations, but may fail or cause fatal problems on another. Test all of your program functions on every machine.<br /> <br /> Test all error and abort code. A program with missing error checks or unsafe cleanup might work fine when all of the items it opens or allocates are available, but may fail fatally when an error or problem is encountered. Try your code with missing files, filenames with spaces, incorrect filenames, cancelled requesters, Ctrl-C, missing libraries or devices, low memory, missing hardware, etc.<br /> <br /> Test all of your text input functions with high-ASCII characters (such as the character produced by pressing Alt-F then &quot;A&quot;). Note that RAWKEY codes can be different keyboard characters on national keyboards (higher levels of keyboard input are automatically translated to the proper characters).<br /> <br /> Write good code. Test it. Then make it great.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Troubleshooting_Your_Software&diff=4847 Troubleshooting Your Software 2013-02-18T15:42:11Z <p>Alexandre Balaban: /* Use debugging tools */ Added internal and external links for GDB, debug kernel, db101 and memguard.</p> <hr /> <div>== Troubleshooting Your Software ==<br /> <br /> Many Amiga programming errors have classic symptoms. This guide will help you to eliminate or avoid these problems in your software.<br /> <br /> == Typical Problems ==<br /> <br /> === Audio: Corrupted Samples ===<br /> <br /> On Classic Amiga hardware, the bit data for audio samples ''must'' be in Chip RAM. Check your compiler manual for directives or flags which will place your audio sample data in Chip RAM. Or dynamically allocate Chip RAM and copy or load the audio sample there.<br /> <br /> === Character Input/Output Problems ===<br /> <br /> RAWKEY users must be aware that RAWKEY codes can be different letters or symbols on national keyboards. If you need to use RAWKEY, run the codes through RawKeyConvert() (see the [[Intuition_Mouse_and_Keyboard|Intuition Mouse and Keyboard]]) to get proper translation to correct ASCII codes. Improper display or processing of high-ASCII international characters can be caused by incorrect tolower()/toupper(), or by sign extension of character values when switched on or assigned into larger size variables. Use unsigned variables such as UBYTE (not char) for strings and characters whenever possible. Internationally correct string functions are provided in the utility.library.<br /> <br /> === CLI Error Message Problems ===<br /> <br /> Improper error messages are caused by calling exit(n) with an invalid or missing return value ''n''. Assembler programmers using startup code should jump to the startup code's _exit with a valid return value on the stack. Programs without startup code should return with a valid value in D0. Valid return values such as RETURN_OK, RETURN_WARN, RETURN_FAIL are defined in &amp;lt;dos/dos.h&amp;gt; and &amp;lt;dos/dos.i&amp;gt;. Values outside of these ranges (-1 for instance) can cause invalid CLI error messages such as &quot;not an object module&quot;. Useful hint - if your program is called from a script, your valid return value can be conditionally branched on in the script (i.e., call program, then perform actions based on IF WARN or IF NOT WARN). RETURN_FAIL will cause the script to stop if a normal FAILAT value is being used in script.<br /> <br /> === CLI Won't Close on RUN ===<br /> <br /> A CLI can't close if a program has a Lock() on the CLI input or output stream (&amp;quot;*&amp;quot;). If your program is &quot;RUN &amp;gt;NIL:&quot; from a CLI, that CLI should be able to close unless your code or your compiler's startup code explicitly opens &amp;quot;*&amp;quot;.<br /> <br /> === Crashes and Memory Corruption ===<br /> <br /> Memory corruption, address errors, and illegal instruction errors are generally caused by use of an uninitialized, incorrectly initialized, or already freed/closed pointer or memory. You may be using the pointer directly, or it may be one that you placed (or forgot to place) in a structure passed to system calls. Or you may be overwriting one of your arrays, or accidentally modifying or incrementing a pointer later used in a free/close. Be sure to test the return of all open/allocation type functions before using the result, and only close/free things that you successfully opened/allocated. Use watchdog/torture utilities such as ''MemGuard'' to catch use of uninitialized pointers or freed memory, and other memory misuse problems. Use the debugging tool ''TNT'' to get additional debugging information instead of a Software Error requester. You may also be overflowing your stack - your compiler's stack checking option may be able to catch this. Cut stack usage by dynamically allocating large structures, buffers, and arrays which are currently defined inside your functions.<br /> <br /> Corruption or crashes can also be caused by passing wrong or missing arguments to a system call (for example SetAPen(3) or SetAPen(win,3), instead of SetAPen(rp,3)). C programmers should use function prototypes to catch such errors. If using short integers be sure to explicitly type long constants as long (e.g., 42L). (For example, with short ints, 1 &amp;lt;&amp;lt; 17 may become zero). If corruption is occurring during exit, use printf() (or kprintf(), etc.) with Delay(n) to slow down your cleanup and broadcast each step. See &amp;lt;exec/alerts.h&amp;gt; for Amiga-specific alert numbers. Also see &quot;Crashes - After Exit&quot; below.<br /> <br /> ==== After Exit ====<br /> <br /> If this only happens when you start your program from Workbench, then you are probably UnLocking() one of the WBStartup message wa_Locks, or UnLocking() the Lock() returned from an initial CurrentDir() call. If you CurrentDir(), save the lock returned initially, and CurrentDir() back to it before you exit. Only UnLock() locks that ''you'' created.<br /> <br /> If you are crashing from both Workbench and CLI, and you are only crashing ''after'' exit, then you are probably either freeing/closing something twice, or freeing/closing something your did not actually allocate/open, or you may be leaving an outstanding device I/O request or other wakeup request. You must abort and WaitIO() any outstanding I/O requests before you free things and exit (see the Autodocs for your device, and for Exec AbortIO() and WaitIO()). Similar problems can be caused by deleting a subtask that might be in a WaitTOF(). Only delete subtasks when you are sure they are in a safe state such as Wait(0L).<br /> <br /> ==== Subtasks, Interrupts ====<br /> <br /> If part of your code runs on a different stack or the system stack, you must turn off compiler stack-checking options. If part of your code is called directly by the system or by other tasks, you must use long code/long data or use special compiler flags or options to assure that the correct base registers are set up for your subtask or interrupt code.<br /> <br /> ==== Window Related ====<br /> <br /> Be careful not to CloseWindow() a window during a while(msg=GetMsg(...)) loop on that window's port (next GetMsg() would be on freed pointer). Also, use ModifyIDCMP(NULL) with care, especially if using one port with multiple windows. Be sure to ClearMenuStrip() any menus before closing a window, and do not free items such as dynamically allocated gadgets and menus while they are attached to a window. Do not reference an IntuiMessage's IAddress field as a structure pointer of any kind before determining it ''is'' a structure pointer (this depends on the Class of the IntuiMessage). If a crash or problem only occurs when opening a window after extended use of your program, check to make sure that your program is properly freeing up signals allocated indirectly by CreatePort(), OpenWindow() or ModifyIDCMP().<br /> <br /> ==== Workbench Only ====<br /> <br /> If you are crashing near the first DOS call, either your stack is too small or your startup code did not GetMsg() the WBStartup message from the process message port. If your program crashes during execution or during your exit procedure only when started from Workbench, and your startup opens no stdio window or &quot;NIL:&quot; file handles for WB programs, then make sure you are not writing anything to stdout (printf(), etc.) when started from WB (argc==0). See also &quot;Crashes - After Exit&quot;.<br /> <br /> === Device-related Problems ===<br /> <br /> Device-related problems may caused by: improperly initialized port or I/O request structures (use CreatePort() and CreateExtIO()); use of a too-small I/O request (see the device's &amp;lt;.h&amp;gt; files and Autodocs for information on the required type of I/O request); re-use of an I/O request before it has returned from the device (use the debugging tool ''IO_Torture'' to catch this); failure to abort and wait for an outstanding device request before exiting; waiting on a signal/port/message allocated by a different task.<br /> <br /> === Disk Icon Won't Go Away ===<br /> <br /> This occurs when a program leaves a Lock() on one or more of a disk's files or directories. A memory loss of exactly 24 bytes is usually Lock() which has not been UnLocked().<br /> <br /> === DOS-related Problems ===<br /> <br /> In general, any dos.library function which fills in a structure for you (for example, Examine()), requires that the structure be longword aligned. In most cases, the only way to insure longword alignment in C is to dynamically allocate the structure. Unless documented otherwise, dos.library functions may only be called from a process, not from a task. Also note that a process's pr_MsgPort is intended for the exclusive use of dos.library. (The port may be used to receive a WbStartup message as long as the message is GetMsg()'d from the port before DOS is used.<br /> <br /> === Fails only on machines with Fast RAM ===<br /> <br /> Data and buffers which will be accessed directly by the custom chips ''must'' be in Chip RAM. This includes bitplanes (use OpenScreen() or AllocRaster()), audio samples, trackdisk buffers, and the graphic image data for sprites, pointers, bobs, images, gadgets, etc. Use compiler or linker flags to force Chip RAM loading of any initialized data needing to be in Chip RAM, or dynamically allocate Chip RAM and copy any initialization data there.<br /> <br /> === Graphics: Corrupted Images ===<br /> <br /> On classic Amiga hardware, the bit data for graphic images such as sprites, pointers, bobs, and gadgets ''must'' be in Chip RAM. Check your compiler manual for directives or flags which will place your graphic image data in Chip RAM. Or dynamically allocate Chip RAM and copy them there.<br /> <br /> === Hangs ===<br /> <br /> ==== One Program Only ====<br /> <br /> Program hangs are generally caused by Wait()ing on the wrong signal bits, on the wrong port, on the wrong message, or on some other event that will never occur. This can occur if the event you are waiting on is not coming, or if one task tries to Wait(), WaitPort(), or WaitIO() on a signal, port, or window that was created by a different task. Both WaitIO() and WaitPort() can call Wait(), and you cannot Wait() on another task's signals. Hangs can also be caused by verify deadlocks. Be sure to turn off all Intuition verify messages (such as MENUVERIFY) before calling AutoRequest() or doing disk access.<br /> <br /> ==== Whole System ====<br /> <br /> This is generally caused by a Disable() without a corresponding Enable(). It can also be caused by memory corruption, especially corruption of low memory. See &quot;Crashes and Memory Corruption&quot;.<br /> <br /> === Memory Loss ===<br /> <br /> First determine that your program is actually causing a memory loss. It is important to boot with a standard Workbench because a number of third party items such as some background utilities, shells, and network handlers dynamically allocate and free pieces of memory. Open a Shell for memory checking, and a Shell or Workbench drawer for starting your program. Arrange windows so that all are accessible, and so that no window rearrangement will be needed to run your program.<br /> <br /> In the Shell, type Avail FLUSH&amp;lt;RET&amp;gt; several times (2.0 option). This will flush all non-open disk-loaded fonts, devices, etc., from memory. Note the amount of free memory. Now without rearranging any windows, start your program and use all of your program features. Exit your program, wait a few seconds, then type Avail FLUSH&amp;lt;RET&amp;gt; several times. Note the amount of free memory. If this matches the first value you noted, your program is fine, and is not causing a memory loss.<br /> <br /> If memory was actually lost, and your program can be run from CLI or Workbench, then try the above procedure with both methods of starting your program. Note that under 2.0, there will be a slight permanent (until reboot) memory usage of about 672 bytes when the audio.device or narrator.device is first opened. See &quot;Memory Loss - CLI Only&quot; and &quot;Memory Loss - WB Only&quot; if appropriate. If you lose memory from both WB and CLI, then check all of the open/alloc/get/create/lock type calls in your code, and make sure that there is a matching close/free/delete/unlock type call for each of them (note - there are a few system calls that have or require no corresponding free - check the Autodocs). Generally, the close/free/delete/unlock calls should be in opposite order of the allocations.<br /> <br /> If you are losing a fixed small amount of memory, look for a structure of that size in the Structure Offsets listing in the SDK. For example, a loss of exactly 24 bytes is probably a Lock() which has not been UnLocked(). If you are using ScrollRaster(), be aware that ScrollRaster() left or right in a Superbitmap window with no TmpRas will lose memory under 1.3 (workaround - attach a TmpRas). If you lose much more memory when started from Workbench, make sure your program is not using Exit(n). This would bypass startup code cleanups and prevent a Workbench-loaded program from being unloaded. Use exit(n) instead.<br /> <br /> ==== CLI Only ====<br /> <br /> Make sure you are testing in a standard environment. Some third-party shells dynamically allocate history buffers, or cause other memory fluctuations. Also, if your program executes different code when started from CLI, check that code and its cleanup. And check your startup.asm if you wrote your own.<br /> <br /> ==== CTRL-C Exit Only ====<br /> <br /> You have Amiga-specific resources opened or allocated and you have not disabled your compiler's automatic Ctrl-C handling (causing all of ''your'' program cleanups to be skipped). Disable the compiler‚Äôs Ctrl-C handling and handle Ctrl-C (SIGBREAKF_CTRL_C) yourself.<br /> <br /> ==== During Execution ====<br /> <br /> A continuing memory loss during execution can be caused by failure to keep up with voluminous IDCMP messages such as MOUSEMOVE messages. Intuition cannot re-use IDCMP message blocks until you ReplyMsg() them. If your window's allotted message blocks are all in use, new sets will be allocated and not freed till the window is closed. Continuing memory losses can also be caused by a program loop containing an allocation-type call without a corresponding free.<br /> <br /> ==== Workbench Only ====<br /> <br /> Commonly, this is caused by a failure of your code to unload after you exit. Make sure that your code is being linked with a standard correct startup module, and do ''not'' use the Exit(n) function to exit your program. This function will bypass your startup code's cleanup, including its ReplyMsg() of the WBStartup message (which would signal Workbench to unload your program from memory). You should exit via either exit(n) where ''n'' is a valid DOS error code such as RETURN_OK (&amp;lt;dos/libraries.h&amp;gt;), or via final &quot;}&quot; or return. Assembler programmers using startup code can JMP to _exit with a long return value on stack, or use the RTS instruction.<br /> <br /> === Menu Problems ===<br /> <br /> A flickering menu is caused by leaving a pixel or more space between menu subitems when designing your menu. Crashing after browsing a menu (looking at menu without selecting any items) is caused by not properly handling MENUNULL select messages. Multiple selection not working is caused by not handling NextSelect properly. See the [[Intuition_Menus|Intuition Menus]] chapter.<br /> <br /> === Out-of-Sync Response to Input ===<br /> <br /> Caused by failing to handle all received signals or all possible messages after a Wait() or WaitPort() call. More than one event or message may have caused your program to awakened. Check the signals returned by Wait() and act on every one that is set. At ports which may have more than one message (for instance, a window's IDCMP port), you must handle the messages in a while(msg=GetMsg(...)) loop.<br /> <br /> === Performance Loss in Other Processes ===<br /> <br /> This is often caused by a one program doing one or more of the following: busy waiting or polling; running at a higher priority; doing lengthy Forbids(), Disables(), or interrupts.<br /> <br /> === Windows ===<br /> <br /> ==== Borders Flicker after Resize ====<br /> <br /> Set the NOCAREREFESH flag. Even SMART_REFRESH windows may generate refresh events if there is a sizing gadget. If you don't have specific code to handle this, you must set the NOCAREREFRESH flag. If you do have refresh code, be sure to use the Begin()/EndRefresh() calls. Failure to do one or the other will leave Intuition in an intermediate state, and slow down operation for all windows on the screen.<br /> <br /> ==== Visual Problems ====<br /> <br /> Many visual problems in windows can be caused by improper font specification or improper setting of gadget flags. See &quot;Release 4 Compatibility&quot; for detailed information on common problems.<br /> <br /> == General Debugging Techniques ==<br /> <br /> === Narrow the search ===<br /> <br /> Use methodical testing procedures, and debugging messages if necessary, to locate the problem area. Low level code can be debugged using IExec-&gt;DebugPrintf() serial messages. Check the initial values, allocation, use, and freeing of all pointers and structures used in the problem area. Check that all of your system and internal function calls pass correct initialized arguments, and that all possible error returns are checked for and handled.<br /> <br /> === Isolate the problem ===<br /> <br /> If errors cannot be found, simplify your code to the smallest possible example that still functions. Often you will find that this smallest example will not have the problem. If so, add back the other features of your code until the problem reappears, then debug that section.<br /> <br /> === Use debugging tools ===<br /> <br /> A variety of debugging tools are available to help locate faulty code. Some of these are source level and other debuggers (e.g. [[GDB_for_Beginners|GDB]] or [http://os4depot.net/index.php?function=showfile&amp;file=development/debug/db101.lha db101]), crash interceptors, vital watchdog and memory invalidation tools like [http://os4depot.net/index.php?function=showfile&amp;file=development/debug/memguard.lha ''MemGuard''].<br /> <br /> AmigaOS4's [[Debug Kernel]] is probably the first step of practical debugging.<br /> <br /> == A Final Word About Testing ==<br /> <br /> Test your program with memory watchdog and invalidation tools on a wide variety of systems and configurations. Programs with coding errors may appear to work properly on one or more configurations, but may fail or cause fatal problems on another. Test all of your program functions on every machine.<br /> <br /> Test all error and abort code. A program with missing error checks or unsafe cleanup might work fine when all of the items it opens or allocates are available, but may fail fatally when an error or problem is encountered. Try your code with missing files, filenames with spaces, incorrect filenames, cancelled requesters, Ctrl-C, missing libraries or devices, low memory, missing hardware, etc.<br /> <br /> Test all of your text input functions with high-ASCII characters (such as the character produced by pressing Alt-F then &quot;A&quot;). Note that RAWKEY codes can be different keyboard characters on national keyboards (higher levels of keyboard input are automatically translated to the proper characters).<br /> <br /> Write good code. Test it. Then make it great.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Debug_Kernel&diff=4846 Debug Kernel 2013-02-18T15:31:05Z <p>Alexandre Balaban: /* Introduction */ Added link to GDB article</p> <hr /> <div>== Introduction ==<br /> <br /> When you program AmigaOS, sooner or later you will need tools to help you debugging your applications (and not only yours) and find out why a program crashes or runs erratically. For that you have debuggers ([[GDB_for_Beginners|GDB]] and db101), small tools to operate with debug outputs and level of debugging (like KDebug, DumpDebugBuffer, etc.), memory trackers (like MemGuard and MemGuardian) and bunch of other small utilities and tricks which help you to do debugging. One of those tricks, which is little known, is the fact that a debug version of the standard AmigaOS kernel is publicly released ready to use for third party developers (or advanced users willing to help debugging applications).<br /> <br /> == What is &quot;debug kernel&quot; exactly? ==<br /> <br /> Debug kernel is the same kernel as the standard one, but with added code to help catching some of the most common programming errors. This additional code adds a slightly (yet non negligible) amount of bytes to the kernel size (around half a megabyte) and is slower. Thus it has been removed from the standard kernel for performance and optimization purposes and is generally not useful to the standard average user. Yet this debug kernel can prove itself to be very useful to programmers, especially it provides the following functionality:<br /> <br /> # Clobbering of succ/pred pointers when a Node is removed from a list.&lt;BR&gt;One common mistake when dealing with lists is to access successor and/or previous pointers from a removed node. While at first sight it may seems harmless it may reveal itself as a dreadful bug to spot because once a node has been removed from a list nothing guarantees you that the list has not be reworked/copied/reallocated or anything else. This will give you a bug that is very hard to reproduce or even non reproducible at all. As such one should NEVER access Succ/Prev pointers from a removed Node, instead if he needs to, he should access them prior the call to Remove(). To help spotting such errors the debug kernel will modify the ln_Succ/ln_Prev pointer to a given fixed invalid value thus the bug automatically becomes always reproducible. This will also help you spot the fact one is trying to free a Node twice.<br /> # &quot;Munge&quot; of memory. With this option the debug kernel will do additional action/tests on memory pointers. It will help spotting what really happen under the hood just by observing the DAR (or DEAR on 440ep and 460ex CPUs) value in the crash log:<br /> #* If the value is 0x00000000 (or thereabouts), then you have accessed a null pointer (and you will see 0xABADCAFE in one or more registers);<br /> #* If the value is 0xDEADBEEF (or thereabouts), then you have accessed some memory after it has been freed;<br /> #* If the value is 0xCCCCCCCC (or thereabouts), then you have tried to free a Node a second time.<br /> <br /> So, in other words, when you want to catch all those problems which you can't catch by just simply running MemGuard on a &quot;plain&quot; kernel, you should use the debug kernel. I.e. to say it more clear, when you develop anything you should be just on debug kernel (+MemGuard/MemGuardian at background ? or its all already in debug kernel?)<br /> <br /> == How to set it up ==<br /> <br /> The kernel.debug file is already placed in the Kickstart directory with all the other kernel modules. By default this directory is &quot;SYS:Kickstart/&quot;.<br /> <br /> The preferred way to use the debug kernel is to edit your file kicklayout, duplicate the current layout and in this duplicate modify the line pointing to &quot;kernel&quot; so it points to &quot;kernel.debug&quot;, do not forget to change the label associated with this new layout by adding 'debug' or something. This way you will be able to choose which configuration you want to boot at any time.<br /> <br /> Once you have setup the use of the debug kernel, you must also add some commandline options to the kernel. Basically in order to activate the munge option of the debug kernel you must set the variable &quot;os4_commandline&quot; adds the keyword &quot;munge&quot;. This part depends on your platform firmware, please report to your documentation to know how to do it, but you have to do something like os4_commandline='munge', if os4_commandline is already set with another value you can concatenate using space e.g. os4_commandline='serial munge' to have the debug output to serial port and the munge option activated.<br /> <br /> The following kernel command line parameters are available:<br /> ; serial<br /> : Output all debug via the serial port.<br /> <br /> ; baudrate [N]<br /> : Sets the serial port baud rate to N.<br /> <br /> ; debuglevel [N]<br /> : Set the debuglevel where N is a value from 0 to 10.<br /> <br /> ; nofpuemu<br /> : Sets the AttnFlags in ExecBase to a 68020 without an FPU.<br /> <br /> ; munge<br /> : Munges freed memory so that memory trashing can be found more easily when using a debug kernel. Does nothing on a release kernel.<br /> <br /> {{Note|text=It is strongly recommended that you do not rename the kernel.debug file.}}<br /> <br /> == FAQ ==<br /> <br /> None at the moment<br /> <br /> [[Category:Debug]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&diff=4845 How to open and use the exec debug interface 2013-02-18T15:30:38Z <p>Alexandre Balaban: /* Tutorial */ Added link to GDB article</p> <hr /> <div>[[Category:Debug]]<br /> == Author ==<br /> <br /> Alfkil Wennermark&lt;br/&gt;<br /> Copyright (c) 2010 Alfkil Wennermark&lt;br/&gt;<br /> Used by permission.<br /> <br /> == Tutorial ==<br /> <br /> Next step in my small series concerns itself with how to open and use the &quot;secret&quot; (but very useful) debug interface. Thanks to Steven Solie and Thomas Frieden.<br /> <br /> Probably the best way to trap exceptions from within your code is to use the debug interface, that is &quot;hidden&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 the adtools project on sourceforge.net.<br /> <br /> 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'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.<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* debugtrap.cExample of use of the exec debug interface<br /> by Alfkil Wennermark 2010<br /> <br /> Thanks to Steven Solie, Thomas Frieden and others<br /> <br /> This code is partially copied from Thomas' GDB source<br /> */<br /> <br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;exec/interrupts.h&gt;<br /> #include &lt;exec/tasks.h&gt;<br /> <br /> #include &lt;dos/dos.h&gt;<br /> <br /> #include &lt;stdio.h&gt;<br /> <br /> <br /> struct DebugIFace *IDebug = 0;<br /> <br /> struct KernelDebugMessage<br /> {<br /> uint32 type;<br /> union<br /> {<br /> struct ExceptionContext *context;<br /> struct Library *library;<br /> } message;<br /> };<br /> <br /> static ULONG amigaos_debug_callback(struct Hook *, struct Task *, struct KernelDebugMessage *);<br /> <br /> struct Hook debug_hook;<br /> struct Task *amiga_task;<br /> <br /> BPTR exec_seglist;<br /> ULONG debug_data = 1234;<br /> <br /> void init()<br /> {<br /> IDebug = (struct DebugIFace *)IExec-&gt;GetInterface((struct Library *)SysBase, &quot;debug&quot;, 1, 0);<br /> if (!IDebug)<br /> {<br /> printf(&quot;Can't get DEBUG accessn&quot;);<br /> exit(RETURN_FAIL);<br /> }<br /> <br /> debug_hook.h_Entry = (ULONG (*)())amigaos_debug_callback;<br /> debug_hook.h_Data =(APTR)&amp;debug_data;<br /> <br /> /* NB: Ideally we would start up another task, that<br /> we want to debug, and attach ourselves to that<br /> task using the debug hook, but for simplicity<br /> we just use our own task here */<br /> amiga_task = IExec-&gt;FindTask(NULL);<br /> IDebug-&gt;AddDebugHook(amiga_task, &amp;debug_hook);<br /> }<br /> <br /> void end()<br /> {<br /> IDebug-&gt;AddDebugHook(amiga_task, 0);<br /> <br /> if (IDebug)IExec-&gt;DropInterface((struct Interface *)IDebug);<br /> IDebug = NULL;<br /> }<br /> <br /> <br /> <br /> ULONG<br /> amigaos_debug_callback(struct Hook *hook, struct Task *currentTask,<br /> struct KernelDebugMessage *dbgmsg)<br /> {<br /> struct ExecIFace *IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)-&gt;MainInterface;<br /> <br /> uint32 *data = (uint32 *)hook-&gt;h_Data;<br /> <br /> /* these are the 4 types of debug msgs: */<br /> switch (dbgmsg-&gt;type)<br /> {<br /> case DBHMT_REMTASK:<br /> *data = 9;<br /> break;<br /> <br /> case DBHMT_EXCEPTION:<br /> *data = 11;<br /> break;<br /> <br /> case DBHMT_OPENLIB:<br /> *data = 13;<br /> break;<br /> <br /> case DBHMT_CLOSELIB:<br /> *data = 15;<br /> break;<br /> <br /> default:<br /> *data = 0;<br /> break;<br /> }<br /> /* returning 1 will suspend the task ! */<br /> return 0;<br /> }<br /> <br /> <br /> int main()<br /> {<br /> init();<br /> <br /> /* Cause an exception on purpose: */<br /> uint32 *beef = 0;<br /> *beef = 0L;<br /> <br /> printf(&quot;We received a&quot;);<br /> switch (debug_data)<br /> {<br /> case 9:<br /> printf(&quot; REMTASK&quot;);<br /> break;<br /> case 11:<br /> printf(&quot;n EXCEPTION&quot;);<br /> break;<br /> case 13:<br /> printf(&quot;n OPENLIB&quot;);<br /> break;<br /> case 15:<br /> printf(&quot; CLOSELIB&quot;);<br /> break;<br /> <br /> default:<br /> printf(&quot;n unknown&quot;);<br /> break;<br /> }<br /> printf(&quot; signal!n&quot;);<br /> <br /> end();<br /> <br /> return RETURN_OK;<br /> }<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=The_Hacking_Way:_Part_1_-_First_Steps&diff=4844 The Hacking Way: Part 1 - First Steps 2013-02-18T15:30:09Z <p>Alexandre Balaban: /* Assembler programming using libc */ Added links to GDB article</p> <hr /> <div>= Author =<br /> <br /> Roman Kargin&lt;br/&gt;<br /> Copyright (c) 2012 Roman Kargin&lt;br/&gt;<br /> Proofread and grammar corrections by Daniel jedlicka.&lt;br/&gt;<br /> Used by permission.<br /> <br /> = Introduction =<br /> <br /> Back in the past, I wanted to make the smallest possible executables on UNIX-ish operating systems (SunOS, Tru64, OS9, OpenVMS and others). As a result of my research I wrote a couple of small tutorials for various hacking-related magazines (like Phrack or x25zine). Doing the same on AmigaOS naturally became a topic of interest for me - even more so when I started seeing, in Amiga forums, questions like &quot;Why are AmigaOS binaries bigger than they should be?&quot; Therefore I believe that producing small AmigaOS executables could make an interesting topic for an article. Further in the text I'll explain how ldscripts can help the linker make non-aligned binaries, and cover various other aspects associated with the topic. I hope that at least for programmers the article will be an interesting and thought-provoking read.<br /> <br /> Before you go on, please note that it is assumed here that you have basic programming skills and understanding of C and assembler, that you are familiar with BSD syntax, know how UNIX and AmigaOS work, and that you have the PPC V.4-ABI and ELF specification at hand. But if you don't, there's no need to stop reading as I'll try to cover the basics where necessary.<br /> <br /> = The Basics =<br /> <br /> To begin with, let's present and discuss some basic terms and concepts. We'll also dispel some popular myths.<br /> <br /> == The C standard library (libc) ==<br /> <br /> Thirty years ago, when the C language developed so much that its different implementations started to pose a practical problem, the American National Institute of Standards (ANSI) formed a committee for the standardization of the language. The standard, generally referred to as ANSI C, was finally adopted in 1989 (this is why it is sometimes called C89). Part of this standard was a library including common functions, called the &quot;C standard library&quot;, or &quot;C library&quot;, or &quot;libc&quot;. The library has been an inherent part of all subsequently adopted C standards.<br /> <br /> Libc is platform-independent in the sense that it provides the same functionality regardless of operating system - be it UNIX, Linux, AmigaOS, OpenVMS, whatever. The actual implementation may vary from OS to OS. For example in UNIX, the most popular implementation of the C standard library is glibc (GNU Library C). But there are others: uClibc (for embedded Linux systems, without MMU), dietlibc (as the name suggests, it is meant to compile/link programs to the smallest possible size) or Newlib. Originally developed for a wide range of embedded systems, Newlib is the preferred C standard library in AmigaOS and is now part of the kernel.<br /> <br /> On AmigaOS, three implementations of libc are used: clib2, newlib and vclib. The GCC compiler supports clib2 and newlib, the VBCC compiler supports newlib and vclib.<br /> <br /> === clib2 ===<br /> <br /> This is an Amiga-specific implementation originally written from scratch by Olaf Barthel, with some ideas borrowed from the BSD libc implementation, libnix, etc. Under AmigaOS, clib2 is most often used when maximum compatibility with POSIX is required. The GCC compiler distributed as part of the AmigaOS SDK uses Newlib by default (as if you used the -mcrt=newlib switch). An important note: clib2 is only available for static linking, while Newlib is opened at runtime (thus making your executables smaller). Clib2 is open source, the latest version can be found at http://sourceforge.net/projects/clib2/<br /> <br /> === Newlib ===<br /> <br /> A better and more modern libc implementation. While the AmigaOS version is closed source (all adaptations and additional work is done by the OS development team), it's based on the open source version of Newlib. The original version is maintained by RedHat developer Jeff Johnston, and is used in most commercial and non-commercical GCC ports for non-Linux embedded systems: http://www.sourceware.org/newlib/<br /> <br /> Newlib does not cover the ANSI C99 standard only: it's an expanded library that also includes common POSIX functions (clib2 implements them as well). But certain POSIX functions - such as glob(), globfree(), or fork() - are missing; and while some of them are easy to implement, others are not - fork() being an example of the latter.<br /> <br /> Newlib is also available as a shared object.<br /> <br /> === vclib ===<br /> <br /> This library was made for the vbcc compiler. Like clib2 it is linked statically, but only provides ANSI C/C99 functions (i.e. no POSIX).<br /> <br /> = Myth #1: AmigaOS behaves like UNIX =<br /> <br /> From time to time you can hear voices saying that AmigaOS is becoming UNIX. This popular myth stems from three main sources. First, many games, utilities and libraries are ported over from the UNIX world. Second, AmigaOS uses genuine ELF, the standard binary file format used in UNIX and UNIX-like systems. Third, the OS supports, as of version 4.1, shared objects. All of this enables AmigaOS to provide more stuff for both programmers and users, and to complement native applications made for it. Today, it is quite normal that an operating system provides all the popular third-party libraries like SDL, OpenGL, Cairo, Boost, OpenAL, FreeType etc. Not only they make software development faster but they also allow platform-independent programming.<br /> <br /> Yet getting close to UNIX or Linux in terms of software or programming tools does not mean that AmigaOS behaves in the same way as regards, for example, library initialization, passing arguments or system calls. On AmigaOS there are no &quot;system calls&quot; as they are on UNIXes, where you can simply pass arguments to registers and then use an instruction (like &quot;int 0x80h&quot; on x86 Linux, &quot;trap 0&quot; on M68 Linux, or &quot;sc&quot; on some PPC/POWER CPU based OSes), which will cause a software interrupt and enter the kernel in supervisor mode. The concept of AmigaOS is completely different. There is no kernel as such; Amiga's Kickstart is actually a collection of libraries (of which &quot;kernel.kmod&quot; is just one module - a new incarnation of the original exec.library). Also, an AmigaOS program, when calling a library function, won’t enter supervisor mode but rather stays in user mode when the function is executed.<br /> <br /> [[File:HackingWayPart1-1.png|center]]<br /> <br /> Since the very first version of the OS that came with the Amigas in 1985, you must open a library and use its vector table to execute a library function, so there’s no &quot;system call&quot; involved. The pointer to the first library (exec.library) is always at address 4 and that hasn’t changed in AmigaOS.<br /> <br /> When you program in assembler under AmigaOS, you cannot do much until you initialize and open all the needed libraries (unlike, for example, on UNIX where the kernel does all the necessary initialisation for you).<br /> <br /> = Myth #2: AmigaOS binaries are fat =<br /> <br /> This misunderstanding stems from the fact that the latest AmigaOS SDK uses a newer version of binutils, which now aligns ELF segments to 64K so that they can be easily loaded with mmap(). Binutils are, naturally, developed with regard to UNIX-like OSes where the mmap() function actually exists so the modifications make sense - but since mmap() isn’t a genuine AmigaOS function (it’s just a wrapper using AllocVec() etc.), this kind of alignment is not needed for AmigaOS.<br /> <br /> Luckily, the size difference is only noticeable in small programs, like Hello World, where the resulting executable grows to 65KB. Which of course is unbelievable and looks like something is wrong. But once you start programming for real and produce bigger programs, the code fills up the ELF segments as required, there’s little need for padding, and so there’s little size difference in the end. The worst-case scenario is ~64KB of extra padding, which only happens (as we said) in very small programs, or when you’re out of luck and your code only just exceeds a boundary between two segments.<br /> <br /> It is likely that a newer SDK will adapt binutils for AmigaOS and the padding will no longer be needed. Currently, to avoid alignment you can use the &quot;-N&quot; switch, which tells the linker to use an ldscript that builds non-aligned binaries. Check the SDK:gcc/ppc-AmigaOS/lib/ldscripts directory; all the files ending with an &quot;n&quot; (like “AmigaOS.xn” or “ELF32ppc.xbn”) are linker scripts that ensure non-aligned builds. Such a script will be used when the GCC compiler receives the “-N” switch. See the following:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type hello.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> main()<br /> {<br /> printf(&quot;aaaa&quot;);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 6/1.Work:&gt; gcc hello.c -o hello<br /> 6/1.Work:&gt; strip hello<br /> 6/1.Work:&gt; filesize format=%s hello <br /> 65k<br /> 6/1.Work:&gt; hello<br /> aaaa<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 6/1.Work:&gt; gcc -N hello.c -o hello<br /> 6/1.Work:&gt; strip hello<br /> 6/1.Work:&gt; filesize format=%s hello <br /> 5480<br /> 6/1.work:&gt; hello<br /> aaaa<br /> &lt;/pre&gt;<br /> <br /> = Genuine ELF executables =<br /> <br /> Just like libc, the Executable and Linkable Format (ELF) is a common standard. It is a file format used for executables, objects and shared libraries. It gets the most attention in connection with UNIX but it is really used on numerous other operating systems: all UNIX derivatives (Solaris, Irix, Linux, BSD, etc.), OpenVMS, several OSes used in mobile phones/devices, game consoles such as the PlayStation, the Wii and others. PowerUP, the PPC Amiga kernel made by Phase5 back in the 1990s used the ELF format as well.<br /> <br /> A more detailed description of the ELF internals will be given later; all you need to know for now is that the executable ELF file contains headers (the main header, and headers for the various sections) and sections/segments. The ELF file layout looks like this:<br /> <br /> [[File:HackingWayPart1-2.png|center]]<br /> <br /> AmigaOS uses genuine ELF executables versus relocatable objects.<br /> <br /> The advantage of objects is that they are smaller and that relocations are always included. But there is a drawback as well: the linker will not tell you automatically whether all symbols have been resolved because an object is allowed to have unresolved references. (On the other hand, vlink could always detect unresolved references when linking PowerUP objects because it sees them as a new format.) This is why ELF shared objects cannot be used easily (though it’s still kind of possible using some hacks), and it explains why the AmigaOS team decided to go for real executables.<br /> <br /> By specification, ELF files are meant to be executed from a fixed absolute address, and so AmigaOS programs need to be relocated (because all processes share the same address space). To do that, the compiler is passed the -q switch (&quot;keep relocations&quot;). Relocations are handled by the MMU, which will create a new virtual address space for each new process.<br /> <br /> If you look at the linker scripts provided to build AmigaOS executables (in the SDK:gcc/ppc-AmigaOS/lib/ldscripts directory), you’ll find the following piece of code:<br /> <br /> &lt;pre&gt;<br /> ENTRY(_start)<br /> ....<br /> SECTIONS<br /> {<br /> PROVIDE (__executable_start = 0x01000000); . = 0x01000000 + SIZEOF_HEADERS;<br /> [...]<br /> &lt;/pre&gt;<br /> <br /> As you can see, AmigaOS executables look like they are linked to be executed at an absolute address of 0x01000000. But this is only faked; the ELF loader and relocations will recalculate all absolute addresses in the program before it executes. Without relocations, each new process would be loaded at 0x01000000, where it would crash happily due to overwriting certain important areas, and because of other reasons. You may ask why 0x01000000 is used at all, considering that it’s just a placeholder and any number (be it 0x00000000, 0x99999999, 0xDEADBEEF or 0xFEEDFACE) can be used instead. We can speculate and assume that 0x01000000 was chosen because it is the beginning of the memory map accessible for instruction execution. But anyway, the value is currently not important.<br /> <br /> To perform a test, let’s see what happens if we build our binary without the &quot;-q&quot; switch (that is, without making the binary relocatable):<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type test.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> main()<br /> {<br /> printf(&quot;aaaa&quot;);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> shell:&gt; gcc test.c -S -o test.s<br /> shell:&gt; as test.s -o test<br /> shell:&gt; ld test.o -o test /SDK/newlib/lib/crtbegin.o /SDK/newlib/lib/LibC.a /SDK/newlib/lib/crtend.o<br /> &lt;/pre&gt;<br /> <br /> When you run the executable, you get a DSI with the 80000003 error, on the 0x1c offset in _start (i.e. the code from the crtbegin.o). Ignoring the error will produce a yellow recoverable alert. The crash occurs because we have compiled an ELF file to be executed at the 0x01000000 address, and as no &quot;-q&quot; switch was used, the remapping did not take place. To better understand why it happens you can check the crtbegin.o code, i.e. the code added to the binary at linking stage, which contains all the OS-dependent initialisations. If you know nothing about PPC assembler you can skip the following part for now and return when you’ve read the entire article:<br /> <br /> &lt;pre&gt;<br /> 6/0.RAM Disk:&gt; objdump -D --no-show-raw-insn --stop-address=0x10000d0 test | grep -A8 &quot;_start&quot;<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 010000b0 &lt;_start&gt;:<br /> <br /> 10000b0: stwu r1,-64(r1) #<br /> 10000b4: mflr r0 # prologue (reserve 64 byte stack frame)<br /> 10000b8: stw r0,68(r1) #<br /> <br /> 10000bc: lis r9,257 # 257 is loaded into the higher half-word (msw) of r9 (257 &lt;&lt; 16)<br /> 10000c0: stmw r25,36(r1) # offset into the stack frame <br /> 10000c4: mr r25,r3 # save command line stack pointer<br /> 10000c8: mr r27,r13 # r13 can be used as small data pointer in the V.4-ABI, and it also saved here<br /> 10000cc: stw r5,20(r9) # Write value (257 &lt;&lt; 16) + 20 = 0x01010014 to the r5 (DOSBase pointer)<br /> &lt;/pre&gt;<br /> <br /> The address in the last instruction points to a data segment starting at 0x010100000. But the address is invalid because, without any relocation, there is no data there and the MMU produces a data storage interrupt (DSI) error.<br /> <br /> Of course it is possible to make a working binary without relocation, if the program doesn’t need to relocate and you are lucky enough to have the 0x1000000 address free of important contents. And of course you can use a different address for the entry point, by hex-editing the binary or at build-time using self-made ldscripts. Making a non-relocatable binary will be discussed further in the text.<br /> <br /> = PowerPC assembly =<br /> <br /> In case you are not familiar and have no experience with PowerPC assembly, the following section will explain some basic terms and concepts.<br /> <br /> == Registers ==<br /> <br /> The PowerPC processor architecture provides 32 general-purpose registers and 32 floating-point registers. We’ll only be interested in certain general-purpose registers and a couple of special ones. The following overview describes the registers as they are used under AmigaOS:<br /> <br /> === General-purpose registers ===<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Register<br /> ! AmigaOS usage<br /> |-<br /> | r0 || volatile register that may be modified during function linkage<br /> |-<br /> | r1 || stack-frame pointer, always valid<br /> |-<br /> | r2 || system reserved register<br /> |-<br /> | r3 || command-line pointer<br /> |-<br /> | r4 || command-line length<br /> |-<br /> | r5 || DOSBase pointer<br /> |-<br /> | colspan=&quot;2&quot; | The contents of registers r3-r5 is only valid when the program starts)<br /> |-<br /> | r6 - r10 || volatile registers used for parameter passing<br /> |-<br /> | r11 - r12 || volatile registers that may be modified during function linkage<br /> |-<br /> | r13 || small data area pointer register<br /> |-<br /> | r14 - r30 || registers used for local variables; they are non-volatile; functions have to save and restore them<br /> |-<br /> | r31 || preferred by GCC in position-independent code (e.g. in shared objects) as a base pointer into the GOT section; however, the pointer can also be stored in another register<br /> |}<br /> <br /> Important note: This general-purpose register description shows that arguments can only be passed in registers r3 and above (that is, not in r0, r1 or r2). You need to keep that in mind when assembling/disassembling under AmigaOS.<br /> <br /> === Some special registers ===<br /> <br /> {| class=&quot;wikitable&quot;<br /> | lr || link register; stores the &quot;ret address&quot; (i.e. the address to which a called function normally returns)<br /> |-<br /> | cr || condition register<br /> |}<br /> <br /> == Instructions ==<br /> <br /> There are many different PowerPC instructions that serve many different purposes: there are branch instructions, condition register instructions, instructions for storage access, integer arithmetic, comparison, logic, rotation, cache control, processor management, and so on. In fact there are so many instructions that it would make no sense to cover them all here. You can download Freescale’s Green Book (see the Links section at the end of the article) if you are interested in a more detailed description but we’ll just stick to a number of instructions that are interesting and useful for our purposes.<br /> <br /> ; b<br /> : Relative branch on address (example: &quot;b 0x7fcc7244&quot;). Note that there are both relative and absolute branches (ba). Relative branches can branch to a distance of -32 to +32MB. Absolute branches can jump to 0x00000000 - 0x01fffffc and 0xfe000000 - 0xfffffffc. However, absolute branches will not be used in AmigaOS programs.<br /> <br /> ; bctr<br /> : Branch with count register. It uses the count register as a target address, so that the link register with, say, our return address remains unmodified.<br /> <br /> ; lis<br /> : Stands for &quot;load immediate shifted&quot;. The PowerPC instruction set doesn’t allow loading a 32-bit constant with a single instruction. You will always need two instructions that load the upper and the lower 16-bit half, respectively. For example, if you want to load 0x12345678 into register r3, you need to do the following:<br /> <br /> &lt;pre&gt;<br /> lis %r3,0x1234<br /> ori %r3,%r3,0x5678<br /> &lt;/pre&gt;<br /> <br /> Later in the article you’ll notice that this kind of construction is used all the time.<br /> <br /> ; mtlr<br /> : &quot;move to link register&quot;. In reality this is just a mnemonic for &quot;mtspr 8,r&quot;. The instruction is typically used for transferring an address from register r0 to the link register (lr), but you can of course move contents to lr from other registers, not just r0.<br /> <br /> ; stwu<br /> : &quot;store word and update&quot; (all instructions starting with “st” are for storing). For example, stwu %r1, -16(%r1) stores the contents of register r1 into a memory location whose effective address is calculated by taking the value of 16 from r1. At the same time, r1 is updated to contain the effective address. As we already know, register r1 contains the stack-frame pointer so our instruction stores the contents of the register to a position at offset -16 from the current top of stack and then decrements the stack pointer by 16.<br /> <br /> The PowerPC processor has many more instructions and various kinds of mnemonics, all of which are well covered in numerous PPC-related tutorials, so to avoid copying-and-pasting (and wasting space here) we have described a few that happen to be used very often. You’ll need to refer to the relevant documentation if you want to read more about the PowerPC instruction set (see Links below).<br /> <br /> == Function prologue and epilogue ==<br /> <br /> When a C function executes, its code – seen from the assembler perspective – will contain two parts called the prologue (at the beginning of the function) and the epilogue (at the end of the function). The purpose of these parts is to save the return address so that the function knows where to jump after the subroutine is finished.<br /> <br /> &lt;pre&gt;<br /> stwu %r1,-16(%r1) <br /> mflr %r0 # prologue, reserve 16 byte stack frame<br /> stw %r0,20(%r1) <br /> <br /> ...<br /> <br /> lwz %r0,20(%r1) <br /> addi %r1,%r1,16 # epilogue, restore back<br /> mtlr %r0 <br /> blr <br /> &lt;/pre&gt;<br /> <br /> The prologue code generally opens a stack frame with a stwu instruction that increments register r1 and stores the old value at the first address of the new frame. The epilogue code just loads r1 with the old stack value.<br /> <br /> C programmers needn’t worry at all about the prologue and epilogue because the compiler will add them to their functions automatically. When you write your programs in pure assembler you can skip the prologue and the epilogue if you don’t need to keep the return address.<br /> <br /> Plus, a new stack frame doesn’t need to be allocated for functions that do not call any subroutine. By the way, the V.4-ABI (application binary interface) defines a specific layout of the stack frame and stipulates that it should be aligned to 16 bytes.<br /> <br /> = Writing programs in assembler =<br /> <br /> There are two ways to write assembler programs under AmigaOS:<br /> <br /> ; using libc<br /> : all initializations are done by crtbegin.o/crtend.o and libc is attached to the binary<br /> <br /> ; the old way<br /> : all initializations - opening libraries, interfaces etc. - have to be done manually in the code<br /> <br /> The advantage of using libc is that you can run your code &quot;out of the box&quot; and that all you need to know is the correct offsets to the function pointers. On the minus side, the full library is attached to the binary, making it bigger. Sure, a size difference of ten or even a hundred kilobytes doesn’t play a big role these days – but here in this article we’re going down the old hacking way (that’s why we’re fiddling with assembler at all) so let’s call it a drawback.<br /> <br /> The advantage of not using libc is that you gain full control of your program, you can only use the functions you need, and the resulting binary will be as small as possible (a fully working binary can have as little as 100 bytes in size). The drawback is that you have to initialize everything manually.<br /> <br /> We’ll first discuss assembler programming with the use of libc.<br /> <br /> == Assembler programming using libc ==<br /> <br /> To illustrate how this works we’ll compile a Newlib-based binary (the default GCC setting) using the –g switch (“include debugging information”) and then put the [[GDB_for_Beginners|GDB]] debugger on the job:<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> <br /> main()<br /> {<br /> printf(&quot;aaaa&quot;);<br /> exit(0);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 6/0.RAM Disk:&gt; gcc -gstabs -O2 2.c -o 2<br /> 2.c: In function 'main':<br /> 2.c:6: warning: incompatible implicit declaration of built-in function 'exit'<br /> <br /> 6/0.RAM Disk:&gt; GDB -q 2<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (GDB) break main<br /> Breakpoint 1 at 0x7fcc7208: file 2.c, line 4.<br /> (GDB) r<br /> Starting program: /RAM Disk/2 <br /> BS 656d6ed8<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at 2.c:4<br /> 4 {<br /> (GDB) disas<br /> Dump of assembler code for function main:<br /> 0x7fcc7208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7fcc720c &lt;main+4&gt;: mflr r0<br /> 0x7fcc7210 &lt;main+8&gt;: lis r3,25875 ; that addr<br /> 0x7fcc7214 &lt;main+12&gt;: addi r3,r3,-16328 ; on our string<br /> 0x7fcc7218 &lt;main+16&gt;: stw r0,20(r1)<br /> 0x7fcc721c &lt;main+20&gt;: crclr 4*cr1+eq<br /> 0x7fcc7220 &lt;main+24&gt;: bl 0x7fcc7234 &lt;printf&gt;<br /> 0x7fcc7224 &lt;main+28&gt;: li r3,0<br /> 0x7fcc7228 &lt;main+32&gt;: bl 0x7fcc722c &lt;exit&gt;<br /> End of assembler dump.<br /> (GDB) <br /> &lt;/pre&gt;<br /> <br /> Now we’ll use [[GDB_for_Beginners|GDB]] to disassemble the printf() and exit() functions from Newlib’s LibC.a. As mentioned above, Newlib is used by default, there’s no need to use the –mcrt switch unless we want clib2 instead (in which case we’d compile the source with “-mcrt=clib2”).<br /> <br /> &lt;pre&gt;<br /> (GDB) disas printf<br /> Dump of assembler code for function printf:<br /> 0x7fcc723c &lt;printf+0&gt;: li r12,1200<br /> 0x7fcc7240 &lt;printf+4&gt;: b 0x7fcc7244 &lt;__NewLibCall&gt;<br /> End of assembler dump.<br /> (GDB)<br /> <br /> (GDB) disas exit<br /> Dump of assembler code for function exit:<br /> 0x7fcc7234 &lt;exit+0&gt;: li r12,1620<br /> 0x7fcc7238 &lt;exit+4&gt;: b 0x7fcc7244 &lt;__NewLibCall&gt;<br /> End of assembler dump.<br /> (GDB) <br /> &lt;/pre&gt;<br /> <br /> You can see that register r12 contains some values depending on the function - they are function pointer offsets in Newlib’s interface structure (INewLib). Then there’s the actual jump to __NewLibCall, so let’s have a look at it:<br /> <br /> &lt;pre&gt;<br /> (GDB) disas __NewLibCall<br /> Dump of assembler code for function __NewLibCall:<br /> 0x7fcc7244 &lt;__NewLibCall+0&gt;: lis r11,26006<br /> 0x7fcc7248 &lt;__NewLibCall+4&gt;: lwz r0,-25500(r11)<br /> 0x7fcc724c &lt;__NewLibCall+8&gt;: lwzx r11,r12,r0<br /> 0x7fcc7250 &lt;__NewLibCall+12&gt;: mtctr r11<br /> 0x7fcc7254 &lt;__NewLibCall+16&gt;: bctr<br /> End of assembler dump.<br /> (GDB)<br /> &lt;/pre&gt;<br /> <br /> Of course you can use &quot;objdump&quot;:<br /> <br /> &lt;pre&gt;<br /> 6/0.RAM Disk:&gt; objdump -d 1 | grep -A5 &quot;&lt;__NewLibCall&gt;:&quot;<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 01000280 &lt;__NewLibCall&gt;:<br /> 1000280: 3d 60 01 01 lis r11,257<br /> 1000284: 80 0b 00 24 lwz r0,36(r11)<br /> 1000288: 7d 6c 00 2e lwzx r11,r12,r0<br /> 100028c: 7d 69 03 a6 mtctr r11<br /> 1000290: 4e 80 04 20 bctr<br /> &lt;/pre&gt;<br /> <br /> But using [[GDB_for_Beginners|GDB]] is more comfortable: you don’t need to scroll through the full objdump output, or search in it with grep, etc. You can, too, obtain assembler output by compiling the source with the –S switch but [[GDB_for_Beginners|GDB]] makes it possible to get as deep into the code as you wish (in fact down to the kernel level).<br /> <br /> We will now remove the prologue (because we don’t need it in this case) and reorganize the code a bit:<br /> <br /> &lt;pre&gt;<br /> .globl main<br /> main:<br /> lis %r3,.msg@ha #<br /> la %r3,.msg@l(%r3) # printf(&quot;aaaa&quot;);<br /> bl printf #<br /> <br /> li %r3,0 # exit(0);<br /> bl exit # <br /> <br /> .msg:<br /> .string &quot;aaaa&quot;<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 6/0.RAM Disk:&gt; as test.s -o test.o<br /> 6/0.RAM Disk:&gt; ld -N -q test.o -o test /SDK/newlib/lib/crtbegin.o /SDK/newlib/lib/LibC.a /SDK/newlib/lib/crtend.o<br /> 6/0.RAM Disk:&gt; strip test <br /> 6/0.RAM Disk:&gt; filesize format=%s test<br /> 5360<br /> 6/0.RAM Disk:&gt; test<br /> aaaa<br /> 6/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> When we compile our Hello World program in C (with the -N switch and stripping, of course) it is 5504 bytes in size; our assembler code gives 5360 bytes. Nice, but let’s try to reduce it some more (even if we’ll still keep libc attached). Instead of branching to the functions themselves (“bl function”) we’ll use function pointer offsets and branch to __NewLibCall:<br /> <br /> &lt;pre&gt;<br /> .globl main<br /> main:<br /> #printf(&quot;aaaa&quot;)<br /> <br /> lis %r3,.msg@ha # arg1 part1<br /> la %r3,.msg@l(%r3) # arg1 part2<br /> li %r12, 1200 # 1200 - pointer offset to function<br /> b __NewLibCall<br /> <br /> #exit(0)<br /> <br /> li %r3, 0 # arg1<br /> li %r12, 1620 # 1620 - pointer offset to function<br /> b __NewLibCall <br /> <br /> .msg:<br /> .string &quot;aaaa&quot;<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 6/0.RAM Disk:&gt; as test.s -o test.o<br /> 6/0.RAM Disk:&gt; ld -N -q test.o -o test /SDK/newlib/lib/crtbegin.o /SDK/newlib/lib/LibC.a /SDK/newlib/lib/crtend.o<br /> 6/0.RAM Disk:&gt; strip test <br /> 6/0.RAM Disk:&gt; filesize format=%s test<br /> 5336<br /> 6/0.RAM Disk:&gt; test<br /> aaaa<br /> 6/0.RAM Disk:&gt;<br /> &lt;/pre&gt;<br /> <br /> The size is now 5336. We’ve saved 24 bytes, no big deal! Now let’s get real heavy and try to mimic __NewLibCall using our own code:<br /> <br /> &lt;pre&gt;<br /> .globl main<br /> main:<br /> lis %r3,.msg@ha #<br /> la %r3,.msg@l(%r3) # printf(&quot;aaaa&quot;);<br /> li %r12, 1200<br /> <br /> lis %r11,26006<br /> lwz %r0,-25500(%r11)<br /> lwzx %r11,%r12,%r0 # __NewLibCall<br /> mtctr %r11<br /> bctr<br /> <br /> li %r3, 0<br /> li %r12, 1620 # exit<br /> <br /> lis %r11,26006<br /> lwz %r0,-25500(%r11)<br /> lwzx %r11,%r12,%r0 # __NewLibCall<br /> mtctr %r11<br /> bctr<br /> <br /> .msg:<br /> .string &quot;aaaa&quot;<br /> &lt;/pre&gt;<br /> <br /> It crashes but why? Because lis %r11,26006 and lwz %r0,-25500(%r11) load a pointer from 0x010100024. In the original __NewLibCall code this is a read access to the NewLib interface pointer. But as we already know, we cannot read from the absolute address 0x01010024 because it’s illegal, and the ELF loader must relocate this address to point to the real NewLib interface pointer (INewlib). We didn’t see that before because we used objdump without the &quot;-r&quot; switch (which shows relocations), so let’s use it now:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; objdump -dr 1 | grep -A7 &quot;&lt;__NewLibCall&gt;:&quot;<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 01000298 &lt;__NewLibCall&gt;:<br /> 1000298: 3d 60 01 01 lis r11,257<br /> 100029a: R_PPC_ADDR16_HA INewlib<br /> 100029c: 80 0b 00 24 lwz r0,36(r11)<br /> 100029e: R_PPC_ADDR16_LO INewlib<br /> 10002a0: 7d 6c 00 2e lwzx r11,r12,r0<br /> 10002a4: 7d 69 03 a6 mtctr r11<br /> 10002a8: 4e 80 04 20 bctr<br /> &lt;/pre&gt;<br /> <br /> So we’ll rewrite our code using the normal interface pointer, and turn the __NewLibCall code into a macro:<br /> <br /> &lt;pre&gt;<br /> .macro OUR_NEWLibCALL <br /> lis %r11,INewlib@ha<br /> lwz %r0,INewlib@l(%r11) <br /> lwzx %r11,%r12,%r0 <br /> mtctr %r11<br /> bctr<br /> .endm<br /> <br /> .globl main<br /> main:<br /> lis %r3,.msg@ha <br /> la %r3,.msg@l(%r3) # printf(&quot;aaaa&quot;);<br /> li %r12, 1200<br /> <br /> OUR_NEWLibCALL<br /> <br /> li %r3, 0<br /> li %r12, 1620 # exit(0);<br /> <br /> OUR_NEWLibCALL <br /> <br /> .msg:<br /> .string &quot;aaaa&quot;<br /> &lt;/pre&gt;<br /> <br /> Works now! Still, after stripping, the size is 5336 bytes but at least the code is fully in our hands and we can play with instructions. It’s time to read some good stuff like the Green Book (see Links below) if you want to do real beefy hacking.<br /> <br /> By the way, when we debug our binary, you’ll notice that GCC has put a strangely-looking instruction right before the call to a libc function: crxor 6,6,6 (crclr 4*cr1+eq). This is done in compliance with the ABI specification, which says that before a variadic function is called, an extra instruction (crxor 6,6,6 or creqv 6,6,6) must be executed that sets Condition Register 6 (CR6) to either 1 or 0. The value depends on whether one or more arguments need to go to a floating-point register. If no arguments are being passed in floating-point registers, crxor 6,6,6 is added in order to set the Condition Register to 0. If you call a variadic function with floating-point arguments, the call will be preceded by a creqv 6,6,6 that sets Condition Register 6 to the value of 1.<br /> <br /> You may ask where on Earth we got the numerical values (offsets) for the libc functions, i.e. “1200” representing printf() and “1620” representing exit(). For newlib.library, there is no documentation, header files or an interface description in the official AmigaOS SDK so you have to find it all out yourself. There are a couple of ways to do it:<br /> <br /> # Write the program in C and obtain the numbers by disassembling the code (using [[GDB_for_Beginners|GDB]] or objdump). Not much fun but at least you can inspect what arguments are used and in which registers they are stored.<br /> # If you only need the list of function offsets you can disassemble the LibC.a file using objdump:<br /> <br /> &lt;pre&gt;<br /> shell:&gt; objdump -dr SDK:newlib/lib/LibC.a <br /> &lt;/pre&gt;<br /> <br /> The library only contains stub functions, and output will look like the following:<br /> <br /> &lt;pre&gt;<br /> ---- SNIP ----<br /> <br /> Disassembly of section .text:<br /> <br /> 00000000 &lt;realloc&gt;:<br /> 0: 39 80 01 64 li r12,356<br /> 4: 48 00 00 00 b 4 &lt;realloc+0x4&gt;<br /> 4: R_PPC_REL24 __NewLibCall<br /> <br /> stub_realpath.o: file format ELF32-AmigAOS<br /> <br /> Disassembly of section .text:<br /> <br /> 00000000 &lt;realpath&gt;:<br /> 0: 39 80 0c 00 li r12,3072<br /> 4: 48 00 00 00 b 4 &lt;realpath+0x4&gt;<br /> 4: R_PPC_REL24 __NewLibCall<br /> <br /> stub_recv.o: file format ELF32-AmigaOS<br /> <br /> ---- SNIP ----<br /> &lt;/pre&gt;<br /> <br /> You can write a simple script that will parse the disassembly and give you the list in any form you wish.<br /> <br /> == Assembler programming without libc ==<br /> <br /> If you want to write programs without using the C standard library, your code should do what runtime objects would normally take care of: that is, initialize all the necessary system-related stuff. It is almost the same as on AmigaOS 3.x, only with some AmigaOS 4.x-specific parts. This is what you should do:<br /> <br /> * obtain SysBase (pointer to exec.library)<br /> * obtain the exec.library interface<br /> * IExec-&gt;Obtain()<br /> * open dos.library and its interface (if you want to use dos.library functions)<br /> * IExec-&gt;GetInterface()<br /> ... your code ...<br /> * IExec-&gt;DropInterface()<br /> * IExec-&gt;CloseLibrary()<br /> * IExec-&gt;Release()<br /> * exit(0)<br /> <br /> As of now, we can no longer use printf() because it’s a libc function - if we want to produce a really small binary, we cannot afford the luxury of attaching the entire libc to be able to use printf() only! Instead, we need to use the AmigaOS API: in this particular case, the Write() function from dos.library.<br /> <br /> There is a Hello World example written by Frank Wille for his assembler 'vasm'; I’ll adapt it for the GNU assembler ('as') in order to make the article related to one compiler. (Both the original and the adapted version can be found in the archive that comes with the article):<br /> <br /> &lt;pre&gt;<br /> # ExecBase<br /> .set ExecBase,4<br /> .set MainInterface,632<br /> <br /> # Exec Interface<br /> .set Obtain,60<br /> .set Release,64<br /> .set OpenLibrary,424<br /> .set CloseLibrary,428<br /> .set GetInterface,448<br /> .set DropInterface,456<br /> <br /> # DOS Interface<br /> .set Write,88<br /> .set Output,96<br /> <br /> <br /> .macro CALLOS reg,val # Interface register, function offset<br /> lwz %r0,\val(\reg)<br /> mr %r3,\reg<br /> mtctr %r0<br /> bctrl<br /> .endm<br /> <br /> .text<br /> <br /> .global _start<br /> _start:<br /> <br /> mflr %r0<br /> stwu %r1,-32(%r1)<br /> stmw %r28,8(%r1)<br /> mr %r31,%r0<br /> <br /> # get SysBase<br /> li %r11,ExecBase<br /> lwz %r3,0(%r11)<br /> <br /> # get Exec-Interface<br /> lwz %r30,MainInterface(%r3) # r30 IExec<br /> <br /> # IExec-&gt;Obtain()<br /> CALLOS %r30,Obtain<br /> <br /> # open dos.library and get DOS-Interface<br /> # IExec-&gt;OpenLibrary(&quot;dos.library&quot;,50)<br /> lis %r4,dos_name@ha<br /> addi %r4,%r4,dos_name@l<br /> li %r5,50<br /> CALLOS %r30,OpenLibrary<br /> mr. %r28,%r3 # r28 DOSBase<br /> beq release_exec<br /> <br /> # IExec-&gt;GetInterface(DOSBase,&quot;main&quot;,1,0)<br /> mr %r4,%r28<br /> lis %r5,main_name@ha<br /> addi %r5,%r5,main_name@l<br /> li %r6,1<br /> li %r7,0<br /> CALLOS %r30,GetInterface<br /> mr. %r29,%r3 # r29 IDOS<br /> beq close_dos<br /> <br /> # IDOS-&gt;Output()<br /> CALLOS %r29,Output<br /> <br /> # IDOS-&gt;Write(stdout,&quot;Hello World!\n&quot;,13)<br /> mr %r4,%r3<br /> lis %r5,hello_world@ha<br /> addi %r5,%r5,hello_world@l<br /> li %r6,hello_world_end-hello_world<br /> CALLOS %r29,Write<br /> <br /> # IExec-&gt;DropInterface(IDOS)<br /> mr %r4,%r29<br /> CALLOS %r30,DropInterface<br /> <br /> close_dos:<br /> # IExec-&gt;CloseLibrary(DOSBase)<br /> mr %r4,%r28<br /> CALLOS %r30,CloseLibrary<br /> <br /> release_exec:<br /> # IExec-&gt;Release()<br /> CALLOS %r30,Release<br /> <br /> # exit(0)<br /> li %r3,0<br /> mtlr %r31<br /> lmw %r28,8(%r1)<br /> addi %r1,%r1,32<br /> blr<br /> <br /> .rodata<br /> <br /> dos_name:<br /> .string &quot;dos.library&quot;<br /> main_name:<br /> .string &quot;main&quot;<br /> hello_world:<br /> .string &quot;Hello World!&quot;<br /> hello_world_end:<br /> &lt;/pre&gt;<br /> <br /> If you did assembler programming under AmigaOS 3.x, you can see that the logic is the same, just the assembler is different and some AmigaOS 4.x-specific bits and pieces (the interface-related stuff) have been added. Now let’s compile and link the source and then strip the binary to see how our “slimming diet” works:<br /> <br /> &lt;pre&gt;<br /> 6/0.Work:&gt; as hello.s -o hello.o<br /> 6/0.Work:&gt; ld -q hello.o -o hello<br /> 6/0.Work:&gt; strip hello<br /> 6/0.Work:&gt; filesize format=%s hello<br /> 4624<br /> &lt;/pre&gt;<br /> <br /> Right, so we got down to 4624 bytes. A little better when compared with the libc version (which was 5336 in size), but still too much for a Hello World program.<br /> <br /> To obtain the numerical values that identify system functions, you need to study the interface description XML files that are provided in the AmigaOS SDK. For example, for exec.library functions you need to read the file “SDK:include/interfaces/exec.xml”. All interfaces contain a jump table. The offset for the first interface &quot;method&quot; is 60, the next one is 64 and so on. So you just open the appropriate interface description XML file, start counting from 60, and add +4 for any method that follows.<br /> <br /> = Hacking it for real =<br /> <br /> == Linker scripts (ldscripts) ==<br /> <br /> Every time you perform linking to produce an executable, the linker uses a special script called ldscript (pass the “-verbose” argument to see which one is used by default). The script is written in the linker’s command language. The main purpose of the linker script is to describe how the sections in the input file(s) should be mapped into the output file, and to control the memory layout of the output file. Most linker scripts do nothing more that that, but – should you have the need – the script can also direct the linker to perform other operations, using the available set of commands in the command language. To provide your own, custom script to the linker, the &quot;-T&quot; switch is used. (By the way, the &quot;-N&quot; switch, mentioned earlier and used to make non-aligned executables, also affects the choice of the default linker script.)<br /> <br /> What does all of that mean for us and how is it related to this article? Well, when you read the ldscripts documentation (see Links below), you can build your own ldscript that will only create the necessary sections. That is: we can produce a minimum working executable and thus get rid of parts that even 'strip' wouldn’t be able to remove.<br /> <br /> So following the first-test example from the ldscript documentation, we’ll write our own script now:<br /> <br /> &lt;pre&gt;<br /> SECTIONS<br /> {<br /> . = 0x00000000;<br /> .text : { *(.text) }<br /> }<br /> &lt;/pre&gt;<br /> <br /> But why did we put 0x00000000 here as the entry point of the code? Well as we discussed earlier, the address is just a placeholder so it has no real meaning under AmigaOS (the ELF loader will perform relocation and calculate the proper address). Nevertheless, the address value is used when the ELF binary is created, and it can make a difference as regards the executable size because of paging. So, let’s compile our non-libc assembler code and provide our custom linker script:<br /> <br /> &lt;pre&gt;<br /> shell:&gt; as hello.s -o hello.o<br /> shell:&gt; ld -Tldscript -q -o hello hello.o<br /> shell:&gt; stat -c=%s hello<br /> =66713<br /> &lt;/pre&gt;<br /> <br /> OMG! 66 kilobytes! But that was quite expected, considering the entry point address we have provided. You can now play with the address value to see what difference in the executable size it makes. For example, if you try 0x11111111, the size of the binary is 5120 bytes; 0xAAAAAAAA will result in 44440 bytes. Apparently, this generally meaningless address does make a difference because it affects paging. So all we need to do is choose a value that will, hopefully, avoid any kind of paging. We can consult the ldscripts manual again and we’ll find this:<br /> <br /> SIZEOF_HEADERS: Returns the size in bytes of the output file’s headers. You can use this number as the start address of the first section, to facilate paging.<br /> <br /> This looks like the thing we need, so:<br /> <br /> &lt;pre&gt;<br /> SECTIONS<br /> {<br /> . = SIZEOF_HEADERS;<br /> .text : { *(.text) }<br /> }<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> shell:&gt; as hello.s -o hello.o<br /> shell:&gt; ld -Tldscript -q -o hello hello.o<br /> shell:&gt; stat -c=%s hello<br /> =1261<br /> <br /> shell:&gt; strip hello<br /> shell:&gt; stat -c=%s hello<br /> =832<br /> <br /> shell:&gt; hello<br /> Hello World!<br /> shell:&gt;<br /> &lt;/pre&gt;<br /> <br /> 832 bytes of size and works!<br /> <br /> == Getting rid of relocation ==<br /> <br /> Now, lets see what kind of sections our 832 bytes binary has:<br /> <br /> &lt;pre&gt;<br /> 7/0.Work:&gt; readelf -S hello<br /> There are 7 section headers, starting at offset 0x198:<br /> <br /> Section Headers:<br /> [Nr] Name Type Addr Off Size ES Flg Lk Inf Al<br /> [ 0] NULL 00000000 000000 000000 00 0 0 0<br /> [ 1] .text PROGBITS 00000054 000054 0000f8 00 AX 0 0 1<br /> [ 2] .rela.text RELA 00000000 0002f8 000048 0c 5 1 4<br /> [ 3] .rodata PROGBITS 0000014c 00014c 00001e 00 A 0 0 1<br /> [ 4] .shstrtab STRTAB 00000000 00016a 00002e 00 0 0 1<br /> [ 5] .symtab SYMTAB 00000000 0002b0 000040 10 6 3 4<br /> [ 6] .strtab STRTAB 00000000 0002f0 000008 00 0 0 1<br /> Key to Flags:<br /> W (write), A (alloc), X (execute), M (merge), S (strings)<br /> I (info), L (link order), G (group), x (unknown)<br /> O (extra OS processing required) o (OS specific), p (processor specific)<br /> <br /> 7/0.Work:&gt;<br /> &lt;/pre&gt;<br /> <br /> As you can see there are some sections that should be relocated:<br /> <br /> # .rela.text - relocations for .text.<br /> # .rodata - data (our strings like &quot;helloworld&quot;, &quot;dos.library&quot;, etc)<br /> <br /> And the next three sections (.shstrtab, .symtab and .strtab) are stanadard in the AmigaOS implementation of ELF, as the AmigaOS ELF loader requires them. Usually the linker ('ld' or 'vlink', does not matter) would remove .symtab and .strtab, when the &quot;-s&quot; option is used at linking stage, but whilst that is true for UNIX, it's not true not for AmigaOS because the AmigaOS ELF loader needs the _start symbol to find the program entry point, so we can't delete those two sections. As for .shstrtab, we can't delete it either because we still need the sections (we will discuss why later).<br /> <br /> So what about .rela.text and .rodata? Well, they can be removed by modifing our code a bit, to avoid any relocations (thanks to Frank again). We place the data to the .text section, together with the code. So the distance between the strings and the code is constant (kind of like base-relative addressing). With &quot;bl initbase&quot; we jump to the following instruction while the CPU places the address of this instruction into LR. This is the base address which we can use:<br /> <br /> &lt;pre&gt;<br /> # non-relocated Hello World <br /> # by Frank Wille, janury 2012<br /> # adapted for &quot;as&quot; by kas1e<br /> <br /> # ExecBase<br /> .set MainInterface,632<br /> <br /> # Exec Interface<br /> .set Obtain,60<br /> .set Release,64<br /> .set OpenLibrary,424<br /> .set CloseLibrary,428<br /> .set GetInterface,448<br /> .set DropInterface,456<br /> <br /> # DOS Interface<br /> .set Write,88<br /> .set Output,96<br /> <br /> <br /> .macro CALLOS reg,val # Interface register, function offset<br /> lwz %r0,\val(\reg)<br /> mr %r3,\reg<br /> mtctr %r0<br /> bctrl<br /> .endm<br /> <br /> .text<br /> <br /> .global _start<br /> _start:<br /> mflr %r0<br /> stw %r0,4(%r1)<br /> stwu %r1,-32(%r1)<br /> stmw %r28,8(%r1)<br /> <br /> # initialize data pointer<br /> bl initbase<br /> initbase:<br /> mflr %r31 # r31 initbase<br /> <br /> # get Exec-Interface<br /> lwz %r30,MainInterface(%r5) # r30 IExec<br /> <br /> # IExec-&gt;Obtain()<br /> CALLOS %r30,Obtain<br /> <br /> # open dos.library and get DOS-Interface<br /> # IExec-&gt;OpenLibrary(&quot;dos.library&quot;,50)<br /> addi %r4,%r31,dos_name-initbase<br /> li %r5,50<br /> CALLOS %r30,OpenLibrary<br /> mr. %r28,%r3 # r28 DOSBase<br /> beq release_exec<br /> <br /> # IExec-&gt;GetInterface(DOSBase,&quot;main&quot;,1,0)<br /> mr %r4,%r28<br /> addi %r5,%r31,main_name-initbase<br /> li %r6,1<br /> li %r7,0<br /> CALLOS %r30,GetInterface<br /> mr. %r29,%r3 # r29 IDOS<br /> beq close_dos<br /> <br /> # IDOS-&gt;Output()<br /> CALLOS %r29,Output<br /> <br /> # IDOS-&gt;Write(stdout,&quot;Hello World!\n&quot;,13)<br /> mr %r4,%r3<br /> addi %r5,%r31,hello_world-initbase<br /> li %r6,hello_world_end-hello_world<br /> CALLOS %r29,Write<br /> <br /> # IExec-&gt;DropInterface(IDOS)<br /> mr %r4,%r29<br /> CALLOS %r30,DropInterface<br /> <br /> close_dos:<br /> # IExec-&gt;CloseLibrary(DOSBase)<br /> mr %r4,%r28<br /> CALLOS %r30,CloseLibrary<br /> <br /> release_exec:<br /> # IExec-&gt;Release()<br /> CALLOS %r30,Release<br /> <br /> # exit(0)<br /> li %r3,0<br /> lmw %r28,8(%r1)<br /> addi %r1,%r1,32<br /> lwz %r0,4(%r1)<br /> mtlr %r0<br /> blr<br /> <br /> dos_name:<br /> .string &quot;dos.library&quot;<br /> main_name:<br /> .string &quot;main&quot;<br /> hello_world:<br /> .string &quot;Hello World!&quot;<br /> hello_world_end:<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 6/0.Work:&gt; as hello.s -o hello.o<br /> 6/0.Work:&gt; ld -Tldscript hello.o -o hello<br /> 6/0.Work:&gt; strip hello<br /> 6/0.Work:&gt; stat -c=%s hello<br /> =644<br /> <br /> 6/0.Work:&gt; hello<br /> Hello World!<br /> 6/0.Work:&gt;<br /> &lt;/pre&gt;<br /> <br /> 644 bytes of size, and still works. If we check the sections in the binary now, we'll see that currently it only contains the .text section and the three symbol-related sections that are required in AmigaOS binaries:<br /> <br /> &lt;pre&gt;<br /> 6/0.Work:&gt; readelf -S hello<br /> There are 5 section headers, starting at offset 0x184:<br /> <br /> Section Headers:<br /> [Nr] Name Type Addr Off Size ES Flg Lk Inf Al<br /> [ 0] NULL 00000000 000000 000000 00 0 0 0<br /> [ 1] .text PROGBITS 10000054 000054 00010e 00 AX 0 0 1<br /> [ 2] .shstrtab STRTAB 00000000 000162 000021 00 0 0 1<br /> [ 3] .symtab SYMTAB 00000000 00024c 000030 10 4 2 4<br /> [ 4] .strtab STRTAB 00000000 00027c 000008 00 0 0 1<br /> Key to Flags:<br /> W (write), A (alloc), X (execute), M (merge), S (strings)<br /> I (info), L (link order), G (group), x (unknown)<br /> O (extra OS processing required) o (OS specific), p (processor specific)<br /> <br /> 6/0.Work:&gt;<br /> &lt;/pre&gt;<br /> <br /> == The ELF loader ==<br /> <br /> If you want to understand the internals of the ELF format, the best book of reference is the ELF specification (see Links), where you can find everything about headers, sections, segments, section headers and so on. But of course it is only a specification and so it does not cover ELF loaders and parsers, which are implemented differenty on different operating systems. While the implementation does not vary too much among UNIXes, the ELF loader in AmigaOS is rather specific.<br /> <br /> Let's briefly cover the parts an ELF executable contains:<br /> <br /> * ELF Header<br /> * Program (segments) header table<br /> * Segments<br /> * Sections header table<br /> * optional sections (certain sections can sometimes come before the sections header table, like for example .shstrtab)<br /> <br /> Although it may seem that sections and segments are the same thing, this is not the case. Sections are elements of the ELF file. When you load the file into memory, sections are joined to form segments. Segments are file elements too but they are loaded to memory and can be directly handled by the loader. So you can think of sections as segments, just you should know that segments are something that executes in memory, while sections is the material from which segments are built in memory.<br /> <br /> This is what our 644-byte Hello World example looks like, with the various parts defined by the ELF specification highlighted in different colours:<br /> <br /> [[File:HackingWayPart1-3.png|center]]<br /> <br /> Every part of an ELF file (be it the ELF header, segments header, or any other part) has a different structure, described in depth in the ELF specification. For a better understanding, let‘s describe the ELF header (the first part in the image above, highlighted in dark green):<br /> <br /> &lt;pre&gt;<br /> db 0x7f, &quot;ELF&quot; ; magic<br /> db 1,2,1 ; 32 bits, big endian, version 1<br /> db 0,0,0,0,0,0,0,0,0 ; os info<br /> <br /> db 0,2 ; e_type (for executable=2)<br /> db 0,0x14 ; 14h = powerpc. <br /> db 0,0,0,1 ; version (always must be set to 1)<br /> dd 0x10000054 ; entry point (on AmigaOS it makes no sense)<br /> dd 0x00000034 ; program header table file offset in bytes<br /> dd 0x00000184 ; section header table file offset in bytes<br /> db 0,0,0,0 ; e_flag - processor specific flags<br /> db 0,0x34 ; e_ehsize - size of ELF header in bytes<br /> <br /> <br /> db 0,0x20 ; e_phentsize - size of one entry in bytes, of program header table (all the entries are the same size) <br /> db 0,2 ; e_phnum - number of entires in the program header table.<br /> <br /> db 0,0x28 ; e_shentsize - section headers size in bytes<br /> db 0,5 ; e_shnum - number of entires in the section header table<br /> db 0,2 ; e_eshstrndx - section header table index of the entry assosiated with the section name string table<br /> &lt;/pre&gt;<br /> <br /> When you try to execute a program, the ELF loader first checks if it's a genuine ELF binary or not. Depending on the result, the loading of the executable is either allowed or denied. Once loaded in memory, code from the respective segments is executed. As I said before, the necessary fields are parsed differently on different operating systems. For example under Linux, the loader parses the ELF structure going into greater depth compared to the AmigaOS loader. Still there is some common ground; on both OSes you can, for instance, write anything you want to the &quot;os info&quot; field. On AmigaOS you can fully reuse more fields, and here is how the AmigaOS ELF loader parses the ELF headers:<br /> <br /> &lt;pre&gt;<br /> * magic (first 7 bytes): db 0x7f,&quot;ELF&quot;, 0x01,0x02,0x01 (100% required)<br /> * all the subsequent fields are not parsed at all and can contain any data, until the loader reaches the section header tables' file offset in bytes field (required)<br /> * then again there can be any data, until e_phnum (the number of entires in the program header table, which is required as well)<br /> * and then the next 8 bytes of info (4 fields) about section headers/sections are required<br /> &lt;/pre&gt;<br /> <br /> Take a look at the image below, which shows an ELF header in which all unparsed bytes are marked by &quot;A&quot; letters. You can use these bytes for anything you want.<br /> <br /> [[File:HackingWayPart1-4.png|center]]<br /> <br /> But please bear in mind that doing so would breach the specification. The fact that it works now doesn't mean it will also work with the next version of the ELF loader, as the AmigaOS developers could use the currently unparsed fields for something meaningful in the future.<br /> <br /> The ELF header is not the only place where you can insert (at least with the current version of the loader) your own data. After the ELF header there come program headers (i.e. headers that describe segments). In our particular case we have one program section header for the .text segment. And here comes the suprise: the AmigaOS ELF loader does not parse the program headers at all! Instead, the parsing is done in sections and section headers only. Apparently, the AmigaOS loader does something that on UNIXes is normally put in the ELF executable and the loader just gets data from it. But under AmigaOS this is not the case. Although the ELF binary produced by GCC is built correctly and according to specification, half of the sections and many fields are not used under AmigaOS.<br /> <br /> So the programs section headers can fully be used for your own needs. We can remove section names completely (and give them, for example, an &quot;empty&quot; name by writing 0 string-offset in the sh_name field of each section header entry). But .shstrtab must still be kept, with a size of 1 byte. A NULL section header can be reused too (you can see that a NULL section header comes after the .shrstab section, so we have plenty of space). Check the file &quot;bonus/unused_fields/hello&quot; to see which areas can be reused (these are indicated by 0xAA bytes).<br /> <br /> Now it‘s clear that we can manipulate sections (i.e. delete empty ones and those ignored by the ELF loader) and recalculate all the addresses in the necessary fields. To do that you will really need to dig into the ELF specification. For example, you can put the _start label to any suitable place (such as the ELF header, or right at the begining of an ignored field) and then just put the adjusted address in the .strtab section offset field. This way you can save 8 bytes, so the size of our binary is now 636 bytes. Then there is the .symtab section at the end of the file, which is 48 bytes long. We can put it right in the place of .shstrtab (34 bytes in our case) and in the following part of the NULL section header (so as to squeeze the remaining 14 bytes in). Just like this:<br /> <br /> [[File:HackingWayPart1-5.png|center]]<br /> <br /> As a result, the size of our binary becomes mere 588 bytes, and the executable still works of course. Tools like 'readelf' will surely be puzzled by such custom-hacked ELF files, but we only need to worry about what the ELF loader thinks about them. If the loader is happy, the binary is working and the code is executed in memory.<br /> <br /> In the bonus directory that comes with this article, you can try out an example binary the altered structure of which is depicted by the image above. In the binary, .strtab (the _start symbol) is moved to the program section header, and .symtab is moved on top of .shstrtab + the NULL section header (see directory &quot;bonus/shift_sections&quot;).<br /> <br /> = Final Words =<br /> <br /> The article, of course, aims at encouraging learning. If you are an application programmer, you'll probably never need to use assembler directly or construct ELFs from scratch byte per byte. But the knowledge of how things work at low level can help you understand and resolve many problems that may turn up from time to time and that are related to compilers, linkers and assembler-code parts. Also, it can give you a better overview of the AmigaOS internals so when you start a project, it will be much easier for you to get rid of problems: without asking questions in the forums and losing hours fiddling with the basics.<br /> <br /> = Links =<br /> <br /> [http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf ELF specification]&lt;br/&gt;<br /> [http://refspecs.linuxbase.org/ELF/ELFspec_ppc.pdf PPC SYSV4 ABI]&lt;br/&gt;<br /> [http://www.freescale.com/files/product/doc/MPCFPE32B.pdf Green Book (MPCFPE32B)]&lt;br/&gt;<br /> [http://www.gnu.org/s/GDB/documentation/ GDB]&lt;br/&gt;<br /> [http://sourceware.org/binutils/docs/ld/Scripts.html#Scripts Linker Scripts] or SDK:Documentation/CompilerTools/ld.pdf , chapter 3.0 &quot;Linker Scripts&quot;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Debug_Logging_on_AmigaOS&diff=4843 Debug Logging on AmigaOS 2013-02-18T15:22:59Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> = Introduction =<br /> <br /> The sole purpose of this article is to introduce developers that may be new to AmigaOS to its debug logging function. The Exec library has a function called DebugPrintF() that provides a printf() like API for outputting text to a debug buffer. This buffer can be dumped to disk via the dumpdebugbuffer command, or it can be redirected to a serial port. The debug buffer can survive a warm-reboot so its contents can be recovered after most crashes (so long as the machine does not require a complete reset). <br /> <br /> In brief:<br /> <br /> * IExec-&gt;DebugPrintF() uses the same syntax as printf() and outputs to a debug buffer,<br /> * The debug buffer can either be directed to memory (the default), or to the serial port. See the documentation for kdebug in the sys:documentation/kernel drawer of AmigaOS, and<br /> * The debug buffer in memory can be saved to disk and cleared via: dumpdebugbuffer log.txt clear.<br /> <br /> For those who are experienced programmers, the details above are probably all that you require; for everyone else, read on to see how and why debug logging is important, and how it can be used.<br /> <br /> = The Importance of Debug Logging =<br /> <br /> Developing software is a complex task. The more functions, if/else calls and loops that a program has, the more complicated its operation becomes. Sooner or later it becomes necessary to examine what a program is doing internally. Many compilers (including GCC on AmigaOS) come with debuggers that allow one to step through a program line by line. However, this can be extremely tedious and is also unsuitable for any program that requires real-time operation. For example, it may be impossible to debug a game by stepping through the code because it has to respond to a player's joystick input (which changes which parts of the program are executed). Regardless, it is useful to provide a log of significant events in a program's operation. See the debug logs output for the RadeonHD driver [http://hdrlab.org.nz/radeonhd-first-screen/ here] and [http://hdrlab.org.nz/successful-radeon-hd-2400-pro-mode-setting-test/ here], as examples.<br /> <br /> So, what should be logged. Essentially, this is up to you, the programmer. Whatever information you think will be useful in tracking down bugs/issues, should be logged. Often it would be nice to have a hierarchy of debug levels so that it is not necessary to wade through huge debug files when only high-level information (e.g., warnings and errors) are required. I have created a debug module with a template makefile and test code precisely for this purpose.<br /> <br /> = The Debug Logging Template =<br /> <br /> The debug logging module provides a framework for multi-level debug logging. By providing a debug level, it is possible to dpecify what level of logging is required. An example executable (DebugLoggingTest) and makefile have been provided that show how to use the module.<br /> <br /> == USING THE DEBUG LOGGING MODULE ==<br /> <br /> The easiest way to learn how to use the debug module is to simply [[Media:DebugLogging.lha|download the template file]] and look at the provided makefile. After initial download the template is configured to output all debug messages. Type make, and then DebugLoggingTest. Now enter dumpdebugbuffer log.txt clear. Examining log.txt will show all the output messages, each of which can be matched to dprintf() calls in DebugLoggingTest.c. <br /> <br /> In the makefile there are three lines containing &quot;CFLAGS.&quot; Two of them are commented out (with a # character). The example code can be reconfigured to the other two modes by uncommenting the desired line, and commenting out the other two. Remember to perform a &quot;make clean&quot; before recompiling after making any change to the makefile. The most interesting configuration is the last one. In this configuration the user can enter a debug log level on the command line (e.g., &quot;DebugLoggingTest 10&quot;) and thus choose how much debug information is to be output.<br /> <br /> To use the debug module in other programs:<br /> <br /> * Set the TARGET variable in the makefile to the name of the appliation,<br /> * compile files with the appropriate DEBUG flags set:<br /> ** -DTARGET=&quot;$(TARGET)&quot;, and<br /> ** -DDEBUG and (optionally) -DDEBUG_LOGLEVEL, or, -DVARLEVEL_DEBUG.<br /> * Insert dprintf() calls into the program where appropriate (e.g., dprintf(DBG_CRITICAL, &quot;A critical error occurred.\n&quot;);), and<br /> * the LOG_FUNCTION or LOG_FUNCENTRY/LOG_FUNCEXIT macros could also be useful.<br /> <br /> == DOCUMENTATION ==<br /> <br /> The debug module/template expects TARGET to be defined as a string giving the name of the program (e.g., in the makefile use -DTARGET=&quot;AppName&quot;). It provides two functions:<br /> <br /> * dprintf(debugLevel, fmt, ...) whichoutputs debug messages formatted printf style and adds a header giving the application name and the debug level of the message, and<br /> * dprintf_cont(debugLevel, fmt, ...) also outputs debug messages, but minus the header (so that more complicated messages can be output).<br /> <br /> Several debug levels have been predefined:<br /> <br /> * DBG_CRITICAL for critical errors,<br /> * DBG_WARNING for warnings,<br /> * DBG_INFO for more detailed information, <br /> * DBG_FUNCTIONCALL which should be used for identifying function calls, and<br /> * DBG_INTERNALINFO for even more detailed information<br /> <br /> There are also three modes of operation. If DEBUG is defined alone, it will output all debug messages. If, DEBUG_LOGLEVEL is set to a number, all messages will be output that have a debug level at or below that value. Finally, if VARLEVEL_DEBUG is defined, the user (or external code) can set the debug logging level on demand; in this case, the following two functions are defined:<br /> <br /> * void setDebugLevel(int debugLevel) which sets the debug logging level, and<br /> * int getDebugLevel() which returns the currently set logging level.<br /> <br /> An example debug log message would be:<br /> <br /> DebugLoggingTest (0): An example critical error<br /> <br /> There are also three macros defined that are useful for monitoring function entries and exits:<br /> <br /> * LOG_FUNCTION logs the name of the function that was called,<br /> * LOG_FUNCENTRY logs the entry to a function, but is useful to put at the top of a function, and<br /> * LOG_FUNCEXIT should be put before any return statement, it logs a funciton exit. <br /> <br /> LOG_FUNCENTRY and LOG_FUNCEXIT should be used together.<br /> <br /> == DOWNLOAD ==<br /> <br /> [[Media:DebugLogging.lha|The debug logging module and template.]]<br /> <br /> = Author =<br /> <br /> Copyright (c) 2008 Hans de Ruiter.&lt;br/&gt;<br /> Reproduced with permission.&lt;br/&gt;<br /> See the original article [http://hdrlab.org.nz/articles/amiga-os-articles/debug-logging/ here].</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Using_Crash-Logs_for_Debugging&diff=4842 Using Crash-Logs for Debugging 2013-02-18T15:22:33Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> = Introduction =<br /> <br /> 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.<br /> <br /> 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.<br /> <br /> [[File:UsingCrashLogs1.jpg|center|frame|The Grim Reaper displaying a DSI error.]]<br /> <br /> 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.<br /> <br /> = Enabling Debugging =<br /> <br /> It is very easy to enable debugging in GCC. Simply add the compiler flag &quot;-gstabs&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. &quot;Stabs&quot; is a particular format in which the debugging symbol; &quot;-ggdb&quot; is another option that also adds debugging symbols, but in a different format. However, I have been told that there are issues with &quot;-ggdb&quot; and large applications, so it is best to stick to &quot;-gstabs.&quot;<br /> <br /> = Debug Binaries =<br /> <br /> 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:<br /> <br /> $(TARGET): $(OBJS)<br /> $(CC) $(CFLAGS) -o $@.debug $(OBJS) $(LIBS) $(LINK)<br /> $(STRIP) $@.debug -o $@ <br /> <br /> Where CFLAGS = -mcrt=newlib $(OPTIMIZE) -Wall -gstabs<br /> <br /> In the [[Media:usingcrashlogs.lha|example template]] this translates to:<br /> gcc -mcrt=newlib -O3 -Wall -gstabs -c -o badboy.o badboy.c<br /> gcc -mcrt=newlib -O3 -Wall -gstabs -o badboy.debug badboy.o<br /> strip badboy.debug badboy<br /> <br /> 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.<br /> <br /> = Interpreting Crash-Logs =<br /> <br /> 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.<br /> <br /> [[File:UsingCrashLogs1.jpg|center|frame|The Grim Reaper displaying a DSI error.]]<br /> <br /> Now click on &quot;More...&quot; and select the &quot;Stack Trace&quot; tab. Clicking on the &quot;Generate Stack Trace&quot; button will generate a stack trace.<br /> <br /> [[File:UsingCrashLogs2.jpg|center|frame|A stack trace for the program &quot;badboy&quot;]]<br /> <br /> This shows that the crash occurred within &quot;badboy&quot; in section 5 at 0x608. Open a new shell window and change to the directory containing &quot;badboy&quot; once again. Enter the following line:<br /> <br /> addr2line -e badboy.debug --section=.text 0x608 <br /> <br /> This performs a lookup of 0x608 inside &quot;badboy.debug,&quot; which contains the debug symbols. Addr2line will respond by giving the file and line number of this address.<br /> <br /> [[File:UsingCrashLogs3.jpg|center|frame|A stack trace for the program &quot;badboy&quot;]]<br /> <br /> Line 25 performs a write to address zero:<br /> <br /> &lt;syntaxhighlight&gt;<br /> int *a = NULL;<br /> *a = 0;<br /> &lt;/syntaxhighlight&gt;<br /> <br /> 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.<br /> <br /> = Download =<br /> <br /> [[Media:usingcrashlogs.lha|Using crash-logs template and example]]<br /> <br /> = Wish-List =<br /> <br /> 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.<br /> <br /> = Author =<br /> <br /> Copyright (c) 2008 Hans de Ruiter.&lt;br/&gt;<br /> Reproduced with permission.&lt;br/&gt;<br /> See the original article [http://hdrlab.org.nz/articles/amiga-os-articles/using-crash-logs/ here].</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=GDB_for_Beginners&diff=4841 GDB for Beginners 2013-02-18T15:21:44Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> = Author =<br /> <br /> Roman Kargin&lt;br/&gt;<br /> Copyright (c) 2012 Roman Kargin&lt;br/&gt;<br /> Used by permission.<br /> <br /> = Introduction =<br /> <br /> On the Internet you can find a lot of GDB tutorials; ranging from the simple ones, which give you a basic reference for GDB, up to the big tutorials and documentation (including the official ones). Of course, none of these are written in the context of AmigaOS, because for the rest of word AmigaOS is very obscure, and as result all those GDB tutorials are oriented towards x86/UNIX, or occasionally PPC/Sparc/Alpha based Unixes (although those still look pretty much the same, as it is still UNIX). In the case of AmigaOS, the SDK documentation just includes the official GDB docs, which cover just the GDB itself without taking AmigaOS into account.<br /> <br /> Some of you may remember that GDB on AmigaOS worked reasonably well on the early versions of the OS, prior to the first releases of AmigaOS 4.1, which brought a lot of changes, some of which caused GDB to stop working properly. But with the release of AmigaOS 4.1 Update 3, GDB once again started to work more-or-less as expected, and lately (with release of Update 4) it is usable again. As result, all of the information in this article are based on the GDB from latest SDK 53.20 (GDB version 6.3, 2005.07.19 build) and AmigaOS 4.1 Update 4.<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -v<br /> GNU gdb 6.3 (AmigaOS build 20050719)<br /> Copyright 2004 Free Software Foundation, Inc.<br /> GDB is free software, covered by the GNU General Public License, and you are<br /> welcome to change it and/or distribute copies of it under certain conditions.<br /> Type &quot;show copying&quot; to see the conditions.<br /> There is absolutely no warranty for GDB. Type &quot;show warranty&quot; for details.<br /> This GDB was configured as &quot;ppc-amigaos&quot;.<br /> <br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> = What is GDB? =<br /> <br /> GDB is not just a simple debugger or disassembler, but a full debug environment that gives you the ability to debug your program in real time, disassemble your code, dump or disassemble memory areas, attach to running processes (e.g. a commodity that is already running), step through source code or assembler instructions, set breakpoints on labels or addresses, display variable values, and just about anything else a serious programmer may need.<br /> <br /> So of what use is GDB to a beginner programmer? Why have GDB on Amiga OS at all? Put simply, GDB enables you to determine exactly what is happening inside your program, and help you figure out why it runs into trouble. For example, if you wrote a program, but it doesn't work how you expected it to, and/or it crashes. You can use GDB to set a breakpoint somewhere before the problem, and launch the program. The program will run until it reaches the breakpoint, and then you can check the state of the program, its variables, dump memory buffers, inspect the stack and see exactly what is going on. Then, after you have investigated everything you need to look at, you can continue execution of the program, or step over the code (either at source code or machine code level), to see what is happening.<br /> <br /> GDB on AmigaOS is also good for any research of work, for example you can find out at which address AmigaOS loads a relocated ELF, which addresses it uses for kernel areas, what happens when programs calls the system parts of OS and so on. Of course, it is entirely possible to do some non-interactive debugging without GDB, like plain disassembly (with &quot;objdump&quot; for example), but static disassembly is not real time debugging, so you can't check what happens exactly at a given moment of a program's execution.<br /> <br /> Sometimes it is useful to be able to use a debugger when you have no documentation to hand, and don't know how some functions behave, or what they return and where. Also, it can sometimes be the case that even the documentation is wrong and the debugger can help you confirm exactly what is wrong to fill out a proper bug-report or for creating a patch/fix.<br /> <br /> When using GDB, it is necessary to include debugging information in the binary. This information will you to reference functions and variables by their names, and set breakpoints at named functions, like at &quot;main()&quot; to debug from the start of your program, or function &quot;foo()&quot;. It is possible to debug binaries which have no debugging information at all, but then you will not be able to follow the high-level source code (usually C/C++), and will only be able to examine the assembly code. Without debug information you are usually forced to start at the &quot;_start&quot; label, which is where the all real code start (which is in the C/C++ library startup code for C/C++ programs).<br /> <br /> GCC has many flags to specify the kind of debugging information to be included in the binary, including -g, -ggdb, -gstabs and -gstabs+. The only flag which is recommended for use on AmigaOS is -gstabs. So, whenever you wish to create a binary with debugging information, you should add &quot;-gstabs&quot;. Other ones may or may not work, but to avoid any issues just use &quot;-gstabs&quot;. The same is true if you want to just use &quot;addr2line&quot; to find the address of a crash from a stack trace.<br /> <br /> = First Steps =<br /> <br /> Lets start with the simple Hello World example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; type hello.c<br /> &lt;/pre&gt;<br /> <br /> &lt;syntaxhighlight&gt;<br /> #include &lt;stdio.h&gt;<br /> int main()<br /> {<br /> printf(&quot;just hello&quot;);<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gcc -gstabs hello.c -o hello<br /> <br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) quit<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; <br /> &lt;/pre&gt;<br /> <br /> In the above example, we compiled a binary with debug information, and ran the binary inside the debugger. The &quot;-q&quot; (or &quot;--quiet&quot;) option on the command line just tells GDB not to print version information on startup.<br /> <br /> For basic debugging, you need to know how to set a breakpoint, how to step over the code, and how to view code, variable and memory contents. The most useful commands for beginners are: run, break, disassemble, next, step, stepi and continue. For all the GDB commands you always have some short names, like b for break, r for run, disas for disassembly, si for stepi and so on. Lets cover the most common scenarios:<br /> <br /> = Breakpoints =<br /> <br /> Just running a program in the debugger isn't very useful - we need to pause execution and examine the state of the code at a particular point. For that, we use the &quot;breakpoint&quot; command (&quot;break&quot; or &quot;b&quot; for short). For example:<br /> <br /> &lt;pre&gt;<br /> break _start : break at beginning of the _start() function<br /> break 10 : break at line 10 of the current file<br /> break *0xXXXXXXX : break at the address<br /> &lt;/pre&gt;<br /> <br /> Lets see it in action: <br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) info break<br /> Num Type Disp Enb Address What<br /> 1 breakpoint keep y 0x7f974208 in main at hello.c:3<br /> (gdb) run<br /> Starting program: /RAM Disk/hello <br /> BS 653c1a68<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Here we have set a breakpoint at beginning of program, displayed the current breakpoints (there is only the one we just set), then ran the program. The program execution stopped at the beginning of the main function (this is called &quot;hitting a breakpoint&quot;), and returned to the GDB prompt. Once at the GDB prompt, you can disable or enable any breakpoint (via &quot;enable&quot; and &quot;disable&quot; commands), as well as removing it with the &quot;clear&quot; command.<br /> <br /> = Step by Step =<br /> <br /> Once you have hit a breakpoint, you can have fine control over the execution of the program, using the following commands:<br /> <br /> &lt;pre&gt;<br /> next (n) - Execute current statement and move to the next one in the function.<br /> <br /> step (s) - The same as next, but with difference that if you are at a function call and you hit next, then the function will execute and return. But if you hit step, then you will step into the first line of the called function.<br /> <br /> stepi (si) - Stepping a single assembly instruction at a time. This is only for experienced developers with knowledge of assembly language.<br /> <br /> continue (c) - If you are finished with manual stepping, then you can just type &quot;c&quot; and the program will continue execution until the next breakpoint is reached, or the program reaches the end.<br /> &lt;/pre&gt;<br /> <br /> Here is a more in-depth example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) list<br /> 1 #include &lt;stdio.h&gt;<br /> 2 main()<br /> 3 {<br /> 4 printf(&quot;just hello&quot;);<br /> 5 return 0;<br /> 6 }<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 63afafd8<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) step<br /> main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) step<br /> 0x7f97424c in printf ()<br /> (gdb) step<br /> 0x7f974254 in __NewlibCall ()<br /> (gdb) step<br /> just hello<br /> Program terminated with signal SIGQUIT, Quit.<br /> The program no longer exists.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> In that example we examine the source of the program via the &quot;list&quot; command, set break at main(), and then step over the main function. As you can see, printf() also calls the __Newlibcall() function (which is part of the newlib libc.a). You could also debug to see what exactly it does, which argument it takes and what happens after.<br /> <br /> By the way; when you are stepping with stepi (i.e. per instruction) and you reach a &quot;bctrl&quot; instruction, then, via stepi you will jump into the kernel, which is probably not what you want, so to jump over that kind of instruction, and to avoid entering to kernel, you can just do:<br /> <br /> &lt;pre&gt;<br /> (gdb) break _start<br /> (gdb) r<br /> (gdb) disas<br /> .....<br /> (gdb) break *0xaddress_after_bctrl<br /> (gdb) c<br /> &lt;/pre&gt;<br /> <br /> and then again continue with stepi.<br /> <br /> = Disassembling =<br /> <br /> When you reach a point in your program where a crash happens, you might want to see how your C function looks &quot;for real&quot;, in assembly language. For this, you use the &quot;disassemble&quot; command (or &quot;disas&quot; for short):<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;/pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 6624f370<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> Of course for real work you should know at least the basics of PowerPC assembly. You might want to read a book on this (the best one is called the &quot;green book&quot;, check the links section), but even without deep knowledge you can see that there some magic happens, then a jump to &quot;printf&quot;, and then the rest of the code performs a clean exit (return 0). Those strange values at the &lt;main+20&gt; and &lt;main+24&gt; are offsets to our &quot;hello world&quot; buffer.<br /> <br /> = Get More Info =<br /> <br /> As you should know, assembly language is a set of instructions that the CPU understands directly. The PowerPC architecture is designed to manipulate data inside CPU registers. It has 32 general registers, 32 floating point registers, and some special ones. So, with every single &quot;stepi&quot;, some registers will change their state, and that can be seen by the:<br /> <br /> &lt;pre&gt;<br /> -- info reg - show all the registers at current moment<br /> -- info reg X - show only register X<br /> &lt;/pre&gt;<br /> <br /> With this information, you can get to know exactly which data is placed in the registers, which memory regions they point to or what values they have.<br /> <br /> You can also dump any memory to which your program has access, and to do this you use the &quot;x&quot; command (short for &quot;examine&quot;). The options for this command are the size and format of data to dump and an address where the data you are interested in resides. This can be pretty helpful when you want to know what kind of data you have in the stack, or an array. Lets take a look at that example:<br /> <br /> &lt;pre&gt;<br /> 7/0.RAM Disk:&gt; gdb -q hello<br /> &lt;pre&gt;<br /> <br /> &lt;pre&gt;<br /> (gdb) break main<br /> Breakpoint 1 at 0x7f974208: file hello.c, line 3.<br /> (gdb) r<br /> Starting program: /RAM Disk/hello <br /> BS 657d7430<br /> Current action: 2<br /> <br /> Breakpoint 1, main () at hello.c:3<br /> 3 {<br /> (gdb) disas<br /> Dump of assembler code for function main:<br /> 0x7f974208 &lt;main+0&gt;: stwu r1,-16(r1)<br /> 0x7f97420c &lt;main+4&gt;: mflr r0<br /> 0x7f974210 &lt;main+8&gt;: stw r31,12(r1)<br /> 0x7f974214 &lt;main+12&gt;: stw r0,20(r1)<br /> 0x7f974218 &lt;main+16&gt;: mr r31,r1<br /> 0x7f97421c &lt;main+20&gt;: lis r9,25978<br /> 0x7f974220 &lt;main+24&gt;: addi r3,r9,-24520<br /> 0x7f974224 &lt;main+28&gt;: crclr 4*cr1+eq<br /> 0x7f974228 &lt;main+32&gt;: bl 0x7f97424c &lt;printf&gt;<br /> 0x7f97422c &lt;main+36&gt;: li r0,0<br /> 0x7f974230 &lt;main+40&gt;: mr r3,r0<br /> 0x7f974234 &lt;main+44&gt;: lwz r11,0(r1)<br /> 0x7f974238 &lt;main+48&gt;: lwz r0,4(r11)<br /> 0x7f97423c &lt;main+52&gt;: mtlr r0<br /> 0x7f974240 &lt;main+56&gt;: lwz r31,-4(r11)<br /> 0x7f974244 &lt;main+60&gt;: mr r1,r11<br /> 0x7f974248 &lt;main+64&gt;: blr<br /> End of assembler dump.<br /> (gdb) break *0x7f974224<br /> Breakpoint 2 at 0x7f974224: file hello.c, line 4.<br /> (gdb) c<br /> Continuing.<br /> Current action: 0<br /> BS 63b03568<br /> Current action: 2<br /> <br /> Breakpoint 2, 0x7f974224 in main () at hello.c:4<br /> 4 printf(&quot;just hello&quot;);<br /> (gdb) info reg r3<br /> r3 0x6579a038 1702469688<br /> (gdb) x/1s 0x6579a038<br /> 0x6579a038 &lt;_SDA_BASE_+28756968&gt;: &quot;just hello&quot;<br /> (gdb) <br /> &lt;/pre&gt;<br /> <br /> We set a breakpoint at main(), just to have ability to run and disassemble the main function. We then set a breakpoint at the &quot;crclr&quot; instruction before the jump to printf, and continued execution. We then hit the breakpoint, which means that the CPU stopped just before executing the instruction at 0x7f974224. The instructions just before that (at addresses 0x7f97421c and 0x7f974220) calculated an address and stored it in register 3 (r3). We viewed the contents of r3 with the &quot;info reg r3&quot; command, and saw that it contained &quot;0x6579a038&quot;. We then examined the memory at that address as a string, and we can see that it is our &quot;just hello&quot; string, so we can see that the first argument to printf is placed in r3.<br /> <br /> = Final Words =<br /> <br /> So now, even at this point, you can already start to do some real-time debugging on AmigaOS. Of course, this article only covers a few commands and examples. GDB has hundreds of commands with all kinds of features and possibilities, but hopefully this small guide will help to get you started.<br /> <br /> Sure, to use GDB fully, you should know PowerPC assembly language, C, and how it all works at low-level, but you don't have to know all that for GDB to be useful. Hopefully this has helped you get started.<br /> <br /> If that there is demand, I will write more articles to explain more aspects of our GDB, its different features and abilities.<br /> <br /> Thanks for reading, and for Peter Gordon for proof-reading and helping with corrections.<br /> <br /> = Quick reference =<br /> <br /> == Basics ==<br /> <br /> &lt;pre&gt;<br /> amiga shell:&gt; gdb filename - start gdb with loaded binary to it<br /> (gdb) file filename - load file when you are already in GDB<br /> (gdb) run - run :)<br /> (gdb) run arg1 arg2 etc - run program with command line args<br /> (gdb) quit - quit :)<br /> (gdb) help command - get help for a certain command<br /> &lt;/pre&gt;<br /> <br /> == Breakpoints ==<br /> <br /> &lt;pre&gt;<br /> &lt;/pre&gt;<br /> <br /> == Disassembling ==<br /> <br /> &lt;pre&gt;<br /> (gdb) disas - disassemble current function<br /> (gdb) disas 0xXXXXXXX - disassemble by address (unlike setting a breakpoint by address, you don't need a '*')<br /> (gdb) x/[num]i 0xXXXXXXX - disassemble by &quot;examine&quot; any amount of instructions ([num]) at any address in memory<br /> &lt;/pre&gt;<br /> <br /> == Stepping ==<br /> <br /> &lt;pre&gt;<br /> (gdb) step - step into the function on this line (if possible)<br /> (gdb) stepi - step by assembly instructions<br /> (gdb) next - run to the next line of this function<br /> (gdb) continue - continue to next breakpoint (or till end)<br /> <br /> for all the stepping functions you can specify how many steps to do, e.g. stepi 100, or next 20.<br /> &lt;/pre&gt;<br /> <br /> == Registers ==<br /> <br /> &lt;pre&gt;<br /> (gdb) info registers - show integer registers only (the most usable)<br /> (gdb) info all-registers - show all registers (including floating point ones, special ones etc, not used so often)<br /> (gdb) info reg X - show only register X <br /> &lt;/pre&gt;<br /> <br /> == Misc == <br /> <br /> &lt;pre&gt;<br /> (gdb) list - examine the source of the program<br /> (gdb) bt - alias of &quot;backtrace&quot;: show the current stack<br /> (gdb) RETURN - if you hit &quot;enter&quot; while you in gdb, it will repeat the last command<br /> &lt;/pre&gt;<br /> <br /> = Links =<br /> <br /> [1] [http://www.freescale.com/files/product/doc/MPCFPE32B.pdf Green Book] - the best for ppc-assembly.&lt;br/&gt;<br /> [2] [http://devpit.org/wiki/GDB Article on DevPit] - GDB relates, and was originally written from a 32bit PowerPC architecture perspective and register information will vary across architectures.&lt;br/&gt;<br /> [3] [http://www.gnu.org/s/gdb/documentation/ Original GDB docs]&lt;br/&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&diff=4840 How to open and use the exec debug interface 2013-02-18T15:21:17Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> == Author ==<br /> <br /> Alfkil Wennermark&lt;br/&gt;<br /> Copyright (c) 2010 Alfkil Wennermark&lt;br/&gt;<br /> Used by permission.<br /> <br /> == Tutorial ==<br /> <br /> Next step in my small series concerns itself with how to open and use the &quot;secret&quot; (but very useful) debug interface. Thanks to Steven Solie and Thomas Frieden.<br /> <br /> Probably the best way to trap exceptions from within your code is to use the debug interface, that is &quot;hidden&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 sources. These can be found inside the adtools project on sourceforge.net.<br /> <br /> 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'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.<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* debugtrap.cExample of use of the exec debug interface<br /> by Alfkil Wennermark 2010<br /> <br /> Thanks to Steven Solie, Thomas Frieden and others<br /> <br /> This code is partially copied from Thomas' GDB source<br /> */<br /> <br /> <br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;exec/interrupts.h&gt;<br /> #include &lt;exec/tasks.h&gt;<br /> <br /> #include &lt;dos/dos.h&gt;<br /> <br /> #include &lt;stdio.h&gt;<br /> <br /> <br /> struct DebugIFace *IDebug = 0;<br /> <br /> struct KernelDebugMessage<br /> {<br /> uint32 type;<br /> union<br /> {<br /> struct ExceptionContext *context;<br /> struct Library *library;<br /> } message;<br /> };<br /> <br /> static ULONG amigaos_debug_callback(struct Hook *, struct Task *, struct KernelDebugMessage *);<br /> <br /> struct Hook debug_hook;<br /> struct Task *amiga_task;<br /> <br /> BPTR exec_seglist;<br /> ULONG debug_data = 1234;<br /> <br /> void init()<br /> {<br /> IDebug = (struct DebugIFace *)IExec-&gt;GetInterface((struct Library *)SysBase, &quot;debug&quot;, 1, 0);<br /> if (!IDebug)<br /> {<br /> printf(&quot;Can't get DEBUG accessn&quot;);<br /> exit(RETURN_FAIL);<br /> }<br /> <br /> debug_hook.h_Entry = (ULONG (*)())amigaos_debug_callback;<br /> debug_hook.h_Data =(APTR)&amp;debug_data;<br /> <br /> /* NB: Ideally we would start up another task, that<br /> we want to debug, and attach ourselves to that<br /> task using the debug hook, but for simplicity<br /> we just use our own task here */<br /> amiga_task = IExec-&gt;FindTask(NULL);<br /> IDebug-&gt;AddDebugHook(amiga_task, &amp;debug_hook);<br /> }<br /> <br /> void end()<br /> {<br /> IDebug-&gt;AddDebugHook(amiga_task, 0);<br /> <br /> if (IDebug)IExec-&gt;DropInterface((struct Interface *)IDebug);<br /> IDebug = NULL;<br /> }<br /> <br /> <br /> <br /> ULONG<br /> amigaos_debug_callback(struct Hook *hook, struct Task *currentTask,<br /> struct KernelDebugMessage *dbgmsg)<br /> {<br /> struct ExecIFace *IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)-&gt;MainInterface;<br /> <br /> uint32 *data = (uint32 *)hook-&gt;h_Data;<br /> <br /> /* these are the 4 types of debug msgs: */<br /> switch (dbgmsg-&gt;type)<br /> {<br /> case DBHMT_REMTASK:<br /> *data = 9;<br /> break;<br /> <br /> case DBHMT_EXCEPTION:<br /> *data = 11;<br /> break;<br /> <br /> case DBHMT_OPENLIB:<br /> *data = 13;<br /> break;<br /> <br /> case DBHMT_CLOSELIB:<br /> *data = 15;<br /> break;<br /> <br /> default:<br /> *data = 0;<br /> break;<br /> }<br /> /* returning 1 will suspend the task ! */<br /> return 0;<br /> }<br /> <br /> <br /> int main()<br /> {<br /> init();<br /> <br /> /* Cause an exception on purpose: */<br /> uint32 *beef = 0;<br /> *beef = 0L;<br /> <br /> printf(&quot;We received a&quot;);<br /> switch (debug_data)<br /> {<br /> case 9:<br /> printf(&quot; REMTASK&quot;);<br /> break;<br /> case 11:<br /> printf(&quot;n EXCEPTION&quot;);<br /> break;<br /> case 13:<br /> printf(&quot;n OPENLIB&quot;);<br /> break;<br /> case 15:<br /> printf(&quot; CLOSELIB&quot;);<br /> break;<br /> <br /> default:<br /> printf(&quot;n unknown&quot;);<br /> break;<br /> }<br /> printf(&quot; signal!n&quot;);<br /> <br /> end();<br /> <br /> return RETURN_OK;<br /> }<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=How_to_install_a_hardware_interrupt&diff=4839 How to install a hardware interrupt 2013-02-18T15:20:52Z <p>Alexandre Balaban: Categorized into Debug</p> <hr /> <div>[[Category:Debug]]<br /> == Author ==<br /> Alfkil Wennermark&lt;br/&gt;<br /> Copyright (c) 2010 Alfkil Wennermark&lt;br/&gt;<br /> Used by permission.<br /> <br /> == Tutorial ==<br /> <br /> I was looking for a way to install a genuine hw interrupt, and this code is the proper way to do so. Thanks to Steven Solie, Thomas Frieden, Jörg Strohmayer and Colin Wenzel.<br /> <br /> When trying to install an exec interrupt handler, I quickly realised, that the IExec-&gt;AddIntServer didn't really do the job. All the trapnumbers defined in exec/interrupts.h will make this function fail, except for TRAPNUM_ILLEGAL_INSTRUCTION, that has no relevance on ppc machines.<br /> <br /> After some (a lot of) guidance from the real people, I have come up with this example, that uses SetIntVector to install a global interrupt handler for that particular interrupt. Beware, when you use SetIntVector, you _have to_ manually call the old interrupt handler (returned from SetIntVector) if you don't want your entire system to go boom!<br /> <br /> Also, if you are trying to catch exceptions from a specific task (eg. for debugging purposes) you should use the AddDebugHook function from the exec debug interface (more on this in a future article).<br /> <br /> Here is the example:<br /> <br /> &lt;syntaxhighlight&gt;<br /> /* realinterrupt.c - HW interrupt example code.<br /> Edited and updated for amigaos 4 by Alfkil T. Wennermark 2010<br /> Edited and bug-tested by Steven Solie.<br /> <br /> Thanks to: Steven Solie, Jörg Strohmayer, Thomas Frieden and Colin Wenzel :-) */<br /> <br /> /* REMARK: On a SAM/SAM-flex system, the returned counter will be a very large number.<br /> We have yet to figure out, why this is. On AmigaOne's it will be 1 as expected. */<br /> <br /> <br /> #include &lt;exec/types.h&gt;<br /> #include &lt;proto/exec.h&gt;<br /> #include &lt;proto/dos.h&gt;<br /> <br /> #include &lt;exec/memory.h&gt;<br /> #include &lt;exec/interrupts.h&gt;<br /> <br /> #include &lt;dos/dos.h&gt;<br /> <br /> <br /> #include &lt;stdio.h&gt;<br /> <br /> uint32 *beef;<br /> <br /> <br /> struct IData {<br /> uint32 counter;<br /> struct Interrupt *oldinterrupt;<br /> };<br /> <br /> struct IData *idata;<br /> <br /> #if 0 //This will not work, because it doesn't call the old interrupt<br /> asm (<br /> &quot; .globl icode\n&quot;<br /> &quot; \n&quot;<br /> &quot;icode: \n&quot;<br /> &quot; lwz %r3, 0(%r5) \n&quot;<br /> &quot; addi %r3, %r3, 5\n&quot;<br /> &quot; stw %r3, 0(%r5) \n&quot;<br /> &quot; li %r3, 1 \n&quot;<br /> &quot; blr \n&quot;<br /> );<br /> #endif<br /> <br /> <br /> ULONG icode( struct ExceptionContext *context, struct ExecBase *sb, APTR trapData)<br /> {<br /> struct IData *d = (struct IData *)trapData;<br /> struct Interrupt *oldi = d-&gt;oldinterrupt;<br /> <br /> d-&gt;counter++;<br /> <br /> //NB-NB-NB: You _need_ to call the old interrupt, otherwise you will go boom!<br /> typedef uint32 (*OLDVEC)();<br /> <br /> return ((OLDVEC)oldi-&gt;is_Code)(context, sb, oldi-&gt;is_Data);<br /> }<br /> <br /> <br /> int main()<br /> {<br /> enum enTrapNumbers trapnumber = TRAPNUM_DATA_SEGMENT_VIOLATION;<br /> <br /> if (!(idata = IExec-&gt;AllocMem(sizeof(struct IData), MEMF_SHARED | MEMF_CLEAR)))<br /> return RETURN_FAIL;<br /> <br /> struct Interrupt *interrupt = IExec-&gt;AllocSysObjectTags(ASOT_INTERRUPT,<br /> ASOINTR_Code, icode,<br /> ASOINTR_Data, idata,<br /> TAG_END);<br /> <br /> if (interrupt == NULL)<br /> {<br /> IExec-&gt;FreeMem (idata, sizeof (struct IData));<br /> return(-20);<br /> }<br /> <br /> interrupt-&gt;is_Node.ln_Pri = -1;<br /> interrupt-&gt;is_Node.ln_Name = &quot;my personal interrupt&quot;;<br /> <br /> //printf(&quot;icode = %#08x\n&quot;, icode (NULL, NULL, idata));<br /> //printf(&quot;counter = %d\n&quot;, idata-&gt;counter);<br /> <br /> printf(&quot;calling AddIntServer...\n&quot;);<br /> fflush(stdout);<br /> <br /> // IExec-&gt;Forbid();<br /> idata-&gt;oldinterrupt = IExec-&gt;SetIntVector(trapnumber, interrupt);<br /> // IExec-&gt;Permit();<br /> <br /> if(!idata-&gt;oldinterrupt)<br /> {<br /> printf(&quot;server not installed!\n&quot;);<br /> }<br /> else<br /> {<br /> printf(&quot;server installed\n&quot;);<br /> fflush(stdout);<br /> <br /> idata-&gt;counter = 0;<br /> <br /> //#if 0<br /> /* cause a data storage violation: */<br /> beef = (uint32 *)0;<br /> *beef = 0L;<br /> //#endif<br /> <br /> IDOS-&gt;Delay(10);<br /> <br /> printf(&quot;counter = %d\n&quot;, idata-&gt;counter);<br /> <br /> /* Remember to restore the old interrupt vector, or you will die ;-) */<br /> IExec-&gt;SetIntVector(trapnumber, idata-&gt;oldinterrupt);<br /> }<br /> <br /> IExec-&gt;FreeSysObject(ASOT_INTERRUPT, interrupt);<br /> IExec-&gt;FreeMem (idata, sizeof (struct IData));<br /> <br /> return 0;<br /> }<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Redirecting_Debug_Output_to_the_Serial_Port_on_Startup&diff=4838 Redirecting Debug Output to the Serial Port on Startup 2013-02-18T15:18:29Z <p>Alexandre Balaban: Categorized into Debug and U-Boot</p> <hr /> <div>[[Category:Debug]][[Category:U-Boot]]<br /> = Introduction =<br /> <br /> Despite all the efforts that are put into software, computers still crash from time to time. Sometimes this occurs on startup. AmigaOS contains a debugging system that enables any application to output to a debug buffer or a serial port. By default, the debug buffer is in memory, and a call to &quot;dumpdebugbuffer&quot; is required in order to save it to disk. However, this buffer does not survive a hard reset, so any debug output produced before a lockup is lost. The easiest solution is to redirect the debug output to the serial port, and record it using another computer.<br /> <br /> Redirecting the debug output to the serial port on start up is achieved as follows:<br /> <br /> * Switch on the computer (or push the reset switch), and enter the UBoot menu by pressing ENTER at the appropriate time,<br /> * Select &quot;Boot Sequence&quot;,&lt;br/&gt; [[File:RedirectDebug1.jpg]]<br /> * In the &quot;Boot Sequence&quot; menu, select &quot;Boot arguments for AmigaOS&quot;,&lt;br/&gt; [[File:RedirectDebug2.jpg]]<br /> * Add serial to the boot arguments,&lt;br/&gt; [[File:RedirectDebug3.jpg]]<br /> * The result should be something like the following,&lt;br/&gt; [[File:RedirectDebug4.jpg]]<br /> * Boot with this configuration, or save the configuration if you always wish to redirect the debug output to serial.<br /> <br /> = Capturing the Debug Output =<br /> <br /> Capturing the debug output requires a null-modem serial cable, and another computer, with a serial port. Simply connect the first serial port on the Amiga, to a serial port on the other computer, and open your favourite terminal program (e.g., PuTTY). Switching on the Amiga should result in debug output appearing in the terminal window (or screen). If the received text looks like random characters, then the baud rate of the serial ports do not match; adjust the bit-rate until the output is readable.<br /> <br /> = Author =<br /> <br /> Copyright (c) 2008 Hans de Ruiter.&lt;br/&gt;<br /> Reproduced with permission.&lt;br/&gt;<br /> See the original article [http://hdrlab.org.nz/articles/amiga-os-articles/redirecting-debug-output-to-the-serial-port-on-startup/ here].</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Debug_Kernel&diff=4830 Debug Kernel 2013-02-14T11:11:46Z <p>Alexandre Balaban: /* What is &quot;debug kernel&quot; exactly? */ Applied kas1e suggestions: DAR is called DEAR on Sam,</p> <hr /> <div>== Introduction ==<br /> <br /> When you program AmigaOS4, sooner or later you will need tools to help you debugging your applications (and not only yours) and find out why a program crashes or runs erratically. For that you have few debuggers (gdb and db101), small tools to operate with debug outputs and level of debugging (like kdebug, dumpdebugbuffer, etc.), memory trackers (like memguard and memguardian) and bunch of other small utilities and tricks which help you to do debugging. One of those tricks, which is little known, is the fact that a debug version of the standard AmigaOS4 kernel is publicly released ready to use for third party developers (or advanced users willing to help debugging applications).<br /> <br /> == What is &quot;debug kernel&quot; exactly? ==<br /> <br /> Debug kernel is the same kernel as the standard one, but with added code to help catching some of the most common programming errors. This additional code adds a slightly (yet non negligible) amount of bytes to the kernel size (around half a megabyte) and is slower. Thus it has been removed from the standard kernel for performance and optimization purposes and is generally not useful to the standard average user. Yet this debug kernel can prove itself to be very useful to programmers, especially it provides the following functionality:<br /> <br /> # Clobbering of succ/pred pointers when a Node is removed from a list.&lt;BR&gt;One common mistake when dealing with lists is to access successor and/or previous pointers from a removed node. While at first sight it may seems harmless it may reveal itself as a dreadful bug to spot because once a node has been removed from a list nothing guarantees you that the list has not be reworked/copied/reallocated or anything else. This will give you a bug that is very hard to reproduce or even non reproducible at all. As such one should NEVER access Succ/Prev pointers from a removed Node, instead if he needs to, he should access them prior the call to Remove(). To help spotting such errors the debug kernel will modify the ln_Succ/ln_Prev pointer to a given fixed invalid value thus the bug automatically becomes always reproducible. This will also help you spot the fact one is trying to free a Node twice.<br /> # &quot;Munge&quot; of memory. With this option the debug kernel will do additional action/tests on memory pointers. It will help spotting what really happen under the hood just by observing the DAR (or DEAR on Sam CPUs) value in the crash log:<br /> #* If the value is 0x00000000 (or thereabouts), then you have accessed a null pointer (and you will see 0xABADCAFE in one or more registers);<br /> #* If the value is 0xDEADBEEF (or thereabouts), then you have accessed some memory after it has been freed;<br /> #* If the value is 0xCCCCCCCC (or thereabouts), then you have tried to free a Node a second time.<br /> <br /> So, in other words, when you want to catch all those problems which you can't catch by just simply running memguard on &quot;plain&quot; kernel, you should use debug kernel. I.e. to say it more clear, when you develop anything you should be just on debug kernel (+memguard/memguardian at background ? or its all already in debug kernel?)<br /> <br /> == How to set it up ==<br /> <br /> Debug.kernel file is already placed in the Kickstart directory with all other modules. By default this directory is &quot;Sys:Kickstart/&quot;. To use the debug kernel there are two options:<br /> # you can just rename the standard kernel named &quot;kernel&quot; to &quot;kernel.standard&quot;, and &quot;kernel.debug&quot; to &quot;kernel&quot; but in this case you loose the opportunity of booting the standard kernel and moreover in case of a kernel update via AmiUpdate you will end up with you modification undone;<br /> # the preferred way of doing is to edit your file kicklayout, duplicate the current layout and in this duplicate modify the line pointing to &quot;kernel&quot; so it points to &quot;kernel.debug&quot;, do not forget to change the label associated with this new layout by adding 'debug' or something. This way you will be able to choose which configuration you want to boot at any time.<br /> <br /> Once you have setup the use of the debug kernel, you must also add some commandline options to the kernel. Basically in order to activate the munge option of the debug kernel you must set the variable &quot;os4_commandline&quot; adds the keyword &quot;munge&quot;. This part depends on your platform firmware, please report to your documentation to know how to do it, but you have to do something like os4_commandline='munge', if os4_commandline is already set with another value you can concatenate using space e.g. os4_commandline='serial munge' to have the debug output to serial port and the munge option activated.<br /> <br /> == FAQ ==<br /> <br /> None at the moment<br /> <br /> [[Category:Debug]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Debug_Kernel&diff=4829 Debug Kernel 2013-02-14T10:32:19Z <p>Alexandre Balaban: Article creation</p> <hr /> <div>== Introduction ==<br /> <br /> When you program AmigaOS4, sooner or later you will need tools to help you debugging your applications (and not only yours) and find out why a program crashes or runs erratically. For that you have few debuggers (gdb and db101), small tools to operate with debug outputs and level of debugging (like kdebug, dumpdebugbuffer, etc.), memory trackers (like memguard and memguardian) and bunch of other small utilities and tricks which help you to do debugging. One of those tricks, which is little known, is the fact that a debug version of the standard AmigaOS4 kernel is publicly released ready to use for third party developers (or advanced users willing to help debugging applications).<br /> <br /> == What is &quot;debug kernel&quot; exactly? ==<br /> <br /> Debug kernel is the same kernel as the standard one, but with added code to help catching some of the most common programming errors. This additional code adds a slightly (yet non negligible) amount of bytes to the kernel size (around half a megabyte) and is slower. Thus it has been removed from the standard kernel for performance and optimization purposes and is generally not useful to the standard average user. Yet this debug kernel can prove itself to be very useful to programmers, especially it provides the following functionality:<br /> <br /> # Clobbering of succ/pred pointers when a Node is removed from a list.&lt;BR&gt;One common mistake when dealing with lists is to access successor and/or previous pointers from a removed node. While at first sight it may seems harmless it may reveal itself as a dreadful bug to spot because once a node has been removed from a list nothing guarantees you that the list has not be reworked/copied/reallocated or anything else. This will give you a bug that is very hard to reproduce or even non reproducible at all. As such one should NEVER access Succ/Prev pointers from a removed Node, instead if he needs to, he should access them prior the call to Remove(). To help spotting such errors the debug kernel will modify the ln_Succ/ln_Prev pointer to a given fixed invalid value thus the bug automatically becomes always reproducible. This will also help you spot the fact one is trying to free a Node twice.<br /> # &quot;Munge&quot; of memory. With this option the debug kernel will do additional action/tests on memory pointers. It will help spotting what really happen under the hood just by observing the DAR value in the crash log (GrimRipper log):<br /> #* If the DAR is 0x00000000 (or thereabouts), then you have accessed a null pointer (and you will see ABADCAFE in one or more registers);<br /> #* If the DAR is 0xDEADBEEF (or thereabouts), then you have accessed some memory after it has been freed;<br /> #* If the DAR is 0xCCCCCCCC (or thereabouts), then you have tried to free a Node a second time.<br /> <br /> So, in other words, when you want to catch all those problems which you can't catch by just simply running memguard on &quot;plain&quot; kernel, you should use debug kernel. I.e. to say it more clear, when you develop anything you should be just on debug kernel (+memguard/memguardian at background ? or its all already in debug kernel?)<br /> <br /> == How to set it up ==<br /> <br /> Debug.kernel file is already placed in the Kickstart directory with all other modules. By default this directory is &quot;Sys:Kickstart/&quot;. To use the debug kernel there are two options:<br /> # you can just rename the standard kernel named &quot;kernel&quot; to &quot;kernel.standard&quot;, and &quot;kernel.debug&quot; to &quot;kernel&quot; but in this case you loose the opportunity of booting the standard kernel and moreover in case of a kernel update via AmiUpdate you will end up with you modification undone;<br /> # the preferred way of doing is to edit your file kicklayout, duplicate the current layout and in this duplicate modify the line pointing to &quot;kernel&quot; so it points to &quot;kernel.debug&quot;, do not forget to change the label associated with this new layout by adding 'debug' or something. This way you will be able to choose which configuration you want to boot at any time.<br /> <br /> Once you have setup the use of the debug kernel, you must also add some commandline options to the kernel. Basically in order to activate the munge option of the debug kernel you must set the variable &quot;os4_commandline&quot; adds the keyword &quot;munge&quot;. This part depends on your platform firmware, please report to your documentation to know how to do it, but you have to do something like os4_commandline='munge', if os4_commandline is already set with another value you can concatenate using space e.g. os4_commandline='serial munge' to have the debug output to serial port and the munge option activated.<br /> <br /> == FAQ ==<br /> <br /> None at the moment<br /> <br /> [[Category:Debug]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Application_Library&diff=4139 Application Library 2012-09-03T21:12:30Z <p>Alexandre Balaban: /* Populating the file */ Added allocation, release and reference counting</p> <hr /> <div>{{WIP}}<br /> <br /> == Introduction ==<br /> The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of ''application'' is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either [[Exec_Tasks|tasks]] or [[AmigaDOS_Data_Structures#Process_Data_Structures|processes]]. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other ''background services''), and genuine full-blown ''applications'' with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.<br /> <br /> AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called [[#Registering the application|application registration]], during which the application receives a [[#Application identifiers|unique identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:<br /> <br /> * It can send/receive messages to/from other registered applications. The library supports a set of common [[#Control messages|control messages]] (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows [[#Custom messages|custom messages]] designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control.<br /> <br /> * It can use [[#PrefsObjects|PrefsObjects]], an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> * It can notify the user about, for example, completed tasks via automatic [[#Pop-up notifications (Ringhio_messages)|pop-up messages]]. These represent a practical, less obtrusive alternative to traditional requesters.<br /> <br /> * It can easily create and manage lists of recently-used documents.<br /> <br /> * It can register as a [[#Unique applications|unique application]], preventing other instances of itself from running.<br /> <br /> * It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.<br /> <br /> * It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.<br /> <br /> == Library opening chores ==<br /> <br /> Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Library *ApplicationBase = NULL;<br /> struct ApplicationIFace *IApplication = NULL;<br /> struct PrefsObjectsIFace *IPrefsObjects = NULL;<br /> <br /> if ( (ApplicationBase = IExec-&gt;OpenLibrary(&quot;application.library&quot;, 52)) )<br /> {<br /> IApplication = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;application&quot;, 1, NULL);<br /> IPrefsObjects = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;prefsobjects&quot;, 1, NULL);<br /> }<br /> <br /> if ( !ApplicationBase || !IApplication || !IPrefsObjects )<br /> {<br /> /* handle library opening error */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that there is no interface called “main” like older, single-interface libraries have.<br /> <br /> When your application has run its course, don’t forget to clean up and close both the library and its interface(s):<br /> <br /> &lt;syntaxhighlight&gt;<br /> IExec-&gt;DropInterface((struct Interface *)IPrefsObjects);<br /> IExec-&gt;DropInterface((struct Interface *)IApplication);<br /> IExec-&gt;CloseLibrary(ApplicationBase);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Registering the application ==<br /> <br /> Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.<br /> <br /> === Application identifiers ===<br /> <br /> A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.<br /> <br /> Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.<br /> <br /> === Registration ===<br /> <br /> Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> TAG_DONE);<br /> <br /> if (!appID)<br /> {<br /> /* report registration error and quit */<br /> }<br /> <br /> /*<br /> do whatever your program does<br /> */<br /> <br /> IApplication-&gt;UnregisterApplication(appID, NULL);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)<br /> <br /> The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.<br /> <br /> During registration you may also indicate certain features your application will support. In particular, you may inform the system that your application opens a dedicated Preferences window, that it can be iconified, that it can create documents, or that it supports printing documents. This information is not required but it can be used by other applications, which may decide to query about the availability of a certain feature before they send a [[#Control messages|control message]]. Set the following boolean tags to TRUE in the RegisterApplication() call if your application supports the respective feature (the default value is FALSE):<br /> <br /> * REGAPP_HasPrefsWindow<br /> * REGAPP_HasIconifyFeature<br /> * REGAPP_CanCreateNewDocs<br /> * REGAPP_CanPrintDocs<br /> <br /> === Unique applications ===<br /> <br /> A program can register as a ''unique application'', thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.<br /> <br /> The following function call registers our Best App as a unique application:<br /> <br /> &lt;syntaxhighlight&gt;<br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> REGAPP_UniqueApplication, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a [[#Special messages|special message]] to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window.<br /> <br /> == Application attributes ==<br /> <br /> An application is described by a set of ''attributes''. There are attributes that:<br /> <br /> * determine the application's behaviour<br /> * describe the application's feature set<br /> * describe the current application state<br /> <br /> Most of the attributes are specified at registration time. Some of them cannot be altered once they are set, while others are freely changeable after registration.<br /> <br /> == Finding applications ==<br /> <br /> The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them ''application managers'' – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.<br /> <br /> “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:<br /> <br /> * the application name, ie. the one under which it was registered via RegisterApplication();<br /> * the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see [[#Application identifiers|Application identifiers]] above;<br /> * the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.<br /> <br /> Based on this information, the respective piece of code that will find our BestApp in the system might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> /* if you only know the application name */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_Name, &quot;BestApp&quot;, TAG_DONE);<br /> <br /> /* if you know the application name identifier */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you specifically want to talk to the second running instance */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp_1.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you know the pathname to the program file */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_FileName, &quot;Work:Utils/BestApp&quot;, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Once you have obtained the appID you can start communicating with the respective application.<br /> <br /> == Messaging ==<br /> <br /> Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported on the low level (through Exec Library’s [[Exec Messages and Ports|messages and ports]]) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.<br /> <br /> Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of [[#Control messages|predefined messages]] that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports [[#Custom messages|custom messages]], thus allowing for more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of [[#Pop-up notifications (Ringhio_messages)|pop-up notification messages]]. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement. Registration alone does not magically turn on any features.<br /> <br /> {{Note|Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named ''AppMessages''. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the [[Workbench and Icon Library#The Workbench Library|Workbench Library]] section of the AmigaOS documentation wiki).<br /> }}<br /> <br /> === Data structures ===<br /> <br /> Rather than introduce yet another system for communication, the Application Library builds upon the existing [[Exec_Messages_and_Ports|Exec messaging framework]]. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of [[Exec_Messages_and_Ports#Getting_a_Message|GetMsg()]] and [[Exec_Messages_and_Ports#Replying|ReplyMsg()]] takes place when processing incoming Application Library messages (see section [[#Message handling|Message handling]] below) – although the library implements its own method for [[#Sending messages|sending messages]].<br /> <br /> The following three structures are defined for carrying Application Library message data:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg<br /> {<br /> struct Message msg;<br /> uint32 senderAppID; // the appID of the sender application<br /> uint32 type; // identifies the type of message<br /> };<br /> <br /> struct ApplicationOpenPrintDocMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR fileName;<br /> };<br /> <br /> struct ApplicationCustomMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR customMsg;<br /> };<br /> &lt;/syntaxhighlight&gt;<br /> <br /> As you can see, structure ''ApplicationMsg'' contains a standard ''[[Exec_Messages_and_Ports#Messages|struct Message]]'', the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ''ApplicationOpenPrintDocMsg'' is used by certain [[#Control messages|control messages]] that require a pointer to a filename; the ''ApplicationCustomMsg'' structure is used for [[#Custom messages|custom messages]].<br /> <br /> Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to [[#Sending messages|send a message]] to an application you specify it in the “type” field.<br /> <br /> === Pop-up notifications (Ringhio messages) ===<br /> <br /> As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called ''Ringhio messages'' because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library). [[File:RinghioNotification.png|frame|Ringhio notification example]]The pop-ups function similarly to [[Intuition Requesters and Alerts|requesters]] in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.<br /> <br /> Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 result;<br /> <br /> result = IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The first parameter is your application’s appID received from the [[#Registration|registration]] function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the [[Intuition Screens#The Default Public Screen and Workbench|Workbench screen]].<br /> <br /> The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.<br /> <br /> The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;All right, ma!&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a [[#Custom messages|custom message]] (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, [[#Custom messages|custom messages]] are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the [[#Message handling|Message handling]] section for how to go about it.<br /> <br /> Whether receiving a “back message” would be useful for a particular application, and whether it would make sense to react upon it, is decided by the programmer. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”.<br /> <br /> If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open our Best App’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click.<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;URL:http://www.supercoders.com&quot;,<br /> APPNOTIFY_CloseOnDC, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> === Control messages ===<br /> <br /> The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided:<br /> <br /> ; APPLIBMT_Quit<br /> : The application shall quit. If data could be lost, it is expected that a confirmation requester is displayed.<br /> <br /> ; APPLIBMT_ForceQuit<br /> : Same as before but this time the application shall quit immediately, without asking for saving documents etc.<br /> <br /> ; APPLIBMT_Hide<br /> : The application shall hide its interface and iconify on the Workbench screen.<br /> <br /> ; APPLIBMT_Unhide<br /> : The application shall come back from the iconified state.<br /> <br /> ; APPLIBMT_ToFront<br /> : The application window shall come to front. Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence.<br /> <br /> ; APPLIBMT_OpenPrefs<br /> : The application shall open its preferences window.<br /> <br /> ; APPLIBMT_NewBlankDoc<br /> : The application shall open a new, blank document.<br /> <br /> ; APPLIBMT_OpenDoc<br /> : The application shall try to open a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> ; APPLIBMT_PrintDoc<br /> : The application shall try to print a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> An application will only react to messages that are implemented and supported. If you [[#Sending messages|send a message]] to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are asked for a minimum cooperation. In the near future, AmigaOS will have an application manager (third-party managers already exist) that will control running applications – much like the Exchange tool controls [[Commodities_Exchange_Library|commodities]]. Therefore, registered applications should “play nice” and support at least the most common commands – APPLIBMT_Quit, APPLIBMT_Hide, APPLIBMT_Unhide and APPLIBMT_ToFront. Implement the rest according to your application’s features and needs.<br /> <br /> {{Note|Providing the APPLIBMT_ForceQuit command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely, possibly causing loss of data. Therefore, implement support for APPLIBMT_ForceQuit in such a way that the user's data is never put in jeopardy!<br /> }}<br /> <br /> === Custom messages ===<br /> <br /> In contrast to a [[#Control messages|control message]] (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]], while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to [[#Finding applications|find]] the receiver application and [[#Sending messages|send a message]] to it.<br /> <br /> The pointer to the message text string is contained in a special [[#Data structures|data structure]], ''struct ApplicationCustomMsg''.<br /> <br /> === Special messages ===<br /> <br /> There are also some special messages that an application can receive from the library or another running application:<br /> <br /> ; APPLIBMT_Unique<br /> : If an application is registered as [[#Unique applications|unique]] and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message.<br /> <br /> ; APPLIBMT_GameModeEntered<br /> : This message is sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. Games or presentation programs are examples of applications that may want to run in the “game mode”. The message simply asks other applications to cooperate and “keep quiet”: not play sounds, open windows, requesters, etc. Please use the game-mode feature moderately and only send the APPLIBMT_GameModeEntered message when it is really needed.<br /> <br /> ; APPLIBMT_GameModeLeft<br /> : This message informs all running application that the sender has left the “game mode”.<br /> <br /> === Message handling ===<br /> <br /> You already know that messaging in AmigaOS takes place between [[Exec_Messages_and_Ports#Message_Ports|message ports]]. Being a superset of standard Exec messages, Application Library notifications (be they predefined [[#Control messages|control messages]], [[#Custom messages|custom messages]] or [[#Special messages|special messages]]) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the ''notification port''. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s [[Intuition_Input_and_Output_Methods#Receiving_Input_Events_from_Intuition|IDCMP messages]]). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications.<br /> <br /> A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.<br /> <br /> &lt;syntaxhighlight&gt;<br /> void event_loop(uint32 appID)<br /> {<br /> struct MsgPort *notificationPort = NULL;<br /> struct ApplicationMsg *appLibMsg = NULL;<br /> struct ApplicationCustomMsg *customMsg = NULL;<br /> uint32 appLibSignal = 0, sigGot = 0;<br /> BOOL done = FALSE;<br /> <br /> /* Obtain pointer to Application Library's notification port and set the signal bit. */<br /> IApplication-&gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;notificationPort, TAG_DONE);<br /> appLibSignal = (1L &lt;&lt; notificationPort-&gt;mp_SigBit);<br /> <br /> /* Go into the event loop. */<br /> while (!done)<br /> {<br /> /* Wait for a signal to arrive. */<br /> sigGot = IExec-&gt;Wait(appLibSignal);<br /> <br /> /* Process all Application Library messages. */<br /> if ( sigGot &amp; appLibSignal )<br /> {<br /> /* Obtain pointer to the message. */<br /> while ( (appLibMsg = (struct ApplicationMsg *) IExec-&gt;GetMsg(notificationPort)) )<br /> {<br /> /* Identify the type of message. */<br /> switch (appLibMsg-&gt;type)<br /> {<br /> case APPLIBMT_Quit:<br /> done = TRUE;<br /> break;<br /> <br /> case APPLIBMT_ToFront:<br /> /* Make the program window come to front . */<br /> break;<br /> <br /> case APPLIBMT_Hide:<br /> /* Iconify the program. */<br /> break;<br /> <br /> case APPLIBMT_Unhide:<br /> /* Uniconify the program. */<br /> break;<br /> <br /> /* Process the custom message as you like.<br /> Here we just use printf() to output the message text. */<br /> case APPLIBMT_CustomMsg:<br /> customMsg = (struct ApplicationCustomMsg *) appLibMsg;<br /> printf(&quot;The message text is: %s\n&quot;, customMsg-&gt; customMsg);<br /> break;<br /> <br /> /*<br /> Process any other Application Library messages.<br /> */<br /> <br /> }<br /> /* Return the processed message to the sender so that it can be freed. */<br /> IExec-&gt;ReplyMsg( (struct Message *) appLibMsg);<br /> }<br /> }<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Like all [[Exec_Messages_and_Ports#Messages|Exec messages]] obtained via the GetMsg() function, Application Library messages must be [[Exec_Messages_and_Ports#Replying|replied to]], i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own.<br /> <br /> Also remember that the notifications are addressed to an abstract ''application'' – this makes them rather different from IDCMP messages, which are always sent to a particular ''window'' (Intuition is unaware of the application concept). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make!<br /> <br /> === Sending messages ===<br /> <br /> While incoming notifications are [[#Message handling|processed]] pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application:<br /> <br /> # Prepare the respective [[#Data structures|data structure]]: specify the type of message and supply the sender's [[#Application identifiers|identifier]] (appID).<br /> # [[#Finding applications|Find the receiver]] application.<br /> # Send a message to it via the SendApplicationMsg() function.<br /> <br /> For example, if you want to tell our BestApp application to quit, you send it an APPLIBMT_Quit message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg appMsg; // message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Step 1: Prepare the message data structure. */<br /> appMsg.senderAppID = appID; // identifier of the sender<br /> appMsg.type = APPLIBMT_Quit; // type of message<br /> <br /> /* Step 2: Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Step 3: Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> &amp;appMsg,<br /> APPLIBMT_Quit);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Sending [[#Custom messages|custom messages]] is done in a similar fashion, the only difference is that you must use a dedicated [[#Data structures|data structure]] instead of the generic ''struct ApplicationMsg''. Let’s say that BestApp is in fact a media player, and that it has defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell BestApp to start playing, it will need to send the custom message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationCustomMsg customMsg; // custom message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Prepare the message data structure. */<br /> customMsg.almsg.senderAppID = appID; // identifier of the sender<br /> customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message<br /> customMsg.customMsg = &quot;Start playback&quot;; // message text<br /> <br /> /* Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> (struct ApplicationMsg *) &amp;customMsg,<br /> APPLIBMT_CustomMsg);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == PrefsObjects ==<br /> <br /> === Introduction ===<br /> <br /> Many applications are user-configurable so they need a way to store their settings; we traditionally use the term ''preferences'' or simply ''prefs'' in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Workbench_and_Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows ''.ini'' format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (system preferences are stored as IFF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:<br /> <br /> # All of the solutions require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).<br /> # None of the various formats used have ever provided a smart enough way to administer complex, structured settings.<br /> <br /> PrefsObjects has largely been promoted as being ''XML-based'' and therefore human-readable and easy to edit. But using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is ''object-oriented''. Among other things, this property also helps address the two drawbacks mentioned above.<br /> <br /> Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, the main program can never count on a particular structure of the prefs file: the contents and sequencing of data depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.<br /> <br /> === Object types ===<br /> <br /> PrefsObjects tackles the task by seeing data as ''objects'', and by providing common functions (methods) for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function - “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.<br /> <br /> PrefsObjects distinguishes between the following object types:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | dictionary || A container to encapsulate other objects. The prefs file itself is an object of the dictionary type.<br /> |-<br /> | array || A container to encapsulate other objects. The difference between a dictionary and an array is in object referencing: in arrays objects are referenced by an index, while in a dictionary they are referenced by a key string.<br /> |-<br /> | number || An object to carry a long integer, double, or a boolean value.<br /> |-<br /> | string || An object to carry a text string.<br /> |-<br /> | date || An object to carry date and time information.<br /> |-<br /> | binary || An object to carry arbitrary binary data.<br /> |}<br /> <br /> === The interface ===<br /> <br /> It has been mentioned above in the [[#Library opening chores|Library opening chores]] section that the Application Library has two interfaces, called “application” and “prefsobjects”. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions (methods) are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.<br /> <br /> === The preferences file ===<br /> <br /> The file that will be associated with your application as its preferences file is a standard XML document. All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated.<br /> <br /> The beauty of using standard file format is that from now on programmers and users will be able to view and edit the preferences file by using the standard tool [[PrefsObjectsEditor]] (usage of this tool is out of the scope of this page). <br /> <br /> ==== Preference organization ====<br /> <br /> It's recommended - but not mandatory - that a preferences file starts with a Dictionary. The advantage of using a Dictionary is that objects stored are identified by a name rather than just put there as they came out of the mind of the programmer. Using named object as several advantages:<br /> * preferences file can be extended without any pain even when adding preferences data in the middle of the file format (the same is also true when removing data);<br /> * it's easier to load and save because order does not matter (as long as dealing with data at the same root level);<br /> * it's easier to read/modify by user.<br /> <br /> It's perfectly legal and accepted to put Dictionnary as value in a Dictionary this enables ability to model preferences data arborescence.<br /> <br /> Binary data type should be avoided as much as possible because it's restrictive in its form and is not future proof as it does not enable addition or removal of the stored data without compromising compatibility.<br /> <br /> === Populating the file ===<br /> <br /> Each of the [[#Object types|Object type]] has a corresponding dispatcher to handle operations on this kind of object. The following table indicates the mapping:<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ PrefsObjects type mapping<br /> ! Type !! PrefsObjects dispatcher name<br /> |-<br /> | dictionary || PrefsDictionary<br /> |-<br /> | array || PrefsArray<br /> |-<br /> | number || PrefsNumber<br /> |-<br /> | string || PrefsString<br /> |-<br /> | date || PrefsDate<br /> |-<br /> | binary || PrefsBinary<br /> |}<br /> <br /> ==== Allocating objects ====<br /> <br /> All those dispatchers share a common set of methods among them are allocation and releasing of the object.<br /> <br /> To allocate a new dictionary all that is needed is to call the ALPO_Alloc method on the PrefsDictionary dispatcher like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> PrefsObjects * pDict = IPrefsObjects-&gt;PrefsDictionary(NULL, &amp;nError, ALPO_Alloc, 0, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Should a string be needed the call would be similar using PrefsString instead:<br /> <br /> &lt;syntaxhighlight&gt;<br /> PrefsObjects * pStr = IPrefsObjects-&gt;PrefsString(NULL, &amp;nError, ALPO_Alloc, 0, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> <br /> ==== Releasing objects ====<br /> <br /> When the object is not needed anymore it can be released calling the ALPO_Release method of the dispatcher and providing the object to release like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IPrefsObjects-&gt;PrefsDictionary(pDict, &amp;nError, ALPO_Release, 0, TAG_DONE);<br /> IPrefsObjects-&gt;PrefsString(pStr, &amp;nError, ALPO_Release, 0, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> ==== Reference counting ====<br /> <br /> Note that because objects can be referenced several times, PrefsObjects maintain a reference count of any created object. Deallocation will really take effect when this count reach 0, else releasing an object only decreases the count by 1.<br /> <br /> In some special cases it may be interesting to have more direct control over this automatic mechanism. This is exactly the purpose of methods ALPO_GetRetainCount and ALPO_Retain.<br /> <br /> The first one allows retreival of the current count, unlike previous methods this one requires an argument to instruct where to store the retrieved count. This argument takes the form of a pointer to an int32:<br /> <br /> &lt;syntaxhighlight&gt;<br /> int32 nCount = -1;<br /> IPrefsObjects-&gt;PrefsDictionary(pDict, &amp;nError, ALPO_GetRetainCount, &amp;nCount, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The second one allows to explicetly increase the count by 1. This can be needed for several reasons the main one being transfer of ownership.<br /> <br /> === Automation ===<br /> <br /> By default, the prefs file is stored in and loaded from the ENV: and ENVARC: system directories. The default location can nevertheless be overridden at [[#Registration|registration]] time. For example, your application may want to use its own subdirectory in the ENV(ARC): path. This is done by passing the REGAPP_ENVDir tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file into “ENV:BestApp/” and “ENVARC:BestApp/”.<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_ENVDir, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The default naming scheme for the prefs file uses the application name combined with the URL identifier. Also, the ''.xml'' extension is appended at the end of the file name to identify the document format. So if our application registered under the name of “BestApp” and with the URL identifier “supercoders.com”, the default preferences file name will be “BestApp.supercoders.com.xml”. This yields a unique file name but might look a little quirky so the library provides a way to override the default naming scheme. This is done by passing the REGAPP_ CustomPrefsBaseName tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file as &quot;BestApp.xml&quot;:<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_CustomPrefsBaseName, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Application_Library&diff=4102 Application Library 2012-08-31T21:43:10Z <p>Alexandre Balaban: Changed some paragraphs levels</p> <hr /> <div>{{WIP}}<br /> <br /> == Introduction ==<br /> The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of ''application'' is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either [[Exec_Tasks|tasks]] or [[AmigaDOS_Data_Structures#Process_Data_Structures|processes]]. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other ''background services''), and genuine full-blown ''applications'' with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.<br /> <br /> AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called [[#Registering the application|application registration]], during which the application receives a [[#Application identifiers|unique identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:<br /> <br /> * It can send/receive messages to/from other registered applications. The library supports a set of common [[#Control messages|control messages]] (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows [[#Custom messages|custom messages]] designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control.<br /> <br /> * It can use [[#PrefsObjects|PrefsObjects]], an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> * It can notify the user about, for example, completed tasks via automatic [[#Pop-up notifications (Ringhio_messages)|pop-up messages]]. These represent a practical, less obtrusive alternative to traditional requesters.<br /> <br /> * It can easily create and manage lists of recently-used documents.<br /> <br /> * It can register as a [[#Unique applications|unique application]], preventing other instances of itself from running.<br /> <br /> * It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.<br /> <br /> * It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.<br /> <br /> == Library opening chores ==<br /> <br /> Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Library *ApplicationBase = NULL;<br /> struct ApplicationIFace *IApplication = NULL;<br /> struct PrefsObjectsIFace *IPrefsObjects = NULL;<br /> <br /> if ( (ApplicationBase = IExec-&gt;OpenLibrary(&quot;application.library&quot;, 52)) )<br /> {<br /> IApplication = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;application&quot;, 1, NULL);<br /> IPrefsObjects = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;prefsobjects&quot;, 1, NULL);<br /> }<br /> <br /> if ( !ApplicationBase || !IApplication || !IPrefsObjects )<br /> {<br /> /* handle library opening error */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that there is no interface called “main” like older, single-interface libraries have.<br /> <br /> When your application has run its course, don’t forget to clean up and close both the library and its interface(s):<br /> <br /> &lt;syntaxhighlight&gt;<br /> IExec-&gt;DropInterface((struct Interface *)IPrefsObjects);<br /> IExec-&gt;DropInterface((struct Interface *)IApplication);<br /> IExec-&gt;CloseLibrary(ApplicationBase);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Registering the application ==<br /> <br /> Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.<br /> <br /> === Application identifiers ===<br /> <br /> A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.<br /> <br /> Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.<br /> <br /> === Registration ===<br /> <br /> Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> TAG_DONE);<br /> <br /> if (!appID)<br /> {<br /> /* report registration error and quit */<br /> }<br /> <br /> /*<br /> do whatever your program does<br /> */<br /> <br /> IApplication-&gt;UnregisterApplication(appID, NULL);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)<br /> <br /> The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.<br /> <br /> During registration you may also indicate certain features your application will support. In particular, you may inform the system that your application opens a dedicated Preferences window, that it can be iconified, that it can create documents, or that it supports printing documents. This information is not required but it can be used by other applications, which may decide to query about the availability of a certain feature before they send a [[#Control messages|control message]]. Set the following boolean tags to TRUE in the RegisterApplication() call if your application supports the respective feature (the default value is FALSE):<br /> <br /> * REGAPP_HasPrefsWindow<br /> * REGAPP_HasIconifyFeature<br /> * REGAPP_CanCreateNewDocs<br /> * REGAPP_CanPrintDocs<br /> <br /> === Unique applications ===<br /> <br /> A program can register as a ''unique application'', thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.<br /> <br /> The following function call registers our Best App as a unique application:<br /> <br /> &lt;syntaxhighlight&gt;<br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> REGAPP_UniqueApplication, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a [[#Special messages|special message]] to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window.<br /> <br /> == Application attributes ==<br /> <br /> An application is described by a set of ''attributes''. There are attributes that:<br /> <br /> * determine the application's behaviour<br /> * describe the application's feature set<br /> * describe the current application state<br /> <br /> Most of the attributes are specified at registration time. Some of them cannot be altered once they are set, while others are freely changeable after registration.<br /> <br /> == Finding applications ==<br /> <br /> The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them ''application managers'' – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.<br /> <br /> “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:<br /> <br /> * the application name, ie. the one under which it was registered via RegisterApplication();<br /> * the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see [[#Application identifiers|Application identifiers]] above;<br /> * the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.<br /> <br /> Based on this information, the respective piece of code that will find our BestApp in the system might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> /* if you only know the application name */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_Name, &quot;BestApp&quot;, TAG_DONE);<br /> <br /> /* if you know the application name identifier */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you specifically want to talk to the second running instance */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp_1.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you know the pathname to the program file */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_FileName, &quot;Work:Utils/BestApp&quot;, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Once you have obtained the appID you can start communicating with the respective application.<br /> <br /> == Messaging ==<br /> <br /> Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported on the low level (through Exec Library’s [[Exec Messages and Ports|messages and ports]]) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.<br /> <br /> Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of [[#Control messages|predefined messages]] that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports [[#Custom messages|custom messages]], thus allowing for more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of [[#Pop-up notifications (Ringhio_messages)|pop-up notification messages]]. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement. Registration alone does not magically turn on any features.<br /> <br /> {{Note|Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named ''AppMessages''. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the [[Workbench and Icon Library#The Workbench Library|Workbench Library]] section of the AmigaOS documentation wiki).<br /> }}<br /> <br /> === Data structures ===<br /> <br /> Rather than introduce yet another system for communication, the Application Library builds upon the existing [[Exec_Messages_and_Ports|Exec messaging framework]]. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of [[Exec_Messages_and_Ports#Getting_a_Message|GetMsg()]] and [[Exec_Messages_and_Ports#Replying|ReplyMsg()]] takes place when processing incoming Application Library messages (see section [[#Message handling|Message handling]] below) – although the library implements its own method for [[#Sending messages|sending messages]].<br /> <br /> The following three structures are defined for carrying Application Library message data:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg<br /> {<br /> struct Message msg;<br /> uint32 senderAppID; // the appID of the sender application<br /> uint32 type; // identifies the type of message<br /> };<br /> <br /> struct ApplicationOpenPrintDocMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR fileName;<br /> };<br /> <br /> struct ApplicationCustomMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR customMsg;<br /> };<br /> &lt;/syntaxhighlight&gt;<br /> <br /> As you can see, structure ''ApplicationMsg'' contains a standard ''[[Exec_Messages_and_Ports#Messages|struct Message]]'', the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ''ApplicationOpenPrintDocMsg'' is used by certain [[#Control messages|control messages]] that require a pointer to a filename; the ''ApplicationCustomMsg'' structure is used for [[#Custom messages|custom messages]].<br /> <br /> Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to [[#Sending messages|send a message]] to an application you specify it in the “type” field.<br /> <br /> === Pop-up notifications (Ringhio messages) ===<br /> <br /> As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called ''Ringhio messages'' because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library). [[File:RinghioNotification.png|frame|Ringhio notification example]]The pop-ups function similarly to [[Intuition Requesters and Alerts|requesters]] in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.<br /> <br /> Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 result;<br /> <br /> result = IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The first parameter is your application’s appID received from the [[#Registration|registration]] function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the [[Intuition Screens#The Default Public Screen and Workbench|Workbench screen]].<br /> <br /> The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.<br /> <br /> The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;All right, ma!&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a [[#Custom messages|custom message]] (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, [[#Custom messages|custom messages]] are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the [[#Message handling|Message handling]] section for how to go about it.<br /> <br /> Whether receiving a “back message” would be useful for a particular application, and whether it would make sense to react upon it, is decided by the programmer. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”.<br /> <br /> If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open our Best App’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click.<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;URL:http://www.supercoders.com&quot;,<br /> APPNOTIFY_CloseOnDC, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> === Control messages ===<br /> <br /> The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided:<br /> <br /> ; APPLIBMT_Quit<br /> : The application shall quit. If data could be lost, it is expected that a confirmation requester is displayed.<br /> <br /> ; APPLIBMT_ForceQuit<br /> : Same as before but this time the application shall quit immediately, without asking for saving documents etc.<br /> <br /> ; APPLIBMT_Hide<br /> : The application shall hide its interface and iconify on the Workbench screen.<br /> <br /> ; APPLIBMT_Unhide<br /> : The application shall come back from the iconified state.<br /> <br /> ; APPLIBMT_ToFront<br /> : The application window shall come to front. Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence.<br /> <br /> ; APPLIBMT_OpenPrefs<br /> : The application shall open its preferences window.<br /> <br /> ; APPLIBMT_NewBlankDoc<br /> : The application shall open a new, blank document.<br /> <br /> ; APPLIBMT_OpenDoc<br /> : The application shall try to open a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> ; APPLIBMT_PrintDoc<br /> : The application shall try to print a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> An application will only react to messages that are implemented and supported. If you [[#Sending messages|send a message]] to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are asked for a minimum cooperation. In the near future, AmigaOS will have an application manager (third-party managers already exist) that will control running applications – much like the Exchange tool controls [[Commodities_Exchange_Library|commodities]]. Therefore, registered applications should “play nice” and support at least the most common commands – APPLIBMT_Quit, APPLIBMT_Hide, APPLIBMT_Unhide and APPLIBMT_ToFront. Implement the rest according to your application’s features and needs.<br /> <br /> {{Note|Providing the APPLIBMT_ForceQuit command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely, possibly causing loss of data. Therefore, implement support for APPLIBMT_ForceQuit in such a way that the user's data is never put in jeopardy!<br /> }}<br /> <br /> === Custom messages ===<br /> <br /> In contrast to a [[#Control messages|control message]] (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]], while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to [[#Finding applications|find]] the receiver application and [[#Sending messages|send a message]] to it.<br /> <br /> The pointer to the message text string is contained in a special [[#Data structures|data structure]], ''struct ApplicationCustomMsg''.<br /> <br /> === Special messages ===<br /> <br /> There are also some special messages that an application can receive from the library or another running application:<br /> <br /> ; APPLIBMT_Unique<br /> : If an application is registered as [[#Unique applications|unique]] and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message.<br /> <br /> ; APPLIBMT_GameModeEntered<br /> : This message is sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. Games or presentation programs are examples of applications that may want to run in the “game mode”. The message simply asks other applications to cooperate and “keep quiet”: not play sounds, open windows, requesters, etc. Please use the game-mode feature moderately and only send the APPLIBMT_GameModeEntered message when it is really needed.<br /> <br /> ; APPLIBMT_GameModeLeft<br /> : This message informs all running application that the sender has left the “game mode”.<br /> <br /> === Message handling ===<br /> <br /> You already know that messaging in AmigaOS takes place between [[Exec_Messages_and_Ports#Message_Ports|message ports]]. Being a superset of standard Exec messages, Application Library notifications (be they predefined [[#Control messages|control messages]], [[#Custom messages|custom messages]] or [[#Special messages|special messages]]) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the ''notification port''. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s [[Intuition_Input_and_Output_Methods#Receiving_Input_Events_from_Intuition|IDCMP messages]]). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications.<br /> <br /> A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.<br /> <br /> &lt;syntaxhighlight&gt;<br /> void event_loop(uint32 appID)<br /> {<br /> struct MsgPort *notificationPort = NULL;<br /> struct ApplicationMsg *appLibMsg = NULL;<br /> struct ApplicationCustomMsg *customMsg = NULL;<br /> uint32 appLibSignal = 0, sigGot = 0;<br /> BOOL done = FALSE;<br /> <br /> /* Obtain pointer to Application Library's notification port and set the signal bit. */<br /> IApplication-&gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;notificationPort, TAG_DONE);<br /> appLibSignal = (1L &lt;&lt; notificationPort-&gt;mp_SigBit);<br /> <br /> /* Go into the event loop. */<br /> while (!done)<br /> {<br /> /* Wait for a signal to arrive. */<br /> sigGot = IExec-&gt;Wait(appLibSignal);<br /> <br /> /* Process all Application Library messages. */<br /> if ( sigGot &amp; appLibSignal )<br /> {<br /> /* Obtain pointer to the message. */<br /> while ( (appLibMsg = (struct ApplicationMsg *) IExec-&gt;GetMsg(notificationPort)) )<br /> {<br /> /* Identify the type of message. */<br /> switch (appLibMsg-&gt;type)<br /> {<br /> case APPLIBMT_Quit:<br /> done = TRUE;<br /> break;<br /> <br /> case APPLIBMT_ToFront:<br /> /* Make the program window come to front . */<br /> break;<br /> <br /> case APPLIBMT_Hide:<br /> /* Iconify the program. */<br /> break;<br /> <br /> case APPLIBMT_Unhide:<br /> /* Uniconify the program. */<br /> break;<br /> <br /> /* Process the custom message as you like.<br /> Here we just use printf() to output the message text. */<br /> case APPLIBMT_CustomMsg:<br /> customMsg = (struct ApplicationCustomMsg *) appLibMsg;<br /> printf(&quot;The message text is: %s\n&quot;, customMsg-&gt; customMsg);<br /> break;<br /> <br /> /*<br /> Process any other Application Library messages.<br /> */<br /> <br /> }<br /> /* Return the processed message to the sender so that it can be freed. */<br /> IExec-&gt;ReplyMsg( (struct Message *) appLibMsg);<br /> }<br /> }<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Like all [[Exec_Messages_and_Ports#Messages|Exec messages]] obtained via the GetMsg() function, Application Library messages must be [[Exec_Messages_and_Ports#Replying|replied to]], i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own.<br /> <br /> Also remember that the notifications are addressed to an abstract ''application'' – this makes them rather different from IDCMP messages, which are always sent to a particular ''window'' (Intuition is unaware of the application concept). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make!<br /> <br /> === Sending messages ===<br /> <br /> While incoming notifications are [[#Message handling|processed]] pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application:<br /> <br /> # Prepare the respective [[#Data structures|data structure]]: specify the type of message and supply the sender's [[#Application identifiers|identifier]] (appID).<br /> # [[#Finding applications|Find the receiver]] application.<br /> # Send a message to it via the SendApplicationMsg() function.<br /> <br /> For example, if you want to tell our BestApp application to quit, you send it an APPLIBMT_Quit message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg appMsg; // message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Step 1: Prepare the message data structure. */<br /> appMsg.senderAppID = appID; // identifier of the sender<br /> appMsg.type = APPLIBMT_Quit; // type of message<br /> <br /> /* Step 2: Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Step 3: Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> &amp;appMsg,<br /> APPLIBMT_Quit);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Sending [[#Custom messages|custom messages]] is done in a similar fashion, the only difference is that you must use a dedicated [[#Data structures|data structure]] instead of the generic ''struct ApplicationMsg''. Let’s say that BestApp is in fact a media player, and that it has defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell BestApp to start playing, it will need to send the custom message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationCustomMsg customMsg; // custom message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Prepare the message data structure. */<br /> customMsg.almsg.senderAppID = appID; // identifier of the sender<br /> customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message<br /> customMsg.customMsg = &quot;Start playback&quot;; // message text<br /> <br /> /* Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> (struct ApplicationMsg *) &amp;customMsg,<br /> APPLIBMT_CustomMsg);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == PrefsObjects ==<br /> <br /> === Introduction ===<br /> <br /> Many applications are user-configurable so they need a way to store their settings; we traditionally use the term ''preferences'' or simply ''prefs'' in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Workbench_and_Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows ''.ini'' format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (system preferences are stored as IFF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:<br /> <br /> # All of the solutions require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).<br /> # None of the various formats used have ever provided a smart enough way to administer complex, structured settings.<br /> <br /> PrefsObjects has largely been promoted as being ''XML-based'' and therefore human-readable and easy to edit. But using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is ''object-oriented''. Among other things, this property also helps address the two drawbacks mentioned above.<br /> <br /> Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, the main program can never count on a particular structure of the prefs file: the contents and sequencing of data depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.<br /> <br /> === Object types ===<br /> <br /> PrefsObjects tackles the task by seeing data as ''objects'', and by providing common functions (methods) for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function - “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.<br /> <br /> PrefsObjects distinguishes between the following object types:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | dictionary || A container to encapsulate other objects. The prefs file itself is an object of the dictionary type.<br /> |-<br /> | array || A container to encapsulate other objects. The difference between a dictionary and an array is in object referencing: in arrays objects are referenced by an index, while in a dictionary they are referenced by a key string.<br /> |-<br /> | number || An object to carry a long integer, double, or a boolean value.<br /> |-<br /> | string || An object to carry a text string.<br /> |-<br /> | date || An object to carry date and time information.<br /> |-<br /> | binary || An object to carry arbitrary binary data.<br /> |}<br /> <br /> === The interface ===<br /> <br /> It has been mentioned above in the [[#Library opening chores|Library opening chores]] section that the Application Library has two interfaces, called “application” and “prefsobjects”. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions (methods) are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.<br /> <br /> === The preferences file ===<br /> <br /> The file that will be associated with your application as its preferences file is a standard XML document. All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated.<br /> <br /> The beauty of using standard file format is that from now on programmers and users will be able to view and edit the preferences file by using the standard tool [[PrefsObjectsEditor]] (usage of this tool is out of the scope of this page). <br /> <br /> ==== Preference organization ====<br /> <br /> It's recommended - but not mandatory - that a preferences file starts with a Dictionary. The advantage of using a Dictionary is that objects stored are identified by a name rather than just put there as they came out of the mind of the programmer. Using named object as several advantages:<br /> * preferences file can be extended without any pain even when adding preferences data in the middle of the file format (the same is also true when removing data);<br /> * it's easier to load and save because order does not matter (as long as dealing with data at the same root level);<br /> * it's easier to read/modify by user.<br /> <br /> It's perfectly legal and accepted to put Dictionnary as value in a Dictionary this enables ability to model preferences data arborescence.<br /> <br /> Binary data type should be avoided as much as possible because it's restrictive in its form and is not future proof as it does not enable addition or removal of the stored data without compromising compatibility.<br /> <br /> === Populating the file ===<br /> <br /> Each of these [[#Object types|Object type]] has a corresponding dispatcher to handle operations on this kind of object. The following table indicates the mapping:<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ PrefsObjects type mapping<br /> ! Type !! PrefsObjects dispatcher name<br /> |-<br /> | dictionary || PrefsDictionary<br /> |-<br /> | array || PrefsArray<br /> |-<br /> | number || PrefsNumber<br /> |-<br /> | string || PrefsString<br /> |-<br /> | date || PrefsDate<br /> |-<br /> | binary || PrefsBinary<br /> |}<br /> <br /> <br /> === Automation ===<br /> <br /> By default, the prefs file is stored in and loaded from the ENV: and ENVARC: system directories. The default location can nevertheless be overridden at [[#Registration|registration]] time. For example, your application may want to use its own subdirectory in the ENV(ARC): path. This is done by passing the REGAPP_ENVDir tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file into “ENV:BestApp/” and “ENVARC:BestApp/”.<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_ENVDir, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The default naming scheme for the prefs file uses the application name combined with the URL identifier. Also, the ''.xml'' extension is appended at the end of the file name to identify the document format. So if our application registered under the name of “BestApp” and with the URL identifier “supercoders.com”, the default preferences file name will be “BestApp.supercoders.com.xml”. This yields a unique file name but might look a little quirky so the library provides a way to override the default naming scheme. This is done by passing the REGAPP_ CustomPrefsBaseName tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file as &quot;BestApp.xml&quot;:<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_CustomPrefsBaseName, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Application_Library&diff=4101 Application Library 2012-08-31T21:40:25Z <p>Alexandre Balaban: /* = Preference organization */</p> <hr /> <div>{{WIP}}<br /> <br /> == Introduction ==<br /> The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of ''application'' is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either [[Exec_Tasks|tasks]] or [[AmigaDOS_Data_Structures#Process_Data_Structures|processes]]. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other ''background services''), and genuine full-blown ''applications'' with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.<br /> <br /> AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called [[#Registering the application|application registration]], during which the application receives a [[#Application identifiers|unique identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:<br /> <br /> * It can send/receive messages to/from other registered applications. The library supports a set of common [[#Control messages|control messages]] (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows [[#Custom messages|custom messages]] designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control.<br /> <br /> * It can use [[#PrefsObjects|PrefsObjects]], an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> * It can notify the user about, for example, completed tasks via automatic [[#Pop-up notifications (Ringhio_messages)|pop-up messages]]. These represent a practical, less obtrusive alternative to traditional requesters.<br /> <br /> * It can easily create and manage lists of recently-used documents.<br /> <br /> * It can register as a [[#Unique applications|unique application]], preventing other instances of itself from running.<br /> <br /> * It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.<br /> <br /> * It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.<br /> <br /> == Library opening chores ==<br /> <br /> Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Library *ApplicationBase = NULL;<br /> struct ApplicationIFace *IApplication = NULL;<br /> struct PrefsObjectsIFace *IPrefsObjects = NULL;<br /> <br /> if ( (ApplicationBase = IExec-&gt;OpenLibrary(&quot;application.library&quot;, 52)) )<br /> {<br /> IApplication = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;application&quot;, 1, NULL);<br /> IPrefsObjects = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;prefsobjects&quot;, 1, NULL);<br /> }<br /> <br /> if ( !ApplicationBase || !IApplication || !IPrefsObjects )<br /> {<br /> /* handle library opening error */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that there is no interface called “main” like older, single-interface libraries have.<br /> <br /> When your application has run its course, don’t forget to clean up and close both the library and its interface(s):<br /> <br /> &lt;syntaxhighlight&gt;<br /> IExec-&gt;DropInterface((struct Interface *)IPrefsObjects);<br /> IExec-&gt;DropInterface((struct Interface *)IApplication);<br /> IExec-&gt;CloseLibrary(ApplicationBase);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Registering the application ==<br /> <br /> Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.<br /> <br /> === Application identifiers ===<br /> <br /> A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.<br /> <br /> Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.<br /> <br /> === Registration ===<br /> <br /> Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> TAG_DONE);<br /> <br /> if (!appID)<br /> {<br /> /* report registration error and quit */<br /> }<br /> <br /> /*<br /> do whatever your program does<br /> */<br /> <br /> IApplication-&gt;UnregisterApplication(appID, NULL);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)<br /> <br /> The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.<br /> <br /> During registration you may also indicate certain features your application will support. In particular, you may inform the system that your application opens a dedicated Preferences window, that it can be iconified, that it can create documents, or that it supports printing documents. This information is not required but it can be used by other applications, which may decide to query about the availability of a certain feature before they send a [[#Control messages|control message]]. Set the following boolean tags to TRUE in the RegisterApplication() call if your application supports the respective feature (the default value is FALSE):<br /> <br /> * REGAPP_HasPrefsWindow<br /> * REGAPP_HasIconifyFeature<br /> * REGAPP_CanCreateNewDocs<br /> * REGAPP_CanPrintDocs<br /> <br /> === Unique applications ===<br /> <br /> A program can register as a ''unique application'', thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.<br /> <br /> The following function call registers our Best App as a unique application:<br /> <br /> &lt;syntaxhighlight&gt;<br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> REGAPP_UniqueApplication, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a [[#Special messages|special message]] to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window.<br /> <br /> == Application attributes ==<br /> <br /> An application is described by a set of ''attributes''. There are attributes that:<br /> <br /> * determine the application's behaviour<br /> * describe the application's feature set<br /> * describe the current application state<br /> <br /> Most of the attributes are specified at registration time. Some of them cannot be altered once they are set, while others are freely changeable after registration.<br /> <br /> == Finding applications ==<br /> <br /> The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them ''application managers'' – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.<br /> <br /> “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:<br /> <br /> * the application name, ie. the one under which it was registered via RegisterApplication();<br /> * the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see [[#Application identifiers|Application identifiers]] above;<br /> * the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.<br /> <br /> Based on this information, the respective piece of code that will find our BestApp in the system might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> /* if you only know the application name */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_Name, &quot;BestApp&quot;, TAG_DONE);<br /> <br /> /* if you know the application name identifier */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you specifically want to talk to the second running instance */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp_1.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you know the pathname to the program file */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_FileName, &quot;Work:Utils/BestApp&quot;, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Once you have obtained the appID you can start communicating with the respective application.<br /> <br /> == Messaging ==<br /> <br /> Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported on the low level (through Exec Library’s [[Exec Messages and Ports|messages and ports]]) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.<br /> <br /> Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of [[#Control messages|predefined messages]] that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports [[#Custom messages|custom messages]], thus allowing for more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of [[#Pop-up notifications (Ringhio_messages)|pop-up notification messages]]. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement. Registration alone does not magically turn on any features.<br /> <br /> {{Note|Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named ''AppMessages''. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the [[Workbench and Icon Library#The Workbench Library|Workbench Library]] section of the AmigaOS documentation wiki).<br /> }}<br /> <br /> === Data structures ===<br /> <br /> Rather than introduce yet another system for communication, the Application Library builds upon the existing [[Exec_Messages_and_Ports|Exec messaging framework]]. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of [[Exec_Messages_and_Ports#Getting_a_Message|GetMsg()]] and [[Exec_Messages_and_Ports#Replying|ReplyMsg()]] takes place when processing incoming Application Library messages (see section [[#Message handling|Message handling]] below) – although the library implements its own method for [[#Sending messages|sending messages]].<br /> <br /> The following three structures are defined for carrying Application Library message data:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg<br /> {<br /> struct Message msg;<br /> uint32 senderAppID; // the appID of the sender application<br /> uint32 type; // identifies the type of message<br /> };<br /> <br /> struct ApplicationOpenPrintDocMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR fileName;<br /> };<br /> <br /> struct ApplicationCustomMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR customMsg;<br /> };<br /> &lt;/syntaxhighlight&gt;<br /> <br /> As you can see, structure ''ApplicationMsg'' contains a standard ''[[Exec_Messages_and_Ports#Messages|struct Message]]'', the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ''ApplicationOpenPrintDocMsg'' is used by certain [[#Control messages|control messages]] that require a pointer to a filename; the ''ApplicationCustomMsg'' structure is used for [[#Custom messages|custom messages]].<br /> <br /> Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to [[#Sending messages|send a message]] to an application you specify it in the “type” field.<br /> <br /> === Pop-up notifications (Ringhio messages) ===<br /> <br /> As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called ''Ringhio messages'' because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library). [[File:RinghioNotification.png|frame|Ringhio notification example]]The pop-ups function similarly to [[Intuition Requesters and Alerts|requesters]] in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.<br /> <br /> Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 result;<br /> <br /> result = IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The first parameter is your application’s appID received from the [[#Registration|registration]] function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the [[Intuition Screens#The Default Public Screen and Workbench|Workbench screen]].<br /> <br /> The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.<br /> <br /> The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;All right, ma!&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a [[#Custom messages|custom message]] (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, [[#Custom messages|custom messages]] are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the [[#Message handling|Message handling]] section for how to go about it.<br /> <br /> Whether receiving a “back message” would be useful for a particular application, and whether it would make sense to react upon it, is decided by the programmer. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”.<br /> <br /> If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open our Best App’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click.<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;URL:http://www.supercoders.com&quot;,<br /> APPNOTIFY_CloseOnDC, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> === Control messages ===<br /> <br /> The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided:<br /> <br /> ; APPLIBMT_Quit<br /> : The application shall quit. If data could be lost, it is expected that a confirmation requester is displayed.<br /> <br /> ; APPLIBMT_ForceQuit<br /> : Same as before but this time the application shall quit immediately, without asking for saving documents etc.<br /> <br /> ; APPLIBMT_Hide<br /> : The application shall hide its interface and iconify on the Workbench screen.<br /> <br /> ; APPLIBMT_Unhide<br /> : The application shall come back from the iconified state.<br /> <br /> ; APPLIBMT_ToFront<br /> : The application window shall come to front. Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence.<br /> <br /> ; APPLIBMT_OpenPrefs<br /> : The application shall open its preferences window.<br /> <br /> ; APPLIBMT_NewBlankDoc<br /> : The application shall open a new, blank document.<br /> <br /> ; APPLIBMT_OpenDoc<br /> : The application shall try to open a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> ; APPLIBMT_PrintDoc<br /> : The application shall try to print a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> An application will only react to messages that are implemented and supported. If you [[#Sending messages|send a message]] to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are asked for a minimum cooperation. In the near future, AmigaOS will have an application manager (third-party managers already exist) that will control running applications – much like the Exchange tool controls [[Commodities_Exchange_Library|commodities]]. Therefore, registered applications should “play nice” and support at least the most common commands – APPLIBMT_Quit, APPLIBMT_Hide, APPLIBMT_Unhide and APPLIBMT_ToFront. Implement the rest according to your application’s features and needs.<br /> <br /> {{Note|Providing the APPLIBMT_ForceQuit command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely, possibly causing loss of data. Therefore, implement support for APPLIBMT_ForceQuit in such a way that the user's data is never put in jeopardy!<br /> }}<br /> <br /> === Custom messages ===<br /> <br /> In contrast to a [[#Control messages|control message]] (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]], while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to [[#Finding applications|find]] the receiver application and [[#Sending messages|send a message]] to it.<br /> <br /> The pointer to the message text string is contained in a special [[#Data structures|data structure]], ''struct ApplicationCustomMsg''.<br /> <br /> === Special messages ===<br /> <br /> There are also some special messages that an application can receive from the library or another running application:<br /> <br /> ; APPLIBMT_Unique<br /> : If an application is registered as [[#Unique applications|unique]] and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message.<br /> <br /> ; APPLIBMT_GameModeEntered<br /> : This message is sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. Games or presentation programs are examples of applications that may want to run in the “game mode”. The message simply asks other applications to cooperate and “keep quiet”: not play sounds, open windows, requesters, etc. Please use the game-mode feature moderately and only send the APPLIBMT_GameModeEntered message when it is really needed.<br /> <br /> ; APPLIBMT_GameModeLeft<br /> : This message informs all running application that the sender has left the “game mode”.<br /> <br /> === Message handling ===<br /> <br /> You already know that messaging in AmigaOS takes place between [[Exec_Messages_and_Ports#Message_Ports|message ports]]. Being a superset of standard Exec messages, Application Library notifications (be they predefined [[#Control messages|control messages]], [[#Custom messages|custom messages]] or [[#Special messages|special messages]]) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the ''notification port''. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s [[Intuition_Input_and_Output_Methods#Receiving_Input_Events_from_Intuition|IDCMP messages]]). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications.<br /> <br /> A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.<br /> <br /> &lt;syntaxhighlight&gt;<br /> void event_loop(uint32 appID)<br /> {<br /> struct MsgPort *notificationPort = NULL;<br /> struct ApplicationMsg *appLibMsg = NULL;<br /> struct ApplicationCustomMsg *customMsg = NULL;<br /> uint32 appLibSignal = 0, sigGot = 0;<br /> BOOL done = FALSE;<br /> <br /> /* Obtain pointer to Application Library's notification port and set the signal bit. */<br /> IApplication-&gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;notificationPort, TAG_DONE);<br /> appLibSignal = (1L &lt;&lt; notificationPort-&gt;mp_SigBit);<br /> <br /> /* Go into the event loop. */<br /> while (!done)<br /> {<br /> /* Wait for a signal to arrive. */<br /> sigGot = IExec-&gt;Wait(appLibSignal);<br /> <br /> /* Process all Application Library messages. */<br /> if ( sigGot &amp; appLibSignal )<br /> {<br /> /* Obtain pointer to the message. */<br /> while ( (appLibMsg = (struct ApplicationMsg *) IExec-&gt;GetMsg(notificationPort)) )<br /> {<br /> /* Identify the type of message. */<br /> switch (appLibMsg-&gt;type)<br /> {<br /> case APPLIBMT_Quit:<br /> done = TRUE;<br /> break;<br /> <br /> case APPLIBMT_ToFront:<br /> /* Make the program window come to front . */<br /> break;<br /> <br /> case APPLIBMT_Hide:<br /> /* Iconify the program. */<br /> break;<br /> <br /> case APPLIBMT_Unhide:<br /> /* Uniconify the program. */<br /> break;<br /> <br /> /* Process the custom message as you like.<br /> Here we just use printf() to output the message text. */<br /> case APPLIBMT_CustomMsg:<br /> customMsg = (struct ApplicationCustomMsg *) appLibMsg;<br /> printf(&quot;The message text is: %s\n&quot;, customMsg-&gt; customMsg);<br /> break;<br /> <br /> /*<br /> Process any other Application Library messages.<br /> */<br /> <br /> }<br /> /* Return the processed message to the sender so that it can be freed. */<br /> IExec-&gt;ReplyMsg( (struct Message *) appLibMsg);<br /> }<br /> }<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Like all [[Exec_Messages_and_Ports#Messages|Exec messages]] obtained via the GetMsg() function, Application Library messages must be [[Exec_Messages_and_Ports#Replying|replied to]], i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own.<br /> <br /> Also remember that the notifications are addressed to an abstract ''application'' – this makes them rather different from IDCMP messages, which are always sent to a particular ''window'' (Intuition is unaware of the application concept). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make!<br /> <br /> === Sending messages ===<br /> <br /> While incoming notifications are [[#Message handling|processed]] pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application:<br /> <br /> # Prepare the respective [[#Data structures|data structure]]: specify the type of message and supply the sender's [[#Application identifiers|identifier]] (appID).<br /> # [[#Finding applications|Find the receiver]] application.<br /> # Send a message to it via the SendApplicationMsg() function.<br /> <br /> For example, if you want to tell our BestApp application to quit, you send it an APPLIBMT_Quit message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg appMsg; // message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Step 1: Prepare the message data structure. */<br /> appMsg.senderAppID = appID; // identifier of the sender<br /> appMsg.type = APPLIBMT_Quit; // type of message<br /> <br /> /* Step 2: Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Step 3: Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> &amp;appMsg,<br /> APPLIBMT_Quit);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Sending [[#Custom messages|custom messages]] is done in a similar fashion, the only difference is that you must use a dedicated [[#Data structures|data structure]] instead of the generic ''struct ApplicationMsg''. Let’s say that BestApp is in fact a media player, and that it has defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell BestApp to start playing, it will need to send the custom message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationCustomMsg customMsg; // custom message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Prepare the message data structure. */<br /> customMsg.almsg.senderAppID = appID; // identifier of the sender<br /> customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message<br /> customMsg.customMsg = &quot;Start playback&quot;; // message text<br /> <br /> /* Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> (struct ApplicationMsg *) &amp;customMsg,<br /> APPLIBMT_CustomMsg);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == PrefsObjects ==<br /> <br /> === Introduction ===<br /> <br /> Many applications are user-configurable so they need a way to store their settings; we traditionally use the term ''preferences'' or simply ''prefs'' in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Workbench_and_Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows ''.ini'' format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (system preferences are stored as IFF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:<br /> <br /> # All of the solutions require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).<br /> # None of the various formats used have ever provided a smart enough way to administer complex, structured settings.<br /> <br /> PrefsObjects has largely been promoted as being ''XML-based'' and therefore human-readable and easy to edit. But using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is ''object-oriented''. Among other things, this property also helps address the two drawbacks mentioned above.<br /> <br /> Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, the main program can never count on a particular structure of the prefs file: the contents and sequencing of data depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.<br /> <br /> === Object types ===<br /> <br /> PrefsObjects tackles the task by seeing data as ''objects'', and by providing common functions (methods) for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function - “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.<br /> <br /> PrefsObjects distinguishes between the following object types:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | dictionary || A container to encapsulate other objects. The prefs file itself is an object of the dictionary type.<br /> |-<br /> | array || A container to encapsulate other objects. The difference between a dictionary and an array is in object referencing: in arrays objects are referenced by an index, while in a dictionary they are referenced by a key string.<br /> |-<br /> | number || An object to carry a long integer, double, or a boolean value.<br /> |-<br /> | string || An object to carry a text string.<br /> |-<br /> | date || An object to carry date and time information.<br /> |-<br /> | binary || An object to carry arbitrary binary data.<br /> |}<br /> <br /> === The interface ===<br /> <br /> It has been mentioned above in the [[#Library opening chores|Library opening chores]] section that the Application Library has two interfaces, called “application” and “prefsobjects”. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions (methods) are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.<br /> <br /> === The preferences file ===<br /> <br /> The file that will be associated with your application as its preferences file is a standard XML document. All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated.<br /> <br /> The beauty of using standard file format is that from now on programmers and users will be able to view and edit the preferences file by using the standard tool [[PrefsObjectsEditor]] (usage of this tool is out of the scope of this page). <br /> <br /> === Preference organization ===<br /> <br /> It's recommended - but not mandatory - that a preferences file starts with a Dictionary. The advantage of using a Dictionary is that objects stored are identified by a name rather than just put there as they came out of the mind of the programmer. Using named object as several advantages:<br /> * preferences file can be extended without any pain even when adding preferences data in the middle of the file format (the same is also true when removing data);<br /> * it's easier to load and save because order does not matter (as long as dealing with data at the same root level);<br /> * it's easier to read/modify by user.<br /> <br /> It's perfectly legal and accepted to put Dictionnary as value in a Dictionary this enables ability to model preferences data arborescence.<br /> <br /> Binary data type should be avoided as much as possible because it's restrictive in its form and is not future proof as it does not enable addition or removal of the stored data without compromising compatibility.<br /> <br /> == Populating the file ==<br /> <br /> Each of these [[#Object types|Object type]] has a corresponding dispatcher to handle operations on this kind of object. The following table indicates the mapping:<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ PrefsObjects type mapping<br /> ! Type !! PrefsObjects dispatcher name<br /> |-<br /> | dictionary || PrefsDictionary<br /> |-<br /> | array || PrefsArray<br /> |-<br /> | number || PrefsNumber<br /> |-<br /> | string || PrefsString<br /> |-<br /> | date || PrefsDate<br /> |-<br /> | binary || PrefsBinary<br /> |}<br /> <br /> <br /> == Automation == <br /> <br /> By default, the prefs file is stored in and loaded from the ENV: and ENVARC: system directories. The default location can nevertheless be overridden at [[#Registration|registration]] time. For example, your application may want to use its own subdirectory in the ENV(ARC): path. This is done by passing the REGAPP_ENVDir tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file into “ENV:BestApp/” and “ENVARC:BestApp/”.<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_ENVDir, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The default naming scheme for the prefs file uses the application name combined with the URL identifier. Also, the ''.xml'' extension is appended at the end of the file name to identify the document format. So if our application registered under the name of “BestApp” and with the URL identifier “supercoders.com”, the default preferences file name will be “BestApp.supercoders.com.xml”. This yields a unique file name but might look a little quirky so the library provides a way to override the default naming scheme. This is done by passing the REGAPP_ CustomPrefsBaseName tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file as &quot;BestApp.xml&quot;:<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_CustomPrefsBaseName, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Application_Library&diff=4099 Application Library 2012-08-31T18:13:58Z <p>Alexandre Balaban: Addition to Preferences paragraphs</p> <hr /> <div>{{WIP}}<br /> <br /> == Introduction ==<br /> The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of ''application'' is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either [[Exec_Tasks|tasks]] or [[AmigaDOS_Data_Structures#Process_Data_Structures|processes]]. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other ''background services''), and genuine full-blown ''applications'' with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.<br /> <br /> AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called [[#Registering the application|application registration]], during which the application receives a [[#Application identifiers|unique identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:<br /> <br /> * It can send/receive messages to/from other registered applications. The library supports a set of common [[#Control messages|control messages]] (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows [[#Custom messages|custom messages]] designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control.<br /> <br /> * It can use [[#PrefsObjects|PrefsObjects]], an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> * It can notify the user about, for example, completed tasks via automatic [[#Pop-up notifications (Ringhio_messages)|pop-up messages]]. These represent a practical, less obtrusive alternative to traditional requesters.<br /> <br /> * It can easily create and manage lists of recently-used documents.<br /> <br /> * It can register as a [[#Unique applications|unique application]], preventing other instances of itself from running.<br /> <br /> * It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.<br /> <br /> * It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.<br /> <br /> == Library opening chores ==<br /> <br /> Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Library *ApplicationBase = NULL;<br /> struct ApplicationIFace *IApplication = NULL;<br /> struct PrefsObjectsIFace *IPrefsObjects = NULL;<br /> <br /> if ( (ApplicationBase = IExec-&gt;OpenLibrary(&quot;application.library&quot;, 52)) )<br /> {<br /> IApplication = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;application&quot;, 1, NULL);<br /> IPrefsObjects = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;prefsobjects&quot;, 1, NULL);<br /> }<br /> <br /> if ( !ApplicationBase || !IApplication || !IPrefsObjects )<br /> {<br /> /* handle library opening error */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that there is no interface called “main” like older, single-interface libraries have.<br /> <br /> When your application has run its course, don’t forget to clean up and close both the library and its interface(s):<br /> <br /> &lt;syntaxhighlight&gt;<br /> IExec-&gt;DropInterface((struct Interface *)IPrefsObjects);<br /> IExec-&gt;DropInterface((struct Interface *)IApplication);<br /> IExec-&gt;CloseLibrary(ApplicationBase);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Registering the application ==<br /> <br /> Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.<br /> <br /> === Application identifiers ===<br /> <br /> A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.<br /> <br /> Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.<br /> <br /> === Registration ===<br /> <br /> Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> TAG_DONE);<br /> <br /> if (!appID)<br /> {<br /> /* report registration error and quit */<br /> }<br /> <br /> /*<br /> do whatever your program does<br /> */<br /> <br /> IApplication-&gt;UnregisterApplication(appID, NULL);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)<br /> <br /> The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.<br /> <br /> During registration you may also indicate certain features your application will support. In particular, you may inform the system that your application opens a dedicated Preferences window, that it can be iconified, that it can create documents, or that it supports printing documents. This information is not required but it can be used by other applications, which may decide to query about the availability of a certain feature before they send a [[#Control messages|control message]]. Set the following boolean tags to TRUE in the RegisterApplication() call if your application supports the respective feature (the default value is FALSE):<br /> <br /> * REGAPP_HasPrefsWindow<br /> * REGAPP_HasIconifyFeature<br /> * REGAPP_CanCreateNewDocs<br /> * REGAPP_CanPrintDocs<br /> <br /> === Unique applications ===<br /> <br /> A program can register as a ''unique application'', thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.<br /> <br /> The following function call registers our Best App as a unique application:<br /> <br /> &lt;syntaxhighlight&gt;<br /> appID = IApplication-&gt;RegisterApplication(&quot;BestApp&quot;,<br /> REGAPP_URLIdentifier, &quot;supercoders.com&quot;,<br /> REGAPP_Description, &quot;The best application there is, really&quot;,<br /> REGAPP_UniqueApplication, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a [[#Special messages|special message]] to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window.<br /> <br /> == Application attributes ==<br /> <br /> An application is described by a set of ''attributes''. There are attributes that:<br /> <br /> * determine the application's behaviour<br /> * describe the application's feature set<br /> * describe the current application state<br /> <br /> Most of the attributes are specified at registration time. Some of them cannot be altered once they are set, while others are freely changeable after registration.<br /> <br /> == Finding applications ==<br /> <br /> The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them ''application managers'' – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.<br /> <br /> “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:<br /> <br /> * the application name, ie. the one under which it was registered via RegisterApplication();<br /> * the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see [[#Application identifiers|Application identifiers]] above;<br /> * the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.<br /> <br /> Based on this information, the respective piece of code that will find our BestApp in the system might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> /* if you only know the application name */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_Name, &quot;BestApp&quot;, TAG_DONE);<br /> <br /> /* if you know the application name identifier */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you specifically want to talk to the second running instance */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, &quot;BestApp_1.supercoders.com&quot;, TAG_DONE);<br /> <br /> /* if you know the pathname to the program file */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_FileName, &quot;Work:Utils/BestApp&quot;, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Once you have obtained the appID you can start communicating with the respective application.<br /> <br /> == Messaging ==<br /> <br /> Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported on the low level (through Exec Library’s [[Exec Messages and Ports|messages and ports]]) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.<br /> <br /> Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of [[#Control messages|predefined messages]] that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports [[#Custom messages|custom messages]], thus allowing for more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of [[#Pop-up notifications (Ringhio_messages)|pop-up notification messages]]. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement. Registration alone does not magically turn on any features.<br /> <br /> {{Note|Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named ''AppMessages''. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the [[Workbench and Icon Library#The Workbench Library|Workbench Library]] section of the AmigaOS documentation wiki).<br /> }}<br /> <br /> === Data structures ===<br /> <br /> Rather than introduce yet another system for communication, the Application Library builds upon the existing [[Exec_Messages_and_Ports|Exec messaging framework]]. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of [[Exec_Messages_and_Ports#Getting_a_Message|GetMsg()]] and [[Exec_Messages_and_Ports#Replying|ReplyMsg()]] takes place when processing incoming Application Library messages (see section [[#Message handling|Message handling]] below) – although the library implements its own method for [[#Sending messages|sending messages]].<br /> <br /> The following three structures are defined for carrying Application Library message data:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg<br /> {<br /> struct Message msg;<br /> uint32 senderAppID; // the appID of the sender application<br /> uint32 type; // identifies the type of message<br /> };<br /> <br /> struct ApplicationOpenPrintDocMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR fileName;<br /> };<br /> <br /> struct ApplicationCustomMsg<br /> {<br /> struct ApplicationMsg almsg;<br /> STRPTR customMsg;<br /> };<br /> &lt;/syntaxhighlight&gt;<br /> <br /> As you can see, structure ''ApplicationMsg'' contains a standard ''[[Exec_Messages_and_Ports#Messages|struct Message]]'', the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ''ApplicationOpenPrintDocMsg'' is used by certain [[#Control messages|control messages]] that require a pointer to a filename; the ''ApplicationCustomMsg'' structure is used for [[#Custom messages|custom messages]].<br /> <br /> Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to [[#Sending messages|send a message]] to an application you specify it in the “type” field.<br /> <br /> === Pop-up notifications (Ringhio messages) ===<br /> <br /> As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called ''Ringhio messages'' because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library). [[File:RinghioNotification.png|frame|Ringhio notification example]]The pop-ups function similarly to [[Intuition Requesters and Alerts|requesters]] in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.<br /> <br /> Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 result;<br /> <br /> result = IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The first parameter is your application’s appID received from the [[#Registration|registration]] function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the [[Intuition Screens#The Default Public Screen and Workbench|Workbench screen]].<br /> <br /> The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.<br /> <br /> The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;All right, ma!&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a [[#Custom messages|custom message]] (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, [[#Custom messages|custom messages]] are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the [[#Message handling|Message handling]] section for how to go about it.<br /> <br /> Whether receiving a “back message” would be useful for a particular application, and whether it would make sense to react upon it, is decided by the programmer. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”.<br /> <br /> If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open our Best App’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click.<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, &quot;Important Message&quot;,<br /> APPNOTIFY_Text, &quot;Your socks need changing!&quot;,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, &quot;PROGDIR:Images/BestApp.jpg&quot;,<br /> APPNOTIFY_BackMsg, &quot;URL:http://www.supercoders.com&quot;,<br /> APPNOTIFY_CloseOnDC, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> === Control messages ===<br /> <br /> The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided:<br /> <br /> ; APPLIBMT_Quit<br /> : The application shall quit. If data could be lost, it is expected that a confirmation requester is displayed.<br /> <br /> ; APPLIBMT_ForceQuit<br /> : Same as before but this time the application shall quit immediately, without asking for saving documents etc.<br /> <br /> ; APPLIBMT_Hide<br /> : The application shall hide its interface and iconify on the Workbench screen.<br /> <br /> ; APPLIBMT_Unhide<br /> : The application shall come back from the iconified state.<br /> <br /> ; APPLIBMT_ToFront<br /> : The application window shall come to front. Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence.<br /> <br /> ; APPLIBMT_OpenPrefs<br /> : The application shall open its preferences window.<br /> <br /> ; APPLIBMT_NewBlankDoc<br /> : The application shall open a new, blank document.<br /> <br /> ; APPLIBMT_OpenDoc<br /> : The application shall try to open a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> ; APPLIBMT_PrintDoc<br /> : The application shall try to print a specific document. The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data structures|Data structures]] above).<br /> <br /> An application will only react to messages that are implemented and supported. If you [[#Sending messages|send a message]] to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are asked for a minimum cooperation. In the near future, AmigaOS will have an application manager (third-party managers already exist) that will control running applications – much like the Exchange tool controls [[Commodities_Exchange_Library|commodities]]. Therefore, registered applications should “play nice” and support at least the most common commands – APPLIBMT_Quit, APPLIBMT_Hide, APPLIBMT_Unhide and APPLIBMT_ToFront. Implement the rest according to your application’s features and needs.<br /> <br /> {{Note|Providing the APPLIBMT_ForceQuit command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely, possibly causing loss of data. Therefore, implement support for APPLIBMT_ForceQuit in such a way that the user's data is never put in jeopardy!<br /> }}<br /> <br /> === Custom messages ===<br /> <br /> In contrast to a [[#Control messages|control message]] (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]], while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to [[#Finding applications|find]] the receiver application and [[#Sending messages|send a message]] to it.<br /> <br /> The pointer to the message text string is contained in a special [[#Data structures|data structure]], ''struct ApplicationCustomMsg''.<br /> <br /> === Special messages ===<br /> <br /> There are also some special messages that an application can receive from the library or another running application:<br /> <br /> ; APPLIBMT_Unique<br /> : If an application is registered as [[#Unique applications|unique]] and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message.<br /> <br /> ; APPLIBMT_GameModeEntered<br /> : This message is sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. Games or presentation programs are examples of applications that may want to run in the “game mode”. The message simply asks other applications to cooperate and “keep quiet”: not play sounds, open windows, requesters, etc. Please use the game-mode feature moderately and only send the APPLIBMT_GameModeEntered message when it is really needed.<br /> <br /> ; APPLIBMT_GameModeLeft<br /> : This message informs all running application that the sender has left the “game mode”.<br /> <br /> === Message handling ===<br /> <br /> You already know that messaging in AmigaOS takes place between [[Exec_Messages_and_Ports#Message_Ports|message ports]]. Being a superset of standard Exec messages, Application Library notifications (be they predefined [[#Control messages|control messages]], [[#Custom messages|custom messages]] or [[#Special messages|special messages]]) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the ''notification port''. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s [[Intuition_Input_and_Output_Methods#Receiving_Input_Events_from_Intuition|IDCMP messages]]). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications.<br /> <br /> A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.<br /> <br /> &lt;syntaxhighlight&gt;<br /> void event_loop(uint32 appID)<br /> {<br /> struct MsgPort *notificationPort = NULL;<br /> struct ApplicationMsg *appLibMsg = NULL;<br /> struct ApplicationCustomMsg *customMsg = NULL;<br /> uint32 appLibSignal = 0, sigGot = 0;<br /> BOOL done = FALSE;<br /> <br /> /* Obtain pointer to Application Library's notification port and set the signal bit. */<br /> IApplication-&gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;notificationPort, TAG_DONE);<br /> appLibSignal = (1L &lt;&lt; notificationPort-&gt;mp_SigBit);<br /> <br /> /* Go into the event loop. */<br /> while (!done)<br /> {<br /> /* Wait for a signal to arrive. */<br /> sigGot = IExec-&gt;Wait(appLibSignal);<br /> <br /> /* Process all Application Library messages. */<br /> if ( sigGot &amp; appLibSignal )<br /> {<br /> /* Obtain pointer to the message. */<br /> while ( (appLibMsg = (struct ApplicationMsg *) IExec-&gt;GetMsg(notificationPort)) )<br /> {<br /> /* Identify the type of message. */<br /> switch (appLibMsg-&gt;type)<br /> {<br /> case APPLIBMT_Quit:<br /> done = TRUE;<br /> break;<br /> <br /> case APPLIBMT_ToFront:<br /> /* Make the program window come to front . */<br /> break;<br /> <br /> case APPLIBMT_Hide:<br /> /* Iconify the program. */<br /> break;<br /> <br /> case APPLIBMT_Unhide:<br /> /* Uniconify the program. */<br /> break;<br /> <br /> /* Process the custom message as you like.<br /> Here we just use printf() to output the message text. */<br /> case APPLIBMT_CustomMsg:<br /> customMsg = (struct ApplicationCustomMsg *) appLibMsg;<br /> printf(&quot;The message text is: %s\n&quot;, customMsg-&gt; customMsg);<br /> break;<br /> <br /> /*<br /> Process any other Application Library messages.<br /> */<br /> <br /> }<br /> /* Return the processed message to the sender so that it can be freed. */<br /> IExec-&gt;ReplyMsg( (struct Message *) appLibMsg);<br /> }<br /> }<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Like all [[Exec_Messages_and_Ports#Messages|Exec messages]] obtained via the GetMsg() function, Application Library messages must be [[Exec_Messages_and_Ports#Replying|replied to]], i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own.<br /> <br /> Also remember that the notifications are addressed to an abstract ''application'' – this makes them rather different from IDCMP messages, which are always sent to a particular ''window'' (Intuition is unaware of the application concept). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make!<br /> <br /> === Sending messages ===<br /> <br /> While incoming notifications are [[#Message handling|processed]] pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application:<br /> <br /> # Prepare the respective [[#Data structures|data structure]]: specify the type of message and supply the sender's [[#Application identifiers|identifier]] (appID).<br /> # [[#Finding applications|Find the receiver]] application.<br /> # Send a message to it via the SendApplicationMsg() function.<br /> <br /> For example, if you want to tell our BestApp application to quit, you send it an APPLIBMT_Quit message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationMsg appMsg; // message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Step 1: Prepare the message data structure. */<br /> appMsg.senderAppID = appID; // identifier of the sender<br /> appMsg.type = APPLIBMT_Quit; // type of message<br /> <br /> /* Step 2: Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Step 3: Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> &amp;appMsg,<br /> APPLIBMT_Quit);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Sending [[#Custom messages|custom messages]] is done in a similar fashion, the only difference is that you must use a dedicated [[#Data structures|data structure]] instead of the generic ''struct ApplicationMsg''. Let’s say that BestApp is in fact a media player, and that it has defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell BestApp to start playing, it will need to send the custom message like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct ApplicationCustomMsg customMsg; // custom message data structure<br /> uint32 bestAppID; // identifier of the receiver<br /> <br /> /* Prepare the message data structure. */<br /> customMsg.almsg.senderAppID = appID; // identifier of the sender<br /> customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message<br /> customMsg.customMsg = &quot;Start playback&quot;; // message text<br /> <br /> /* Find the receiver application. */<br /> if ( (bestAppID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE)) )<br /> {<br /> /* Send the message. */<br /> IApplication-&gt;SendApplicationMsg(appID,<br /> bestAppID,<br /> (struct ApplicationMsg *) &amp;customMsg,<br /> APPLIBMT_CustomMsg);<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == PrefsObjects ==<br /> <br /> === Introduction ===<br /> <br /> Many applications are user-configurable so they need a way to store their settings; we traditionally use the term ''preferences'' or simply ''prefs'' in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Workbench_and_Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows ''.ini'' format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (system preferences are stored as IFF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:<br /> <br /> # All of the solutions require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).<br /> # None of the various formats used have ever provided a smart enough way to administer complex, structured settings.<br /> <br /> PrefsObjects has largely been promoted as being ''XML-based'' and therefore human-readable and easy to edit. But using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is ''object-oriented''. Among other things, this property also helps address the two drawbacks mentioned above.<br /> <br /> Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, the main program can never count on a particular structure of the prefs file: the contents and sequencing of data depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.<br /> <br /> === Object types ===<br /> <br /> PrefsObjects tackles the task by seeing data as ''objects'', and by providing common functions (methods) for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function - “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.<br /> <br /> PrefsObjects distinguishes between the following object types:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | dictionary || A container to encapsulate other objects. The prefs file itself is an object of the dictionary type.<br /> |-<br /> | array || A container to encapsulate other objects. The difference between a dictionary and an array is in object referencing: in arrays objects are referenced by an index, while in a dictionary they are referenced by a key string.<br /> |-<br /> | number || An object to carry a long integer, double, or a boolean value.<br /> |-<br /> | string || An object to carry a text string.<br /> |-<br /> | date || An object to carry date and time information.<br /> |-<br /> | binary || An object to carry arbitrary binary data.<br /> |}<br /> <br /> === The interface ===<br /> <br /> It has been mentioned above in the [[#Library opening chores|Library opening chores]] section that the Application Library has two interfaces, called “application” and “prefsobjects”. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions (methods) are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.<br /> <br /> === The preferences file ===<br /> <br /> The file that will be associated with your application as its preferences file is a standard XML document. All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated.<br /> <br /> The beauty of using standard file format is that from now on programmers and users will be able to view and edit the preferences file by using the standard tool [[PrefsObjectsEditor]] (usage of this tool is out of the scope of this page). <br /> <br /> ==== Preference organization ===<br /> <br /> It's recommended - but not mandatory - that a preferences file starts with a Dictionary. The advantage of using a Dictionary is that objects stored are identified by a name rather than just put there as they came out of the mind of the programmer. Using named object as several advantages:<br /> * preferences file can be extended without any pain even when adding preferences data in the middle of the file format (the same is also true when removing data);<br /> * it's easier to load and save because order does not matter (as long as dealing with data at the same root level);<br /> * it's easier to read/modify by user.<br /> <br /> == Populating the file ==<br /> <br /> Each of these [[#Object types|Object type]] has a corresponding dispatcher to handle operations on this kind of object. The following table indicates the mapping:<br /> <br /> {| class=&quot;wikitable&quot;<br /> |+ PrefsObjects type mapping<br /> ! Type !! PrefsObjects dispatcher name<br /> |-<br /> | dictionary || PrefsDictionary<br /> |-<br /> | array || PrefsArray<br /> |-<br /> | number || PrefsNumber<br /> |-<br /> | string || PrefsString<br /> |-<br /> | date || PrefsDate<br /> |-<br /> | binary || PrefsBinary<br /> |}<br /> <br /> <br /> == Automation == <br /> <br /> By default, the prefs file is stored in and loaded from the ENV: and ENVARC: system directories. The default location can nevertheless be overridden at [[#Registration|registration]] time. For example, your application may want to use its own subdirectory in the ENV(ARC): path. This is done by passing the REGAPP_ENVDir tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file into “ENV:BestApp/” and “ENVARC:BestApp/”.<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_ENVDir, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The default naming scheme for the prefs file uses the application name combined with the URL identifier. Also, the ''.xml'' extension is appended at the end of the file name to identify the document format. So if our application registered under the name of “BestApp” and with the URL identifier “supercoders.com”, the default preferences file name will be “BestApp.supercoders.com.xml”. This yields a unique file name but might look a little quirky so the library provides a way to override the default naming scheme. This is done by passing the REGAPP_ CustomPrefsBaseName tag-value pair in the RegisterApplication() call. The following line of code will cause saving the prefs file as &quot;BestApp.xml&quot;:<br /> <br /> &lt;syntaxhighlight&gt;<br /> REGAPP_CustomPrefsBaseName, &quot;BestApp&quot;,<br /> &lt;/syntaxhighlight&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Application_Library&diff=3552 Application Library 2012-07-13T14:46:17Z <p>Alexandre Balaban: /* PrefsObjects */ Added links to BOOPSI and Tag ITem, created section Supported Data Types.</p> <hr /> <div>{{WIP}}<br /> <br /> == Introduction ==<br /> The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of ''application'' is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either ''[[Exec_Tasks|tasks]]'' or ''[[AmigaDOS_Data_Structures#Process_Data_Structures|processes]]''. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other ''background services''), and genuine full-blown ''applications'' with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.<br /> <br /> AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called [[#Registering the application|application registration]], during which the application receives a [[#Application identifiers|unique identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:<br /> <br /> * It can send/receive messages to/from other registered applications. The library supports a set of common control messages (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows custom messages designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control.<br /> <br /> * It can use [[#PrefsObjects|PrefsObjects]], an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> * It can notify the user about, for example, completed tasks via automatic [[#Pop-up notifications (Ringhio_messages)|pop-up messages]]. These represent a practical, less obtrusive alternative to traditional requesters.<br /> <br /> * It can easily create and manage lists of recently-used documents.<br /> <br /> * It can register as a [[#Unique applications|unique application]], preventing other instances of itself from running.<br /> <br /> * It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.<br /> <br /> * It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.<br /> <br /> == Library opening chores ==<br /> <br /> Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.<br /> <br /> &lt;syntaxhighlight&gt;<br /> struct Library *ApplicationBase = NULL;<br /> struct ApplicationIFace *IApplication = NULL;<br /> struct PrefsObjectsIFace *IPrefsObjects = NULL;<br /> <br /> if ( (ApplicationBase = IExec-&gt;OpenLibrary(&quot;application.library&quot;, 52)) )<br /> {<br /> IApplication = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;application&quot;, 1, NULL);<br /> IPrefsObjects = (APTR)IExec-&gt;GetInterface(ApplicationBase, &quot;prefsobjects&quot;, 1, NULL);<br /> }<br /> <br /> if ( !ApplicationBase || !IApplication || !IPrefsObjects )<br /> {<br /> /* handle library opening error */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that there is no interface called “main” like older, single-interface libraries have.<br /> <br /> When your application has run its course, don’t forget to clean up and close both the library and its interface(s):<br /> <br /> &lt;syntaxhighlight&gt;<br /> IExec-&gt;DropInterface((struct Interface *)IPrefsObjects);<br /> IExec-&gt;DropInterface((struct Interface *)IApplication);<br /> IExec-&gt;CloseLibrary(ApplicationBase);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> == Registering the application ==<br /> <br /> Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.<br /> <br /> === Application identifiers ===<br /> <br /> A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.<br /> <br /> Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.<br /> <br /> === Registration ===<br /> <br /> Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> appID = IApplication-&gt;RegisterApplication(“BestApp”,<br /> REGAPP_URLIdentifier, “supercoders.com”,<br /> REGAPP_Description, “The best application there is, really”,<br /> TAG_DONE);<br /> <br /> if (!appID)<br /> {<br /> /* report registration error and quit */<br /> }<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)<br /> <br /> The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.<br /> <br /> === Unique applications ===<br /> <br /> A program can register as a ''unique application'', thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.<br /> <br /> The following function call registers our Best App as a unique application:<br /> <br /> &lt;syntaxhighlight&gt;<br /> appID = IApplication-&gt;RegisterApplication(“BestApp”,<br /> REGAPP_URLIdentifier, “supercoders.com”,<br /> REGAPP_Description, “The best application there is, really”,<br /> REGAPP_UniqueApplication, TRUE,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a special message to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to front and activate its window. See the Messaging section below for more information.<br /> <br /> == Finding applications ==<br /> <br /> The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them ''application managers'' – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.<br /> <br /> “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:<br /> <br /> * the application name, ie. the one under which it was registered via RegisterApplication();<br /> * the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see [[#Application identifiers|Application identifiers]] above;<br /> * the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.<br /> <br /> Based on this information, the respective piece of code that will find our BestApp in the system might look like this:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 appID;<br /> <br /> /* if you only know the application name */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE);<br /> <br /> /* if you know the application name identifier */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, “BestApp.supercoders.com”, TAG_DONE);<br /> <br /> /* if you specifically want to talk to the second running instance */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_AppIdentifier, “BestApp_1.supercoders.com”, TAG_DONE);<br /> <br /> /* if you know the pathname to the program file */<br /> appID = IApplication-&gt;FindApplication(FINDAPP_FileName, “Work:Utils/BestApp”, TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> Once you have obtained the appID you can start communicating with the respective application.<br /> <br /> == Messaging ==<br /> <br /> Modern software applications often need to communicate with other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, the operating system provides the necessary means. AmigaOS is no exception: inter-program communication has been supported in the OS on the low level (through Exec Library’s [[Exec Messages and Ports|messages and ports]]) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.<br /> <br /> Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of pre-defined messages that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports custom messages, thus allowing for much more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of pop-up notification messages. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement.<br /> <br /> Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named ''AppMessages''. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the [[Workbench and Icon Library#The Workbench Library|Workbench Library]] section of the AmigaOS documentation wiki).<br /> <br /> === Pop-up notifications (Ringhio messages) ===<br /> <br /> As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called ''Ringhio messages'' because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library). [[File:RinghioNotification.png|frame|Ringhio Notification sample]]The pop-ups function similarly to [[Intuition Requesters and Alerts|requesters]] in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.<br /> <br /> Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:<br /> <br /> &lt;syntaxhighlight&gt;<br /> uint32 result;<br /> <br /> result = IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, “Important Message”,<br /> APPNOTIFY_Text, “Your socks need changing!”,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> The first parameter is your application’s appID received from the [[#Registration|registration]] function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the [[Intuition Screens#The Default Public Screen and Workbench|Workbench screen]].<br /> <br /> The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.<br /> <br /> The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:<br /> <br /> &lt;syntaxhighlight&gt;<br /> IApplication-&gt;Notify(appID,<br /> APPNOTIFY_Title, “Important Message”,<br /> APPNOTIFY_Text, “Your socks need changing!”,<br /> APPNOTIFY_PubScreenName, &quot;FRONT&quot;,<br /> APPNOTIFY_ImageFile, “PROGDIR:Images/BestApp.jpg”,<br /> APPNOTIFY_BackMsg, &quot;All right, ma!&quot;,<br /> TAG_DONE);<br /> &lt;/syntaxhighlight&gt;<br /> <br /> But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a custom message (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. This custom message is sent to the same event stream as other Application Library messages – see the following section (sorry, not written yet!) for information about how to process it within the program.<br /> <br /> Whether receiving a “back message” would be useful for a particular application (and whether it would make sense to react upon it) is decided by the programmer. However, remember that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could possibly be taken as user acknowledgement of what Ringio has said, but never as a selection of an option! Do not use the back-message feature to request input: the text of the message should be a statement, not a question or an offer of choice. The double click effectively means “I understand”; it doesn't mean “Yes”.<br /> <br /> <br /> (to be continued)<br /> <br /> <br /> == PrefsObjects ==<br /> <br /> === Introduction ===<br /> <br /> Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.<br /> <br /> All methods to handle the preferences are grouped into a separate specific interface of the Application Library: &quot;PrefsObjects&quot;. Prior the use of any of these methods and even if he's already using other methods of the Application Library; the programmer is required to properly obtain this interface, see [[#Library opening chores|Library opening chores]] to know how. <br /> {{Note|Obtaining the other interface is not required to use methods from the interface ''PrefsObjects'', however in practice the programmer will use both because he will surely want to benefit from some of its features too.<br /> }}<br /> <br /> PrefsObjects model is highly object inspired, any manipulated type has its own construction function, and a set of associated methods. One should probably be familiar with [[BOOPSI]] philosophy before reading the following section. Similarly PrefsObjects makes high usage of [[Tag Items]] so it's recommended to be comfortable with this technique too.<br /> <br /> === Supported Data Types ===<br /> <br /> The PrefsObjects system supports the most basic data types, that will be sufficient to describe any more complex types (i.e. structured data).<br /> {| class=&quot;wikitable&quot;<br /> |+ PrefsObjects supported data types<br /> ! Common type name<br /> ! PrefsObjects name <br /> |-<br /> | boolean || {{inLink|PrefsNumber}}<br /> |-<br /> | long integer (32 bits) || {{inLink|PrefsNumber}}<br /> |-<br /> | double || {{inLink|PrefsNumber}}<br /> |-<br /> | string || {{inLink|PrefsString}}<br /> |-<br /> | date/time || {{inLink|PrefsDate}}<br /> |-<br /> | other (binary) || {{inLink|PrefsBinary}}<br /> |}<br /> <br /> In addition to these basic data types, PrefsObjects also supports two containers: Arrays ({{inLink|PrefsArray}}) and Dictionaries ({{inLink|PrefsDictionary}}). The main difference between the two being that in arrays elements are accessible by their index in the array, while in a dictionary they are accessible by a key string.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Template:InLink&diff=3551 Template:InLink 2012-07-13T14:42:06Z <p>Alexandre Balaban: Another documentation fix</p> <hr /> <div>&lt;includeonly&gt;[[#{{{1}}}|{{{1}}}]]&lt;/includeonly&gt;&lt;noinclude&gt;<br /> Utility template to ease insertion of intra-page hyperlinks.<br /> <br /> == Usage ==<br /> &lt;nowiki&gt;{{InLink|termtolink}}&lt;/nowiki&gt;<br /> <br /> ; termtolink<br /> : the name of the section from the ''same page'' to link to.<br /> <br /> == Sample ==<br /> Following example links to the above Usage section :<br /> &lt;nowiki&gt;Please see {{InLink|Usage}} for complete information.&lt;/nowiki&gt;<br /> <br /> renders as<br /> <br /> ''Please see {{InLink|Usage}} for complete information.''<br /> &lt;/noinclude&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Template:InLink&diff=3550 Template:InLink 2012-07-13T14:40:08Z <p>Alexandre Balaban: Fixed documentation</p> <hr /> <div>&lt;includeonly&gt;[[#{{{1}}}|{{{1}}}]]&lt;/includeonly&gt;&lt;noinclude&gt;<br /> Utility template to ease insertion of intra-page hyperlinks.<br /> <br /> == Usage ==<br /> &lt;nowiki&gt;{{InLink|termtolink}}&lt;/nowiki&gt;<br /> <br /> == Sample ==<br /> Following example links to the above Usage section :<br /> &lt;nowiki&gt;Please see {{InLink|Usage}} for complete information.&lt;/nowiki&gt;<br /> <br /> renders as<br /> ''Please see {{InLink|Usage}} for complete information.''<br /> &lt;/noinclude&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=Template:InLink&diff=3549 Template:InLink 2012-07-13T14:39:30Z <p>Alexandre Balaban: Created page with &quot;&lt;includeonly&gt;{{{1}}}&lt;/includeonly&gt;&lt;noinclude&gt; Utility template to ease insertion of intra-page hyperlinks. == Usage == &lt;nowiki&gt;{{InLink|termtolink}}&lt;/nowiki&gt; =...&quot;</p> <hr /> <div>&lt;includeonly&gt;[[#{{{1}}}|{{{1}}}]]&lt;/includeonly&gt;&lt;noinclude&gt;<br /> Utility template to ease insertion of intra-page hyperlinks.<br /> <br /> == Usage ==<br /> &lt;nowiki&gt;{{InLink|termtolink}}&lt;/nowiki&gt;<br /> <br /> == Sample ==<br /> Following example links to the above Usage section :<br /> &lt;nowiki&gt;Please see {{InLink|Usage}} for complete information.&lt;/nowiki&gt;<br /> <br /> renders as<br /> Please see {{InLink|Usage}} for complete information.<br /> &lt;/noinclude&gt;</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=BOOPSI&diff=3548 BOOPSI 2012-07-13T14:29:57Z <p>Alexandre Balaban: Redirected page to BOOPSI - Object Oriented Intuition</p> <hr /> <div>#REDIRECT [[BOOPSI - Object Oriented Intuition]]</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_ARexx&diff=3546 UI Style Guide ARexx 2012-07-13T08:13:55Z <p>Alexandre Balaban: /* General Description and Guidelines */ Changed links to Shell section</p> <hr /> <div>Previous chapters covered aspects of the GUI and the Shell. The third built-in Amiga interface is ARexx.<br /> <br /> Like the Shell, ARexx is a text-based facility for giving commands to the Amiga, but while the Shell operates externally to programs (copy, delete, etc.), ARexx can operate internally with programs.<br /> <br /> ARexx has two main uses: as a scripting language and for Inter-Process Communication (IPC). With the latter, ARexx acts as the standard hub for programs to communicate with each other by sending command and data messages.<br /> <br /> == Scripting and IPC ==<br /> <br /> The ability to handle macros, or scripts, is a powerful feature for any application. Whenever a user has a repetitive, well-defined job to do with application software, scripts allow him to automate.<br /> <br /> For example, many communications programs allow the user to set up a macro that will dial the phone number of a host system, log into it, check for messages and download those messages for later reading. The macro allows the user to do automatically what is usually done interactively.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | ARexx provides Amiga users with a standard macro language.<br /> |}<br /> <br /> === Benefits of ARexx ===<br /> <br /> Unfortunately, if every application vendor creates his own macro language, the user will have to learn several macro languages instead of just one. Also, each developer would have to spend time creating a new macro language.<br /> <br /> With ARexx in place as a system-level macro language, there exists a consistent syntax and user interface for all applications. Part of the reason it can do this is its versatility. Although ARexx is an interpreted language with its own set of keywords, these keywords can be extended to include new words specific to your application.<br /> <br /> ARexx has another role, too. It is the system module that allows application software from different vendors to inter-operate, share data and communicate with one another in a multitasking environment. For instance, with ARexx a communications package could be set up to download data from a financial bulletin board and pass the data to a separate spreadsheet application for further analysis.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | ARexx is based on REXX, variants of which are used across many platforms.<br /> |}<br /> <br /> When told about ARexx, most people imagine its use by power users - but the benefits of ARexx aren't strictly limited to power users. In a corporate environment, an information manager could take off-the-shelf software, throw in a mix of macros, batch commands and IPC, and come up with a system that truly meets the needs of his office. Once set up, the new, customized program could be used even by novices.<br /> <br /> Value-added resellers can use ARexx to design a custom front end that melds applications from different vendors. The result is a highly-specialized tool and an instant niche market for the application vendor.<br /> <br /> ARexx is like an open door on your application. Supporting it leaves the user free to combine your program with others - as a result, buyers of your program could end up using it in ways you never imagined.<br /> <br /> == Standards for ARexx ==<br /> <br /> In order for your program to work with ARexx, it must have an ARexx interface: a software structure that allows your application to send and receive messages from ARexx.<br /> <br /> If your application supports scripts, it should do so via ARexx. Even if your program doesn't use macros, you are urged to add an ARexx interface.<br /> <br /> Having said that, the purpose of the rest of this chapter is simple: to give a listing of standard ARexx commands so the same commands do the same thing from program to program.<br /> <br /> The fast-growing popularity of ARexx combined with its flexibility create, unfortunately, the likelihood of command name and function conflicts. It's frustrating to the user when the OPEN command works differently in his paint program than in his typesetting program.<br /> <br /> Your program should support at least a minimal subset of ARexx commands. It increases the power of your program, and as a powerful and seamless standard, it's good for the platform as a whole. Applications that don't support ARexx decrease the overall power and lure of the Amiga. Average users who decide to learn ARexx will almost certainly experience frustration when they run into the brick wall of the program that doesn't support ARexx.<br /> <br /> Once you learn how, incorporating ARexx support into your application requires about as much programming effort as supporting menus - without having to deal with the graphics. This chapter discusses which ARexx commands to support and the style in which they should be supported. For more details on the ARexx language itself refer to the Release 2 Users Manual ''Using the System Software''.<br /> <br /> === General Description and Guidelines ===<br /> <br /> ARexx is an interpreted programming language. As in BASIC, ARexx scripts can be written with any text editor and run without having to use a compiler. If an error occurs, ARexx outputs error messages indicating which line or lines are in error so the user can make changes and try again.<br /> <br /> ARexx is unique in that an ARexx script can contain commands that are specific to ARexx as well as commands that are specific to an application. ARexx provides commands typical of most programming languages (variable manipulation, arithmetic, conditional loops, string manipulation, etc.).<br /> <br /> Your application should provide, through ARexx, a subset, or even a superset, of the commands available through the GUI. This refers to menu or action gadget commands such as New, Save, Save As... and Quit.<br /> <br /> By combining ARexx commands and application-specific commands, the user can create simple or complex scripts to automate common tasks, create new functions, and integrate application software.<br /> <br /> For the sake of consistency, your application's ARexx commands should be similar in syntax to the AmigaDOS commands. They take the form:<br /> <br /> COMMAND [&lt;argument1&gt;, &lt;argument2&gt;...]<br /> <br /> where COMMAND is a keyword that your application recognizes followed by a list of arguments. In many cases the arguments will be optional and your application should be set to take some default action.<br /> <br /> As a general rule, your command set should:<br /> <br /> * accept arguments in the [[UI_Style_Guide_Shell#Standard_Form|standard AmigaDOS style]];<br /> <br /> * honor the AmigaDOS style of [[UI_Style_Guide_Shell#Pattern_Matching|pattern matching]] (e.g. the ''#?'' wildcard) when appropriate;<br /> <br /> * not be case-sensitive, and keywords should not contain spaces;<br /> <br /> * recognize and accept quoted arguments;<br /> <br /> * be verbose rather than terse (the user can make more sense of a keyword called OPEN versus O);<br /> <br /> * accept abbreviations for commonly used commands.<br /> <br /> === Errors ===<br /> <br /> ARexx scripts can be written by the everyday user or the professional programmer. Scripts must be able to handle error conditions and take some kind of action when an error occurs (i.e. notify the user, prompt the user, terminate, etc.). Therefore, it is important that your application return error codes when a command cannot be performed.<br /> <br /> The standard convention in ARexx is for application-specific commands to return error codes of zero (0) for no error, five (5) for warnings such as &quot;Aborted by user&quot;, ten (10) for errors such as &quot;&lt;file&gt; wrong type&quot;, and twenty (20) for failures such as &quot;Couldn't open the clipboard&quot;.<br /> <br /> Return codes are placed in ARexx's special variable called &quot;RC&quot; which can be checked for an manipulated in an ARexx script. For example, the user might try to save a file using the &quot;SAVEAS&quot; command. If the file could not be saved for any reason, it would be appropriate to return an error code of ten (10) so that the script could be terminated early, or some other action could be taken.<br /> <br /> You may choose to support a more comprehensive set of error codes. This is acceptable but your application should still return zero (0) for no error and use return codes of less than ten (10) for warnings.<br /> <br /> === Returning Data ===<br /> <br /> Some commands may return information back to ARexx. For example, a text editor or word processor might have a command that returns the string of text under the cursor. The string of text would be placed in a special ARexx variable called RESULT; the user, however, should be allowed to override this with a VAR or STEM switch in the command.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | VAR = return the value.<br /> |}<br /> <br /> {| class=&quot;wikitable&quot;<br /> | STEM =place the value in the specified variable.<br /> |}<br /> <br /> In order for result strings to be returned to ARexx, the script must use ARexx's OPTIONS RESULTS command. This command tells the application that it is OK to return result strings. Data placed in RESULT can then be copied and manipulated within an ARexx script.<br /> <br /> Here's an example: a text editor supports a command called GETLINE that returns the line of text under the cursor. The following is a sample ARexx script that uses the GETLINE command to get the line of text under the cursor and place it in a variable called &quot;line&quot;. Note that comments are surrounded by pairs of &quot;/*&quot; and<br /> &quot;*/&quot;.<br /> <br /> &lt;pre&gt;<br /> /* Example ARexx script */<br /> <br /> OPTIONS RESULTS<br /> <br /> 'GETLINE' /* Command telling the application to<br /> return the line of text under the<br /> cursor. */<br /> <br /> line=RESULT /* Set the variable called &quot;line&quot; equal<br /> to ARexx's special variable called<br /> &quot;RESULT&quot;. */<br /> &lt;/pre&gt;<br /> <br /> If the VAR (simple variable) or STEM (complex variable) switches are used, information should be placed into the named variable. For instance:<br /> <br /> &lt;pre&gt;<br /> MYCOMMAND STEM comresult<br /> &lt;/pre&gt;<br /> <br /> This would place the return result in the variable named ''comresult''. The VAR and STEM switches make it easier for applications from different vendors to operate on the same data under ARexx.<br /> <br /> Text strings that contain records with spaces should be returned with quotes around the record. A space should separate multiple records. For example, if you were trying to return the following list of records:<br /> <br /> &lt;pre&gt;<br /> Letter to John Doe<br /> Notes on Standards<br /> Addresses of the Stars<br /> &lt;/pre&gt;<br /> <br /> the information would be returned as:<br /> <br /> &lt;pre&gt;<br /> &quot;Letter to John Doe&quot; &quot;Notes on Standards&quot; &quot;Addresses of the Stars&quot;<br /> &lt;pre&gt;<br /> <br /> When the command returns multiple records, it should allow the user to specify a stem variable to place the contents in. For example, a command that would return the names of the loaded documents in a text editor would normally return information in the RESULT field, but if the user chooses to place the information in a stem variable, he could specify:<br /> <br /> &lt;pre&gt;<br /> GETATTRS DOCUMENTS STEM DocList.<br /> &lt;/pre&gt;<br /> <br /> which would return:<br /> <br /> &lt;pre&gt;<br /> DocList.count = 3<br /> DocList.0 = Letter to John Doe<br /> DocList.1 = Notes on Standards<br /> DocList.2 = Addresses of the Stars<br /> &lt;/pre&gt;<br /> <br /> In the above example, DocList.count contains the number of records in the array. Also note that the elements do not contain quotes around the information.<br /> <br /> === ARexx Port Naming ===<br /> <br /> Part of the job of supporting ARexx is adding an ARexx port to your application - this is a software structure through which ARexx communicates with your program. Each ARexx port must have a unique name and must be in upper-case. In ARexx, a port name is derived from:<br /> <br /> &lt;basename&gt;.&lt;slot #&gt;<br /> <br /> In the above line, &lt;basename&gt; is the same name your application's executable uses (see Chapter 2), and &lt;slot #&gt; is the next available ARexx port slot for that application.<br /> <br /> This ARexx port name should be displayed in the window brought up by the About menu item (along with the version of your application, etc.). The user should also have the ability to rename the port. Being able to specify the ARexx port name in the project icon's Tool Types field, or from the command line by using the PORTNAME keyword, is a good thing.<br /> <br /> Unique port names should be given to each project started within your application and for each instance of your application. For example, a fictional word processing package named GonzoWord with a basename of GWord should have an ARexx port of GWORD.1 when the first document is opened. A second document running concurrently should be given the port name of GWORD.2. If the user opened the GonzoWord program again without<br /> choosing QUIT in the first instance, that third document should have a port name of GWORD.3.<br /> <br /> === Command Shell ===<br /> <br /> Your application should allow the user to open a console window or command shell within your application's environment. There are two possible ways of handling this: you can provide a window that looks like a basic Shell window or one that looks like Workbench's Execute Command window.<br /> <br /> Fig 9.1: Workbench's Execute Command window.<br /> <br /> Fig 9.2: A sample ARexx command shell.<br /> <br /> Commands entered into this window would allow direct control of your application.<br /> <br /> As with any other type of window, your application should allow to user to snapshot the placement and size of the command shell.<br /> <br /> == Standard ARexx Commands ==<br /> <br /> The following commands are the minimum commands that your application should support. These commands are reserved across all applications - don't use these commands for any other functions.<br /> <br /> As listed here, the command template is named in bold on the first line. The definition for the command is on the following line, and that is followed by definitions of the options. If the options are self-explanatory, no definition is given.<br /> <br /> The commands are presented here in the same style as Shell commands discussed in Chapter 8. You should also use this style when you implement ARexx.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Your application should support at least these 15 ARexx commands.<br /> |}<br /> <br /> === Project-Related Commands ===<br /> <br /> ; NEW PORTNAME/K<br /> : NEW creates a new project and work area. This command should return the ARexx port name assigned to the project. PORTNAME is used to assign a specific port name to the project.<br /> <br /> ; CLEAR FORCE/S<br /> : This command clears the current project and its work area. FORCE suppresses the modified project requester.<br /> <br /> ; OPEN FILENAME/K,FORCE/S<br /> : This command opens the specified project into the current work area. If no FILENAME is given, prompt the user for a file name via a file requester. FORCE suppresses the modified project requester.<br /> <br /> ; SAVE ,<br /> : This command saves the current project to the current file name. If the project is unnamed, bring up the file requester so that the user can specify a file name.<br /> <br /> ; SAVEAS NAME/K<br /> : This command saves the current project to the specified file name. NAME specifies what the new project will be called. If no name is provided, it should bring up the file requester so that the user can specify the file name.<br /> <br /> ; CLOSE FORCE/S<br /> : This command closes the current project and window. FORCE suppresses the modified project requester.<br /> <br /> ; PRINT PROMPT/S<br /> : PRINT will print the specified object using the current settings. PROMPT provides a requester for use in setting print parameters.<br /> <br /> ; QUIT FORCE/S<br /> : QUIT stops the program. If the project was modified, the user should be prompted to save the work. FORCE suppresses the modified project requester.<br /> <br /> === Block-Related Commands ===<br /> <br /> ; CUT ,<br /> : CUT removes the currently selected block of information from the project and places it in the clipboard.<br /> <br /> ; COPY ,<br /> : COPY places a duplicate of the currently selected block of information into the clipboard.<br /> <br /> ; PASTE ,<br /> : PASTE puts the contents of the clipboard into the project - at the currently active point.<br /> <br /> ; ERASE FORCE/S<br /> : ERASE removes the currently selected block of information from the project. FORCE suppresses the &quot;Are you sure?&quot; requester.<br /> <br /> === Other Standard Commands ===<br /> <br /> ; HELP COMMAND,PROMPT/S<br /> : HELP provides access to information about your application. Information about things such as the supported functions and parameters required for<br /> functions should be readily available. When triggered from a non-graphical user interface, HELP should present the user with a text list of all the commands that the application supports. COMMAND presents the user with the list of options available for that command. PROMPT activates a graphical help system.<br /> <br /> ; FAULT /N<br /> : FAULT gives the user the text message assigned to the given error number. The text should be sent to the RESULT field. (See Returning Data section.)<br /> <br /> ; RX CONSOLE/S,ASYNC/S,COMMAND/F<br /> : RX allows the user to start an ARexx macro. CONSOLE indicates that a console (for default I/O) is needed. ASYNC indicates that the command should be run asynchronously. COMMAND sends whatever is typed on the rest of the line (usually a command) to ARexx for execution.<br /> <br /> === Other Common ARexx Commands ===<br /> <br /> These commands aren't applicable for every program, but please use them if your program provides this sort of functionality.<br /> <br /> ==== Cursor Positioning Commands ====<br /> <br /> ; GOTOLINE /N/A<br /> : This command moves the cursor to a specified line.<br /> <br /> ; GOTOCOLUMN /N/A<br /> : This command moves the cursor to a specified column.<br /> <br /> ; CURSOR UP/S/,DOWN/S,LEFT/S/RIGHT/S<br /> : CURSOR moves the cursor up, down, left or right a single line or column position.<br /> <br /> ; LINE /N/A<br /> : LINE accepts positive or negative arguments to move the cursor up or down relative to its current position.<br /> <br /> ; COLUMN /N/A<br /> : COLUMN accepts positive or negative arguments to move the cursor left or right relative to its current position.<br /> <br /> ; NEXT WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S<br /> : NEXT moves the cursor to the next word, sentence, paragraph or page.<br /> <br /> ; PREVIOUS WORD/S,SENTENCE/S,PARAGRAPH/S,PAGE/S<br /> : PREVIOUS moves the cursor to the previous word, sentence, paragraph or page.<br /> <br /> ; SETBOOKMARK /N<br /> : This command is used to remember a place in text. If the program supports multiple bookmarks, a number can be used to differentiate them.<br /> <br /> ; GOTOBOOKMARK /N<br /> : Move cursor to a bookmark. If the program supports multiple bookmarks, a number can be used to differentiate them.<br /> <br /> ; POSITION SOF/S,EOF/S,SOL/S,EOL/S,SOW/S,EOW/S,SOV/S,EOV/S<br /> : POSITION moves the cursor to the position specified by the argument. SOF moves it to the beginning of the file. EOF moves it to the end of the file. SOL moves it to the beginning of the current line. EOL moves it to the end of the current line. SOW moves it to the start of the current word. EOW moves it to the end of the current word. SOV moves it to the top of the current view. EOV moves it to the bottom of the current view.<br /> <br /> ==== Find and Replace Commands ====<br /> <br /> ; FIND TEXT/F<br /> : FIND searches for text that matches the specified string. If no string is specified on the command line, a requester should be presented allowing the user to enter a search string.<br /> <br /> ; FINDCHANGE ALL/S,PROMPT/S,FIND/K,CHANGE/K<br /> : PROMPT brings up a requester. FIND searches for the string specified after the keyword. CHANGE replaces the specified FIND string with the string specified after the CHANGE keyword.<br /> <br /> ; FINDNEXT ,<br /> : This moves the cursor to the next occurrence of the current search string without displaying a requester.<br /> <br /> ==== Other Text-Related Commands ====<br /> <br /> ; TEXT TEXT/F<br /> : This command can be used in ARexx macros, allowing text to be entered via a command and honoring word wrap, insertion or any other current modes.<br /> <br /> ; UPPERCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> ; LOWERCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> ; SWAPCASE CHAR/S,WORD/S,LINE/S,SENTENCE/S,PARAGRAPH/S<br /> : These three commands can be used if your program has the ability to swap the case of a letter or letters. The default switch should be WORD.<br /> <br /> ; FONT NAME/S,SIZE/N,ITALIC/S,BOLD/S,PLAIN/S,UNDERLINED/S<br /> : Use this command if your application supports font selection. A combination of the text style arguments should be allowed following the SIZE argument<br /> <br /> ; UNDO /N<br /> : This command reverts back to the last state of the project. If your program supports multiple levels of undo, a number should be recognized as an argument. The default should be one level of undo.<br /> <br /> ; REDO ,<br /> : For applications with multiple levels of UNDO, REDO will allow the user to undo the UNDO command. See Chapter 6 for more information.<br /> <br /> ==== Window-Related Commands ====<br /> <br /> ; MOVEWINDOW LEFTEDGE/N,TOPEDGE/N<br /> : This command should be used to change the position of a window. An argument of -1 means no change or don't care. Your application should either do the best job it can to move the window to the specified position, or return an error code.<br /> <br /> ; SIZEWINDOW WIDTH/N,HEIGHT/N<br /> : This command should be used to change the size of a window. An argument of -1 means no change or don't care. Your application should either do the best job it can to size the window or return an error code.<br /> <br /> ; CHANGEWINDOW LEFTEDGE/N,TOPEDGE/N,WIDTH/N,HEIGHT/N<br /> : This command is similar to MOVEWINDOW and SIZEWINDOW but the application should move and size the window as one operation. This is important to the user because moving a window to a new size and position can require as many as three commands, depending on the original<br /> position and size as well as the target position and size.<br /> <br /> ; WINDOWTOFRONT ,<br /> : This command moves a window to the front.<br /> <br /> ; WINDOWTOBACK ,<br /> : This command moves a window behind all others.<br /> <br /> ; ACTIVATEWINDOW ,<br /> : This command activates a window.<br /> <br /> ; ZOOMWINDOW ,<br /> : If your program supports zoom gadgets on your windows, use this command to make a window small.<br /> <br /> ; UNZOOMWINDOWS ,<br /> : If your program supports zoom gadgets on your windows, use this command to unshrink it.<br /> <br /> ==== Telecommunications Commands ====<br /> <br /> ; BAUD /N<br /> : This command sets the baud rate.<br /> <br /> ; PARITY EVEN/S,ODD/S,NONE/S<br /> : Use this command to set the parity.<br /> <br /> ; STOPBITS 1/S,0/S<br /> : Use this to set the stopbits.<br /> <br /> ; DUPLEX FULL/S,HALF/S<br /> : Use this to set the duplex mode.<br /> <br /> ; PROTOCOL /K<br /> : Use this command to set the protocol.<br /> <br /> ; SENDFILE NAME/K<br /> : Use this to start a file send. If the file name is omitted, the user should be presented with a file requester.<br /> <br /> ; CAPTURE NAME/K<br /> : Use this to open a capture buffer. If the file name is omitted, the user should be presented with a file requester.<br /> <br /> ; DIAL NUM/F<br /> : Use this command to dial a phone number.<br /> <br /> ; REDIAL ,<br /> : Use this to redial the last phone number.<br /> <br /> ; SEND ,<br /> : Use this to send a text string.<br /> <br /> ; WAIT ,<br /> : Use this to wait for a text string.<br /> <br /> ; TIMEOUT /N<br /> : Use this to set the timeout value (in seconds) for waits.<br /> <br /> ==== Miscellaneous Commands ====<br /> <br /> ; TEXTPEN /N<br /> ; BACKGROUNDPEN /N<br /> : Text-oriented applications that support the ability to set pen and paper colors can use these two commands. Valid values should be bounded by the screen's maximum number of colors rather than by existing Amiga hardware.<br /> <br /> ; LOCKGUI ,<br /> : This inhibits the graphical user interface of the application.<br /> <br /> ; UNLOCKGUI ,<br /> : This undoes LOCKGUI, allowing GUI events to resume.<br /> <br /> ; GETATTR OBJECT/A,NAME,FIELD,STEM/K,VAR/K<br /> : GETATTR obtains information about the attributes of an object. OBJECT specifies the object type to obtain information on. NAME specifies the name of the object. FIELD specifies which field should be checked for information. STEM (keyword) specifies that the information is to be placed in the following stem variable rather than the RESULT field. VAR (keyword) specifies that the information is to be returned in a simple variable.<br /> <br /> : In the above OBJECT argument, the user could be seeking information on the attributes of any of the following items:<br /> :* APPLICATION<br /> :* PROJECT &lt;name&gt;<br /> :* WINDOW &lt;name&gt;<br /> :* PROJECTS<br /> :* WINDOWS<br /> <br /> : If the destination variable is a stem variable, the following fields are recommended:<br /> <br /> :; APPLICATION<br /> :: would consist of the following nodes (an aspect of APPLICATION that the user can seek sub-information about):<br /> ::* '''.VERSION''' Contains the current version information for the application.<br /> ::* '''.SCREEN''' Contains the name of the screen that the application resides in (if the screen is public).<br /> <br /> :; PROJECTS.COUNT<br /> :: would contain the number of projects;<br /> :: '''PROJECTS.0''' through '''PROJECTS.n''' would contain the handle for the project.<br /> <br /> :; PROJECT<br /> :: would consist of the following nodes. Other variables could be added based on the type of application. For example, a text editor could add a variable called .LINES that would contain the number of lines in the project's file.<br /> :: '''.AREXX''' Contains the ARexx port assigned to the project.<br /> :: '''.FILENAME''' Contains the complete filename of the project.<br /> :: '''.PATH''' Contains the path portion of the filename.<br /> :: '''.FILE''' Contains the file portion of the filename.<br /> :: '''.CHANGES''' Contains the number of changes made to the project (since last save).<br /> :: '''.PRIORITY''' Priority at which the project's process is running. Allows the user to set the priority. For example, a 3-D modelling application could be working on several different projects at one time. Using this field, the user would be able to give more time to the most important project.<br /> <br /> :; WINDOWS.COUNT<br /> :: would contain the number of windows;<br /> :: '''WINDOWS.0''' through '''WINDOWS.n''' would contain the handle for the window.<br /> <br /> :; WINDOW<br /> :: would consist of the following nodes:<br /> :: '''.LEFT''' Left edge of the window.<br /> :: '''.TOP''' Top edge of the window.<br /> :: '''.WIDTH''' Width of the window.<br /> :: '''.HEIGHT''' Height of the window.<br /> :: '''.TITLE''' Window title.<br /> :: '''.MIN.WIDTH''' Minimum width of the window.<br /> :: '''.MIN.HEIGHT''' Minimum height of the window.<br /> :: '''.MAX.WIDTH''' Maximum width of the window.<br /> :: '''.MAX.HEIGHT''' Maximum height of the window.<br /> :: '''.SCREEN''' Name of the screen in which the window resides.<br /> <br /> ; SETATTR OBJECT/A,NAME,FIELD,STEM/K,VAR/K<br /> : SETATTR manipulates the aspects of an object.<br /> :* OBJECT specifies the object type to manipulate.<br /> :* NAME specifies the name of the object.<br /> :* FIELD specifies which field should be modified.<br /> :* STEM specifies that the information is to be extracted from the following stem variable.<br /> :* VAR specifies that the information is to be extracted from the following simple variable.<br /> <br /> ; WINDOW NAMES/M,OPEN/S,CLOSE/S,SNAPSHOT/S,ACTIVATE/S,MIN/S,MAX/S,FRONT/S,BACK/S<br /> : WINDOW allows the user to manipulate several aspects of the window.<br /> :* NAMES specifies a list of windows to manipulate. This command should support wildcard expansion.<br /> :* OPEN is used to indicate that the window(s) should be opened.<br /> :* CLOSE is used to indicate that the window(s) should be closed.<br /> :* SNAPSHOT records the current position and size of the named window(s).<br /> :* ACTIVATE will make the window the input focal point.<br /> :* MIN sets the named window(s) to the minimum size.<br /> :* MAX sets the named window(s) to the maximum size.<br /> :* FRONT places the named window(s) in front of other windows.<br /> :* BACK places the named window(s) behind all others.<br /> <br /> ; CMDSHELL OPEN/S,CLOSE/S<br /> : CMDSHELL opens a command shell so the user can interact directly with the application at a command level. CMDSHELL allows the user quick access to infrequently used functions or macros that are not bound to a key, menu or button.<br /> <br /> ; ACTIVATE ,<br /> : ACTIVATE starts the GUI of an application. This involves &quot;de-iconifying&quot; it, bringing its screen to the front, unzooming the main window (ie. expanding it to its previously set size) and making that window active.<br /> <br /> ; DEACTIVATE ,<br /> : This command shuts down the GUI of an application. DEACTIVATE restores the GUI to the state it was in before receiving the ACTIVATE command.<br /> <br /> ; DISABLE NAMES/M<br /> : This command disables a command or list of commands. DISABLE should also disable any user interface items to which the command is bound. For example, if a command is bound to a gadget, then that gadget should be disabled as well. This command should support pattern expansion.<br /> <br /> ; ENABLE NAMES/M<br /> : ENABLE makes a command or a list of commands available to the user. ENABLE should also enable the GUI items to which they are bound (see DISABLE). This command should support pattern expansion.<br /> <br /> ; LEARN FILE/K,STOP/S<br /> : LEARN allows the application to build an ARexx macro consisting of the actions the user performs.<br /> :* FILE specifies the name of the file in which the user will save the commands.<br /> :* STOP tells the application to stop learning.<br /> <br /> ; ALIAS NAME/A,COMMAND/F<br /> : ALIAS assigns a user-definable name to a function and its options.<br /> :* NAME specifies what the new command will be called.<br /> :* COMMAND represents the rest of the line where the command and whatever options are to be bound to that command are entered.<br /> <br /> ; SELECT NAME/A,FROM/K,NEXT/S,PREVIOUS/S,TOP/S,BOTTOM/S<br /> : SELECT is used to select an object from a list of similar objects. The main use for this command is to allow the user to select the current project from a list of projects. Returns the name of the current project.<br /> :* FROM specifies the list from which the user can select. The default should be the main project list.<br /> :* NEXT specifies that the next object on the list should be selected.<br /> :* PREVIOUS specifies that the previous object on the list should be selected.<br /> :* TOP specifies that the first object on the list should be selected.<br /> :* BOTTOM specifies that the last object on the list should be selected.<br /> <br /> === Advanced ARexx Commands ===<br /> <br /> Increasingly, programmers are supporting ARexx commands that allow users to add their own ARexx scripts or even to modify the program's interface. If you would like your program to support any of the following functions, please use these standard commands:<br /> <br /> ; BUTTON NAME/A,LABEL/K,WINDOW/K,PROMPT/S,CMD/F<br /> : Some applications could set aside a number of action gadgets (buttons) to which the user can assign ARexx scripts. The BUTTON command would allow the user to modify these special gadgets.<br /> :* NAME specifies the name of the button to be edited.<br /> :* CMD specifies the command name assigned to the button.<br /> :* LABEL specifies the text label assigned to the button.<br /> :* WINDOW specifies the window in which the button belongs. This should default to the active or last active Intuition window for that application.<br /> :* PROMPT activates a window in which the user can graphically edit the button.<br /> <br /> ; KEYBOARD KEY/A,WINDOW/K,GLOBAL/S,HOTKEY/S,PROMPT/S,CMD/F<br /> : KEYBOARD allows commands to be tied to keystrokes.<br /> :* KEY specifies which keystroke to edit.<br /> :* CMD specifies what command will be assigned to that keystroke.<br /> :* WINDOW specifies which window the hotkey will perform in. It should default to the active or last active Intuition window for that application.<br /> :* GLOBAL indicates that the keystroke will operate when any of the application's windows are active.<br /> :* HOTKEY indicates that the keystroke will operate regardless of what window or application is active.<br /> :* PROMPT gives the user a window in which to edit the keystroke.<br /> <br /> ; MENU NAME/A,LABEL/K,MENU/K,WINDOW/K,PROMPT/K,CMD/F<br /> : MENU provides a means to add, edit or delete a menu item. MENU also allows access to the function that the menu item triggers.<br /> :* NAME specifies which menu item the user will edit.<br /> :* CMD speciifies what command will be assigned to the menu item.<br /> :* LABEL assigns a text label to the menu item.<br /> :* MENU specifies which menu strip the new or edited item will be added to.<br /> :* WINDOW specifies which window the new or edited menu belongs in. This should default to the active or last active Intuition window for that application.<br /> :* PROMPT (switch) activates a window in which the user can graphically edit the menu item.<br /> <br /> ; NOP ,<br /> : The NOP command is a special do nothing command (short for No OPeration) that is often extremely useful. For example, if your program supports custom keyboard<br /> definitions, it is often necessary to disable some keys when writing custom macros.<br /> <br /> ==== Programmable Requesters ====<br /> <br /> When a user writes an ARexx macro, it is often very useful to be able to bring up a file requester, or other type of requester, as part of the macro. If your program supports programmatic access to its requesters, the following commands should be used:<br /> <br /> ; REQUESTFILE TITLE/K,PATH/K,FILE/K,PATTERN/K<br /> : This command brings up a file requester. The user can specify the title, path and file name. If any of these arguments are omitted, they should be filled in with some defaults (eg. a default title and empty strings for path and/or file name).<br /> : The path and file name should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTSTRING PROMPT/K,DEFAULT/K<br /> : This command brings up a string entry requester. The user can specify a prompt string and a default string to be placed in the string editing gadget. Like the REQUESTFILE command, defaults should be provided if arguments are omitted.<br /> : The string entered should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTNUMBER PROMPT/K,DEFAULT/K<br /> : This command brings up a number entry requester. The user can specify a prompt string and a default number to be placed in the string editing gadget. Like the REQUESTFILE command, defaults should be provided if arguments are omitted.<br /> : The number entered should be returned in RESULT. A warning should be returned if the user cancels the requester.<br /> <br /> ; REQUESTRESPONSE TITLE/K,PROMPT/K<br /> : This command bring up a query requester. The user can specify a prompt string and possibly text for the OK and CANCEL gadgets if your program wants to support these additional options.<br /> : A warning should be returned if the user selects CANCEL.<br /> <br /> ; REQUESTNOTIFY PROMPT/S<br /> : This command brings up a notification requester that can only be satisfied with an OK type of response.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Workbench&diff=3545 UI Style Guide Workbench 2012-07-13T08:07:46Z <p>Alexandre Balaban: /* Preferences */ Changed link to Preferences section</p> <hr /> <div>In a way, Workbench is just another program - but as the default interface on the Amiga, it's one program that many users will pass through on their way to your application. Although it has other facets and functions, it's the interface between your program and Workbench that is of concern in this chapter.<br /> <br /> Fig 7.1: The workbench screen.<br /> <br /> == Icons ==<br /> <br /> Icons are pictorial representations of directories, files, applications, objects or actions. Your program should have icons for anything the user can access including the main program itself, documentation files, and any tools that may accompany the program.<br /> <br /> === The .info File ===<br /> <br /> The icon imagery is found in a file bearing the suffix &quot;.info&quot;. For example, the icon for a data file called &quot;myletter&quot; would be found, along with some other information, in the file &quot;myletter.info&quot;.<br /> <br /> === Icon Design ===<br /> <br /> A good icon quickly communicates the function it represents. The Calculator icon and the SetMap icon are good examples.<br /> <br /> Fig 7.2: The Calculator and SetMap icons.<br /> <br /> ==== Size ====<br /> <br /> Icons should be small. The maximum recommended size for an icon is 80 pixels wide by 40 pixels high. Large icons take up valuable screen and disk space, make for an unprofessional Workbench look and, in general, can be just plain annoying.<br /> <br /> ==== The 3-D Look ====<br /> <br /> Icons should be designed with the light source coming from the upper left-hand corner. They should be viewable in one bitplane as well as two.<br /> <br /> ==== Text in Icons ====<br /> <br /> Before you use words in an icon, think about how extensive a distribution you would like to see your product achieve. It's better to have an icon communicate through symbolism than language.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Before using words in an icon, think about how wide a distribution you envision for you product.<br /> |}<br /> <br /> === Icon Types ===<br /> <br /> There are five types of icons on the Workbench: disk icons, drawer icons, trashcan icons, tool icons and project icons. Of these, only tool icons and project icons bear discussion here.<br /> <br /> ==== Tool Icons ====<br /> <br /> A tool icon represents an executable file such as an application. A double-click on a tool icon will run the application. The look of tool icons varies from application to application.<br /> <br /> ==== Project Icons ====<br /> <br /> A project icon represents a data file. Typically, a double-click on this icon will cause the application that created the data to run and automatically load this data.<br /> <br /> The look of a project icon is usually determined by the application that created it and by the type of data it represents. However there is also a default project icon included in the system.<br /> <br /> Fig 7.3: The default project icon.<br /> <br /> ==== Create Icons? ====<br /> <br /> When your application creates a data file, it should, by default, create a .info file to go with it. Many users are accustomed to accessing their projects via an icon.<br /> <br /> Create the icon after the user successfully saves the project. This will prevent the possibility of project-less icons in the system.<br /> <br /> Your application, however, should allow the user the option of saving files without creating icons. The recommended way of doing this is to have an item in the Settings menu called Create Icons?. This item should be enabled by default. See [[UI Style Guide Menus]] for more information.<br /> <br /> ==== Positioning ====<br /> <br /> New project icons should never be saved in a specific position. Rather they should be positioned algorithmically by Workbench (see the Workbench flag NO_ICON_POSITION). In the same vein, if the imagery of an existing icon is changed (e.g. the application creates a reduced version of the project for use as the icon imagery) the icon should be saved without a specific position.<br /> <br /> == Argument Passing ==<br /> <br /> To make the common operation of starting up programs more powerful, the Amiga provides ways of passing along more specific information to the application. This process of providing additional information to a program that's about to run is known as argument passing.<br /> <br /> When using the Shell, the system is straightforward. The user runs an application by typing its name along with additional information such as the name of a file to operate on and any command options. The entire command line is then passed to the program as an argument.<br /> <br /> The Workbench also provides means of passing arguments - but it is tied to icons instead of lines of type.<br /> <br /> === Tool Types and Default Tool ===<br /> <br /> Applications started from Workbench receive startup information from the system in the form of a WBStartup message. The WBStartup message can be used to get various Workbench arguments including those found in the Tool Types and Default Tool fields of the icon the user clicked on.<br /> <br /> ==== Tool Types ====<br /> <br /> If you click once on any tool icon and choose &quot;Information...&quot; from the Workbench's Icon menu, you should see a requester with a field for Tool Types.<br /> <br /> Fig 7.4: The Information requester for a tool icon.<br /> <br /> Arguments placed in this field take the form KEYWORD=value. KEYWORD is an argument name specified by your application, and value is a number or string that should be assigned to that argument.<br /> <br /> For instance, the Tool Types field of a text display program might be set to LINES=20 to indicate how many lines to display at once.<br /> <br /> Normally, the Tool Types information is filled in when the .info file is created; subsequent operations only read this information.<br /> <br /> ==== Default Tool ====<br /> <br /> The Information requester for a project icon is different than that of a tool icon - it has both a Default Tool field and a Tool Types field. The Default Tool field tells the system which application is used to edit the project. When a user double-clicks on a project icon, the application specified in the Default Tool field gets run and the data is passed to that application via the WBStartup message.<br /> <br /> Fig 7.5: An Information requester for a project icon.<br /> <br /> ==== Altering Icons ====<br /> <br /> If your application needs to alter an icon for some reason, change only the things you intend to change and preserve the rest. If your application needs to change the Tool Types field, for instance, leave the imagery, position and Default Tool field alone.<br /> <br /> More important, change only the Tool Types entries relevant to your application - do not rewrite Tool Types from scratch for an existing project. If the icon has a Tool Types entry that your application does not recognize, that entry should be preserved.<br /> <br /> ==== Tool Types and Networks ====<br /> <br /> You should support project-specific Tool Types arguments. With the advent of networked Amigas sharing the same applications, this becomes increasingly important. Tool Types arguments written to specific projects will allow networked users to override Tool Types arguments written to the application's tool icon - arguments that have probably been set for the least common denominator.<br /> <br /> ==== Standard Tool Types Arguments ====<br /> <br /> Some Tool Types arguments are already used in the system. If they apply to your application, support them; if not, take care that your Tool Types arguments don't conflict with these. Listed below are some of the more common Tool Types arguments found in the system:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Take care that your Tool Types arguments don't conflict with those listed here.<br /> |}<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Tool Type<br /> ! Notes<br /> |-<br /> | WINDOW=CON:&lt;window spec&gt;<br /> |<br /> |-<br /> | DONOTWAIT<br /> | Don't wait for return; used by wbstartup<br /> |-<br /> | TOOLPRI=&lt;priority&gt;<br /> STARTPRI=&lt;priority (-127 to 128)<br /> | Used to set your program's priority<br /> |-<br /> | PUBSCREEN=&lt;name&gt;<br /> | The name of the public screen to open on<br /> |-<br /> | STARTUP=&lt;name&gt;<br /> | ARexx script to run at startup time<br /> |-<br /> | PORTNAME=&lt;name&gt;<br /> | Name to assign to your application's ARexx port; overrides default naming system<br /> |-<br /> | SETTINGS=&lt;name&gt;<br /> | Allows a user to specify a settings file<br /> |-<br /> | UNIT=&lt;number&gt;<br /> | Device or unit number<br /> |-<br /> | DEVICE=&lt;parallel/serial&gt;<br /> | The name of the device to use<br /> |-<br /> | FILE=&lt;file pathname&gt;<br /> |<br /> |-<br /> | WAIT=&lt;number (of seconds)&gt;<br /> |<br /> |-<br /> | PREFS=&lt;prefstype&gt;<br /> |<br /> |-<br /> | CX_POPUP=&lt;yes/no&gt;<br /> |<br /> |-<br /> | CX_POPKEY=&lt;CX key specifier&gt;<br /> |<br /> |-<br /> | CX_PRIORITY=&lt;CX priority level&gt;<br /> |<br /> |-<br /> | &lt;CX fkey spec&gt;=&lt;CX string spec&gt;<br /> |<br /> |}<br /> <br /> === The Apps ===<br /> <br /> Other facilities also exist in Workbench that allow an application to get more information while the application is already running. These are known as AppWindow, AppIcon and AppMenu.<br /> <br /> For example, a text editor's windows may function as AppWindows. A user would be able to drag the icon of a file into the window and that file would be loaded automatically.<br /> <br /> AppWindows, AppIcons and AppMenus are aimed at the user. By using techniques that are totally graphic-oriented (Tool Types arguments still eventually come down to a line of type), the Apps bring the power of argument passing more into line with the &quot;point-and-click&quot; metaphor.<br /> <br /> ==== AppWindows ====<br /> <br /> An AppWindow is a special kind of Workbench window that allows the user to drag icons into it. It's basically a graphical alternative to a file requester.<br /> <br /> Applications that set up an AppWindow will receive a message from Workbench whenever the user moves an icon into that AppWindow. The message contains the name of the file or directory that the icon represents.<br /> <br /> For instance, Workbench's IconEdit is an AppWindow with three different areas which users can drag icons into - each with a different purpose. When the user drags an icon into the large box, the icon is loaded as a project. When an icon is dragged into the box labelled &quot;Normal&quot;, its image is used for the normal (not selected) image. Likewise for an icon that is dragged into the box labelled &quot;Selected&quot; - its image becomes the activated image for the icon.<br /> <br /> Fig 7.6: IconEdit is an example of an AppWindow. It has three different drop areas which do three different things.<br /> <br /> An AppWindow will often use icon drop box gadgets to indicate the active area where the user may drop an icon. Note: IconEdit did not follow this convention because it was more important to indicate the function of each area by its imagery (e.g. the &quot;normal&quot; area can be clicked on, etc.).<br /> <br /> The window should activate when an icon is dragged into it.<br /> <br /> AppWindows only work when your application is running on the Workbench screen. This makes sense because you need to be able to drag icons from Workbench to the AppWindow and draggable objects can't be dragged across screens. If the user opts to run your application on a screen other than Workbench, set your AppWindows so they will revert to AppMenus (see below).<br /> <br /> As a general rule, AppWindows are appropriate when your application needs to have a window anyway.<br /> <br /> ==== AppIcons ====<br /> <br /> An AppIcon is similar an AppWindow - it allows the user to pass a graphical argument to a running application. The only difference is that AppWindows use a window to accept the argument and AppIcons use an icon.<br /> <br /> The image for an AppIcon should give some indication what operations it supports; ie. whether it represents an iconized application or supports dropped projects. Avoid vague imagery.<br /> <br /> AppIcons are useful for programs that operate in the background with little other user interaction - a print spooler is a good example.<br /> <br /> Double-clicking on an AppIcon should normally open a window offering information and controls. For example, a print spooler could open a status window in which the user could rename, abort or re-order the things he sent to be printed.<br /> <br /> ==== AppMenus ====<br /> <br /> An AppMenu allows your application to add a custom menu item to the Tools menu on Workbench. An application that sets up an AppMenu item will receive a message from Workbench whenever the user picks that item from the Workbench menu.<br /> <br /> === Preferences ===<br /> <br /> Applications can also get arguments from Preferences. Via Preferences, the user can graphically set up the system defaults to suit his taste and needs.<br /> <br /> Through Preferences, the user can control such things as screen fonts, colors and other global information that your application should respect.<br /> <br /> You can create a preference editor to handle the defaults used by your application. See [[UI Style Guide Preferences]] for more information.</div> Alexandre Balaban https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Workbench&diff=3544 UI Style Guide Workbench 2012-07-13T08:06:36Z <p>Alexandre Balaban: /* Create Icons? */ Changed link to Menus section</p> <hr /> <div>In a way, Workbench is just another program - but as the default interface on the Amiga, it's one program that many users will pass through on their way to your application. Although it has other facets and functions, it's the interface between your program and Workbench that is of concern in this chapter.<br /> <br /> Fig 7.1: The workbench screen.<br /> <br /> == Icons ==<br /> <br /> Icons are pictorial representations of directories, files, applications, objects or actions. Your program should have icons for anything the user can access including the main program itself, documentation files, and any tools that may accompany the program.<br /> <br /> === The .info File ===<br /> <br /> The icon imagery is found in a file bearing the suffix &quot;.info&quot;. For example, the icon for a data file called &quot;myletter&quot; would be found, along with some other information, in the file &quot;myletter.info&quot;.<br /> <br /> === Icon Design ===<br /> <br /> A good icon quickly communicates the function it represents. The Calculator icon and the SetMap icon are good examples.<br /> <br /> Fig 7.2: The Calculator and SetMap icons.<br /> <br /> ==== Size ====<br /> <br /> Icons should be small. The maximum recommended size for an icon is 80 pixels wide by 40 pixels high. Large icons take up valuable screen and disk space, make for an unprofessional Workbench look and, in general, can be just plain annoying.<br /> <br /> ==== The 3-D Look ====<br /> <br /> Icons should be designed with the light source coming from the upper left-hand corner. They should be viewable in one bitplane as well as two.<br /> <br /> ==== Text in Icons ====<br /> <br /> Before you use words in an icon, think about how extensive a distribution you would like to see your product achieve. It's better to have an icon communicate through symbolism than language.<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Before using words in an icon, think about how wide a distribution you envision for you product.<br /> |}<br /> <br /> === Icon Types ===<br /> <br /> There are five types of icons on the Workbench: disk icons, drawer icons, trashcan icons, tool icons and project icons. Of these, only tool icons and project icons bear discussion here.<br /> <br /> ==== Tool Icons ====<br /> <br /> A tool icon represents an executable file such as an application. A double-click on a tool icon will run the application. The look of tool icons varies from application to application.<br /> <br /> ==== Project Icons ====<br /> <br /> A project icon represents a data file. Typically, a double-click on this icon will cause the application that created the data to run and automatically load this data.<br /> <br /> The look of a project icon is usually determined by the application that created it and by the type of data it represents. However there is also a default project icon included in the system.<br /> <br /> Fig 7.3: The default project icon.<br /> <br /> ==== Create Icons? ====<br /> <br /> When your application creates a data file, it should, by default, create a .info file to go with it. Many users are accustomed to accessing their projects via an icon.<br /> <br /> Create the icon after the user successfully saves the project. This will prevent the possibility of project-less icons in the system.<br /> <br /> Your application, however, should allow the user the option of saving files without creating icons. The recommended way of doing this is to have an item in the Settings menu called Create Icons?. This item should be enabled by default. See [[UI Style Guide Menus]] for more information.<br /> <br /> ==== Positioning ====<br /> <br /> New project icons should never be saved in a specific position. Rather they should be positioned algorithmically by Workbench (see the Workbench flag NO_ICON_POSITION). In the same vein, if the imagery of an existing icon is changed (e.g. the application creates a reduced version of the project for use as the icon imagery) the icon should be saved without a specific position.<br /> <br /> == Argument Passing ==<br /> <br /> To make the common operation of starting up programs more powerful, the Amiga provides ways of passing along more specific information to the application. This process of providing additional information to a program that's about to run is known as argument passing.<br /> <br /> When using the Shell, the system is straightforward. The user runs an application by typing its name along with additional information such as the name of a file to operate on and any command options. The entire command line is then passed to the program as an argument.<br /> <br /> The Workbench also provides means of passing arguments - but it is tied to icons instead of lines of type.<br /> <br /> === Tool Types and Default Tool ===<br /> <br /> Applications started from Workbench receive startup information from the system in the form of a WBStartup message. The WBStartup message can be used to get various Workbench arguments including those found in the Tool Types and Default Tool fields of the icon the user clicked on.<br /> <br /> ==== Tool Types ====<br /> <br /> If you click once on any tool icon and choose &quot;Information...&quot; from the Workbench's Icon menu, you should see a requester with a field for Tool Types.<br /> <br /> Fig 7.4: The Information requester for a tool icon.<br /> <br /> Arguments placed in this field take the form KEYWORD=value. KEYWORD is an argument name specified by your application, and value is a number or string that should be assigned to that argument.<br /> <br /> For instance, the Tool Types field of a text display program might be set to LINES=20 to indicate how many lines to display at once.<br /> <br /> Normally, the Tool Types information is filled in when the .info file is created; subsequent operations only read this information.<br /> <br /> ==== Default Tool ====<br /> <br /> The Information requester for a project icon is different than that of a tool icon - it has both a Default Tool field and a Tool Types field. The Default Tool field tells the system which application is used to edit the project. When a user double-clicks on a project icon, the application specified in the Default Tool field gets run and the data is passed to that application via the WBStartup message.<br /> <br /> Fig 7.5: An Information requester for a project icon.<br /> <br /> ==== Altering Icons ====<br /> <br /> If your application needs to alter an icon for some reason, change only the things you intend to change and preserve the rest. If your application needs to change the Tool Types field, for instance, leave the imagery, position and Default Tool field alone.<br /> <br /> More important, change only the Tool Types entries relevant to your application - do not rewrite Tool Types from scratch for an existing project. If the icon has a Tool Types entry that your application does not recognize, that entry should be preserved.<br /> <br /> ==== Tool Types and Networks ====<br /> <br /> You should support project-specific Tool Types arguments. With the advent of networked Amigas sharing the same applications, this becomes increasingly important. Tool Types arguments written to specific projects will allow networked users to override Tool Types arguments written to the application's tool icon - arguments that have probably been set for the least common denominator.<br /> <br /> ==== Standard Tool Types Arguments ====<br /> <br /> Some Tool Types arguments are already used in the system. If they apply to your application, support them; if not, take care that your Tool Types arguments don't conflict with these. Listed below are some of the more common Tool Types arguments found in the system:<br /> <br /> {| class=&quot;wikitable&quot;<br /> | Take care that your Tool Types arguments don't conflict with those listed here.<br /> |}<br /> <br /> {| class=&quot;wikitable&quot;<br /> ! Tool Type<br /> ! Notes<br /> |-<br /> | WINDOW=CON:&lt;window spec&gt;<br /> |<br /> |-<br /> | DONOTWAIT<br /> | Don't wait for return; used by wbstartup<br /> |-<br /> | TOOLPRI=&lt;priority&gt;<br /> STARTPRI=&lt;priority (-127 to 128)<br /> | Used to set your program's priority<br /> |-<br /> | PUBSCREEN=&lt;name&gt;<br /> | The name of the public screen to open on<br /> |-<br /> | STARTUP=&lt;name&gt;<br /> | ARexx script to run at startup time<br /> |-<br /> | PORTNAME=&lt;name&gt;<br /> | Name to assign to your application's ARexx port; overrides default naming system<br /> |-<br /> | SETTINGS=&lt;name&gt;<br /> | Allows a user to specify a settings file<br /> |-<br /> | UNIT=&lt;number&gt;<br /> | Device or unit number<br /> |-<br /> | DEVICE=&lt;parallel/serial&gt;<br /> | The name of the device to use<br /> |-<br /> | FILE=&lt;file pathname&gt;<br /> |<br /> |-<br /> | WAIT=&lt;number (of seconds)&gt;<br /> |<br /> |-<br /> | PREFS=&lt;prefstype&gt;<br /> |<br /> |-<br /> | CX_POPUP=&lt;yes/no&gt;<br /> |<br /> |-<br /> | CX_POPKEY=&lt;CX key specifier&gt;<br /> |<br /> |-<br /> | CX_PRIORITY=&lt;CX priority level&gt;<br /> |<br /> |-<br /> | &lt;CX fkey spec&gt;=&lt;CX string spec&gt;<br /> |<br /> |}<br /> <br /> === The Apps ===<br /> <br /> Other facilities also exist in Workbench that allow an application to get more information while the application is already running. These are known as AppWindow, AppIcon and AppMenu.<br /> <br /> For example, a text editor's windows may function as AppWindows. A user would be able to drag the icon of a file into the window and that file would be loaded automatically.<br /> <br /> AppWindows, AppIcons and AppMenus are aimed at the user. By using techniques that are totally graphic-oriented (Tool Types arguments still eventually come down to a line of type), the Apps bring the power of argument passing more into line with the &quot;point-and-click&quot; metaphor.<br /> <br /> ==== AppWindows ====<br /> <br /> An AppWindow is a special kind of Workbench window that allows the user to drag icons into it. It's basically a graphical alternative to a file requester.<br /> <br /> Applications that set up an AppWindow will receive a message from Workbench whenever the user moves an icon into that AppWindow. The message contains the name of the file or directory that the icon represents.<br /> <br /> For instance, Workbench's IconEdit is an AppWindow with three different areas which users can drag icons into - each with a different purpose. When the user drags an icon into the large box, the icon is loaded as a project. When an icon is dragged into the box labelled &quot;Normal&quot;, its image is used for the normal (not selected) image. Likewise for an icon that is dragged into the box labelled &quot;Selected&quot; - its image becomes the activated image for the icon.<br /> <br /> Fig 7.6: IconEdit is an example of an AppWindow. It has three different drop areas which do three different things.<br /> <br /> An AppWindow will often use icon drop box gadgets to indicate the active area where the user may drop an icon. Note: IconEdit did not follow this convention because it was more important to indicate the function of each area by its imagery (e.g. the &quot;normal&quot; area can be clicked on, etc.).<br /> <br /> The window should activate when an icon is dragged into it.<br /> <br /> AppWindows only work when your application is running on the Workbench screen. This makes sense because you need to be able to drag icons from Workbench to the AppWindow and draggable objects can't be dragged across screens. If the user opts to run your application on a screen other than Workbench, set your AppWindows so they will revert to AppMenus (see below).<br /> <br /> As a general rule, AppWindows are appropriate when your application needs to have a window anyway.<br /> <br /> ==== AppIcons ====<br /> <br /> An AppIcon is similar an AppWindow - it allows the user to pass a graphical argument to a running application. The only difference is that AppWindows use a window to accept the argument and AppIcons use an icon.<br /> <br /> The image for an AppIcon should give some indication what operations it supports; ie. whether it represents an iconized application or supports dropped projects. Avoid vague imagery.<br /> <br /> AppIcons are useful for programs that operate in the background with little other user interaction - a print spooler is a good example.<br /> <br /> Double-clicking on an AppIcon should normally open a window offering information and controls. For example, a print spooler could open a status window in which the user could rename, abort or re-order the things he sent to be printed.<br /> <br /> ==== AppMenus ====<br /> <br /> An AppMenu allows your application to add a custom menu item to the Tools menu on Workbench. An application that sets up an AppMenu item will receive a message from Workbench whenever the user picks that item from the Workbench menu.<br /> <br /> === Preferences ===<br /> <br /> Applications can also get arguments from Preferences. Via Preferences, the user can graphically set up the system defaults to suit his taste and needs.<br /> <br /> Through Preferences, the user can control such things as screen fonts, colors and other global information that your application should respect.<br /> <br /> You can create a preference editor to handle the defaults used by your application. See Chapter 12 for more information.</div> Alexandre Balaban