Copyright (c) Hyperion Entertainment and contributors.
Workbench Library
This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally. |
Contents
Workbench and Icon Library
Workbench is the graphic user interface to the Amiga file system that uses symbols called icons to represent disks, directories and files. This chapter shows how to use Workbench and its two support libraries workbench.library and icon.library.
Workbench is both a system program and a screen. Normally it is the first thing the user sees when the machine is booted providing a friendly operating environment for launching applications and performing other important system activities like navigating through the Amiga's hierarchical filing system.
All application programs should be compatible with Workbench. There are only two things you need to know to do this: how to make icons for your application, data files and directories; and how to get arguments if your application is launched from Workbench.
The Info File
The iconic representation of Amiga filing system objects is implemented through .info files. In general, for each file, disk or directory that is visible in the Workbench environment, there is an associated .info file which contains the icon imagery and other information needed by Workbench.
Icons are associated with a particular file or directory by name. For example, the icon for a file named myapp would be stored in a .info file named myapp.info in the same directory.
To make your application program accessible (and visible) in the Workbench environment, you need only supply a .info file with the appropriate name and type. The are four main types of icons (and .info files) used to represent Amiga filing system objects.
Basic Workbench Icon Types
Icons can be created with the IconEdit program (in the Tools directory of the Extras disk), or by copying an existing .info file of the correct type. Icons can also be created under program control with PutDiskObject(). See the discussion of the icon library functions below for more on this.
For an executable file the icon type must be set to tool. For a data file the icon type must be set to project. Create icons for your application disk and directories too. For a directory, the icon is stored in a .info file at the same level where the directory name appears (not in the directory itself). The icon type should be set to drawer. The icon for a disk should always be stored in a file named disk.info at the root level directory of the disk. The icon type should be set to disk. (The icon type can be set and the icon imagery edited with the IconEdit program.)
Workbench Environment
On the Amiga there are at least two ways to start a program running:
- By activating a tool or project icon in Workbench (an icon is activated by pointing to it with the mouse and double-clicking the mouse select button.)
- By typing the name of an executable file at the Shell (also known as the CLI or Command Line Interface)
In the Workbench environment, a program is run as a separate process. A process is simply a task with additional information needed to use DOS library.
By default, a Workbench program does not have a window to which its output will go. Therefore, stdin, stdout and stderr do not point to legal file handles. This means you cannot use stdio functions such as printf() if your program is started from Workbench unless you first set up a stdio window.
Some compilers have options or defaults to provide a stdio window for programs started from Workbench. Applications can use an auto console window for stdio when started from Workbench by opening "CON:0/0/640/200/auto/close/wait" as a file. An auto console window will only open if stdio input or output occurs. This can also be handled in the startup code module that comes with your compiler.
Argument Passing in Workbench
Applications started from Workbench receive arguments in the form of a WBStartup structure. This is similar to obtaining arguments from a command line interface through argc and argv. The WBStartup message contains an argument count and a pointer to a list of file and directory names.
One Argument
A program started by activating its tool icon gets one argument in the WBStartup message: the name of the tool itself.
Two Arguments
All project icons (data files) have a default tool field associated with them that tells Workbench which application tool to run in order to operate on the data that the icon represents. When the user activates a project icon, Workbench runs the application specified in the default tool field passing it two arguments in the WBStartup message: the name of the tool and the project icon that the user activated.
Multiple Arguments
With extended select, the user can activate many icons at once. (Extended select means the user holds down the Shift key while clicking the mouse select button once on each icon in a group, double-clicking on the last icon.) If one of the icons in a group activated with extended select is an application tool, Workbench runs that application passing it the name of all the other icons in the group. This allows the user to start an application with multiple project files as arguments. If none of the icons in a group activated with extended select is a tool icon, then Workbench looks in the default tool field of each icon in the order they were selected and runs the first tool it finds.
WBStartup Message
When Workbench loads and starts a program, its sends the program a WBStartup message containing the arguments as summarized above. Normally, the startup code supplied with your compiler will place a pointer to WBStartup in argv for you, set argc to zero and call your program.
The WBStartup message, whose structure is outlined in <workbench/startup.h>, has the following structure elements:
struct WBStartup { struct Message sm_Message; /* a standard message structure */ struct MsgPort * sm_Process; /* the process descriptor for you */ BPTR sm_Segment; /* a descriptor for your code */ LONG sm_NumArgs; /* the number of elements in ArgList */ char * sm_ToolWindow; /* reserved for future use */ struct WBArg * sm_ArgList; /* the arguments themselves */ };
The fields of the WBStartup structure are used as follows.
- sm_Message
- A standard Exec message. The reply port is set to the Workbench.
- sm_Process
- The process descriptor for the tool (as returned by CreateProcess())
- sm_Segment
- The loaded code for the tool (returned by LoadSeg())
- sm_NumArgs
- The number of arguments in sm_ArgList
- sm_ToolWindow
- Reserved (not currently passed in startup message)
- sm_ArgList
- This is the argument list itself. It is a pointer to an array of WBArg structures with sm_NumArgs elements.
Workbench arguments are passed as an array of WBArg structures in the sm_ArgList field of WBStartup. The first WBArg in the list is always the tool itself. If multiple icons have been selected when a tool is activated, the selected icons are passed to the tool as additional WBArgs. If the tool was derived from a default tool, the project will be the second WBArg. If extended select was used, arguments other than the tool are passed in the order of selection; the first icon selected will be first (after the tool), and so on.
Each argument is a struct WBArg and has two parts: wa_Name and wa_Lock.
struct WBArg { BPTR wa_Lock; /* a lock descriptor */ BYTE * wa_Name; /* a string relative to that lock */ };
The wa_Name element is the name of an AmigaDOS filing system object. The wa_Name field of the first WBArg is always the name of your program and the wa_Lock field is an AmigaDOS Lock on the directory where your program is stored.
If your program was started by activating a project icon, then you get a second WBarg with the wa_Name field containing the file name of the project and the wa_Lock containing an AmigaDOS Lock on the directory where the project file is stored.
If your program was started through extended select, then you get one WBArg for each icon in the selected group in the order they were selected. The wa_Name field contains the file name corresponding to each icon unless the icon is for a directory, disk, or the Trashcan in which case the wa_Name is set to NULL. The wa_Lock field contains an AmigaDOS Lock on the directory where the file is stored. (For disk or drawer icons the wa_Lock is a lock on the directory represented by the icon. Or, wa_Lock may be NULL if the icon type does not support locks.)
Workbench Locks Belong to Workbench |
---|
You must never call UnLock() on a wa_Lock. These locks belong to Workbench, and Workbench will UnLock() them when the WBStartup message is replied by your startup code. You must also never UnLock() your program's initial current directory lock (i.e., the lock returned by an initial CurrentDir() call). The classic symptom caused by unlocking Workbench locks is a system hang after your program exits, even though the same program exits with no problems when started from the Shell. |
You should save the lock returned from an initial CurrentDir(), and CurrentDir() back to it before exiting. In the Workbench environment, depending on your startup code, the current directory will generally be set to one of the wa_Locks. By using CurrentDir(wa_Lock) and then referencing wa_Name, you can find, read, and modify the files that have been passed to your program as WBArgs.
Example of Parsing Workbench Arguments
The following example will display all WBArgs if started from Workbench, and all Shell arguments if started from the Shell.
/* prargs.c ** ** PrArgs.c - This program prints all Workbench or Shell (CLI) arguments. */ #include <exec/types.h> #include <workbench/startup.h> #include <proto/dos.h> int main(int argc, char **argv) { struct WBStartup *argmsg; struct WBArg *wb_arg; int32 ktr; BPTR olddir; BPTR outFile; /* argc is zero when run from the Workbench, ** positive when run from the CLI. */ if (argc == 0) { /* AmigaDOS has a special facility that allows a window */ /* with a console and a file handle to be easily created. */ /* CON: windows allow you to use fprintf() with no hassle */ if (ZERO != (outFile = IDOS->Open("CON:0/0/640/200/PrArgs", MODE_NEWFILE))) { /* In AmigaOS, argv is a pointer to the WBStartup message ** when argc is zero. (run under the Workbench.) */ argmsg = (struct WBStartup *)argv ; wb_arg = argmsg->sm_ArgList ; /* head of the arg list */ IDOS->FPrintf(outFile, "Run from the workbench, %ld args.\n", argmsg->sm_NumArgs); for (ktr = 0; ktr < argmsg->sm_NumArgs; ktr++, wb_arg++) { if (ZERO != wb_arg->wa_Lock) { /* locks supported, change to the proper directory */ olddir = IDOS->CurrentDir(wb_arg->wa_Lock) ; /* process the file. ** If you have done the CurrentDir() above, then you can ** access the file by its name. Otherwise, you have to ** examine the lock to get a complete path to the file. */ IDOS->FPrintf(outFile, "\tArg %2.2ld (w/ lock): '%s'.\n", ktr, wb_arg->wa_Name); /* change back to the original directory when done. ** be sure to change back before you exit. */ IDOS->CurrentDir(olddir) ; } else { /* something that does not support locks */ IDOS->FPrintf(outFile, "\tArg %2.2ld (no lock): '%s'.\n", ktr, wb_arg->wa_Name); } } /* wait before closing down */ IDOS->Delay(500L); IDOS->Close(outFile); } } else { /* using 'tinymain' from lattice c. ** define a place to send the output (originating CLI window = "*") ** Note - if you open "CONSOLE:" and your program is RUN, the user will not ** be able to close the CLI window until you close the "CONSOLE:" file. */ if (ZERO != (outFile = IDOS->Open("CONSOLE:", MODE_NEWFILE))) { IDOS->FPrintf(outFile, "Run from the CLI, %d args.\n", argc); for ( ktr = 0; ktr < argc; ktr++) { /* print an arg, and its number */ IDOS->FPrintf(outFile, "\tArg %2.2ld: '%s'.\n", ktr, argv[ktr]); } IDOS->Close(outFile); } } return 0; }
The Icon Library
The .info file is the center of interaction between applications and Workbench. To help support the Workbench iconic interface and manage .info files, the Amiga operating system provides the icon library. The icon library allows you to create icons for data files and directories under program control and examine icons to obtain their Tool Types and other characteristics.
Icon Library Data Structures
The preceding sections discussed how icons are used to pass file name arguments to an application run from the Workbench. Workbench allows other types of arguments to be passed in the Tool Types array of an icon. To examine the Tool Types array or find other characteristics of the icon such as its type, applications need to read in the .info file for the icon.
The DiskObject Structure
The actual data present in the .info file is organized as a DiskObject structure which is defined in the include file <workbench/workbench.h>. For a complete listing, see the SDK. The DiskObject structure contains the following elements:
struct DiskObject { UWORD do_Magic; /* magic number at start of file */ UWORD do_Version; /* so we can change structure */ struct Gadget do_Gadget; /* a copy of in core gadget */ UBYTE do_Type; char *do_DefaultTool; char **do_ToolTypes; LONG do_CurrentX; LONG do_CurrentY; struct DrawerData *do_DrawerData; char *do_ToolWindow; /* only applies to tools */ LONG do_StackSize; /* only applies to tools */ };
- do_Magic
- A magic number that the icon library looks for to make sure that the file it is reading really contains an icon. It should be the manifest constant WB_DISKMAGIC. PutDiskObject() will put this value in the structure, and GetDiskObject will not believe that a file is really an icon unless this value is correct.
- do_Version
- This provides a way to enhance the .info file in an upwardly-compatible way. It should be WB_DISKVERSION. The icon library will set this value for you and will not believe weird values.
- do_Gadget
- This contains all the imagery for the icon. See the "Gadget Structure" section below for more details.
- do_Type
- The type of the icon; can be set to any of the following values.
WBDISK The root of a disk WBDRAWER A directory on the disk WBTOOL An executable program WBPROJECT A data file WBGARBAGE The Trashcan directory WBKICK A Kickstart disk WBAPPICON Any object not directly associated with a filing system object, such as a print spooler.
- do_DefaultTool
- Default tools are used for project and disk icons. For projects (data files), the default tool is the program Workbench runs when the project is activated. Any valid AmigaDOS path may be entered in this field such as "SYS:myprogram", "df0:mypaint", "myeditor" or ":work/mytool".
- For disk icons, the default tool is the diskcopy program ("SYS:System/DiskCopy") that will be used when this disk is the source of a copy.
- do_ToolTypes
- This is an array of free-format strings. Workbench does not enforce any rules on these strings, but they are useful for passing environment information. See the section on "The ToolTypes Array" below for more information.
- do_CurrentX, do_CurrentY
- Drawers have a virtual coordinate system. The user can scroll around in this system using the scroll gadgets on the window that opens when the drawer is activated. Each icon in the drawer has a position in the coordinate system. CurrentX and CurrentY contain the icon's current position in the drawer. Picking a position for a newly created icon can be tricky. NO_ICON_POSITION is a system constant for do_CurrentX and do_CurrentY that instructs Workbench to pick a reasonable place for the icon. Workbench will place the icon in an unused region of the drawer. If there is no space in the drawers window, the icon will be placed just to the right of the visible region.
- do_DrawerData
- If the icon is associated with a directory (WBDISK, WBDRAWER, WBGARBAGE), it needs a DrawerData structure to go with it. This structure contains an Intuition NewWindow structure (see the "Intuition Windows" chapter for more information):
struct DrawerData
{ struct NewWindow dd_NewWindow; /* structure to open window */ LONG dd_CurrentX; /* current x coordinate of origin */ LONG dd_CurrentY; /* current y coordinate of origin */
};
- Workbench uses this to hold the current window position and size of the window so it will reopen in the same place.
- do_ToolWindow
- This field is reserved for future use.
- do_StackSize
- This is the size of the stack (in bytes) used for running the tool. If this is NULL, then Workbench will use a reasonable default stack size (currently 4K bytes).
Stack Size is Taken from the Project Icon |
---|
When a tool is run via the default tool mechanism (i.e., a project was activated, not the tool itself), Workbench uses the stack size specified in the project's .info file and the tool's .info file is ignored. |
The Gadget Structure
To hold the icon's image, Workbench uses an Intuition Gadget structure, defined in <intuition/intuition.h>. Workbench restricts some of the values of the gadget. All unused fields should be set to 0 or NULL. The Intuition gadget structure members that Workbench icons use are listed below.
- Width
- This is the width (in pixels) of the icon's active region. Any mouse button press within this range will be interpreted as having selected this icon.
- Height
- This is the height (in pixels) of the icon's active region. Any mouse button press within this range will be interpreted as having selected this icon.
- Flags
- The gadget must be of type GADGIMAGE. Three highlight modes are supported: GADGHCOMP, GADGHIMAGE, and GADGBACKFILL. GADGHCOMP complements everything within the area defined by CurrentX, CurrentY, Width, Height. GADGHIMAGE uses an alternate selection image. GADGBACKFILL is similar to GADGHCOMP, but ensures that there is no "ring" around the selected image. It does this by first complementing the image, and then flooding all color 3 pixels that are on the border of the image to color 0. All other flag bits should be 0.
- Activation
- The activation should have only RELVERIFY and GADGIMMEDIATE set.
- Type
- The gadget type should be BOOLGADGET.
- GadgetRender
- Set this to an appropriate Image structure.
- SelectRender
- Set this to an appropriate alternate Image structure if and only if the highlight mode is GADGHIMAGE.
The Image structure is typically the same size as the gadget, except that Height is often one pixel less than the gadget height. This allows a blank line between the icon image and the icon name. The image depth must be 2; PlanePick must be 3; and PlaneOnOff should be 0. The NextImage field should be null.
Icon Library Functions
The icon library functions do all the work needed to read, write and examine an icon's .info file and corresponding DiskObject structure:
struct DiskObject *GetDiskObject(UBYTE *name); struct DiskObject *GetDiskObjectNew(UBYTE *name); BOOL PutDiskObject(UBYTE *name, struct DiskObject *diskobj); void FreeDiskObject(struct DiskObject *diskobj); BOOL DeleteDiskObject(UBYTE *); UBYTE *FindToolType(UBYTE **toolTypeArray, UBYTE *typeName); BOOL MatchToolValue(UBYTE *typeString, UBYTE *value); struct DiskObject *GetDefDiskObjectNew(LONG type); BOOL PutDefDiskObject(struct DiskObject *diskobj); UBYTE *BumpRevision(UBYTE *newbuf, UBYTE *oldname);
The icon library routine GetDiskObject() reads an icon's .info file from disk into a DiskObject structure it creates in memory where it can be examined or altered. PutDiskObject() writes the DiskObject out to disk and FreeDiskObject() frees the memory it used. If you modify any pointers in a DiskObject acquired via GetDiskObject(), replace the old pointers before calling FreeDiskObject() so that the proper memory will be freed.
GetDiskObjectNew() works the same as GetDiskObject() except that if no .info file is found, a default DiskObject will be created for you. DeleteDiskObject() is for removing .info files from disk, and the functions GetDefDiskObject() and PutDefDiskObject() allow the default icons to be copied or replaced with new defaults.
Once an icon's .info file has been read into a DiskObject structure, the functions FindToolType() and MatchToolType() can be used to examine the icon's Tool Types array.
The Tool Types Array
Earlier sections discussed how Workbench passes filenames as arguments to a program that's about to run. Workbench also allows other types of arguments to be passed in the Tool Types array of an icon. The Tool Types array is found in the do_ToolTypes field of the icon's DiskObject structure.
In brief, Tool Types is an array of pointers to strings that contain any information an application wants to store such as the program options that were in effect when the icon was created. These strings can be used to encode information which will be available to all applications that read the icon's .info file. Users can enter and change a selected icon's Tool Types by choosing Information in the Workbench Icons menu.
Workbench does not place many restrictions on the Tool Types array, but there are a few conventions you should follow. A string may be no more than 128 bytes long. The alphabet used is 8-bit ANSI (for example, normal ASCII with foreign-language extensions). This means that users may enter Tool Type strings containing international characters. Avoid special or nonprinting characters. The case of the characters is currently significant, so the string "Window" is not equal to "WINDOW".
The general format for a Tool Types entry is <name>=<value>[|<value>], where <name> is the field name and <value> is the text to associate with that name. Multiple values for one name may be separated by a vertical bar. The values may be the type of the file, programs that can access the data, parameters to be passed to an application, etc. For example, a paint program might set:
FILETYPE = PaintProgram | ILBM
This Tool Type indicates that the file is an ILBM, perhaps with some additional chunks of data specific to PaintProgram.
Tool Type strings have few restrictions but there are some reserved Tool Types that are parsed by Workbench itself when an application is started from an icon. The reserved Tool Types are TOOLPRI=n (sets the Exec task priority at which Workbench will start the application), STARTPRI=n (sets the starting order for icons in the Wbstartup drawer), and DONOTWAIT (tells Workbench not to wait for the return of a program started via an icon in the Wbstartup drawer). In addition to the reserved Tool Types, which applications should not use, there are standard Tool Types, which applications should use only in the standard way. For a list of standard Tool Types refer to the Amiga User Interface Style Guide.
Two routines are provided to help you deal with the Tool Types array. FindToolType() returns the value of a Tool Type element. Using the above example, if you are looking for FILETYPE, the string "PaintProgram|ILBM" will be returned. MatchToolValue() returns nonzero if the specified string is in the reference value string. This routine knows how to parse vertical bars. For example, using the reference value strings of "PaintProgram" or "ILBM", MatchToolValue() will return TRUE for "ILBM" and "PaintProgram" and FALSE for everything else.
Example of Reading Icons and Parsing Tool Types
The following example demonstrates icon creation, icon reading and Tool Type parsing in the Workbench environment. When called from the Shell, the example creates a small data file in RAM: and creates or updates a project icon for the data file. The created project icon points to this example as its default tool. When the new project icon is double-clicked, Workbench will invoke the default tool (this example) as a Workbench process, and pass it a description of the project data file as a Workbench argument (WBArg) in the WBStartup message.
/* ** iconexample.c - Workbench icon startup, creation, and parsing example */ #include <exec/types.h> #include <libraries/dos.h> #include <workbench/workbench.h> #include <workbench/startup.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/icon.h> #include <stdlib.h> #include <stdio.h> #include <string.h> /* our functions */ void cleanexit(UBYTE *,LONG); void cleanup(void); void message(UBYTE *); BOOL makeIcon(UBYTE *, char **, char *); BOOL showToolTypes(struct WBArg *); UBYTE *projname = "RAM:Example_Project"; UBYTE *conwinname = "CON:10/10/620/180/iconexample"; UBYTE deftoolname[] = {"iconexample"}; USHORT IconImageData1[] = { /* Plane 0 */ 0x0000,0x0000,0x0000,0x1000,0x0000,0x0000,0x0000,0x3000, 0x0FFF,0xFFFC,0x0000,0x3000,0x0800,0x0004,0x0000,0x3000, 0x0800,0x07FF,0xFFC0,0x3000,0x08A8,0xA400,0x00A0,0x3000, 0x0800,0x0400,0x0090,0x3000,0x08AA,0xA400,0x0088,0x3000, 0x0800,0x042A,0xA0FC,0x3000,0x082A,0xA400,0x0002,0x3000, 0x0800,0x0400,0x0002,0x3000,0x0800,0xA42A,0xA0A2,0x3000, 0x0800,0x0400,0x0002,0x3000,0x0950,0xA42A,0x8AA2,0x3000, 0x0800,0x0400,0x0002,0x3000,0x082A,0xA400,0x0002,0x3000, 0x0800,0x042A,0x2AA2,0x3000,0x0FFF,0xFC00,0x0002,0x3000, 0x0000,0x0400,0x0002,0x3000,0x0000,0x07FF,0xFFFE,0x3000, 0x0000,0x0000,0x0000,0x3000,0x7FFF,0xFFFF,0xFFFF,0xF000, /* Plane 1 */ 0xFFFF,0xFFFF,0xFFFF,0xE000,0xD555,0x5555,0x5555,0x4000, 0xD000,0x0001,0x5555,0x4000,0xD7FF,0xFFF9,0x5555,0x4000, 0xD7FF,0xF800,0x0015,0x4000,0xD757,0x5BFF,0xFF55,0x4000, 0xD7FF,0xFBFF,0xFF65,0x4000,0xD755,0x5BFF,0xFF75,0x4000, 0xD7FF,0xFBD5,0x5F01,0x4000,0xD7D5,0x5BFF,0xFFFD,0x4000, 0xD7FF,0xFBFF,0xFFFD,0x4000,0xD7FF,0x5BD5,0x5F5D,0x4000, 0xD7FF,0xFBFF,0xFFFD,0x4000,0xD6AF,0x5BD5,0x755D,0x4000, 0xD7FF,0xFBFF,0xFFFD,0x4000,0xD7D5,0x5BFF,0xFFFD,0x4000, 0xD7FF,0xFBD5,0xD55D,0x4000,0xD000,0x03FF,0xFFFD,0x4000, 0xD555,0x53FF,0xFFFD,0x4000,0xD555,0x5000,0x0001,0x4000, 0xD555,0x5555,0x5555,0x4000,0x8000,0x0000,0x0000,0x0000, }; struct Image iconImage1 = { 0, 0, /* Top Corner */ 52, 22, 2, /* Width, Height, Depth */ &IconImageData1[0], /* Image Data */ 0x003, 0x000, /* PlanePick,PlaneOnOff */ NULL /* Next Image */ }; UBYTE *toolTypes[] = { "FILETYPE=text", "FLAGS=BOLD|ITALICS", NULL }; struct DiskObject projIcon = { WB_DISKMAGIC, /* Magic Number */ WB_DISKVERSION, /* Version */ { /* Embedded Gadget Structure */ NULL, /* Next Gadget Pointer */ 97,12,52,23, /* Left,Top,Width,Height */ GADGIMAGE|GADGHBOX, /* Flags */ GADGIMMEDIATE|RELVERIFY, /* Activation Flags */ BOOLGADGET, /* Gadget Type */ (APTR)&iconImage1, /* Render Image */ NULL, /* Select Image */ NULL, /* Gadget Text */ NULL, /* Mutual Exclude */ NULL, /* Special Info */ 0, /* Gadget ID */ NULL /* User Data */ }, WBPROJECT, /* Icon Type */ deftoolname, /* Default Tool */ toolTypes, /* Tool Type Array */ NO_ICON_POSITION, /* Current X */ NO_ICON_POSITION, /* Current Y */ NULL, /* Drawer Structure */ NULL, /* Tool Window */ 4000 /* Stack Size */ }; /* Opens and allocations we must clean up */ struct Library *IconBase = NULL; struct IconIFace *IIcon = NULL; FILE *conwin = NULL; LONG olddir = -1; BOOL FromWb; int main(int argc, char **argv) { struct WBStartup *WBenchMsg; struct WBArg *wbarg; FILE *file; LONG wLen; SHORT i; FromWb = (argc==0) ? TRUE : FALSE; /* Open icon.library */ IconBase = IExec->OpenLibrary("icon.library", 50); IIcon = (struct IconIFace*)IExec->GetInterface(IconBase, "main", 1, NULL); if (IIcon == NULL) cleanexit("Can't open icon.library\n",RETURN_FAIL); /* If started from CLI, this example will create a small text * file RAM:Example_Project, and create an icon for the file * which points to this program as its default tool. */ if(!FromWb) { /* Make a sample project (data) file */ wLen = -1; if(file=fopen(projname,"w")) { wLen = fprintf(file,"Have a nice day\n"); fclose(file); } if(wLen < 0) cleanexit("Error writing data file\n",RETURN_FAIL); /* Now save/update icon for this data file */ if(makeIcon(projname, toolTypes, deftoolname)) { IDOS->Printf("%s data file and icon saved.\n",projname); IDOS->Printf("Use Workbench menu Icon Information to examine the icon.\n"); IDOS->Printf("Then copy this example (iconexample) to RAM:\n"); IDOS->Printf("and double-click the %s project icon\n",projname); } else cleanexit("Error writing icon\n",RETURN_FAIL); } else /* Else we are FromWb - ie. we were either * started by a tool icon, or as in this case, * by being the default tool of a project icon. */ { if(!(conwin = fopen(conwinname,"r+"))) cleanexit("Can't open output window\n",RETURN_FAIL); WBenchMsg = (struct WBStartup *)argv; /* Note wbarg++ at end of FOR statement steps through wbargs. * First arg is our executable (tool). Any additional args * are projects/icons passed to us via either extend select * or default tool method. */ for(i=0, wbarg=WBenchMsg->sm_ArgList; i < WBenchMsg->sm_NumArgs; i++, wbarg++) { /* if there's a directory lock for this wbarg, CD there */ olddir = -1; if((wbarg->wa_Lock)&&(*wbarg->wa_Name)) olddir = IDOS->CurrentDir(wbarg->wa_Lock); showToolTypes(wbarg); if((i>0)&&(*wbarg->wa_Name)) fprintf(conwin,"In Main. We could open the %s file here\n", wbarg->wa_Name); if(olddir != -1) IDOS->CurrentDir(olddir); /* CD back where we were */ } IDOS->Delay(500); } cleanup(); return RETURN_OK; } BOOL makeIcon(UBYTE *name, char **newtooltypes, char *newdeftool) { struct DiskObject *dobj; char *olddeftool; char **oldtooltypes; BOOL success = FALSE; if(dobj = IIcon->GetDiskObject(name)) { /* If file already has an icon, we will save off any fields we * need to update, update those fields, put the object, restore * the old field pointers and then free the object. This will * preserve any custom imagery the user has, and the user's * current placement of the icon. If your application does * not know where the user currently keeps your application, * you should not update his dobj->do_DefaultTool. */ oldtooltypes = dobj->do_ToolTypes; olddeftool = dobj->do_DefaultTool; dobj->do_ToolTypes = newtooltypes; dobj->do_DefaultTool = newdeftool; success = IIcon->PutDiskObject(name,dobj); /* we must restore the original pointers before freeing */ dobj->do_ToolTypes = oldtooltypes; dobj->do_DefaultTool = olddeftool; IIcon->FreeDiskObject(dobj); } /* Else, put our default icon */ if(!success) success = IIcon->PutDiskObject(name,&projIcon); return(success); } BOOL showToolTypes(struct WBArg *wbarg) { struct DiskObject *dobj; char **toolarray; char *s; BOOL success = FALSE; fprintf(conwin,"\nWBArg Lock=0x%lx, Name=%s\n", wbarg->wa_Lock,wbarg->wa_Name); if((*wbarg->wa_Name) && (dobj = IIcon->GetDiskObject(wbarg->wa_Name))) { fprintf(conwin," We have read the DiskObject (icon) for this arg\n"); toolarray = (char **)dobj->do_ToolTypes; if(s=(char *)IIcon->FindToolType(toolarray,"FILETYPE")) { fprintf(conwin," Found tooltype FILETYPE with value %s\n",s); } if(s=(char *)IIcon->FindToolType(toolarray,"FLAGS")) { fprintf(conwin," Found tooltype FLAGS with value %s\n",s); if(IIcon->MatchToolValue(s,"BOLD")) fprintf(conwin," BOLD flag requested\n"); if(IIcon->MatchToolValue(s,"ITALICS")) fprintf(conwin," ITALICS flag requested\n"); } /* Free the diskobject we got */ IIcon->FreeDiskObject(dobj); success = TRUE; } else if(!(*wbarg->wa_Name)) fprintf(conwin," Must be a disk or drawer icon\n"); else fprintf(conwin," Can't find any DiskObject (icon) for this WBArg\n"); return(success); } /* Workbench-started programs with no output window may want to display * messages in a different manner (requester, window title, etc) */ void message(UBYTE *s) { if(FromWb && conwin) fprintf(conwin,s,strlen(s)); else if (!FromWb) printf(s); } void cleanexit(UBYTE *s, LONG n) { if(*s) message(s); cleanup(); exit(n); } void cleanup() { if(conwin) fclose(conwin); IExec->DropInterface((struct Interface*)IIcon); IExec->CloseLibrary(IconBase); }
The Workbench Library
Workbench arguments are sent to an application when it is started. There are also special facilities in Release 2 of Workbench that allow an application that is already running to get additional arguments. These special facilities are known as AppWindow, AppIcon and AppMenuItem.
An AppWindow is a special kind of window that allows the user to drag icons into it. Applications that set up an AppWindow will receive a message from Workbench whenever the user moves an icon into the AppWindow. The message contains the name of the file or directory that the icon represents.
An AppIcon is similar to an AppWindow. It is a special type of icon that allows the user to drag other icons on top of it. Like AppWindows, an application that sets up an AppIcon will receive a message from Workbench whenever the user moves another icon on top of the AppIcon. The message contains the name of the file or directory that the moved icon represents.
An AppMenuItem allows an application to add a custom menu item to the usual set of menu choices supported by Workbench. An application that sets up an AppMenuItem will receive a message from Workbench whenever the user picks that item from the Workbench menus.
When an application receives the messages described above, the message will include struct WBArg *am_ArgList containing the names (wa_Name) and directory locks (wa_Lock) of all selected icons that were passed as arguments by the user. This am_ArgList has the same format as the sm_ArgList of a WBStartup message.
Workbench Library Functions
AppWindows, AppIcons and AppMenuItems extend the user's ability to perform operations with the Workbench iconic interface. They all provide graphical methods for passing arguments to a running application. In order to manage AppWindows, AppIcons and AppMenuItems, the Amiga OS includes these Workbench library functions:
struct AppIcon *AddAppIconA( ULONG, ULONG, char *, struct MsgPort *, struct FileLock *, struct DiskObject *, struct *TagItem ); struct AppMenuItem *AddAppMenuItemA( ULONG, ULONG, char *, struct MsgPort *, struct *TagItem); struct AppWindow *AddAppWindowA( ULONG, ULONG, struct Window *, struct MsgPort *, struct *TagItem); BOOL RemoveAppIcon(struct AppIcon *); BOOL RemoveAppMenuItem(struct AppMenuItem *); BOOL RemoveAppWindow(struct AppWindow *);
The functions AddAppMenuItemA(), AddAppWindowA() and AddAppIconA() have alternate entry points using the same function name without the trailing A. The alternate functions accept any TagItem arguments on the stack instead of from an array. See the listings below for examples.
An AppIcon Example
The example listed here shows how to create an AppIcon and obtain arguments from Workbench when the user drops other icons on top of it. The AppIcon will appear as a disk icon named "TestAppIcon" on the Workbench screen. (All AppIcons appear on the Workbench screen or window.)
For convenience, this example code uses GetDefDiskObject() to create the icon imagery for the AppIcon. Applications should never do this. Use your own custom imagery for AppIcons instead.
/* appicon.c - Compiled under SAS C 5.10 with lc -L appicon.c */ /* Requires Kickstart version 37 or later. Works from the Shell (CLI) only */ #include <exec/types.h> /* Need this for the Amiga variable types */ #include <workbench/workbench.h> /* This has DiskObject and AppIcon structs */ #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ #include <exec/libraries.h> /* Need this to check library versions */ #include <clib/icon\_protos.h> /* Icon (DiskObject) function prototypes */ #include <clib/exec\_protos.h> /* Exec message, port and library functions*/ #include <clib/wb\_protos.h> /* AppIcon function protos */ #ifdef LATTICE int CXBRK(void) { return(0); } /* Disable SAS Lattice CTRL/C handling */ int chkabort(void) { return(0); }/* really */ #endif extern struct Library *SysBase; struct Library *IconBase; struct Library *WorkbenchBase; void main(int argc, char **argv) { struct DiskObject *dobj=NULL; struct MsgPort *myport=NULL; struct AppIcon *appicon=NULL; struct AppMessage *appmsg=NULL; LONG dropcount=0L; ULONG x; BOOL success=0L; /* Get the the right version of the Icon Library, initialize IconBase */ if(IconBase = OpenLibrary("icon.library",37)) { /* Get the the right version of the Workbench Library */ if (WorkbenchBase=OpenLibrary("workbench.library",37)) { /* This is the easy way to get some icon imagery */ /* Real applications should use custom imagery */ dobj=GetDefDiskObject(WBDISK); if(dobj!=0) { /* The type must be set to NULL for a WBAPPICON */ dobj->do\_Type=NULL; /* The CreateMsgPort() function is in Exec version 37 and later only */ myport=CreateMsgPort(); if(myport) { /* Put the AppIcon up on the Workbench window */ appicon=AddAppIconA(0L,0L,"TestAppIcon",myport,NULL,dobj,NULL); if(appicon) { /* For the sake of this example, we allow the AppIcon */ /* to be activated only five times. */ printf("Drop files on the Workbench AppIcon\n"); printf("Example exits after 5 drops\n"); while(dropcount<5) { /* Here's the main event loop where we wait for */ /* messages to show up from the AppIcon */ WaitPort(myport); /* Might be more than one message at the port... */ while(appmsg=(struct AppMessage *)GetMsg(myport)) { if(appmsg->am\_NumArgs==0L) { /* If NumArgs is 0 the AppIcon was activated directly */ printf("User activated the AppIcon.\n"); printf("A Help window for the user would be good here\n"); } else if(appmsg->am\_NumArgs>0L) { /* If NumArgs is >0 the AppIcon was activated by */ /* having one or more icons dropped on top of it */ printf("User dropped %ld icons on the AppIcon\n", appmsg->am\_NumArgs); for(x=0;x<appmsg->am\_NumArgs;x++) { printf("#%ld name='%s'\n",x+1,appmsg->am\_ArgList[x].wa\_Name); } } /* Let Workbench know we're done with the message */ ReplyMsg((struct Message *)appmsg); } dropcount++; } success=RemoveAppIcon(appicon); } /* Clear away any messages that arrived at the last moment */ while(appmsg=(struct AppMessage *)GetMsg(myport)) ReplyMsg((struct Message *)appmsg); DeleteMsgPort(myport); } FreeDiskObject(dobj); } CloseLibrary(WorkbenchBase); } CloseLibrary(IconBase); } }
An AppMenuItem Example
This example shows how to create an AppMenuItem. The example adds a menu item named “Browse Files” to the Workbench Tools menu. (All AppMenuItems appear in the Workbench Tools menu.) When the menu item is activated, the example program receives a message from Workbench and then attempts to start up an instance of the More program. (The More program is in the Utilities directory of the Workbench disk.)
The example starts up the More program as a separate, asynchronous process using the new SystemTags() function of Release 2 AmigaDOS. For more about the SystemTags() function refer to the AmigaDOS Manual, 3rd Edition. When the AppMenuItem has been activated five times, the program exits after freeing any system resources it has used.
/* appmenuitem.c - Compiled under SAS C 5.10 with lc -L appmenuitem.c */ /* Requires Kickstart version 37 or later. Works from the Shell (CLI) only */ #include <exec/types.h> /* Need this for the Amiga variable types */ #include <workbench/workbench.h> /* This has DiskObject and AppIcon structs */ #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ #include <exec/libraries.h> #include <dos/dostags.h> #include <stdio.h> #include <clib/dos\_protos.h> #include <clib/exec\_protos.h> /* Exec message, port and library functions*/ #include <clib/wb\_protos.h> /* AppMenuItem function protos */ #ifdef LATTICE int CXBRK(void) { return(0); } /* Disable Lattice CTRL/C handling */ int chkabort(void) { return(0); }/* really */ #endif extern struct Library *SysBase; struct Library *WorkbenchBase; void main(int argc, char **argv) { struct MsgPort *myport=NULL; struct AppMenuItem *appitem=NULL; struct AppMessage *appmsg=NULL; LONG result, x, count=0L; BOOL success=0L; BPTR file; if (WorkbenchBase = OpenLibrary("workbench.library",37)) { /* The CreateMsgPort() function is in Exec version 37 and later only */ if(myport = CreateMsgPort()) { /* Add our own AppMenuItem to the Workbench Tools Menu */ appitem=AddAppMenuItemA(0L, /* Our ID# for item */ (ULONG)"SYS:Utilities/More", /* Our UserData */ "Browse Files", /* MenuItem Text */ myport,NULL); /* MsgPort, no tags */ if(appitem) { printf("Select Workbench Tools demo menuitem 'Browse Files'\n"); /* For this example, we allow the AppMenuItem to be selected */ /* only once, then we remove it and exit */ WaitPort(myport); while((appmsg=(struct AppMessage *)GetMsg(myport)) && (count<1)) { /* Handle messages from the AppMenuItem - we have only one */ /* item so we don't have to check its appmsg->am\_ID number. */ /* We'll System() the command string that we passed as */ /* userdata when we added the menu item. */ /* We find our userdata pointer in appmsg->am\_UserData */ printf("User picked AppMenuItem with %ld icons selected\n", appmsg->am\_NumArgs); for(x=0;x<appmsg->am\_NumArgs;x++) printf(" #%ld name='%s'\n",x+1,appmsg->am\_ArgList[x].wa\_Name); count++; if( file=Open("CON:0/40/640/150/AppMenu Example/auto/close/wait", MODE\_OLDFILE) ) /* for any stdio output */ { result=SystemTags((UBYTE *)appmsg->am\_UserData,SYS\_Input,file, SYS\_Output,NULL, SYS\_Asynch,TRUE, TAG\_DONE); /* If Asynch System() itself fails, we must close file */ if(result == -1) Close(file); } ReplyMsg((struct Message *)appmsg); } success=RemoveAppMenuItem(appitem); } /* Clear away any messages that arrived at the last moment */ /* and let Workbench know we're done with the messages */ while(appmsg=(struct AppMessage *)GetMsg(myport)) { ReplyMsg((struct Message *)appmsg); } DeleteMsgPort(myport); } CloseLibrary(WorkbenchBase); } }
An AppWindow Example
This example shows how to create an AppWindow and obtain arguments from Workbench when the user drops an icon into it. The AppWindow will appear on the Workbench screen with the name “AppWindow” and will run until the window’s close gadget is selected. If any icons are dropped into the AppWindow, the program prints their arguments in the Shell window.
/* appwindow.c - Compiled under SAS C 5.10 with lc -L appwindow.c */ /* Requires Kickstart version 37 or later. Works from the Shell (CLI) only */ #include <exec/types.h> /* Need this for the Amiga variable types */ #include <workbench/workbench.h> /* This has DiskObject and AppWindow */ #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ #include <exec/libraries.h> /* Need this to check library versions */ #include <stdio.h> #include <clib/intuition\_protos.h> #include <clib/exec\_protos.h> #include <clib/wb\_protos.h> #ifdef LATTICE int CXBRK(void) { return(0); } /* Disable SAS Lattice CTRL/C handling */ int chkabort(void) { return(0); }/* really */ #endif struct Library *IntuitionBase; struct Library *WorkbenchBase; void main(int argc, char **argv) { struct MsgPort *awport; struct Window *win; struct AppWindow *appwin; struct IntuiMessage *imsg; struct AppMessage *amsg; struct WBArg *argptr; ULONG winsig, appwinsig, signals, id = 1, userdata = 0; BOOL done = FALSE; int i; if (IntuitionBase = OpenLibrary("intuition.library", 37)) { if (WorkbenchBase = OpenLibrary("workbench.library", 37)) { /* The CreateMsgPort() function is in Exec version 37 and later only */ if (awport = CreateMsgPort()) { if (win = OpenWindowTags(NULL, WA\_Width, 200, WA\_Height, 50, WA\_IDCMP, CLOSEWINDOW, WA\_Flags, WINDOWCLOSE | WINDOWDRAG, WA\_Title, "AppWindow", TAG\_DONE)) { if (appwin = AddAppWindow(id, userdata, win, awport, NULL)) { printf("AppWindow added... Drag files into AppWindow\n"); winsig = 1L << win->UserPort->mp\_SigBit; appwinsig = 1L << awport->mp\_SigBit; while (! done) { /* Wait for IDCMP messages and AppMessages */ signals = Wait( winsig | appwinsig ); if(signals & winsig) /* Got an IDCMP message */ { while (imsg = (struct IntuiMessage *) GetMsg(win->UserPort)) { if (imsg->Class = CLOSEWINDOW) done = TRUE; ReplyMsg((struct Message *) imsg); } } if(signals & appwinsig) /* Got an AppMessage */ { while (amsg = (struct AppMessage *) GetMsg(awport)) { printf("AppMsg: Type=%ld, ID=%ld, NumArgs=%ld\n", amsg->am\_Type, amsg->am\_ID, amsg->am\_NumArgs); argptr = amsg->am\_ArgList; for (i = 0; i < amsg->am\_NumArgs; i++) { printf(" arg(%ld): Name='%s', Lock=%lx\n", i, argptr->wa\_Name, argptr->wa\_Lock); argptr++; } ReplyMsg((struct Message *) amsg); } } } /* done */ RemoveAppWindow(appwin); } CloseWindow(win); } /* Make sure there are no more outstanding messages */ while(amsg = (struct AppMessage *)GetMsg(awport)) ReplyMsg((struct Message *)amsg); DeleteMsgPort(awport); } CloseLibrary(WorkbenchBase); } CloseLibrary(IntuitionBase); } }
Workbench and the Startup Code Module
Standard startup code handles the detail work of interfacing with the arguments and environment of Workbench and the Shell (or CLI). This section describes the behavior of standard startup modules such as the ones supplied with newlib and clib2.
The environment for a program started from Workbench is quite different from the environment for a program started from the Shell. The Shell does not create a new process for a program; it jumps to the program's code and the program shares the process with the Shell. Programs run under the Shell have access to all the Shell's environment, including the ability to modify that environment. (Programs run from the Shell should be careful to restore all values that existed on startup.) Workbench starts a program as a new DOS process, explicitly passing the execution environment to the program.
Workbench Startup
When the user activates a project or tool icon, the program is run as a separate process asynchronous to Workbench. This allows the user to take full advantage of the multitasking features of the Amiga. A process is simply a task with additional information needed to use DOS library.
When Workbench loads and starts a program, it sends the program a WBStartup message containing the arguments as described earlier. The WBStartup also contains a pointer to the new Process structure which describes the execution environment of the program. The WBStartup message is posted to the message port of the program's Process structure.
The Process message port is for the exclusive use of DOS, so this message must be removed from the port before using any DOS library functions. Normally this is handled by the startup code module that comes with your compiler so you don't have to worry about this unless you are writing your own startup code.
Standard startup code modules also set up SysBase, the pointer to the Exec master library, and open the DOS library setting up DOSBase. That is why Exec and AmigaDOS functions can be called by C applications without first opening a library; the startup code that applications are linked with handles this. Some special startups may also set up NIL: input and output streams, or may open a stdio window so that the Workbench applications can use stdio functions such as printf().
The startup code can tell if it is running in the Workbench environment because the pr_CLI field of the Process structure will contain NULL. In that case the startup code removes the WBStartup message from the Process message port with GetMsg() before using any functions in the DOS library.
Do Not Use the Process Message Port for Anything Else |
---|
The message port in a Process structure is for the exclusive use of the DOS library. |
Standard startup code will pass the WBStartup message pointer in argv and 0 (zero) in argc if the program is started from Workbench. These values are pushed onto the stack, and the startup code calls the application code that it is linked with as a function. When the application code exits back to the startup code, the startup code closes and frees all opens and allocations it made. It will then Forbid(), and ReplyMsg() the WBStartup message, notifying Workbench that the application Process may be terminated and its code unloaded from memory.
Avoid the DOS Exit() function |
---|
The DOS Exit() function does not return an application to the startup code that called it. If you wish to exit your application, use the exit function provided by your startup code (usually lower-case exit(), or _exit for assembler), passing it a valid DOS return code as listed in the include file <libraries/dos.h>. |
Shell Startup
When a program is started from the Shell (or a Shell script), standard startup modules will parse the command line into an array of pointers to individual argument strings placing them in argv, and an argument count in argc.
If a program is started from the Shell, argc will always equal at least one and the first element in argv will always be a pointer to the command name. Other command line arguments are stored in turn. For example, if the command line was:
df0:myprogram "my file1" file2 ;this is a comment
then argc will be 3, argv[0] will be "df0:myprogram", argv[1] will be "my file1", and argv[2] will be "file2". Correct startup code will strip spaces between arguments and trailing spaces from the last argument and will also properly deal with quoted arguments with embedded spaces.
As with Workbench, standard startup code for the Shell sets up SysBase, the pointer to the Exec master library, and opens the DOS library setting up DOSBase. C applications that are linked with standard startup code can call an Exec or AmigaDOS functions without opening the library first.
The startup code also fills in the stdio file handles (_stdin, _stdout, etc.) for the application. Finally argv and argc, are pushed onto the stack and the application is called. When the application returns or exits back to the startup code, the startup code closes and frees all opens and allocations it has made for the application, and then returns to the system with the whatever value the program exited with.
Link your applications only with standard, tested startup code of some type such as the module supplied with your compiler. Startup code provides your programs with correct, consistent handling of Shell command line and Workbench arguments and will perform some initializations and cleanups which would otherwise need to be handled by your own code. Very small startups can be used for programs that do not require command line arguments.
A few words of warning for those of you who do not use standard startup code:
- If you are started as a Workbench process, you must GetMsg() the WBStartup message before using any functions in the DOS library.
- You must turn off task switching (with Forbid()) before replying the WBStartup message from Workbench. This will prevent Workbench from unloading your code before you can exit properly.
- If you do your own command line parsing, you must provide the user with consistent and correct handling of command line arguments.
Function Reference
The following are brief descriptions of the functions in workbench.library and icon.library. See SDK for details on each function call.
Function | Description |
---|---|
GetDiskObject() | Read the .info file of an icon into a DiskObject structure |
GetDiskObjectNew() | Same as GetDiskObject() but returns a default icon if none exists |
PutDiskObject() | Write a DiskObject structure to disk as a .info file |
FreeDiskObject() | Free the DiskObject structure created by GetDiskObject() |
DeleteDiskObject() | Deletes a given .info file from disk |
FindToolType() | Return the value of an entry in the icon's Tool Type array |
MatchToolValue() | Check a Tool Type entry against a given value |
GetDefDiskObject() | Read the default icon for a given icon type |
PutDefDiskObject() | Replace the default icon for a given icon type |
AddFreeList() | Add memory you have allocated to a FreeList |
FreeFreeList() | Free all the memory for entries in the FreeList |
BumpRevision() | Create a new name for a second copy of a Workbench object |
Function | Description |
---|---|
AddAppIcon() | Add an AppIcon to Workbench |
AddAppMenuItem() | Add an AppMenuItem to the Workbench Tools menu |
AddAppWindow() | Add an AppWindow to Workbench |
RemoveAppIcon() | Remove an AppIcon to Workbench |
RemoveAppMenuItem() | Remove an AppMenuItem to the Workbench Tools menu |
RemoveAppWindow() | Remove an AppWindow to Workbench |