Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Workbench Library"
Steven Solie (talk | contribs) |
m (→An AppIcon Example: Typo fix) |
||
(32 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
+ | == Workbench Library == |
||
− | {{WIP}} |
||
− | == 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 |
+ | Workbench is the graphic user interface to the Amiga file system that uses symbols called icons to represent disks, directories and files. This article shows how to use workbench.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 |
+ | 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. |
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. |
||
Line 15: | Line 14: | ||
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. |
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 |
||
{| class="wikitable" |
{| class="wikitable" |
||
+ | |+ Basic Workbench Icon Types |
||
! Workbench Icon Type |
! Workbench Icon Type |
||
! Image |
! Image |
||
Line 33: | Line 31: | ||
|} |
|} |
||
− | 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 |
+ | 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 [[Icon Library]] for a discussion of the icon library functions. |
− | 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.''' 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. |
== Workbench Environment == |
== Workbench Environment == |
||
Line 46: | Line 44: | ||
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. |
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'' and '' |
+ | 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. |
+ | 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 === |
=== Argument Passing in Workbench === |
||
Line 72: | Line 70: | ||
The WBStartup message, whose structure is outlined in <workbench/startup.h>, has the following structure elements: |
The WBStartup message, whose structure is outlined in <workbench/startup.h>, has the following structure elements: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
struct WBStartup |
struct WBStartup |
||
{ |
{ |
||
Line 82: | Line 80: | ||
struct WBArg * sm_ArgList; /* the arguments themselves */ |
struct WBArg * sm_ArgList; /* the arguments themselves */ |
||
}; |
}; |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The fields of the WBStartup structure are used as follows. |
The fields of the WBStartup structure are used as follows. |
||
Line 108: | Line 106: | ||
Each argument is a struct WBArg and has two parts: wa_Name and wa_Lock. |
Each argument is a struct WBArg and has two parts: wa_Name and wa_Lock. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
struct WBArg |
struct WBArg |
||
{ |
{ |
||
Line 114: | Line 112: | ||
BYTE * wa_Name; /* a string relative to that lock */ |
BYTE * wa_Name; /* a string relative to that lock */ |
||
}; |
}; |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
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. |
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. |
||
Line 130: | Line 128: | ||
The following example will display all WBArgs if started from Workbench, and all Shell arguments if started from the Shell. |
The following example will display all WBArgs if started from Workbench, and all Shell arguments if started from the Shell. |
||
+ | <syntaxhighlight> |
||
− | <pre>;/* prargs.c - Execute me to compile me with SAS C 5.10 |
||
− | + | /* prargs.c |
|
− | Blink FROM LIB:c.o,prargs.o LIB LIB:LC.lib,LIB:Amiga.lib TO prargs DEFINE __main=__tinymain |
||
− | quit |
||
− | ** NOTE: main and tinymain are prepended with two underscores. |
||
** |
** |
||
** PrArgs.c - This program prints all Workbench or Shell (CLI) arguments. |
** PrArgs.c - This program prints all Workbench or Shell (CLI) arguments. |
||
*/ |
*/ |
||
− | #include |
+ | #include <exec/types.h> |
− | #include |
+ | #include <workbench/startup.h> |
− | #include |
+ | #include <proto/dos.h> |
− | #include <clib/icon_protos.h> |
||
+ | int main(int argc, char **argv) |
||
− | #include <stdlib.h> |
||
− | #include <stdio.h> |
||
− | |||
− | #ifdef LATTICE |
||
− | int CXBRK(void) { return(0); } /* Disable SAS Lattice CTRL/C handling */ |
||
− | int chkabort(void) { return(0); }/* really */ |
||
− | #endif |
||
− | |||
− | void main(int argc, char **argv) |
||
{ |
{ |
||
− | struct WBStartup *argmsg; |
+ | struct WBStartup *argmsg; |
− | struct WBArg *wb_arg; |
+ | struct WBArg *wb_arg; |
− | + | int32 ktr; |
|
− | BPTR olddir; |
+ | BPTR olddir; |
− | + | BPTR outFile; |
|
− | /* argc is zero when run from the Workbench, |
+ | /* argc is zero when run from the Workbench, |
− | ** positive when run from the CLI. |
+ | ** positive when run from the CLI. |
− | */ |
+ | */ |
− | if (argc == 0) |
+ | if (argc == 0) |
{ |
{ |
||
− | /* AmigaDOS has a special facility that |
+ | /* AmigaDOS has a special facility that allows a window */ |
/* with a console and a file handle to be easily created. */ |
/* with a console and a file handle to be easily created. */ |
||
/* CON: windows allow you to use fprintf() with no hassle */ |
/* CON: windows allow you to use fprintf() with no hassle */ |
||
− | if ( |
+ | 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.) |
** when argc is zero. (run under the Workbench.) |
||
*/ |
*/ |
||
argmsg = (struct WBStartup *)argv ; |
argmsg = (struct WBStartup *)argv ; |
||
− | wb_arg = argmsg- |
+ | wb_arg = argmsg->sm_ArgList ; /* head of the arg list */ |
− | + | IDOS->FPrintf(outFile, "Run from the workbench, %ld args.\n", |
|
− | argmsg- |
+ | argmsg->sm_NumArgs); |
− | for (ktr = 0; ktr |
+ | for (ktr = 0; ktr < argmsg->sm_NumArgs; ktr++, wb_arg++) |
{ |
{ |
||
− | if ( |
+ | if (ZERO != wb_arg->wa_Lock) |
{ |
{ |
||
/* locks supported, change to the proper directory */ |
/* locks supported, change to the proper directory */ |
||
− | olddir = CurrentDir(wb_arg- |
+ | olddir = IDOS->CurrentDir(wb_arg->wa_Lock) ; |
/* process the file. |
/* process the file. |
||
Line 190: | Line 176: | ||
** examine the lock to get a complete path to the file. |
** examine the lock to get a complete path to the file. |
||
*/ |
*/ |
||
− | + | IDOS->FPrintf(outFile, "\tArg %2.2ld (w/ lock): '%s'.\n", |
|
− | ktr, wb_arg- |
+ | ktr, wb_arg->wa_Name); |
/* change back to the original directory when done. |
/* change back to the original directory when done. |
||
** be sure to change back before you exit. |
** be sure to change back before you exit. |
||
*/ |
*/ |
||
− | CurrentDir(olddir) ; |
+ | IDOS->CurrentDir(olddir) ; |
} |
} |
||
else |
else |
||
{ |
{ |
||
/* something that does not support locks */ |
/* something that does not support locks */ |
||
− | + | IDOS->FPrintf(outFile, "\tArg %2.2ld (no lock): '%s'.\n", |
|
− | ktr, wb_arg- |
+ | ktr, wb_arg->wa_Name); |
} |
} |
||
} |
} |
||
/* wait before closing down */ |
/* wait before closing down */ |
||
− | Delay(500L); |
+ | IDOS->Delay(500L); |
− | + | IDOS->Close(outFile); |
|
} |
} |
||
} |
} |
||
− | else |
+ | else |
{ |
{ |
||
/* using 'tinymain' from lattice c. |
/* using 'tinymain' from lattice c. |
||
− | ** define a place to send the output (originating CLI window = |
+ | ** define a place to send the output (originating CLI window = "*") |
− | ** Note - if you open |
+ | ** 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 |
+ | ** be able to close the CLI window until you close the "CONSOLE:" file. |
*/ |
*/ |
||
− | if ( |
+ | if (ZERO != (outFile = IDOS->Open("CONSOLE:", MODE_NEWFILE))) |
{ |
{ |
||
− | + | IDOS->FPrintf(outFile, "Run from the CLI, %d args.\n", argc); |
|
− | for ( ktr = 0; ktr |
+ | for ( ktr = 0; ktr < argc; ktr++) |
{ |
{ |
||
/* print an arg, and its number */ |
/* print an arg, and its number */ |
||
− | + | IDOS->FPrintf(outFile, "\tArg %2.2ld: '%s'.\n", ktr, argv[ktr]); |
|
} |
} |
||
− | + | IDOS->Close(outFile); |
|
} |
} |
||
} |
} |
||
+ | |||
− | }</pre> |
||
+ | 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: |
||
− | |||
− | <pre> |
||
− | 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 */ |
||
− | }; |
||
− | </pre> |
||
− | |||
− | ; 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. |
||
− | |||
− | :{| class="wikitable" |
||
− | | 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 (new in Release 2). |
||
− | |} |
||
− | |||
− | ; 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): |
||
− | |||
− | :<syntaxhighlight> |
||
− | 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 */ |
||
− | }; |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
+ | == The Workbench Library == |
||
− | : Workbench uses this to hold the current window position and size of the window so it will reopen in the same place. |
||
+ | Workbench arguments are sent to an application when it is started. There are also special facilities that allow an application that is already running to get additional arguments. These special facilities are known as AppWindow, AppIcon and AppMenuItem. |
||
− | ; do_ToolWindow |
||
− | : This field is reserved for future use. |
||
+ | 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. |
||
− | ; 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). |
||
+ | 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. |
||
− | {{Note|title=Stack Size is Taken from the Project Icon|text=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.}} |
||
+ | 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. |
||
− | ==== The Gadget Structure ==== |
||
+ | 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. |
||
− | 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. |
||
+ | === The AppMessage Structure === |
||
− | ; 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. |
||
+ | When Workbench notifies an application of AppWindow, AppIcon, or AppMenuItem activity, it sends an AppMessage to the application's message port (from <workbench/workbench.h>): |
||
− | ; 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. |
||
+ | <syntaxhighlight> |
||
− | ; Flags |
||
+ | #define AM_VERSION 1 |
||
− | : 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. |
||
+ | struct AppMessage { |
||
− | ; Activation |
||
+ | struct Message am_Message; /* standard message structure */ |
||
− | : The activation should have only RELVERIFY and GADGIMMEDIATE set. |
||
+ | UWORD am_Type; /* message type */ |
||
− | |||
+ | ULONG am_UserData; /* application specific */ |
||
− | ; Type |
||
+ | ULONG am_ID; /* application definable ID */ |
||
− | : The gadget type should be BOOLGADGET. |
||
+ | LONG am_NumArgs; /* # of elements in arglist */ |
||
− | |||
+ | struct WBArg *am_ArgList; /* the arguments themselves */ |
||
− | ; GadgetRender |
||
+ | UWORD am_Version; /* will be AM_VERSION */ |
||
− | : Set this to an appropriate Image structure. |
||
+ | UWORD am_Class; /* message class */ |
||
− | |||
+ | WORD am_MouseX; /* mouse x position of event */ |
||
− | ; SelectRender |
||
+ | WORD am_MouseY; /* mouse y position of event */ |
||
− | : Set this to an appropriate alternate Image structure if and only if the highlight mode is GADGHIMAGE. |
||
+ | ULONG am_Seconds; /* current system clock time */ |
||
− | |||
+ | ULONG am_Micros; /* current system clock time */ |
||
− | 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. |
||
+ | ULONG am_Reserved[8]; |
||
− | |||
− | === 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: |
||
− | |||
− | <pre> |
||
− | 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); |
||
− | </pre> |
||
− | |||
− | 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. |
||
− | |||
− | Release 2 includes a new function named GetDiskObjectNew() that works the same as GetDiskObject(), except that if no ''.info'' file is found, a default DiskObject will be created for you. Also new for Release 2 is DeleteDiskObject() for removing ''.info'' files from disk, and the functions GetDefDiskObject() and PutDefDiskObject() which allow the default icons in ROM to be copied or replaced with new defaults in RAM. |
||
− | |||
− | 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: |
||
− | |||
− | <pre> |
||
− | FILETYPE = PaintProgram | ILBM |
||
− | </pre> |
||
− | |||
− | 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 [[User_Interface_Style_Guide|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. |
||
− | |||
− | <pre>;/* iconexample.c - Execute me to compile me with SAS C 5.10 |
||
− | LC -b1 -cfistq -v -y -j73 iconexample.c |
||
− | Blink FROM LIB:c.o,iconexample.o TO iconexample LIBRARY LIB:LC.lib,LIB:Amiga.lib |
||
− | quit |
||
− | |||
− | ** 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 <clib/alib\_protos.h> |
||
− | #include <clib/exec\_protos.h> |
||
− | #include <clib/dos\_protos.h> |
||
− | #include <clib/icon\_protos.h> |
||
− | #include <stdlib.h> |
||
− | #include <stdio.h> |
||
− | #include <string.h> |
||
− | |||
− | #ifdef LATTICE |
||
− | int CXBRK(void) { return(0); } /* Disable SAS Lattice CTRL/C handling */ |
||
− | int chkabort(void) { return(0); }/* really */ |
||
− | #endif |
||
− | |||
− | /* 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 */ |
||
}; |
}; |
||
+ | </syntaxhighlight> |
||
+ | The AppMessage's am_Type field tells the application which type of AppObject the message is about. The field will be: |
||
− | UBYTE *toolTypes[] = |
||
+ | * AMTYPE_APPWINDOW if the message is about an AppWindow, |
||
− | { |
||
+ | * AMTYPE_APPICON if the message is about an AppIcon, or |
||
− | "FILETYPE=text", |
||
+ | * AMTYPE_APPMENUITEM if the message is about an AppMenuItem. |
||
− | "FLAGS=BOLD|ITALICS", |
||
− | NULL |
||
− | }; |
||
+ | When an application creates an AppObject, it can assign the AppObject application specific data (most likely a pointer) and an ID. Workbench will pass an AppObject's data and ID back to the application when it sends an AppMessage about the AppObject. The AppMessage's am_UserData and am_ID fields hold the user data and the ID. |
||
− | 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 */ |
||
− | }; |
||
+ | The am_NumArgs field tells how many icons were involved in the user's AppObject action. For an AppWindow or AppIcon, am_NumArgs is the number of icons the user dropped on the AppWindow or AppIcon. For an AppMenuItem, am_NumArgs represents the number of icons that were selected when the user selected this AppMenuItem. If no icons were selected during an AppMenuItem event or the user double-clicked on an AppIcon, am_NumArgs will be zero. Workbench does not send AppMessages if the user double-clicks an AppWindow. |
||
− | /* Opens and allocations we must clean up */ |
||
− | struct Library *IconBase = NULL; |
||
− | FILE *conwin = NULL; |
||
− | LONG olddir = -1; |
||
+ | The am_ArgList field is a pointer to a list of WBArgs (from <workbench/startup.h>) corresponding to each icon dropped (or selected). If there were no icons dropped or selected, this field will be NULL. |
||
− | BOOL FromWb; |
||
+ | For future expansion possibilities, the AppMessage structure has a version number. The version number is #defined as AM_VERSION in <workbench/workbench.h>. |
||
− | void main(int argc, char **argv) |
||
− | { |
||
− | struct WBStartup *WBenchMsg; |
||
− | struct WBArg *wbarg; |
||
− | FILE *file; |
||
− | LONG wLen; |
||
− | SHORT i; |
||
+ | The am_MouseX and am_MouseY fields apply only to AppWindows and contain the coordinates of the mouse pointer when the user dropped the icon(s). These coordinates are relative to the AppWindow's upper left corner. |
||
− | FromWb = (argc==0) ? TRUE : FALSE; |
||
+ | The am_Seconds and am_Micros fields represent the time that the event took place. |
||
− | /* Open icon.library */ |
||
− | if(!(IconBase = OpenLibrary("icon.library",33))) |
||
− | cleanexit("Can't open icon.library\n",RETURN\_FAIL); |
||
+ | Any remaining fields are undefined at present and should be set to NULL. |
||
− | /* 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)) |
||
− | { |
||
− | printf("%s data file and icon saved.\n",projname); |
||
− | printf("Use Workbench menu Icon Information to examine the icon.\n"); |
||
− | printf("Then copy this example (iconexample) to RAM:\n"); |
||
− | 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 = 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) CurrentDir(olddir); /* CD back where we were */ |
||
− | } |
||
− | Delay(500); |
||
− | } |
||
− | cleanup(); |
||
− | exit(RETURN\_OK); |
||
− | } |
||
− | |||
− | BOOL makeIcon(UBYTE *name, char **newtooltypes, char *newdeftool) |
||
− | { |
||
− | struct DiskObject *dobj; |
||
− | char *olddeftool; |
||
− | char **oldtooltypes; |
||
− | BOOL success = FALSE; |
||
− | |||
− | if(dobj=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 = PutDiskObject(name,dobj); |
||
− | |||
− | /* we must restore the original pointers before freeing */ |
||
− | dobj->do\_ToolTypes = oldtooltypes; |
||
− | dobj->do\_DefaultTool = olddeftool; |
||
− | FreeDiskObject(dobj); |
||
− | } |
||
− | /* Else, put our default icon */ |
||
− | if(!success) success = 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=GetDiskObject(wbarg->wa\_Name))) |
||
− | { |
||
− | fprintf(conwin," We have read the DiskObject (icon) for this arg\n"); |
||
− | toolarray = (char **)dobj->do\_ToolTypes; |
||
− | |||
− | if(s=(char *)FindToolType(toolarray,"FILETYPE")) |
||
− | { |
||
− | fprintf(conwin," Found tooltype FILETYPE with value %s\n",s); |
||
− | } |
||
− | if(s=(char *)FindToolType(toolarray,"FLAGS")) |
||
− | { |
||
− | fprintf(conwin," Found tooltype FLAGS with value %s\n",s); |
||
− | if(MatchToolValue(s,"BOLD")) |
||
− | fprintf(conwin," BOLD flag requested\n"); |
||
− | if(MatchToolValue(s,"ITALICS")) |
||
− | fprintf(conwin," ITALICS flag requested\n"); |
||
− | } |
||
− | /* Free the diskobject we got */ |
||
− | 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); |
||
− | if(IconBase) CloseLibrary(IconBase); |
||
− | }</pre> |
||
− | == 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 === |
=== Workbench Library Functions === |
||
Line 703: | Line 279: | ||
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: |
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: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | struct AppIcon |
+ | struct AppIcon *AddAppIconA( ULONG, ULONG, char *, struct MsgPort *, struct FileLock *, |
− | + | struct DiskObject *, struct *TagItem ); |
|
− | struct AppMenuItem *AddAppMenuItemA( ULONG, ULONG, char *, struct MsgPort *, |
+ | struct AppMenuItem *AddAppMenuItemA( ULONG, ULONG, char *, struct MsgPort *, struct *TagItem); |
− | + | struct AppWindow *AddAppWindowA( ULONG, ULONG, struct Window *, struct MsgPort *, struct *TagItem); |
|
− | struct AppWindow *AddAppWindowA( ULONG, ULONG, struct Window *, struct MsgPort *, |
||
− | struct *TagItem); |
||
− | BOOL |
+ | BOOL RemoveAppIcon(struct AppIcon *); |
− | BOOL |
+ | BOOL RemoveAppMenuItem(struct AppMenuItem *); |
− | BOOL |
+ | BOOL RemoveAppWindow(struct AppWindow *); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
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. |
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. |
||
Line 720: | Line 294: | ||
=== An AppIcon Example === |
=== 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 |
+ | 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. |
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. |
||
+ | <syntaxhighlight> |
||
− | <pre>/* appicon.c - Compiled under SAS C 5.10 with lc -L appicon.c */ |
||
+ | /* appicon.c |
||
− | /* Requires Kickstart version 37 or later. Works from the Shell (CLI) only */ |
||
+ | /* Works from the Shell (CLI) only */ |
||
− | #include |
+ | #include <exec/types.h> /* Need this for the Amiga variable types */ |
− | #include |
+ | #include <workbench/workbench.h> /* This has DiskObject and AppIcon structs */ |
− | #include |
+ | #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ |
− | #include |
+ | #include <exec/libraries.h> /* Need this to check library versions */ |
− | #include |
+ | #include <proto/icon.h> /* Icon (DiskObject) function prototypes */ |
− | #include |
+ | #include <proto/exec.h> /* Exec message, port and library functions*/ |
− | #include |
+ | #include <proto/workbench.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 *IconBase; |
||
struct Library *WorkbenchBase; |
struct Library *WorkbenchBase; |
||
+ | struct IconIFace *IIcon; |
||
+ | struct WorkbenchIFace *IWorkbench; |
||
− | + | int main(int argc, char **argv) |
|
{ |
{ |
||
− | struct DiskObject *dobj=NULL; |
+ | struct DiskObject *dobj=NULL; |
− | struct MsgPort *myport=NULL; |
+ | struct MsgPort *myport=NULL; |
− | struct AppIcon *appicon=NULL; |
+ | struct AppIcon *appicon=NULL; |
− | struct AppMessage *appmsg=NULL; |
+ | struct AppMessage *appmsg=NULL; |
− | LONG dropcount=0L; |
+ | LONG dropcount=0L; |
− | ULONG x; |
+ | ULONG x; |
− | BOOL success=0L; |
+ | BOOL success=0L; |
+ | IconBase = IExec->OpenLibrary("icon.library", 50); |
||
− | /* Get the the right version of the Icon Library, initialize IconBase */ |
||
− | + | WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); |
|
+ | |||
+ | IIcon = (struct IconIFace*)IExec->GetInterface(IconBase, "main", 1, NULL); |
||
+ | IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); |
||
+ | |||
+ | /* Get the the right version of the Icon Library, initialize IconBase */ |
||
+ | if(IIcon != NULL && IWorkbench != NULL) |
||
{ |
{ |
||
− | /* 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 */ |
/* This is the easy way to get some icon imagery */ |
||
/* Real applications should use custom imagery */ |
/* Real applications should use custom imagery */ |
||
− | dobj=GetDefDiskObject(WBDISK); |
+ | dobj = IIcon->GetDefDiskObject(WBDISK); |
− | if(dobj!=0) |
+ | if(dobj != 0) |
{ |
{ |
||
/* The type must be set to NULL for a WBAPPICON */ |
/* The type must be set to NULL for a WBAPPICON */ |
||
− | dobj- |
+ | dobj->do_Type = NULL; |
+ | myport = IExec->AllocSysObjectTags(ASOT_PORT, NULL); |
||
− | /* The CreateMsgPort() function is in Exec version 37 and later only */ |
||
− | myport=CreateMsgPort(); |
||
if(myport) |
if(myport) |
||
{ |
{ |
||
/* Put the AppIcon up on the Workbench window */ |
/* Put the AppIcon up on the Workbench window */ |
||
− | appicon=AddAppIconA(0L,0L, |
+ | appicon = IWorkbench->AddAppIconA(0L,0L,"TestAppIcon",myport,NULL,dobj,NULL); |
if(appicon) |
if(appicon) |
||
{ |
{ |
||
/* For the sake of this example, we allow the AppIcon */ |
/* For the sake of this example, we allow the AppIcon */ |
||
/* to be activated only five times. */ |
/* to be activated only five times. */ |
||
− | + | IDOS->Printf("Drop files on the Workbench AppIcon\n"); |
|
− | + | IDOS->Printf("Example exits after 5 drops\n"); |
|
− | while(dropcount |
+ | while(dropcount<5) |
{ |
{ |
||
/* Here's the main event loop where we wait for */ |
/* Here's the main event loop where we wait for */ |
||
/* messages to show up from the AppIcon */ |
/* messages to show up from the AppIcon */ |
||
− | WaitPort(myport); |
+ | IExec->WaitPort(myport); |
/* Might be more than one message at the port... */ |
/* Might be more than one message at the port... */ |
||
− | while(appmsg=(struct AppMessage *)GetMsg(myport)) |
+ | while(appmsg=(struct AppMessage *)IExec->GetMsg(myport)) |
{ |
{ |
||
− | if(appmsg- |
+ | if(appmsg->am_NumArgs==0L) |
{ |
{ |
||
/* If NumArgs is 0 the AppIcon was activated directly */ |
/* If NumArgs is 0 the AppIcon was activated directly */ |
||
− | + | IDOS->Printf("User activated the AppIcon.\n"); |
|
− | + | IDOS->Printf("A Help window for the user would be good here\n"); |
|
} |
} |
||
− | else if(appmsg- |
+ | else if(appmsg->am_NumArgs>0L) |
{ |
{ |
||
− | /* If NumArgs is |
+ | /* If NumArgs is >0 the AppIcon was activated by */ |
/* having one or more icons dropped on top of it */ |
/* having one or more icons dropped on top of it */ |
||
− | + | IDOS->Printf("User dropped %ld icons on the AppIcon\n", |
|
− | appmsg- |
+ | appmsg->am_NumArgs); |
− | for(x=0;x |
+ | for(x=0;x<appmsg->am_NumArgs;x++) |
{ |
{ |
||
− | + | IDOS->Printf("#%ld name='%s'\n",x+1,appmsg->am_ArgList[x].wa_Name); |
|
} |
} |
||
} |
} |
||
/* Let Workbench know we're done with the message */ |
/* Let Workbench know we're done with the message */ |
||
− | ReplyMsg((struct Message *)appmsg); |
+ | IExec->ReplyMsg((struct Message *)appmsg); |
} |
} |
||
dropcount++; |
dropcount++; |
||
} |
} |
||
− | success=RemoveAppIcon(appicon); |
+ | success = IWorkbench->RemoveAppIcon(appicon); |
} |
} |
||
/* Clear away any messages that arrived at the last moment */ |
/* Clear away any messages that arrived at the last moment */ |
||
− | while(appmsg=(struct AppMessage *)GetMsg(myport)) |
+ | while(appmsg = (struct AppMessage *)IExec->GetMsg(myport)) |
− | ReplyMsg((struct Message *)appmsg); |
+ | IExec->ReplyMsg((struct Message *)appmsg); |
− | + | IExec->FreeSysObject(ASOT_PORT, myport); |
|
} |
} |
||
− | FreeDiskObject(dobj); |
+ | IIcon->FreeDiskObject(dobj); |
} |
} |
||
+ | |||
− | CloseLibrary(WorkbenchBase); |
||
+ | IExec->DropInterface((struct Interface*)IWorkbench); |
||
− | } |
||
+ | IExec->DropInterface((struct Interface*)IIcon); |
||
− | CloseLibrary(IconBase); |
||
+ | |||
− | } |
||
+ | IExec->CloseLibrary(WorkbenchBase); |
||
− | }</pre> |
||
+ | IExec->CloseLibrary(IconBase); |
||
+ | |||
+ | return 0; |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | |||
=== An AppMenuItem Example === |
=== An AppMenuItem Example === |
||
− | This example shows how to create an AppMenuItem. The example adds a menu item named |
+ | 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 volume.) |
− | The example starts up the More program as a separate, asynchronous process using the |
+ | The example starts up the More program as a separate, asynchronous process using the SystemTags() function. When the AppMenuItem has been activated five times, the program exits after freeing any system resources it has used. |
+ | <syntaxhighlight> |
||
− | <pre>/* appmenuitem.c - Compiled under SAS C 5.10 with lc -L appmenuitem.c */ |
||
+ | /* appmenuitem.c |
||
− | /* Requires Kickstart version 37 or later. Works from the Shell (CLI) only */ |
||
+ | /* Works from the Shell (CLI) only */ |
||
− | #include |
+ | #include <exec/types.h> /* Need this for the Amiga variable types */ |
− | #include |
+ | #include <workbench/workbench.h> /* This has DiskObject and AppIcon structs */ |
− | #include |
+ | #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ |
− | #include |
+ | #include <exec/libraries.h> |
− | #include |
+ | #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 */ |
||
+ | #include <proto/dos.h> |
||
− | #ifdef LATTICE |
||
+ | #include <proto/exec.h> /* Exec message, port and library functions*/ |
||
− | int CXBRK(void) { return(0); } /* Disable Lattice CTRL/C handling */ |
||
+ | #include <proto/workbench.h> /* AppMenuItem function protos */ |
||
− | int chkabort(void) { return(0); }/* really */ |
||
− | #endif |
||
− | extern struct Library *SysBase; |
||
struct Library *WorkbenchBase; |
struct Library *WorkbenchBase; |
||
+ | struct WorkbenchIFace *IWorkbench; |
||
− | + | int main(int argc, char **argv) |
|
{ |
{ |
||
− | struct MsgPort *myport=NULL; |
+ | struct MsgPort *myport=NULL; |
− | struct AppMenuItem *appitem=NULL; |
+ | struct AppMenuItem *appitem=NULL; |
− | struct AppMessage *appmsg=NULL; |
+ | struct AppMessage *appmsg=NULL; |
− | LONG result, x, count=0L; |
+ | LONG result, x, count=0L; |
− | BOOL success=0L; |
+ | BOOL success=0L; |
− | BPTR file; |
+ | BPTR file; |
− | + | WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); |
|
+ | IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); |
||
+ | |||
+ | if (IWorkbench != NULL) |
||
{ |
{ |
||
+ | if(myport = IExec->AllocSysObjectTags(ASOT_PORT, NULL)) |
||
− | /* The CreateMsgPort() function is in Exec version 37 and later only */ |
||
− | if(myport = CreateMsgPort()) |
||
{ |
{ |
||
/* Add our own AppMenuItem to the Workbench Tools Menu */ |
/* Add our own AppMenuItem to the Workbench Tools Menu */ |
||
− | appitem=AddAppMenuItemA(0L, /* Our ID# for item */ |
+ | appitem = IWorkbench->AddAppMenuItemA(0L, /* Our ID# for item */ |
− | (ULONG) |
+ | (ULONG)"SYS:Utilities/More", /* Our UserData */ |
− | + | "Browse Files", /* MenuItem Text */ |
|
myport,NULL); /* MsgPort, no tags */ |
myport,NULL); /* MsgPort, no tags */ |
||
if(appitem) |
if(appitem) |
||
{ |
{ |
||
− | + | IDOS->Printf("Select Workbench Tools demo menuitem 'Browse Files'\n"); |
|
/* For this example, we allow the AppMenuItem to be selected */ |
/* For this example, we allow the AppMenuItem to be selected */ |
||
/* only once, then we remove it and exit */ |
/* only once, then we remove it and exit */ |
||
− | WaitPort(myport); |
+ | IExec->WaitPort(myport); |
− | while((appmsg=(struct AppMessage *)GetMsg(myport)) & |
+ | while((appmsg = (struct AppMessage *)IExec->GetMsg(myport)) && (count<1)) |
{ |
{ |
||
/* Handle messages from the AppMenuItem - we have only one */ |
/* Handle messages from the AppMenuItem - we have only one */ |
||
− | /* item so we don't have to check its appmsg- |
+ | /* item so we don't have to check its appmsg->am_ID number. */ |
/* We'll System() the command string that we passed as */ |
/* We'll System() the command string that we passed as */ |
||
/* userdata when we added the menu item. */ |
/* userdata when we added the menu item. */ |
||
− | /* We find our userdata pointer in appmsg- |
+ | /* We find our userdata pointer in appmsg->am_UserData */ |
− | + | IDOS->Printf("User picked AppMenuItem with %ld icons selected\n", |
|
− | appmsg- |
+ | appmsg->am_NumArgs); |
− | for(x=0;x |
+ | for(x=0;x<appmsg->am_NumArgs;x++) |
− | + | IDOS->Printf(" #%ld name='%s'\n",x+1,appmsg->am_ArgList[x].wa_Name); |
|
count++; |
count++; |
||
− | if( file=Open( |
+ | if( file=Open("CON:0/40/640/150/AppMenu Example/auto/close/wait", |
− | + | MODE_OLDFILE) ) /* for any stdio output */ |
|
{ |
{ |
||
− | result=SystemTags((UBYTE *)appmsg- |
+ | result=IDOS->SystemTags((UBYTE *)appmsg->am_UserData,SYS_Input,file, |
− | + | SYS_Output,NULL, |
|
− | + | SYS_Asynch,TRUE, |
|
− | + | TAG_END); |
|
/* If Asynch System() itself fails, we must close file */ |
/* If Asynch System() itself fails, we must close file */ |
||
− | if(result == -1) Close(file); |
+ | if(result == -1) IDOS->Close(file); |
} |
} |
||
− | ReplyMsg((struct Message *)appmsg); |
+ | IExec->ReplyMsg((struct Message *)appmsg); |
} |
} |
||
− | success=RemoveAppMenuItem(appitem); |
+ | success = IWorkbench->RemoveAppMenuItem(appitem); |
} |
} |
||
/* Clear away any messages that arrived at the last moment */ |
/* Clear away any messages that arrived at the last moment */ |
||
/* and let Workbench know we're done with the messages */ |
/* and let Workbench know we're done with the messages */ |
||
− | while(appmsg=(struct AppMessage *)GetMsg(myport)) |
+ | while(appmsg=(struct AppMessage *)IExec->GetMsg(myport)) |
{ |
{ |
||
− | ReplyMsg((struct Message *)appmsg); |
+ | IExec->ReplyMsg((struct Message *)appmsg); |
} |
} |
||
− | + | IExec->FreeSysObject(ASOT_PORT, myport); |
|
} |
} |
||
− | CloseLibrary(WorkbenchBase); |
||
− | } |
||
− | }</pre> |
||
− | === An AppWindow Example === |
||
+ | IExec->DropInterface((struct Interface*)IWorkbench); |
||
− | 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. |
||
+ | IExec->CloseLibrary(WorkbenchBase); |
||
+ | |||
+ | return 0; |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | === An AppWindow Example === |
||
− | <pre>/* 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 */ |
||
+ | 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. |
||
− | #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 */ |
||
+ | <syntaxhighlight> |
||
− | #include <stdio.h> |
||
+ | /* appwindow.c */ |
||
+ | /* Works from the Shell (CLI) only */ |
||
+ | #include <exec/types.h> /* Need this for the Amiga variable types */ |
||
− | #include <clib/intuition\_protos.h> |
||
+ | #include <workbench/workbench.h> /* This has DiskObject and AppWindow */ |
||
− | #include <clib/exec\_protos.h> |
||
+ | #include <workbench/startup.h> /* This has WBStartup and WBArg structs */ |
||
− | #include <clib/wb\_protos.h> |
||
+ | #include <exec/libraries.h> /* Need this to check library versions */ |
||
+ | #include <proto/exec.h> |
||
− | #ifdef LATTICE |
||
+ | #include <proto/dos.h> |
||
− | int CXBRK(void) { return(0); } /* Disable SAS Lattice CTRL/C handling */ |
||
+ | #include <proto/intuition.h> |
||
− | int chkabort(void) { return(0); }/* really */ |
||
+ | #include <proto/workbench.h> |
||
− | #endif |
||
− | struct Library *IntuitionBase; |
+ | struct Library *IntuitionBase; |
− | struct Library *WorkbenchBase; |
+ | struct Library *WorkbenchBase; |
+ | struct IntuitionIFace *IIntuition; |
||
+ | struct WorkbenchIFace *IWorkbench; |
||
− | + | int main(int argc, char **argv) |
|
{ |
{ |
||
struct MsgPort *awport; |
struct MsgPort *awport; |
||
Line 960: | Line 539: | ||
int i; |
int i; |
||
− | + | IntuitionBase = IExec->OpenLibrary("intuition.library", 50); |
|
+ | WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); |
||
+ | |||
+ | IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); |
||
+ | IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); |
||
+ | |||
+ | if (IIntuition != NULL) |
||
+ | { |
||
+ | if (IWorkbench != NULL) |
||
{ |
{ |
||
+ | if (awport = IExec->AllocSysObjectTags(ASOT_PORT, NULL)) |
||
− | if (WorkbenchBase = OpenLibrary("workbench.library", 37)) |
||
− | { |
||
− | /* The CreateMsgPort() function is in Exec version 37 and later only */ |
||
− | if (awport = CreateMsgPort()) |
||
{ |
{ |
||
− | if (win = OpenWindowTags(NULL, |
+ | if (win = IIntuition->OpenWindowTags(NULL, |
− | + | WA_Width, 200, WA_Height, 50, |
|
− | + | WA_IDCMP, CLOSEWINDOW, |
|
− | + | WA_Flags, WINDOWCLOSE | WINDOWDRAG, |
|
− | + | WA_Title, "AppWindow", |
|
− | + | TAG_END)) |
|
{ |
{ |
||
− | if (appwin = AddAppWindow(id, userdata, win, awport, NULL)) |
+ | if (appwin = IWorkbench->AddAppWindow(id, userdata, win, awport, NULL)) |
{ |
{ |
||
− | + | IDOS->Printf("AppWindow added... Drag files into AppWindow\n"); |
|
− | winsig = 1L |
+ | winsig = 1L << win->UserPort->mp_SigBit; |
− | appwinsig = 1L |
+ | appwinsig = 1L << awport->mp_SigBit; |
while (! done) |
while (! done) |
||
{ |
{ |
||
/* Wait for IDCMP messages and AppMessages */ |
/* Wait for IDCMP messages and AppMessages */ |
||
− | signals = Wait( winsig | appwinsig ); |
+ | signals = IExec->Wait( winsig | appwinsig ); |
− | if(signals & |
+ | if(signals & winsig) /* Got an IDCMP message */ |
{ |
{ |
||
− | while (imsg = (struct IntuiMessage *) GetMsg(win- |
+ | while (imsg = (struct IntuiMessage *) IExec->GetMsg(win->UserPort)) |
{ |
{ |
||
− | if (imsg- |
+ | if (imsg->Class = CLOSEWINDOW) done = TRUE; |
− | ReplyMsg((struct Message *) imsg); |
+ | IExec->ReplyMsg((struct Message *) imsg); |
} |
} |
||
} |
} |
||
− | if(signals & |
+ | if(signals & appwinsig) /* Got an AppMessage */ |
{ |
{ |
||
− | while (amsg = (struct AppMessage *) GetMsg(awport)) |
+ | while (amsg = (struct AppMessage *) IExec->GetMsg(awport)) |
{ |
{ |
||
− | + | IDOS->Printf("AppMsg: Type=%ld, ID=%ld, NumArgs=%ld\n", |
|
− | amsg- |
+ | amsg->am_Type, amsg->am_ID, amsg->am_NumArgs); |
− | argptr = amsg- |
+ | argptr = amsg->am_ArgList; |
− | for (i = 0; i |
+ | for (i = 0; i < amsg->am_NumArgs; i++) |
{ |
{ |
||
− | + | IDOS->Printf(" arg(%ld): Name='%s', Lock=%lx\n", |
|
− | i, argptr- |
+ | i, argptr->wa_Name, argptr->wa_Lock); |
argptr++; |
argptr++; |
||
} |
} |
||
− | ReplyMsg((struct Message *) amsg); |
+ | IExec->ReplyMsg((struct Message *) amsg); |
} |
} |
||
} |
} |
||
} /* done */ |
} /* done */ |
||
− | RemoveAppWindow(appwin); |
+ | IWorkbench->RemoveAppWindow(appwin); |
} |
} |
||
− | CloseWindow(win); |
+ | IIntuition->CloseWindow(win); |
} |
} |
||
/* Make sure there are no more outstanding messages */ |
/* Make sure there are no more outstanding messages */ |
||
− | while(amsg = (struct AppMessage *)GetMsg(awport)) |
+ | while(amsg = (struct AppMessage *)IExec->GetMsg(awport)) |
− | ReplyMsg((struct Message *)amsg); |
+ | IExec->ReplyMsg((struct Message *)amsg); |
− | + | IExec->FreeSysObject(ASOT_PORT, awport); |
|
} |
} |
||
− | CloseLibrary(WorkbenchBase); |
||
} |
} |
||
− | CloseLibrary(IntuitionBase); |
||
} |
} |
||
+ | |||
− | }</pre> |
||
+ | IExec->DropInterface((struct Interface*)IIntuition); |
||
+ | IExec->DropInterface((struct Interface*)IWorkbench); |
||
+ | IExec->CloseLibrary(IntuitionBase); |
||
+ | IExec->CloseLibrary(WorkbenchBase); |
||
+ | |||
+ | return 0; |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | |||
== Workbench and the Startup Code Module == |
== Workbench and the Startup Code Module == |
||
Line 1,042: | Line 633: | ||
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. |
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. |
||
+ | {{Note|title=Do Not Use the Process Message Port for Anything Else|text=The message port in a Process structure is for the exclusive use of the DOS library.}} |
||
− | {| class="wikitable" |
||
− | | ''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. |
+ | Standard startup code will pass the WBStartup message pointer in argv and 0 (zero) in argc if the program is started from Workbench. 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. |
+ | {{Note|title=Avoid the DOS Exit() function|text=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>.}} |
||
− | {| class="wikitable" |
||
− | | ''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 === |
=== Shell Startup === |
||
Line 1,078: | Line 665: | ||
== Function Reference == |
== Function Reference == |
||
− | The following are brief descriptions of the functions in workbench |
+ | The following are brief descriptions of the functions in workbench.library. See SDK for details on each function call. |
+ | {| class="wikitable" |
||
− | Icon Library Functions |
||
+ | |+Workbench Library Functions |
||
− | |||
+ | |- |
||
− | <table> |
||
+ | ! Function |
||
− | <tr class="header"> |
||
+ | ! Description |
||
− | <th align="left">'''Function'''</th> |
||
+ | |- |
||
− | <th align="left">'''Description'''</th> |
||
+ | | AddAppIcon() |
||
− | </tr> |
||
+ | | Add an AppIcon to Workbench |
||
− | <tr class="odd"> |
||
+ | |- |
||
− | <td align="left">GetDiskObject()</td> |
||
+ | | AddAppMenuItem() |
||
− | <td align="left">Read the ''.info'' file of an icon into a DiskObject structure</td> |
||
+ | | Add an AppMenuItem to the Workbench Tools menu |
||
− | </tr> |
||
+ | |- |
||
− | <tr class="even"> |
||
+ | | AddAppWindow() |
||
− | <td align="left">GetDiskObjectNew()</td> |
||
+ | | Add an AppWindow to Workbench |
||
− | <td align="left">Same as GetDiskObject() but returns a default icon if none exists</td> |
||
+ | |- |
||
− | </tr> |
||
+ | | RemoveAppIcon() |
||
− | <tr class="odd"> |
||
+ | | Remove an AppIcon to Workbench |
||
− | <td align="left">PutDiskObject()</td> |
||
+ | |- |
||
− | <td align="left">Write a DiskObject structure to disk as a ''.info'' file</td> |
||
+ | | RemoveAppMenuItem() |
||
− | </tr> |
||
+ | | Remove an AppMenuItem to the Workbench Tools menu |
||
− | <tr class="even"> |
||
+ | |- |
||
− | <td align="left">FreeDiskObject()</td> |
||
+ | | RemoveAppWindow() |
||
− | <td align="left">Free the DiskObject structure created by GetDiskObject()</td> |
||
+ | | Remove an AppWindow to Workbench |
||
− | </tr> |
||
+ | |} |
||
− | <tr class="odd"> |
||
− | <td align="left">DeleteDiskObject()</td> |
||
− | <td align="left">Deletes a given ''.info'' file from disk</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">FindToolType()</td> |
||
− | <td align="left">Return the value of an entry in the icon‚Äôs Tool Type array</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">MatchToolValue()</td> |
||
− | <td align="left">Check a Tool Type entry against a given value</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">GetDefDiskObject()</td> |
||
− | <td align="left">Read the default icon for a given icon type</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">PutDefDiskObject()</td> |
||
− | <td align="left">Replace the default icon for a given icon type (V36)</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">AddFreeList()</td> |
||
− | <td align="left">Add memory you have allocated to a FreeList</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">FreeFreeList()</td> |
||
− | <td align="left">Free all the memory for entries in the FreeList</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">BumpRevision()</td> |
||
− | <td align="left">Create a new name for a second copy of a Workbench object</td> |
||
− | </tr> |
||
− | </table> |
||
− | |||
− | Workbench Library Functions |
||
− | |||
− | <table> |
||
− | <tr class="header"> |
||
− | <th align="left">'''Function'''</th> |
||
− | <th align="left">'''Description'''</th> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">AddAppIcon()</td> |
||
− | <td align="left">Add an AppIcon to Workbench</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">AddAppMenuItem()</td> |
||
− | <td align="left">Add an AppMenuItem to the Workbench Tools menu</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">AddAppWindow()</td> |
||
− | <td align="left">Add an AppWindow to Workbench</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">RemoveAppIcon()</td> |
||
− | <td align="left">Remove an AppIcon to Workbench</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">RemoveAppMenuItem()</td> |
||
− | <td align="left">Remove an AppMenuItem to the Workbench Tools menu</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">RemoveAppWindow()</td> |
||
− | <td align="left">Remove an AppWindow to Workbench</td> |
||
− | </tr> |
||
− | </table> |
Latest revision as of 11:00, 19 February 2024
Contents
Workbench Library
Workbench is the graphic user interface to the Amiga file system that uses symbols called icons to represent disks, directories and files. This article shows how to use workbench.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.
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 Icon Library for a discussion of the icon library functions.
The icon type can be set and the icon imagery edited with the IconEdit program. 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.
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 Workbench Library
Workbench arguments are sent to an application when it is started. There are also special facilities 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.
The AppMessage Structure
When Workbench notifies an application of AppWindow, AppIcon, or AppMenuItem activity, it sends an AppMessage to the application's message port (from <workbench/workbench.h>):
#define AM_VERSION 1 struct AppMessage { struct Message am_Message; /* standard message structure */ UWORD am_Type; /* message type */ ULONG am_UserData; /* application specific */ ULONG am_ID; /* application definable ID */ LONG am_NumArgs; /* # of elements in arglist */ struct WBArg *am_ArgList; /* the arguments themselves */ UWORD am_Version; /* will be AM_VERSION */ UWORD am_Class; /* message class */ WORD am_MouseX; /* mouse x position of event */ WORD am_MouseY; /* mouse y position of event */ ULONG am_Seconds; /* current system clock time */ ULONG am_Micros; /* current system clock time */ ULONG am_Reserved[8]; };
The AppMessage's am_Type field tells the application which type of AppObject the message is about. The field will be:
- AMTYPE_APPWINDOW if the message is about an AppWindow,
- AMTYPE_APPICON if the message is about an AppIcon, or
- AMTYPE_APPMENUITEM if the message is about an AppMenuItem.
When an application creates an AppObject, it can assign the AppObject application specific data (most likely a pointer) and an ID. Workbench will pass an AppObject's data and ID back to the application when it sends an AppMessage about the AppObject. The AppMessage's am_UserData and am_ID fields hold the user data and the ID.
The am_NumArgs field tells how many icons were involved in the user's AppObject action. For an AppWindow or AppIcon, am_NumArgs is the number of icons the user dropped on the AppWindow or AppIcon. For an AppMenuItem, am_NumArgs represents the number of icons that were selected when the user selected this AppMenuItem. If no icons were selected during an AppMenuItem event or the user double-clicked on an AppIcon, am_NumArgs will be zero. Workbench does not send AppMessages if the user double-clicks an AppWindow.
The am_ArgList field is a pointer to a list of WBArgs (from <workbench/startup.h>) corresponding to each icon dropped (or selected). If there were no icons dropped or selected, this field will be NULL.
For future expansion possibilities, the AppMessage structure has a version number. The version number is #defined as AM_VERSION in <workbench/workbench.h>.
The am_MouseX and am_MouseY fields apply only to AppWindows and contain the coordinates of the mouse pointer when the user dropped the icon(s). These coordinates are relative to the AppWindow's upper left corner.
The am_Seconds and am_Micros fields represent the time that the event took place.
Any remaining fields are undefined at present and should be set to NULL.
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 /* 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 <proto/icon.h> /* Icon (DiskObject) function prototypes */ #include <proto/exec.h> /* Exec message, port and library functions*/ #include <proto/workbench.h> /* AppIcon function protos */ struct Library *IconBase; struct Library *WorkbenchBase; struct IconIFace *IIcon; struct WorkbenchIFace *IWorkbench; int 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; IconBase = IExec->OpenLibrary("icon.library", 50); WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); IIcon = (struct IconIFace*)IExec->GetInterface(IconBase, "main", 1, NULL); IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); /* Get the the right version of the Icon Library, initialize IconBase */ if(IIcon != NULL && IWorkbench != NULL) { /* This is the easy way to get some icon imagery */ /* Real applications should use custom imagery */ dobj = IIcon->GetDefDiskObject(WBDISK); if(dobj != 0) { /* The type must be set to NULL for a WBAPPICON */ dobj->do_Type = NULL; myport = IExec->AllocSysObjectTags(ASOT_PORT, NULL); if(myport) { /* Put the AppIcon up on the Workbench window */ appicon = IWorkbench->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. */ IDOS->Printf("Drop files on the Workbench AppIcon\n"); IDOS->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 */ IExec->WaitPort(myport); /* Might be more than one message at the port... */ while(appmsg=(struct AppMessage *)IExec->GetMsg(myport)) { if(appmsg->am_NumArgs==0L) { /* If NumArgs is 0 the AppIcon was activated directly */ IDOS->Printf("User activated the AppIcon.\n"); IDOS->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 */ IDOS->Printf("User dropped %ld icons on the AppIcon\n", appmsg->am_NumArgs); for(x=0;x<appmsg->am_NumArgs;x++) { IDOS->Printf("#%ld name='%s'\n",x+1,appmsg->am_ArgList[x].wa_Name); } } /* Let Workbench know we're done with the message */ IExec->ReplyMsg((struct Message *)appmsg); } dropcount++; } success = IWorkbench->RemoveAppIcon(appicon); } /* Clear away any messages that arrived at the last moment */ while(appmsg = (struct AppMessage *)IExec->GetMsg(myport)) IExec->ReplyMsg((struct Message *)appmsg); IExec->FreeSysObject(ASOT_PORT, myport); } IIcon->FreeDiskObject(dobj); } IExec->DropInterface((struct Interface*)IWorkbench); IExec->DropInterface((struct Interface*)IIcon); IExec->CloseLibrary(WorkbenchBase); IExec->CloseLibrary(IconBase); return 0; }
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 volume.)
The example starts up the More program as a separate, asynchronous process using the SystemTags() function. When the AppMenuItem has been activated five times, the program exits after freeing any system resources it has used.
/* appmenuitem.c /* 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 <proto/dos.h> #include <proto/exec.h> /* Exec message, port and library functions*/ #include <proto/workbench.h> /* AppMenuItem function protos */ struct Library *WorkbenchBase; struct WorkbenchIFace *IWorkbench; int 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; WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); if (IWorkbench != NULL) { if(myport = IExec->AllocSysObjectTags(ASOT_PORT, NULL)) { /* Add our own AppMenuItem to the Workbench Tools Menu */ appitem = IWorkbench->AddAppMenuItemA(0L, /* Our ID# for item */ (ULONG)"SYS:Utilities/More", /* Our UserData */ "Browse Files", /* MenuItem Text */ myport,NULL); /* MsgPort, no tags */ if(appitem) { IDOS->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 */ IExec->WaitPort(myport); while((appmsg = (struct AppMessage *)IExec->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 */ IDOS->Printf("User picked AppMenuItem with %ld icons selected\n", appmsg->am_NumArgs); for(x=0;x<appmsg->am_NumArgs;x++) IDOS->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=IDOS->SystemTags((UBYTE *)appmsg->am_UserData,SYS_Input,file, SYS_Output,NULL, SYS_Asynch,TRUE, TAG_END); /* If Asynch System() itself fails, we must close file */ if(result == -1) IDOS->Close(file); } IExec->ReplyMsg((struct Message *)appmsg); } success = IWorkbench->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 *)IExec->GetMsg(myport)) { IExec->ReplyMsg((struct Message *)appmsg); } IExec->FreeSysObject(ASOT_PORT, myport); } IExec->DropInterface((struct Interface*)IWorkbench); IExec->CloseLibrary(WorkbenchBase); return 0; }
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 */ /* 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 <proto/exec.h> #include <proto/dos.h> #include <proto/intuition.h> #include <proto/workbench.h> struct Library *IntuitionBase; struct Library *WorkbenchBase; struct IntuitionIFace *IIntuition; struct WorkbenchIFace *IWorkbench; int 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; IntuitionBase = IExec->OpenLibrary("intuition.library", 50); WorkbenchBase = IExec->OpenLibrary("workbench.library", 50); IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); IWorkbench = (struct WorkbenchIFace*)IExec->GetInterface(WorkbenchBase, "main", 1, NULL); if (IIntuition != NULL) { if (IWorkbench != NULL) { if (awport = IExec->AllocSysObjectTags(ASOT_PORT, NULL)) { if (win = IIntuition->OpenWindowTags(NULL, WA_Width, 200, WA_Height, 50, WA_IDCMP, CLOSEWINDOW, WA_Flags, WINDOWCLOSE | WINDOWDRAG, WA_Title, "AppWindow", TAG_END)) { if (appwin = IWorkbench->AddAppWindow(id, userdata, win, awport, NULL)) { IDOS->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 = IExec->Wait( winsig | appwinsig ); if(signals & winsig) /* Got an IDCMP message */ { while (imsg = (struct IntuiMessage *) IExec->GetMsg(win->UserPort)) { if (imsg->Class = CLOSEWINDOW) done = TRUE; IExec->ReplyMsg((struct Message *) imsg); } } if(signals & appwinsig) /* Got an AppMessage */ { while (amsg = (struct AppMessage *) IExec->GetMsg(awport)) { IDOS->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++) { IDOS->Printf(" arg(%ld): Name='%s', Lock=%lx\n", i, argptr->wa_Name, argptr->wa_Lock); argptr++; } IExec->ReplyMsg((struct Message *) amsg); } } } /* done */ IWorkbench->RemoveAppWindow(appwin); } IIntuition->CloseWindow(win); } /* Make sure there are no more outstanding messages */ while(amsg = (struct AppMessage *)IExec->GetMsg(awport)) IExec->ReplyMsg((struct Message *)amsg); IExec->FreeSysObject(ASOT_PORT, awport); } } } IExec->DropInterface((struct Interface*)IIntuition); IExec->DropInterface((struct Interface*)IWorkbench); IExec->CloseLibrary(IntuitionBase); IExec->CloseLibrary(WorkbenchBase); return 0; }
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. 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. See SDK for details on each function call.
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 |