Introduction The following routines are an implementation of a global clipboard facility. It is used in the ST HomePak package, and it has received the approval of Atari as the recommended way of handling passing data between programs. The Clipboard Manager is implemented in two parts. The first part is a wedge into the Operating System, loaded as a boot process in the \AUTO folder. There are a total of seven routines in the Manager, invoked via TRAP #10. As such, it can be called by any program (and Desk Accessories!) and is a convenient, easy-to-use method to pass data between various processes. The second part is the glue routines package which you must include in your program in order to properly reference the TRAP vector. The Clipboard Manager was written by Russ Wetmore, and has been released to the public domain in the hopes that it will help unite new programs by allowing them to share data. You can include the CLIP.PRG wedge file with your programs at no charge, and you can use the routines below in your own source code. Using the Clipboard Manager The clipboard actually exists in two places: in memory, and on a disk storage file (usually) named CLIP.SYS in the root directory of the default drive. When a program is first run, the first call should always be to InfoClip(), which tells you: 1) Whether or not the clipboard routines have been loaded. The clipboard is accessed through calls to TRAP #10, and the routines must previously have been loaded and initialized at boot time in order for things to work. It's more than possible that someone would try to run your program after copying it to another disk or forgetting to put CLIP.PRG on their boot disk. 2) Where the clipboard's variables are kept. Your program needs constant access to several of these variables, and should keep a global variable handy which points to them. If InfoClip() doesn't return a favorable response, do NOT attempt to call the other routines (you'll be sorry.) Inform the user to install CLIP.PRG in the \AUTO folder of their boot disk or, alternately, just disallow any clipboard calls. What happens next depends on whether or not you're an application or a desk accessory. It is up to an application to perform the maintenance work on the clipboard. Desk accessories are "guests in application's homes" and as such shouldn't ever do more than just read/write data to/from the clipboard. The last thing an application wants is to have a desk accessory change the clipboard out from underneath it. An application then calls LoadClip() to read the clipboard disk file into the memory image. Conversely, when your program is finished, you should call UnloadClip() to put the memory image back on disk, so that other programs can find it. You fetch data from the clipboard using the GetClip() call. To write to the clipboard, you call PutClip(). ANY WRITES TO THE CLIPBOARD ACT STRICTLY ON THE MEMORY IMAGE. Reads from the clipboard will only read in the data from disk if it hasn't previously been loaded in. It is up to you to explicitly store the memory image back to the clipboard disk file when your program terminates with UnloadClip(). You can determine via InfoClip() whether or not the clipboard memory image is "dirty" and needs to be written back to disk. Desk accessories can fetch the current contents of the clipboard if desired, via the same GetClip() call that normal programs make. This makes it possible to implement "note pads" and "scrap book" desk accessories similar to those available on the Macintosh, and for your program to export and import data from other programs. Four potential problems must be accounted for. 1) In order for the clipboard to work as transparently as possible, memory must be allocated (and re-allocated) from the stack space. If your program doesn't shrink memory as part of its initialization, or if you're assuming that you have all of memory to yourself, the clipboard won't find enough room to operate (and you won't have a clipboard.) In practice, this shouldn't affect too many programs - it's considered poor programming practice to allocate everything to yourself, especially in an open environment such as the ST uses. 2) The user can change disks out from underneath you at any time. It is for this reason that you probably should limit your calls to LoadClip() and UnloadClip() to the very beginning and end of your program, so that you can specifically ask for the proper disk in case you can't find it. Also, keep disk errors in mind (they DO happen, you know.) 3) Since memory is allocated from the operating system's heap during the running of a process, any memory allocated to the clipboard will be released by the operating system when that process terminates. Don't expect the clipboard data to remain in memory beyond the life of your application. However, there is an interesting subtle side effect of this having to do with running desk accessories from the Desktop. The Desktop doesn't know about the clipboard, but it is ALWAYS in memory, and as such writes to the clipboard made while in the Desktop program will survive the launching of an application. The Desktop doesn't know to UnloadClip() before an application is run, so the clipboard may in fact be active when your application is run. In other words, don't expect a clean slate - the clipboard may already be in memory the first time you go looking for it. 4) The last problem has to do with the process by which data is "put" into the clipboard. In order for there to be enough room for a "put" to clipboard to work, you must have enough memory to hold TWO copies of the data. This is because the clipboard must be written to in one fell swoop and must be copied from another area of memory. A general workaround is this: if you don't have enough memory left to write an existing RAM image to the clipboard, you can bypass the RAM clipboard altogether and write directly to the disk file. You can obtain the name of the clipboard file via the InfoClip() call. As far as the _format_ of the clipboard data is concerned, it is not defined. Atari ST HomePak uses the IFF Interchange File Format protocol for formatting its clipboard data. (Documentation on IFF can be obtained from Electronic Arts or from Commodore/Amiga). The Routines (A note on the nomenclature: for portability reasons, the following data types are used: LONG a 32-bit signed integer WORD a 16-bit signed integer BYTE an 8-bit unsigned character ) There are seven routines in The Clipboard Manager. They are: ************************************************************* WORD InfoClip(info) clipInfo **info; InfoClip() returns information about the clipboard. The result of this routine is non-zero if the clipboard has been installed and is active. In addition, this routine returns a pointer to a structure which gives useful (and necessary) information about the clipboard itself. clipInfo is defined as: typedef struct { LONG size; /* size of present clipboard */ BYTE *location; /* pointer to the data itself */ WORD dirty; /* data has changed flag */ WORD where; /* where clipboard data is now */ BYTE filename[16]; /* file name of clipboard file */ } clipInfo; 'size' is the present size of the data "on the clipboard". 'location' is the memory location of the data itself, or is zero if the data isn't presently in memory (i.e. it's in the clipboard disk file). 'dirty' is a boolean flag which tells whether the clipboard has been changed since it was last stored to the clipboard disk file (non-zero if it has.) 'where' tells where the clipboard data currently resides: non-zero if in memory, zero if on disk (or if location is unknown, which means it needs loading from disk.) 'filename' is the ASCII filename of the clipboard disk file. It is usually called "CLIP.SYS" at the root of the default drive. UnloadClip() and LoadClip() will use this filename when they read/write from/to the disk. (NOTE: 'info' is a POINTER TO A POINTER!) ************************************************************* VOID InitClip(); InitClip() initializes the clipInfo variables. If InfoClip() is called and it determines that the clipInfo area is invalid, it will call InitClip() for you, so you'll rarely, if ever, need to call it yourself. ************************************************************* WORD UnloadClip(); UnloadClip() writes the current clipboard data in memory to the clipboard disk file. If the clipboard disk file's status is current (that is, the clipboard hasn't been changed and the data in memory matches the disk file) nothing is done. UnloadClip() returns a zero result if no error occurs, otherwise it returns the offending TOS error code. ************************************************************* WORD LoadClip(); LoadClip() is the inverse of the UnloadClip() routine - it reads the clipboard disk file into memory. LoadClip() returns an TOS error code, or zero if no error occurs. ************************************************************* VOID ZeroClip(); ZeroClip() initializes the clipboard in memory if it doesn't already exist. Otherwise, it clears its contents. ************************************************************* WORD PutClip(length, source) LONG length; BYTE *source; PutClip() writes data to the clipboard, from the memory location pointed to by 'source', for 'length' number of bytes. If the clipboard doesn't as yet exist, it is initialized for you via ZeroClip(). All changes are made with reference to the clipboard in memory - that is, the clipboard disk file is not altered. Any previous contents of the clipboard are overwritten. If you want to append to the present contents of the clipboard, you must first read it to local storage, append the new data yourself, and write the resultant data yourself as the new clipboard. The only error that can occur is lack of available memory space, which is returned as the proper TOS error code. ************************************************************* WORD GetClip(length, source, loadflag) LONG *length; BYTE **source; BYTE loadflag; GetClip() is the inverse of PutClip() in that it allows you to fetch the clipboard's contents. It is a misnomer, however, because all it really does is return a pointer to the data in memory, and how long that data really is. (NOTE: 'source' is a POINTER TO A POINTER!) It is up to you to copy the data itself to your local physical storage area. GetClip() will, however, load the clipboard memory from the clipboard disk file if it hasn't previously been read in. 'loadflag' is handy in the particular instance where the clipboard hasn't already been read into memory - if zero, GetClip() will simply determine the length of the clipboard data without reading it into memory if it isn't there already. (This is useful for determining whether or not there is enough memory left to read the clipboard in off of disk in case it's very large.) If an error occurs, it is returned as the proper TOS error code, or zero for no error. The Source Code As mentioned before, in addition to the CLIP.PRG installer program, you must also include the proper "glue routines" in your code to properly set up the trap calls. They are provided here written in C, and will compile under Megamax C or Alcyon (DRI) C, depending on a "#define". Users of other languages will have to duplicate the function of these routines. Personal Pascal programmers, for example, can compile these routines with Alcyon C (or assemble an equivalent with AS68 or the MetaComCo macro assembler) and reference them with the C directive. It is assumed that versions of these routines for most popular languages will be readily available via CompuServe and other services. ít`Cùoèt´Cl–BSCr a‚QËÿøSBavQÊ