Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Clipboard Device"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
 
(18 intermediate revisions by 2 users not shown)
Line 1: Line 1:
  +
[[Category:Devices|Clipboard]]
{{WIP}}
 
 
== Clipboard Device ==
 
== Clipboard Device ==
   
Line 29: Line 29:
 
== Device Interface ==
 
== Device Interface ==
   
The clipboard device operates like the other Amiga devices. To use it, you must first open the clipboard device, then send I/O requests to it, and then close it when finished. See [[Introduction_to_Amiga_System_Devices|Introduction to Amiga System Devices]] for general information on device usage.
+
The clipboard device operates like the other Amiga devices. To use it, you must first open the clipboard device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.
   
 
<syntaxhighlight>
 
<syntaxhighlight>
Line 56: Line 56:
 
Three primary steps are required to open the clipboard device:
 
Three primary steps are required to open the clipboard device:
   
* Create a message port using CreatePort(). Reply messages from the device must be directed to a message port.
+
* Create a message port using AllocSysObject() and ASOT_PORT type. Reply messages from the device must be directed to a message port.
* Create an extended I/O request structure of type IOClipReq using CreateExtIO().
+
* Create an extended I/O request structure of type IOClipReq using AllocSysObject() and ASOT_IOREQUEST type.
 
* Open the clipboard device. Call OpenDevice(), passing the IOClipReq.
 
* Open the clipboard device. Call OpenDevice(), passing the IOClipReq.
   
  +
<syntaxhighlight>
<pre>
 
struct MsgPort *ClipMP; /* pointer to message port*/
+
struct MsgPort *ClipMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
  +
struct IOClipReq *ClipIO; /* pointer to IORequest */
 
  +
if (ClipMP != NULL)
  +
{
  +
struct IOClipReq *ClipIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
  +
ASOIOR_Size, sizeof(struct IOClipReq),
  +
ASOIOR_ReplyPort, ClipMP,
  +
TAG_END);
   
  +
if (ClipIO != NULL)
if (ClipMP=CreatePort(0L,0L) )
 
 
{
 
{
  +
if (IExec->OpenDevice("clipboard.device", 0, ClipIO, 0))
if (ClipIO=(struct IOClipReq *)
 
  +
IDOS->Printf("clipboard.device did not open\n");
CreateExtIO(ClipMP,sizeof(struct IOClipReq)))
 
{
 
if (OpenDevice(&quot;clipboard.device&quot;,0L,ClipIO,0))
 
printf(&quot;clipboard.device did not open\n&quot;);
 
 
else
 
else
{
 
... do device processing
 
}
 
 
{
 
{
  +
... do device processing
else
 
  +
}
printf(&quot;Error: Could not create IORequest\n&quot;);
 
 
}
 
}
  +
else
  +
IDOS->Printf("Error: Could not create IORequest\n");
  +
}
 
else
 
else
printf(&quot;Error: Could not create message port\n&quot;);
+
IDOS->Printf("Error: Could not create message port\n");
  +
</syntaxhighlight>
</pre>
 
   
 
=== Clipboard Data ===
 
=== Clipboard Data ===
Line 95: Line 98:
 
You should not, in any way, rely on the specifics of how files in CLIPS: are handled or named. The only proper way to read or write clipboard data is via the clipboard device.
 
You should not, in any way, rely on the specifics of how files in CLIPS: are handled or named. The only proper way to read or write clipboard data is via the clipboard device.
   
  +
{{Note|title=Play Nice!|text=Keep in mind that while your task is reading from or writing to a clipboard unit, other tasks cannot. Therefore, it is important to be fast. If possible, make a copy of the clipboard data in RAM instead of processing it while the read or write is in progress.}}
{| class="wikitable"
 
| ''Play Nice!'' Keep in mind that while your task is reading from or writing to a clipboard unit, other tasks cannot. Therefore, it is important to be fast. If possible, make a copy of the clipboard data in RAM instead of processing it while the read or write is in progress.
 
|}
 
   
 
=== Multiple Clips ===
 
=== Multiple Clips ===
Line 105: Line 106:
 
The multiple clips are implemented as different units in the clipboard device. The unit is specified at OpenDevice() time.
 
The multiple clips are implemented as different units in the clipboard device. The unit is specified at OpenDevice() time.
   
  +
<syntaxhighlight>
<pre>
 
 
struct IOClipReq *ClipIO;
 
struct IOClipReq *ClipIO;
LONG unit;
+
int32 unit;
   
OpenDevice(&quot;clipboard.device&quot;, unit, ClipIO, 0);
+
IExec->OpenDevice("clipboard.device", unit, ClipIO, 0);
  +
</syntaxhighlight>
</pre>
 
   
 
By default, applications should use clipboard unit 0. However, it is recommended that each application provide a mechanism for selecting the unit number which will be used when the clipboard is opened. This will allow the user to create a convention for storing different types of data in the clipboard. Applications should never write to clipboard unit 0 unless the user requests it (e.g., selecting '''Copy''' or '''Cut''' within an application).
 
By default, applications should use clipboard unit 0. However, it is recommended that each application provide a mechanism for selecting the unit number which will be used when the clipboard is opened. This will allow the user to create a convention for storing different types of data in the clipboard. Applications should never write to clipboard unit 0 unless the user requests it (e.g., selecting '''Copy''' or '''Cut''' within an application).
Line 127: Line 128:
 
You write to the clipboard device by passing an IOClipReq to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.
 
You write to the clipboard device by passing an IOClipReq to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.
   
  +
<syntaxhighlight>
<pre>
 
ClipIO-&gt;io_Data = (char *) data;
+
ClipIO->io_Data = (char *) data;
ClipIO-&gt;io_Length = 4L;
+
ClipIO->io_Length = 4;
ClipIO-&gt;io_Command = CMD_WRITE;
+
ClipIO->io_Command = CMD_WRITE;
  +
</syntaxhighlight>
</pre>
 
   
 
An initial write should set io_Offset to zero. Each time a write is done, the device will increment io_Offset by the length of the write.
 
An initial write should set io_Offset to zero. Each time a write is done, the device will increment io_Offset by the length of the write.
Line 193: Line 194:
 
A code fragment for doing this:
 
A code fragment for doing this:
   
  +
<syntaxhighlight>
<pre>
 
LONG slen = strlen (&quot;Enterprise&quot;);
+
LONG slen = strlen ("Enterprise");
BOOL odd = (slen &amp; 1); /* pad byte flag */
+
BOOL odd = (slen & 1); /* pad byte flag */
   
 
/* set length depending on whether string is odd or even length */
 
/* set length depending on whether string is odd or even length */
Line 201: Line 202:
   
 
/* Reset the clip id */
 
/* Reset the clip id */
ClipIO-&gt;io_ClipID = 0;
+
ClipIO->io_ClipID = 0;
ClipIO-&gt;io_Offset = 0;
+
ClipIO->io_Offset = 0;
   
error = writeLong ((LONG *) &quot;FORM&quot;);/* &quot;FORM&quot; */
+
error = writeLong ((LONG *) "FORM");/* "FORM" */
   
length += 12; /* add 12 bytes for FTXT, CHRS &amp; length byte to string length */
+
length += 12; /* add 12 bytes for FTXT, CHRS & length byte to string length */
error = writeLong (&amp;length);
+
error = writeLong (&length);
error = writeLong ((LONG *) &quot;FTXT&quot;);/* &quot;FTXT&quot; for example */
+
error = writeLong ((LONG *) "FTXT");/* "FTXT" for example */
error = writeLong ((LONG *) &quot;CHRS&quot;);/* &quot;CHRS&quot; for example */
+
error = writeLong ((LONG *) "CHRS");/* "CHRS" for example */
error = writeLong (&amp;slen); /* # (length of string) */
+
error = writeLong (&slen); /* # (length of string) */
   
ClipIO-&gt;io_Command = CMD_WRITE;
+
ClipIO->io_Command = CMD_WRITE;
ClipIO-&gt;io_Data = (char *) string;
+
ClipIO->io_Data = (char *) string;
ClipIO-&gt;io_Length = slen; /* length of string */
+
ClipIO->io_Length = slen; /* length of string */
error = (LONG) DoIO (clipIO); /* text string */
+
error = (LONG) IExec->DoIO (clipIO); /* text string */
   
   
 
LONG writeLong (LONG * ldata)
 
LONG writeLong (LONG * ldata)
 
{
 
{
ClipIO-&gt;io_Command = CMD_WRITE;
+
ClipIO->io_Command = CMD_WRITE;
ClipIO-&gt;io_Data = (char *) ldata;
+
ClipIO->io_Data = (char *) ldata;
ClipIO-&gt;io_Length = 4L;
+
ClipIO->io_Length = 4L;
return ( (LONG) DoIO (clipIO) );
+
return ( (LONG) IExec->DoIO (clipIO) );
 
}
 
}
  +
</syntaxhighlight>
</pre>
 
   
The fragment above does no error checking because it’s a fragment. You should always error check. See the example programs at the end of this chapter for the proper method of error checking.
+
The fragment above does no error checking because it’s a fragment. You should always error check. See the example programs at the end of this article for the proper method of error checking.
   
  +
{{Note|title=IFFParse That Data!|text=Keep in mind that the functions in the iffparse.library can be used to write data to the clipboard. See the [[IFFParse_Library|IFFParse Library]] for more information.}}
{| class="wikitable"
 
| ''IFFParse That Data!'' Keep in mind that the functions in the iffparse.library can be used to write data to the clipboard. See the [[IFFParse_Library|IFFParse Library]] for more information.
 
|}
 
   
 
=== Updating the Clipboard Device ===
 
=== Updating the Clipboard Device ===
 
 
   
 
When the final write is done, an update command must be sent to the device to indicate that the writing is complete and the data is available. You update the clipboard device by passing an IOClipReq to the device with CMD_UPDATE set in io_Command.
 
When the final write is done, an update command must be sent to the device to indicate that the writing is complete and the data is available. You update the clipboard device by passing an IOClipReq to the device with CMD_UPDATE set in io_Command.
   
  +
<syntaxhighlight>
<pre>ClipIO-&gt;io_Command = CMD_UPDATE;
 
  +
ClipIO->io_Command = CMD_UPDATE;
DoIO(ClipIO);</pre>
 
  +
IEXec->DoIO(ClipIO);
  +
</syntaxhighlight>
  +
 
=== Clipboard Messages ===
 
=== Clipboard Messages ===
   
Line 278: Line 278:
 
io_Offset must be set to zero for the first read of a paste sequence. An io_Actual that is less than the io_Length indicates that all the data has been read. After all the data has been read, a subsequent read must be performed (one whose io_Actual returns zero) to indicate to the clipboard device that all the data has been read. This allows random access of the clip while reading. Providing only valid reads are performed, your program can seek/read anywhere within the clip by setting the io_Offset field of the I/O request appropriately.
 
io_Offset must be set to zero for the first read of a paste sequence. An io_Actual that is less than the io_Length indicates that all the data has been read. After all the data has been read, a subsequent read must be performed (one whose io_Actual returns zero) to indicate to the clipboard device that all the data has been read. This allows random access of the clip while reading. Providing only valid reads are performed, your program can seek/read anywhere within the clip by setting the io_Offset field of the I/O request appropriately.
   
  +
{{Note|title=Tell The Clipboard You Are Finished Reading|text=Your application must perform an extra read (one whose io_Actual returns zero) to indicate to the clipboard device that all data has been read, ''if io_Actual is not already zero.''}}
{| class="wikitable"
 
| ''Tell The Clipboard You Are Finished Reading.'' Your application must perform an extra read (one whose io_Actual returns zero) to indicate to the clipboard device that all data has been read, ''if io_Actual is not already zero.''
 
|}
 
   
 
The data you read from the clipboard will be in IFF format. Conversion from IFF may be necessary depending on your application.
 
The data you read from the clipboard will be in IFF format. Conversion from IFF may be necessary depending on your application.
   
  +
{{Note|title=IFFParse That Data!|text=Keep in mind that the functions in the iffparse.library can be used to read data from the clipboard. See the [[IFFParse_Library|IFFParse Library]] for more information.}}
{| class="wikitable"
 
| ''IFFParse That Data!'' Keep in mind that the functions in the iffparse.library can be used to read data from the clipboard. See the [[IFFParse_Library|IFFParse Library]] for more information.
 
|}
 
   
 
=== Closing the Clipboard Device ===
 
=== Closing the Clipboard Device ===
Line 292: Line 288:
 
Each OpenDevice() must eventually be matched by a call to CloseDevice().
 
Each OpenDevice() must eventually be matched by a call to CloseDevice().
   
  +
<syntaxhighlight>
<pre>CloseDevice(ClipIO);</pre>
 
  +
IExec->CloseDevice(ClipIO);
  +
</syntaxhighlight>
  +
 
When the last task closes a clipboard unit with CloseDevice(), the contents of the unit may be copied to a disk file in CLIPS: so that the clipboard device can be expunged.
 
When the last task closes a clipboard unit with CloseDevice(), the contents of the unit may be copied to a disk file in CLIPS: so that the clipboard device can be expunged.
   
Line 303: Line 302:
 
You specify a hook for the clipboard device by initializing a Hook structure and then passing an IOClipReq to the device with CBD_CHANGEHOOK set in io_Command, 1 set in io_Length, and the address of the Hook structure set in io_Data.
 
You specify a hook for the clipboard device by initializing a Hook structure and then passing an IOClipReq to the device with CBD_CHANGEHOOK set in io_Command, 1 set in io_Length, and the address of the Hook structure set in io_Data.
   
  +
<syntaxhighlight>
<pre>
 
ULONG HookEntry (); /* Declare the hook assembly function */
 
 
struct IOClipReq *ClipIO; /* Declare the IOClipReq */
 
struct IOClipReq *ClipIO; /* Declare the IOClipReq */
 
struct Hook *ClipHook; /* Declare the Hook */
 
struct Hook *ClipHook; /* Declare the Hook */
   
 
/* Prepare the hook */
 
/* Prepare the hook */
ClipHook-&gt;h_Entry = HookEntry; /* C interface in assembly routine HookEntry */
+
ClipHook->h_Entry = HookFunc; /* Function to call when Hook is activated */
ClipHook-&gt;h_SubEntry = HookFunc; /* Function to call when Hook is activated */
+
ClipHook->h_Data = FindTask(NULL); /* Set pointer to current task */
ClipHook-&gt;h_Data = FindTask(NULL); /* Set pointer to current task */
 
   
ClipIO-&gt;io_Data = (char *) ClipHook; /* Point to hook struct */
+
ClipIO->io_Data = (char *) ClipHook; /* Point to hook struct */
ClipIO-&gt;io_Length = 1; /* Add hook to clipboard */
+
ClipIO->io_Length = 1; /* Add hook to clipboard */
ClipIO-&gt;io_Command = CBD_CHANGEHOOK;
+
ClipIO->io_Command = CBD_CHANGEHOOK;
DoIO(clipIO);</pre>
+
IExec->DoIO(clipIO);
  +
</syntaxhighlight>
The above code fragment assumes that an assembly language routine HookEntry() has been coded:
 
   
  +
It also assumes that the function HookFunc() has been coded. One of the example programs at the end of this article has hook processing in it. See the include file utility/hooks.h and [[Utility_Library|Utility Library]] for further information on hooks.
<pre>; entry interface for C code
 
_HookEntry:
 
move.l a1,-(sp) ; push message packet pointer
 
move.l a2,-(sp) ; push object pointer
 
move.l a0,-(sp) ; push hook pointer
 
move.l h_SubEntry(a0),a0 ; fetch C entry point ...
 
jsr (a0) ; ... and call it
 
lea 12(sp),sp ; fix stack
 
rts
 
</pre>
 
 
It also assumes that the function HookFunc() has been coded. One of the example programs at the end of this chapter has hook processing in it. See the include file utility/hooks.h and [[Utility_Library|Utility Library]] for further information on hooks.
 
   
 
You remove a hook by passing an IOClipReq to the device with the address of the Hook structure set in io_Data, 0 set in io_Length and CBD_CHANGEHOOK set in io_Command.
 
You remove a hook by passing an IOClipReq to the device with the address of the Hook structure set in io_Data, 0 set in io_Length and CBD_CHANGEHOOK set in io_Command.
   
  +
<syntaxhighlight>
<pre>
 
ClipIO-&gt;io_Data = (char *) ClipHook; /* point to hook struct */
+
ClipIO->io_Data = (char *) ClipHook; /* point to hook struct */
ClipIO-&gt;io_Length = 0; /* Remove hook from clipboard */
+
ClipIO->io_Length = 0; /* Remove hook from clipboard */
ClipIO-&gt;io_Command = CBD_CHANGEHOOK;
+
ClipIO->io_Command = CBD_CHANGEHOOK;
(DoIO (clipIO))
+
IExec->DoIO(clipIO);
  +
</syntaxhighlight>
</pre>
 
   
 
You must remove the hook or it will continue indefinitely.
 
You must remove the hook or it will continue indefinitely.
Line 345: Line 331:
 
=== Caveats for CBD_CHANGEHOOK ===
 
=== Caveats for CBD_CHANGEHOOK ===
   
  +
* CBD_CHANGEHOOK should only be used by a special application, such as a clipboard viewing program. Most applications can check the contents of the clipboard when, and if, the user requests a paste.
<ul>
 
  +
* Do not add system overhead by blindly reading and parsing the clipboard every time a user copies data to it. If all applications did this, the system could become intolerably slow whenever an application wrote to the clipboard. Only read and parse when it is necessary.
<li><p>CBD_CHANGEHOOK</p>
 
<p>should only be used by a special application, such as a clipboard viewing program. Most applications can check the contents of the clipboard when, and if, the user requests a paste.</p></li>
 
<li><p>Do not add system overhead by blindly reading and parsing the clipboard everytime a user copies data to it. If all applications did this, the system could become intolerably slow whenever an application wrote to the clipboard. Only read and parse when it is necessary.</p></li></ul>
 
   
 
== Example Clipboard Programs ==
 
== Example Clipboard Programs ==
   
  +
=== Clipdemo.c ===
<pre>/*
 
  +
  +
<syntaxhighlight>
  +
/*
 
* Clipdemo.c
 
* Clipdemo.c
 
*
 
*
 
* Demonstrate use of clipboard I/O. Uses general functions
 
* Demonstrate use of clipboard I/O. Uses general functions
 
* provided in cbio.c
 
* provided in cbio.c
*
 
* Compile with SAS C 5.10: LC -b1 -cfistq -v -y -L+cbio.o
 
 
*
 
*
 
* Run from CLI only
 
* Run from CLI only
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/ports.h&gt;
+
#include <exec/ports.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;devices/clipboard.h&gt;
+
#include <devices/clipboard.h>
#include &lt;libraries/dosextens.h&gt;
+
#include <libraries/dosextens.h>
#include &lt;libraries/dos.h&gt;
+
#include <libraries/dos.h>
   
#include &quot;cb.h&quot;
+
#include "cb.h"
   
#include &lt;clib/exec_protos.h&gt;
+
#include <proto/dos.h>
#include &lt;clib/alib_protos.h&gt;
+
#include <proto/exec.h>
   
#include &lt;stdlib.h&gt;
+
#include <string.h>
#include &lt;stdio.h&gt;
 
#include &lt;string.h&gt;
 
 
#ifdef LATTICE
 
int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */
 
int chkabort(void) { return(0); } /* really */
 
#endif
 
   
 
#define FORGETIT 0
 
#define FORGETIT 0
Line 396: Line 374:
 
int PostClip( char * ); /* Demonstrate posting data to clipboard */
 
int PostClip( char * ); /* Demonstrate posting data to clipboard */
   
  +
char message[] = "\
void main( USHORT, char **);
 
 
char message[] = &quot;\
 
 
\nPossible switches are:\n\n\
 
\nPossible switches are:\n\n\
 
-r Read, and output contents of clipboard.\n\
 
-r Read, and output contents of clipboard.\n\
Line 413: Line 389:
 
in which case this example checks the CLIP write ID\n\
 
in which case this example checks the CLIP write ID\n\
 
to determine if it should write to the clipboard before\n\
 
to determine if it should write to the clipboard before\n\
exiting.\n\n&quot;;
+
exiting.\n\n";
   
   
void main(argc,argv)
+
int main(int argc, char **argv)
USHORT argc;
 
char **argv;
 
 
{
 
{
 
 
int todo;
 
int todo;
 
char *string;
 
char *string;
Line 433: Line 406:
 
*/
 
*/
   
if (argc &gt; 1)
+
if (argc > 1)
 
{
 
{
if (!(strcmp(argv[1],&quot;-r&quot;)))
+
if (!(strcmp(argv[1],"-r")))
 
todo = READIT;
 
todo = READIT;
if (!(strcmp(argv[1],&quot;-w&quot;)))
+
if (!(strcmp(argv[1],"-w")))
 
todo = WRITEIT;
 
todo = WRITEIT;
if (!(strcmp(argv[1],&quot;-p&quot;)))
+
if (!(strcmp(argv[1],"-p")))
 
todo = POSTIT;
 
todo = POSTIT;
   
 
string = NULL;
 
string = NULL;
   
if (argc &gt; 2)
+
if (argc > 2)
 
string=argv[2];
 
string=argv[2];
   
Line 469: Line 442:
 
default:
 
default:
   
printf(&quot;%s&quot;,message);
+
IDOS->Printf("%s",message);
 
break;
 
break;
   
Line 483: Line 456:
 
*/
 
*/
   
ReadClip()
+
int ReadClip(void)
 
{
 
{
 
struct IOClipReq *ior;
 
struct IOClipReq *ior;
Line 505: Line 478:
 
/* Process data */
 
/* Process data */
   
printf(&quot;%s\n&quot;,buf-&gt;mem);
+
IDOS->Printf("%s\n",buf->mem);
   
 
/* Free buffer allocated by CBReadCHRS() */
 
/* Free buffer allocated by CBReadCHRS() */
Line 519: Line 492:
 
else
 
else
 
{
 
{
puts(&quot;No FTXT in clipboard&quot;);
+
IDOS->PutStr("No FTXT in clipboard");
 
}
 
}
   
Line 527: Line 500:
 
else
 
else
 
{
 
{
puts(&quot;Error opening clipboard unit 0&quot;);
+
IDOS->PutStr("Error opening clipboard unit 0");
 
}
 
}
   
return(0L);
+
return(0);
 
}
 
}
   
Line 538: Line 511:
 
*/
 
*/
   
WriteClip(string)
+
int WriteClip(char *string)
char *string;
 
 
{
 
{
   
Line 546: Line 518:
 
if (string == NULL)
 
if (string == NULL)
 
{
 
{
puts(&quot;No string argument given&quot;);
+
IDOS->PutStr("No string argument given");
 
return(0L);
 
return(0L);
 
}
 
}
Line 556: Line 528:
 
if (!(CBWriteFTXT(ior,string)))
 
if (!(CBWriteFTXT(ior,string)))
 
{
 
{
printf(&quot;Error writing to clipboard: io_Error = %ld\n&quot;,ior-&gt;io_Error);
+
IDOS->Printf("Error writing to clipboard: io_Error = %ld\n",ior->io_Error);
 
}
 
}
 
CBClose(ior);
 
CBClose(ior);
Line 562: Line 534:
 
else
 
else
 
{
 
{
puts(&quot;Error opening clipboard.device&quot;);
+
IDOS->PutStr("Error opening clipboard.device");
 
}
 
}
   
Line 600: Line 572:
 
*/
 
*/
   
PostClip(string)
+
int PostClip(char *string)
char *string;
 
 
{
 
{
   
Line 608: Line 579:
 
struct IOClipReq *ior;
 
struct IOClipReq *ior;
 
int mustwrite;
 
int mustwrite;
ULONG postID;
+
uint32 postID;
   
 
if (string == NULL)
 
if (string == NULL)
 
{
 
{
puts(&quot;No string argument given&quot;);
+
IDOS->PutStr("No string argument given");
 
return(0L);
 
return(0L);
 
}
 
}
   
if (satisfy = CreatePort(0L,0L))
+
if (satisfy = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END))
 
{
 
{
   
Line 627: Line 598:
 
/* Notify clipboard we have data */
 
/* Notify clipboard we have data */
   
ior-&gt;io_Data = (STRPTR)satisfy;
+
ior->io_Data = (STRPTR)satisfy;
ior-&gt;io_ClipID = 0L;
+
ior->io_ClipID = 0L;
ior-&gt;io_Command = CBD_POST;
+
ior->io_Command = CBD_POST;
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
postID = ior-&gt;io_ClipID;
+
postID = ior->io_ClipID;
   
printf(&quot;\nClipID = %ld\n&quot;,postID);
+
IDOS->Printf("\nClipID = %ld\n",postID);
   
 
/* Wait for CTRL-C break, or message from clipboard */
 
/* Wait for CTRL-C break, or message from clipboard */
Wait(SIGBREAKF_CTRL_C|(1L &lt;&lt; satisfy-&gt;mp_SigBit));
+
IExec->Wait(SIGBREAKF_CTRL_C|(1L << satisfy->mp_SigBit));
   
 
/* see if we got a message, or a break */
 
/* see if we got a message, or a break */
puts(&quot;Woke up&quot;);
+
IDOS->PutStr("Woke up");
 
   
 
if (sm = (struct SatisfyMsg *)GetMsg(satisfy))
 
if (sm = (struct SatisfyMsg *)GetMsg(satisfy))
 
{
 
{
puts(&quot;Got a message from the clipboard\n&quot;);
+
IDOS->PutStr("Got a message from the clipboard\n");
   
 
/* We got a message - we MUST write some data */
 
/* We got a message - we MUST write some data */
Line 656: Line 626:
 
*/
 
*/
   
ior-&gt;io_Command = CBD_CURRENTWRITEID;
+
ior->io_Command = CBD_CURRENTWRITEID;
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
printf(&quot;CURRENTWRITEID = %ld\n&quot;,ior-&gt;io_ClipID);
+
IDOS->Printf("CURRENTWRITEID = %ld\n",ior->io_ClipID);
   
if (postID &gt;= ior-&gt;io_ClipID)
+
if (postID >= ior->io_ClipID)
 
mustwrite = TRUE;
 
mustwrite = TRUE;
   
Line 671: Line 641:
 
{
 
{
 
if (!(CBWriteFTXT(ior,string)))
 
if (!(CBWriteFTXT(ior,string)))
puts(&quot;Error writing to clipboard&quot;);
+
IDOS->PutStr("Error writing to clipboard");
 
}
 
}
 
else
 
else
 
{
 
{
puts(&quot;No need to write to clipboard&quot;);
+
IDOS->PutStr("No need to write to clipboard");
 
}
 
}
   
Line 682: Line 652:
 
else
 
else
 
{
 
{
puts(&quot;Error opening clipboard.device&quot;);
+
IDOS->PutStr("Error opening clipboard.device");
 
}
 
}
   
DeletePort(satisfy);
+
IExec->FreeSysObject(ASOT_PORT, satisfy);
 
}
 
}
 
else
 
else
 
{
 
{
puts(&quot;Error creating message port&quot;);
+
IDOS->PutStr("Error creating message port");
 
}
 
}
   
 
return(0);
 
return(0);
  +
}
}</pre>
 
  +
</syntaxhighlight>
<pre>/*
 
  +
  +
=== Changehook_Test.c ===
  +
  +
<syntaxhighlight>
  +
/*
 
* Changehook_Test.c
 
* Changehook_Test.c
 
*
 
*
Line 700: Line 675:
 
* The program will set a hook and wait for the clipboard data to change.
 
* The program will set a hook and wait for the clipboard data to change.
 
* You must put something in the clipboard in order for it to return.
 
* You must put something in the clipboard in order for it to return.
*
 
* Compile with SAS C 5.10: LC -cfist -v -y -L+Hookface.o+cbio.o
 
*
 
* Requires Kickstart 36 or greater.
 
 
*
 
*
 
* Run from CLI only
 
* Run from CLI only
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;exec/ports.h&gt;
+
#include <exec/ports.h>
#include &lt;exec/tasks.h&gt;
+
#include <exec/tasks.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;devices/clipboard.h&gt;
+
#include <devices/clipboard.h>
#include &lt;dos/dos.h&gt;
+
#include <dos/dos.h>
#include &lt;utility/hooks.h&gt;
+
#include <utility/hooks.h>
#include &quot;cb.h&quot;
+
#include "cb.h"
 
#include &lt;clib/macros.h&gt;
 
#include &lt;clib/alib_protos.h&gt;
 
#include &lt;clib/exec_protos.h&gt;
 
 
#include &lt;stdio.h&gt;
 
#include &lt;string.h&gt;
 
 
   
  +
#include <proto/exec.h>
LONG version = 1L;
 
  +
#include <proto/dos.h>
   
  +
int32 version = 1;
extern ULONG SysBase, DOSBase;
 
   
 
/* Data to pass around with the clipHook */
 
/* Data to pass around with the clipHook */
Line 743: Line 707:
 
ULONG clipHook (struct Hook * h, VOID * o, struct ClipHookMsg * msg)
 
ULONG clipHook (struct Hook * h, VOID * o, struct ClipHookMsg * msg)
 
{
 
{
struct CHData *ch = (struct CHData *) h-&gt;h_Data;
+
struct CHData *ch = (struct CHData *) h->h_Data;
   
 
if (ch)
 
if (ch)
 
{
 
{
 
/* Remember the ID of clip */
 
/* Remember the ID of clip */
ch-&gt;ch_ClipID = msg-&gt;chm_ClipID;
+
ch->ch_ClipID = msg->chm_ClipID;
   
 
/* Signal the task that started the hook */
 
/* Signal the task that started the hook */
Signal (ch-&gt;ch_Task, SIGBREAKF_CTRL_E);
+
IExec->Signal (ch->ch_Task, SIGBREAKF_CTRL_E);
 
}
 
}
   
Line 765: Line 729:
 
if (clipIO = CBOpen( 0L ))
 
if (clipIO = CBOpen( 0L ))
 
{
 
{
ULONG hookEntry ();
 
 
 
/* Fill out the IORequest */
 
/* Fill out the IORequest */
clipIO-&gt;io_Data = (char *) &amp;hook;
+
clipIO->io_Data = (char *) &hook;
clipIO-&gt;io_Length = 1;
+
clipIO->io_Length = 1;
clipIO-&gt;io_Command = CBD_CHANGEHOOK;
+
clipIO->io_Command = CBD_CHANGEHOOK;
   
 
/* Set up the hook data */
 
/* Set up the hook data */
ch.ch_Task = FindTask (NULL);
+
ch.ch_Task = IExec->FindTask (NULL);
   
 
/* Prepare the hook */
 
/* Prepare the hook */
hook.h_Entry = hookEntry;
+
hook.h_Entry = clipHook;
hook.h_SubEntry = clipHook;
+
hook.h_Data = &ch;
hook.h_Data = &amp;ch;
 
   
 
/* Start the hook */
 
/* Start the hook */
if (DoIO (clipIO))
+
if (IExec->DoIO (clipIO))
printf (&quot;unable to set hook\n&quot;);
+
IDOS->Printf ("unable to set hook\n");
 
else
 
else
printf (&quot;hook set\n&quot;);
+
IDOS->Printf ("hook set\n");
   
 
/* Return success */
 
/* Return success */
Line 798: Line 759:
   
 
/* Fill out the IO request */
 
/* Fill out the IO request */
clipIO-&gt;io_Data = (char *) &amp;hook;
+
clipIO->io_Data = (char *) &hook;
clipIO-&gt;io_Length = 0;
+
clipIO->io_Length = 0;
clipIO-&gt;io_Command = CBD_CHANGEHOOK;
+
clipIO->io_Command = CBD_CHANGEHOOK;
   
 
/* Stop the hook */
 
/* Stop the hook */
if (DoIO (clipIO))
+
if (IExec->DoIO (clipIO))
printf (&quot;unable to stop hook\n&quot;);
+
IDOS->Printf ("unable to stop hook\n");
 
else
 
else
 
/* Indicate success */
 
/* Indicate success */
printf (&quot;hook is stopped\n&quot;);
+
IDOS->Printf ("hook is stopped\n");
   
 
CBClose(clipIO);
 
CBClose(clipIO);
 
}
 
}
   
main (int argc, char **argv)
+
int main (int argc, char **argv)
 
{
 
{
 
struct IOClipReq *clipIO;
 
struct IOClipReq *clipIO;
Line 818: Line 779:
 
ULONG sig_rcvd;
 
ULONG sig_rcvd;
   
printf (&quot;Test v%ld\n&quot;, version);
+
IDOS->Printf ("Test v%ld\n", version);
   
 
if (clipIO=OpenCB (0L))
 
if (clipIO=OpenCB (0L))
 
{
 
{
sig_rcvd = Wait ((SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E));
+
sig_rcvd = IExec->Wait ((SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E));
   
if (sig_rcvd &amp; SIGBREAKF_CTRL_C)
+
if (sig_rcvd & SIGBREAKF_CTRL_C)
printf (&quot;^C received\n&quot;);
+
IDOS->Printf ("^C received\n");
   
if (sig_rcvd &amp; SIGBREAKF_CTRL_E)
+
if (sig_rcvd & SIGBREAKF_CTRL_E)
printf (&quot;clipboard change, current ID is %ld\n&quot;, ch.ch_ClipID);
+
IDOS->Printf ("clipboard change, current ID is %ld\n", ch.ch_ClipID);
   
 
CloseCB(clipIO);
 
CloseCB(clipIO);
 
}
 
}
  +
return 0;
}</pre>
 
  +
}
  +
</syntaxhighlight>
  +
 
== Support Functions Called from Example Programs ==
 
== Support Functions Called from Example Programs ==
   
  +
<syntaxhighlight>
<pre>/* Cbio.c
 
  +
/* Cbio.c
 
*
 
*
 
* Provide standard clipboard device interface routines
 
* Provide standard clipboard device interface routines
 
* such as Open, Close, Post, Read, Write, etc.
 
* such as Open, Close, Post, Read, Write, etc.
*
 
* Compile with SAS C 5.10: LC -b1 -cfistq -v -y
 
 
*
 
*
 
* NOTE - These functions are useful for writing, and reading simple
 
* NOTE - These functions are useful for writing, and reading simple
 
* FTXT. Writing, and reading complex FTXT, ILBM, etc.,
 
* FTXT. Writing, and reading complex FTXT, ILBM, etc.,
* requires more work - under 2.0 it is highly recommended that
+
* requires more work - it is highly recommended that
 
* you use iffparse.library.
 
* you use iffparse.library.
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/ports.h&gt;
+
#include <exec/ports.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;devices/clipboard.h&gt;
+
#include <devices/clipboard.h>
   
 
#define CBIO 1
 
#define CBIO 1
   
#include &quot;cb.h&quot;
+
#include "cb.h"
   
#include &lt;clib/exec_protos.h&gt;
+
#include <proto/exec.h>
#include &lt;clib/alib_protos.h&gt;
 
   
#include &lt;stdlib.h&gt;
+
#include <string.h>
#include &lt;stdio.h&gt;
 
#include &lt;string.h&gt;
 
   
 
/****** cbio/CBOpen *************************************************
 
/****** cbio/CBOpen *************************************************
Line 887: Line 847:
 
*********************************************************************/
 
*********************************************************************/
   
struct IOClipReq *CBOpen(unit)
+
struct IOClipReq *CBOpen(uint32 unit)
ULONG unit;
 
 
{
 
{
struct MsgPort *mp;
+
struct MsgPort *mp = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
struct IOStdReq *ior;
 
   
if (mp = CreatePort(0L,0L))
+
if (mp != NULL)
 
{
 
{
  +
struct IOStdReq *ior = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
if (ior=CreateExtIO(mp,sizeof(struct IOClipReq)))
 
  +
ASOIOR_Size, sizeof(struct IOClipReq),
  +
ASOIOR_ReplyPort, mp,
  +
TAG_END);
  +
  +
if (ior != NULL)
 
{
 
{
if (!(OpenDevice(&quot;clipboard.device&quot;,unit,ior,0L)))
+
if (!(IExec->OpenDevice("clipboard.device", unit, ior, 0)))
 
{
 
{
 
return((struct IOClipReq *)ior);
 
return((struct IOClipReq *)ior);
 
}
 
}
DeleteExtIO(ior);
+
IExec->FreeSysObject(ASOT_IOREQUEST, ior);
 
}
 
}
DeletePort(mp);
+
IExec->FreeSysObject(ASOT_PORT, mp);
 
}
 
}
 
return(NULL);
 
return(NULL);
 
 
}
 
}
   
Line 925: Line 887:
 
*********************************************************************/
 
*********************************************************************/
   
void CBClose(ior)
+
void CBClose(struct IOClipReq *ior)
struct IOClipReq *ior;
 
 
{
 
{
struct MsgPort *mp;
+
struct MsgPort *mp = ior->io_Message.mn_ReplyPort;
 
mp = ior-&gt;io_Message.mn_ReplyPort;
 
 
CloseDevice((struct IOStdReq *)ior);
 
DeleteExtIO((struct IOStdReq *)ior);
 
DeletePort(mp);
 
   
  +
IExec->CloseDevice((struct IOStdReq *)ior);
  +
IExec->FreeSysObject(ASOT_IOREQUEST, ior);
  +
IExec->FreeSysObject(ASOT_PORT, mp);
 
}
 
}
   
Line 960: Line 918:
 
*********************************************************************/
 
*********************************************************************/
   
int CBWriteFTXT(ior,string)
+
int CBWriteFTXT(struct IOClipReq *ior, STRPTR string)
struct IOClipReq *ior;
 
char *string;
 
 
{
 
{
  +
uint32 length, slen;
 
ULONG length, slen;
 
 
BOOL odd;
 
BOOL odd;
 
int success;
 
int success;
   
 
slen = strlen(string);
 
slen = strlen(string);
odd = (slen &amp; 1); /* pad byte flag */
+
odd = (slen & 1); /* pad byte flag */
   
 
length = (odd) ? slen+1 : slen;
 
length = (odd) ? slen+1 : slen;
Line 976: Line 931:
 
/* initial set-up for Offset, Error, and ClipID */
 
/* initial set-up for Offset, Error, and ClipID */
   
ior-&gt;io_Offset = 0;
+
ior->io_Offset = 0;
ior-&gt;io_Error = 0;
+
ior->io_Error = 0;
ior-&gt;io_ClipID = 0;
+
ior->io_ClipID = 0;
   
   
 
/* Create the IFF header information */
 
/* Create the IFF header information */
   
WriteLong(ior, (long *) &quot;FORM&quot;); /* &quot;FORM&quot; */
+
WriteLong(ior, (long *) "FORM"); /* "FORM" */
length+=12L; /* + &quot;[size]FTXTCHRS&quot; */
+
length+=12L; /* + "[size]FTXTCHRS" */
WriteLong(ior, &amp;length); /* total length */
+
WriteLong(ior, &length); /* total length */
WriteLong(ior, (long *) &quot;FTXT&quot;); /* &quot;FTXT&quot; */
+
WriteLong(ior, (long *) "FTXT"); /* "FTXT" */
WriteLong(ior, (long *) &quot;CHRS&quot;); /* &quot;CHRS&quot; */
+
WriteLong(ior, (long *) "CHRS"); /* "CHRS" */
WriteLong(ior, &amp;slen); /* string length */
+
WriteLong(ior, &slen); /* string length */
   
 
/* Write string */
 
/* Write string */
ior-&gt;io_Data = (STRPTR)string;
+
ior->io_Data = (STRPTR)string;
ior-&gt;io_Length = slen;
+
ior->io_Length = slen;
ior-&gt;io_Command = CMD_WRITE;
+
ior->io_Command = CMD_WRITE;
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
 
/* Pad if needed */
 
/* Pad if needed */
 
if (odd)
 
if (odd)
 
{
 
{
ior-&gt;io_Data = (STRPTR)&quot;&quot;;
+
ior->io_Data = (STRPTR)"";
ior-&gt;io_Length = 1L;
+
ior->io_Length = 1L;
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
 
}
 
}
   
 
/* Tell the clipboard we are done writing */
 
/* Tell the clipboard we are done writing */
   
ior-&gt;io_Command=CMD_UPDATE;
+
ior->io_Command=CMD_UPDATE;
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
 
/* Check if io_Error was set by any of the preceding IO requests */
 
/* Check if io_Error was set by any of the preceding IO requests */
success = ior-&gt;io_Error ? FALSE : TRUE;
+
success = ior->io_Error ? FALSE : TRUE;
   
 
return(success);
 
return(success);
 
}
 
}
   
WriteLong(ior, ldata)
+
WriteLong(struct IOClipReq *ior, long *ldata)
struct IOClipReq *ior;
 
long *ldata;
 
 
{
 
{
  +
ior->io_Data = (STRPTR)ldata;
  +
ior->io_Length = 4L;
  +
ior->io_Command = CMD_WRITE;
  +
IExec->DoIO( (struct IORequest *) ior);
   
  +
if (ior->io_Actual == 4)
ior-&gt;io_Data = (STRPTR)ldata;
 
ior-&gt;io_Length = 4L;
 
ior-&gt;io_Command = CMD_WRITE;
 
DoIO( (struct IORequest *) ior);
 
 
if (ior-&gt;io_Actual == 4)
 
 
{
 
{
return( ior-&gt;io_Error ? FALSE : TRUE);
+
return( ior->io_Error ? FALSE : TRUE);
 
}
 
}
   
 
return(FALSE);
 
return(FALSE);
 
 
}
 
}
   
Line 1,062: Line 1,013:
 
*********************************************************************/
 
*********************************************************************/
   
int CBQueryFTXT(ior)
+
int CBQueryFTXT(struct IOClipReq *ior)
struct IOClipReq *ior;
 
 
{
 
{
ULONG cbuff[4];
+
uint32 cbuff[4];
   
   
 
/* initial set-up for Offset, Error, and ClipID */
 
/* initial set-up for Offset, Error, and ClipID */
   
ior-&gt;io_Offset = 0;
+
ior->io_Offset = 0;
ior-&gt;io_Error = 0;
+
ior->io_Error = 0;
ior-&gt;io_ClipID = 0;
+
ior->io_ClipID = 0;
   
/* Look for &quot;FORM[size]FTXT&quot; */
+
/* Look for "FORM[size]FTXT" */
   
ior-&gt;io_Command = CMD_READ;
+
ior->io_Command = CMD_READ;
ior-&gt;io_Data = (STRPTR)cbuff;
+
ior->io_Data = (STRPTR)cbuff;
ior-&gt;io_Length = 12;
+
ior->io_Length = 12;
   
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
   
 
/* Check to see if we have at least 12 bytes */
 
/* Check to see if we have at least 12 bytes */
   
if (ior-&gt;io_Actual == 12L)
+
if (ior->io_Actual == 12L)
 
{
 
{
/* Check to see if it starts with &quot;FORM&quot; */
+
/* Check to see if it starts with "FORM" */
 
if (cbuff[0] == ID_FORM)
 
if (cbuff[0] == ID_FORM)
 
{
 
{
/* Check to see if its &quot;FTXT&quot; */
+
/* Check to see if its "FTXT" */
 
if (cbuff[2] == ID_FTXT)
 
if (cbuff[2] == ID_FTXT)
 
return(TRUE);
 
return(TRUE);
 
}
 
}
   
/* It's not &quot;FORM[size]FTXT&quot;, so tell clipboard we are done */
+
/* It's not "FORM[size]FTXT", so tell clipboard we are done */
 
}
 
}
   
Line 1,140: Line 1,090:
 
*********************************************************************/
 
*********************************************************************/
   
struct cbbuf *CBReadCHRS(ior)
+
struct cbbuf *CBReadCHRS(struct IOClipReq *ior)
struct IOClipReq *ior;
 
 
{
 
{
ULONG chunk,size;
+
uint32 chunk,size;
 
struct cbbuf *buf;
 
struct cbbuf *buf;
 
int looking;
 
int looking;
Line 1,156: Line 1,105:
 
looking = FALSE;
 
looking = FALSE;
   
if (ReadLong(ior,&amp;chunk))
+
if (ReadLong(ior,&chunk))
 
{
 
{
 
/* Is CHRS chunk ? */
 
/* Is CHRS chunk ? */
Line 1,162: Line 1,111:
 
{
 
{
 
/* Get size of chunk, and copy data */
 
/* Get size of chunk, and copy data */
if (ReadLong(ior,&amp;size))
+
if (ReadLong(ior,&size))
 
{
 
{
 
if (size)
 
if (size)
Line 1,172: Line 1,121:
 
else
 
else
 
{
 
{
if (ReadLong(ior,&amp;size))
+
if (ReadLong(ior,&size))
 
{
 
{
 
looking = TRUE;
 
looking = TRUE;
if (size &amp; 1)
+
if (size & 1)
 
size++; /* if odd size, add pad byte */
 
size++; /* if odd size, add pad byte */
   
ior-&gt;io_Offset += size;
+
ior->io_Offset += size;
 
}
 
}
 
}
 
}
Line 1,191: Line 1,140:
   
   
ReadLong(ior, ldata)
+
ReadLong(struct IOClipReq *ior, uint32 *ldata)
struct IOClipReq *ior;
 
ULONG *ldata;
 
 
{
 
{
ior-&gt;io_Command = CMD_READ;
+
ior->io_Command = CMD_READ;
ior-&gt;io_Data = (STRPTR)ldata;
+
ior->io_Data = (STRPTR)ldata;
ior-&gt;io_Length = 4L;
+
ior->io_Length = 4L;
   
DoIO( (struct IORequest *) ior);
+
IExec->DoIO( (struct IORequest *) ior);
   
if (ior-&gt;io_Actual == 4)
+
if (ior->io_Actual == 4)
 
{
 
{
return( ior-&gt;io_Error ? FALSE : TRUE);
+
return( ior->io_Error ? FALSE : TRUE);
 
}
 
}
   
Line 1,209: Line 1,156:
 
}
 
}
   
struct cbbuf *FillCBData(ior,size)
+
struct cbbuf *FillCBData(struct IOClipReq *ior, uint32 size)
struct IOClipReq *ior;
 
ULONG size;
 
 
{
 
{
register UBYTE *to,*from;
+
uint8 *to,*from;
register ULONG x,count;
+
uint32 x,count;
   
ULONG length;
+
uint32 length;
 
struct cbbuf *buf,*success;
 
struct cbbuf *buf,*success;
   
 
success = NULL;
 
success = NULL;
   
if (buf = AllocMem(sizeof(struct cbbuf),MEMF_PUBLIC))
+
if (buf = IExec->AllocVecTags(sizeof(struct cbbuf), TAG_END))
 
{
 
{
   
 
length = size;
 
length = size;
if (size &amp; 1)
+
if (size & 1)
 
length++; /* if odd size, read 1 more */
 
length++; /* if odd size, read 1 more */
   
if (buf-&gt;mem = AllocMem(length+1L,MEMF_PUBLIC))
+
if (buf->mem = IExec->AllocVecTags(length+1, TAG_END))
 
{
 
{
buf-&gt;size = length+1L;
+
buf->size = length+1L;
   
ior-&gt;io_Command = CMD_READ;
+
ior->io_Command = CMD_READ;
ior-&gt;io_Data = (STRPTR)buf-&gt;mem;
+
ior->io_Data = (STRPTR)buf->mem;
ior-&gt;io_Length = length;
+
ior->io_Length = length;
   
to = buf-&gt;mem;
+
to = buf->mem;
 
count = 0L;
 
count = 0L;
   
 
if (!(DoIO( (struct IOStdReq *) ior)))
 
if (!(DoIO( (struct IOStdReq *) ior)))
 
{
 
{
if (ior-&gt;io_Actual == length)
+
if (ior->io_Actual == length)
 
{
 
{
 
success = buf; /* everything succeeded */
 
success = buf; /* everything succeeded */
   
 
/* strip NULL bytes */
 
/* strip NULL bytes */
for (x=0, from=buf-&gt;mem ;x&lt;size;x++)
+
for (x=0, from=buf->mem ;x<size;x++)
 
{
 
{
 
if (*from)
 
if (*from)
Line 1,258: Line 1,203:
 
}
 
}
 
*to=0x0; /* Null terminate buffer */
 
*to=0x0; /* Null terminate buffer */
buf-&gt;count = count; /* cache count of chars in buf */
+
buf->count = count; /* cache count of chars in buf */
 
}
 
}
 
}
 
}
   
 
if (!(success))
 
if (!(success))
FreeMem(buf-&gt;mem,buf-&gt;size);
+
IExec->FreeVec(buf->mem);
 
}
 
}
 
if (!(success))
 
if (!(success))
FreeMem(buf,sizeof(struct cbbuf));
+
IExec->FreeVec(buf);
 
}
 
}
   
Line 1,288: Line 1,233:
 
*********************************************************************/
 
*********************************************************************/
   
void CBReadDone(ior)
+
void CBReadDone(struct IOClipReq *ior)
struct IOClipReq *ior;
 
 
{
 
{
 
char buffer[256];
 
char buffer[256];
   
ior-&gt;io_Command = CMD_READ;
+
ior->io_Command = CMD_READ;
ior-&gt;io_Data = (STRPTR)buffer;
+
ior->io_Data = (STRPTR)buffer;
ior-&gt;io_Length = 254;
+
ior->io_Length = 254;
   
   
 
/* falls through immediately if io_Actual == 0 */
 
/* falls through immediately if io_Actual == 0 */
   
while (ior-&gt;io_Actual)
+
while (ior->io_Actual)
 
{
 
{
if (DoIO( (struct IORequest *) ior))
+
if (IExec->DoIO( (struct IORequest *) ior))
 
break;
 
break;
 
}
 
}
Line 1,325: Line 1,269:
 
struct cbbuf *buf;
 
struct cbbuf *buf;
 
{
 
{
FreeMem(buf-&gt;mem, buf-&gt;size);
+
IExec->FreeVec(buf->mem);
  +
IExec->FreeVec(buf);
FreeMem(buf, sizeof(struct cbbuf));
 
  +
}
}</pre>
 
  +
</syntaxhighlight>
<pre>****************************************************************************
 
* Hookface.asm
 
* assembly routines for Chtest
 
*
 
* Assemble with Adapt hx68 hookface.a to hookface.o
 
* Link with Changehook_Test.o as shown in Changehook_Test.c header
 
*
 
****************************************************************************
 
INCDIR 'include:'
 
INCLUDE 'exec/types.i'
 
INCLUDE 'utility/hooks.i'
 
xdef _callHook
 
xdef _callHookPkt
 
xdef _hookEntry
 
xdef _stubReturn
 
***************************************************************************
 
* new hook standard
 
* use struct Hook (with minnode at the top)
 
*
 
* *** register calling convention: ***
 
* A0 - pointer to hook itself
 
* A1 - pointer to parameter packed (&quot;message&quot;)
 
* A2 - Hook specific address data (&quot;object,&quot; e.g, gadget )
 
*
 
* *** C conventions: ***
 
* Note that parameters are in unusual register order: a0, a2, a1.
 
* This is to provide a performance boost for assembly language
 
* programming (the object in a2 is most frequently untouched).
 
* It is also no problem in &quot;register direct&quot; C function parameters.
 
*
 
* calling through a hook
 
* callHook( hook, object, msgid, p1, p2, ... );
 
* callHookPkt( hook, object, msgpkt );
 
*
 
* using a C function: CFunction( hook, object, message );
 
* hook.h_Entry = hookEntry;
 
* hook.h_SubEntry = CFunction;
 
****************************************************************************
 
* C calling hook interface for prepared message packet
 
_callHookPkt:
 
movem.l a2/a6,-(sp) ; protect
 
move.l 12(sp),a0 ; hook
 
move.l 16(sp),a2 ; object
 
move.l 20(sp),a1 ; message
 
; ------ now have registers ready, invoke function
 
pea.l hreturn(pc)
 
move.l h_Entry(a0),-(sp) ; old rts-jump trick
 
rts
 
hreturn:
 
movem.l (sp)+,a2/a6
 
rts
 
   
* C calling hook interface for &quot;varargs message packet&quot;
 
_callHook:
 
movem.l a2/a6,-(sp) ; protect
 
move.l 12(sp),a0 ; hook
 
move.l 16(sp),a2 ; object
 
lea.l 20(sp),a1 ; message
 
; ------ now have registers ready, invoke function
 
pea.l hpreturn(pc)
 
move.l h_Entry(a0),-(sp) ; old rts-jump trick
 
rts
 
hpreturn:
 
movem.l (sp)+,a2/a6
 
rts
 
 
* entry interface for C code (large-code, stack parameters)
 
_hookEntry:
 
move.l a1,-(sp)
 
move.l a2,-(sp)
 
move.l a0,-(sp)
 
move.l h_SubEntry(a0),a0 ; C entry point
 
jsr (a0)
 
lea 12(sp),sp
 
_stubReturn:
 
rts</pre>
 
 
== Include File for the Example Programs ==
 
== Include File for the Example Programs ==
   
  +
<syntaxhighlight>
<pre>/***********************************************************************
 
  +
/***********************************************************************
 
*
 
*
 
* cb.h -- Include file used by clipdemo.c, changehook_test.c and cbio.c
 
* cb.h -- Include file used by clipdemo.c, changehook_test.c and cbio.c
Line 1,414: Line 1,285:
 
struct cbbuf {
 
struct cbbuf {
   
ULONG size; /* size of memory allocation */
+
uint32 size; /* size of memory allocation */
ULONG count; /* number of characters after stripping */
+
uint32 count; /* number of characters after stripping */
UBYTE *mem; /* pointer to memory containing data */
+
uint8 *mem; /* pointer to memory containing data */
 
};
 
};
   
#define MAKE_ID(a,b,c,d) ((a&lt;&lt;24L) | (b&lt;&lt;16L) | (c&lt;&lt;8L) | d)
+
#define MAKE_ID(a,b,c,d) ((a<<24L) | (b<<16L) | (c<<8L) | d)
   
 
#define ID_FORM MAKE_ID('F','O','R','M')
 
#define ID_FORM MAKE_ID('F','O','R','M')
Line 1,429: Line 1,300:
 
/* prototypes */
 
/* prototypes */
   
struct IOClipReq *CBOpen ( ULONG );
+
struct IOClipReq *CBOpen ( uint32 );
 
void CBClose (struct IOClipReq *);
 
void CBClose (struct IOClipReq *);
 
int CBWriteFTXT (struct IOClipReq *, char *);
 
int CBWriteFTXT (struct IOClipReq *, char *);
Line 1,441: Line 1,312:
   
 
int WriteLong (struct IOClipReq *, long *);
 
int WriteLong (struct IOClipReq *, long *);
int ReadLong (struct IOClipReq *, ULONG *);
+
int ReadLong (struct IOClipReq *, uint32 *);
struct cbbuf *FillCBData (struct IOClipReq *, ULONG);
+
struct cbbuf *FillCBData (struct IOClipReq *, uint32);
   
 
#else
 
#else
Line 1,448: Line 1,319:
 
/* prototypes */
 
/* prototypes */
   
extern struct IOClipReq *CBOpen ( ULONG );
+
extern struct IOClipReq *CBOpen ( uint32 );
 
extern void CBClose (struct IOClipReq *);
 
extern void CBClose (struct IOClipReq *);
 
extern int CBWriteFTXT (struct IOClipReq *, char *);
 
extern int CBWriteFTXT (struct IOClipReq *, char *);
Line 1,456: Line 1,327:
 
extern void CBFreeBuf (struct cbbuf *);
 
extern void CBFreeBuf (struct cbbuf *);
   
#endif</pre>
+
#endif
  +
</syntaxhighlight>
  +
 
== Additional Information on the Clipboard Device ==
 
== Additional Information on the Clipboard Device ==
   

Latest revision as of 22:25, 6 November 2015

Clipboard Device

The clipboard device allows the exchange of data dynamically between one application and another. It is responsible for caching data that has been “cut” and providing data to “paste” in an application. A special “post” mode allows an application to inform the clipboard device that the application has data available. The clipboard device will request this data only if the data is actually needed. The clipboard will cache the data in RAM and will automatically spool the data to disk if necessary.

The clipboard device is implemented as an Exec-style device, and supports random access reads and writes on data within the clipboard. All data in the clipboard must be in IFF format. A new library, iffparse.library, has been added to the Amiga libraries. The routines in iffparse.library can and should be used for reading and writing data to the clipboard. This section contains a brief discussion of IFF as it relates to the clipboard.

Clipboard Device Commands and Functions

Command Command Operation
CBD_CHANGEHOOK Specify a hook to be called when the data on the clipboard has changed.
CBD_CURRENTREADID Return the Clip ID of the current clip to read. This is used to determine if a clip posting is still the latest cut.
CBD_CURRENTWRITEID Return the Clip ID of the latest clip written. This is used to determine if the clip posting data is obsolete.
CBD_POST Post the availability of clip data.
CMD_READ Read data from the clipboard for a paste. Data can be read from anywhere in the clipboard by specifying an offset 0 in the I/O request.
CMD_UPDATE Indicate that the data provided with a write command is complete and available for subsequent read/pastes.
CMD_WRITE Write data to the clipboard as a cut.

Device Interface

The clipboard device operates like the other Amiga devices. To use it, you must first open the clipboard device, then send I/O requests to it, and then close it when finished. See Exec Device I/O for general information on device usage.

struct IOClipReq
{
    struct  Message io_Message;
    struct  Device  *io_Device; /* device node pointer  */
    struct  Unit    *io_Unit;   /* unit (driver private) */
    UWORD   io_Command;         /* device command */
    UBYTE   io_Flags;           /* including QUICK and SATISFY */
    BYTE    io_Error;           /* error or warning num */
    ULONG   io_Actual;          /* number of bytes transferred */
    ULONG   io_Length;          /* number of bytes requested */
    STRPTR  io_Data;            /* either clip stream or post port */
    ULONG   io_Offset;          /* offset in clip stream */
    LONG    io_ClipID;          /* ordinal clip identifier */
};

See the include file devices/clipboard.h for the complete structure definition.

The clipboard device I/O request, IOClipReq, looks like a standard IORequest structure except for the addition of the io_ClipID field, which is used by the device to identify clips. It must be set to zero by the application for a post or an initial write or read, but preserved for subsequent writes or reads, as the clipboard device uses this field internally for bookkeeping purposes.

Opening the Clipboard Device

Three primary steps are required to open the clipboard device:

  • Create a message port using AllocSysObject() and ASOT_PORT type. Reply messages from the device must be directed to a message port.
  • Create an extended I/O request structure of type IOClipReq using AllocSysObject() and ASOT_IOREQUEST type.
  • Open the clipboard device. Call OpenDevice(), passing the IOClipReq.
struct MsgPort *ClipMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
if (ClipMP != NULL)
{
    struct IOClipReq *ClipIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_Size, sizeof(struct IOClipReq),
        ASOIOR_ReplyPort, ClipMP,
        TAG_END);
 
    if (ClipIO != NULL)
    {
        if (IExec->OpenDevice("clipboard.device", 0, ClipIO, 0))
            IDOS->Printf("clipboard.device did not open\n");
        else
        {
             ... do device processing
        }
    }
    else
        IDOS->Printf("Error: Could not create IORequest\n");
}
else
    IDOS->Printf("Error: Could not create message port\n");

Clipboard Data

Data on the clipboard resides in one of three places. When an application posts a cut, the data resides in the private memory space of that application. When an application writes to the clipboard, either of its own volition or in response to a message from the clipboard requesting that it satisfy a post, the data is copied to the clipboard which is either memory or a special disk file. When the clipboard is not open, the data resides in the special disk file located in the directory specified by the CLIPS: logical AmigaDOS assign.

Data on the clipboard is self-identifying. It must be a correct IFF (Interchange File Format) file; the rest of this section refers to IFF concepts. See the Appendix A in this manual for a complete description of IFF. If the top-level chunk is of type CAT with an identifier of CLIP, that indicates that the contained chunks are different representations of the same data, in decreasing order of preference on the part of the producer of the clip. Any other data is as defined elsewhere (probably a single representation of the cut data produced by an application).

The iffparse.library also contains functions which simplify reading and writing of IFF data to the clipboard device. See the IFFParse Library for more information.

A clipboard tool, which is an application that allows a Workbench user to view a clip, should understand the text (FTXT) and graphics (ILBM) form types. Applications using the clipboard to export data should include at least one of these types in a CAT CLIP so that their data can be represented on the clipboard in some form for user feedback.

You should not, in any way, rely on the specifics of how files in CLIPS: are handled or named. The only proper way to read or write clipboard data is via the clipboard device.

Play Nice!
Keep in mind that while your task is reading from or writing to a clipboard unit, other tasks cannot. Therefore, it is important to be fast. If possible, make a copy of the clipboard data in RAM instead of processing it while the read or write is in progress.

Multiple Clips

The clipboard supports multiple clips, i.e., the clipboard device can contain more than one distinct piece of data. This is not to be confused with the IFF CAT CLIP, which allows for different representation of the same data.

The multiple clips are implemented as different units in the clipboard device. The unit is specified at OpenDevice() time.

struct IOClipReq *ClipIO;
int32 unit;
 
IExec->OpenDevice("clipboard.device", unit, ClipIO, 0);

By default, applications should use clipboard unit 0. However, it is recommended that each application provide a mechanism for selecting the unit number which will be used when the clipboard is opened. This will allow the user to create a convention for storing different types of data in the clipboard. Applications should never write to clipboard unit 0 unless the user requests it (e.g., selecting Copy or Cut within an application).

Clipboard units 1–255 can be used by the more advanced user for:

  • Sharing data between applications within an ARexx Script.
  • Customizing applications to store different kinds of data in different clipboard units.
  • Customizing an application to use multiple cut/copy/paste buffers.
  • Specialized utilities which might display and/or automatically modify the contents of a clipboard unit.

All applications which provide Cut, Copy and Paste capabilities, should, at a minimum, provide support for clipboard unit 0.

Writing to the Clipboard Device

You write to the clipboard device by passing an IOClipReq to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.

ClipIO->io_Data    = (char *) data;
ClipIO->io_Length  = 4;
ClipIO->io_Command = CMD_WRITE;

An initial write should set io_Offset to zero. Each time a write is done, the device will increment io_Offset by the length of the write.

As previously stated, the data you write to the clipboard must be in IFF format. This requires a certain amount of preparation prior to actually writing the data if it is not already in IFF format. A brief explanation of the IFF format will be helpful in this regard.

For our purposes, we will limit our discussion to a simple formatted text (FTXT) IFF file. An FTXT file looks like:

FORM length of succeeding bytes
FTXT
CHRS length of succeeding bytes
data bytes
pad byte of zero if the preceding chunk has odd length

Based on the above figure, a hex dump of an IFF FTXT file containing the string “Enterprise” would look like:

0000 464F524D FORM
0004 00000016 (length of FTXT, CHRS, 0xA and data)
0008 46545854 FTXT
000C 43485253 CHRS
0010 0000000A (length of “Enterprise”)
0014 456E7465 Ente
0018 72707269 rpri
001C 7365 se

A code fragment for doing this:

LONG slen = strlen ("Enterprise");
BOOL odd = (slen & 1);      /* pad byte flag */
 
/* set length depending on whether string is odd or even length */
LONG length = (odd) ? slen + 1 : slen;
 
/* Reset the clip id */
ClipIO->io_ClipID = 0;
ClipIO->io_Offset = 0;
 
error = writeLong ((LONG *) "FORM");/* "FORM" */
 
length += 12;  /* add 12 bytes for FTXT, CHRS & length byte to string length */
error = writeLong (&length);
error = writeLong ((LONG *) "FTXT");/* "FTXT" for example */
error = writeLong ((LONG *) "CHRS");/* "CHRS" for example */
error = writeLong (&slen);      /* #  (length of string) */
 
ClipIO->io_Command = CMD_WRITE;
ClipIO->io_Data    = (char *) string;
ClipIO->io_Length  = slen;               /* length of string */
error = (LONG) IExec->DoIO (clipIO);   /* text string */
 
 
LONG writeLong (LONG * ldata)
{
    ClipIO->io_Command = CMD_WRITE;
    ClipIO->io_Data    = (char *) ldata;
    ClipIO->io_Length  = 4L;
    return ( (LONG) IExec->DoIO (clipIO) );
}

The fragment above does no error checking because it’s a fragment. You should always error check. See the example programs at the end of this article for the proper method of error checking.

IFFParse That Data!
Keep in mind that the functions in the iffparse.library can be used to write data to the clipboard. See the IFFParse Library for more information.

Updating the Clipboard Device

When the final write is done, an update command must be sent to the device to indicate that the writing is complete and the data is available. You update the clipboard device by passing an IOClipReq to the device with CMD_UPDATE set in io_Command.

ClipIO->io_Command = CMD_UPDATE;
IEXec->DoIO(ClipIO);

Clipboard Messages

When an application performs a post, it must specify a message port for the clipboard to send a message to if it needs the application to satisfy the post with a write called the SatisfyMsg.

struct SatisfyMsg
{
struct  Message sm_Message; /* the length will be 6 */
UWORD   sm_Unit;            /* 0 for the primary clip unit */
LONG    sm_ClipID;          /* the clip identifier of the post */
}

This structure is defined in the include file devices/clipboard.h.

If the application wishes to determine if a post it has recently performed is still the current clip, it should compare the io_ClipID found in the post request upon return with that returned by the CBD_CURRENTREADID command.

If an application has a pending post and wishes to determine if it should satisfy it (for example, before it exits), it should compare the io_ClipID of the post I/O request with that of the CBD_CURRENTWRITEID command. If the application receives a satisfy message from the clipboard device (format described below), it must immediately perform the write with the io_ClipID of the post. The satisfy message from the clipboard may be removed from the application message port by the clipboard device at any time (because it is re-used by the clipboard device). It is not dangerous to spuriously satisfy a post, however, because it is identified by the io_ClipID.

The cut data is provided to the clipboard device via either a write or a post of the cut data. The write command accepts the data immediately and copies it onto the clipboard. The post command allows an application to inform the clipboard of a cut, but defers the write until the data is actually required for a paste.

In the preceding discussion, references to the read and write commands of the clipboard device actually refer to a sequence of read or write commands, where the clip data is acquired and provided in pieces instead of all at once.

The clipboard has an end-of-clip concept that is analogous to end-of-file for both read and write. The read end-of-file must be triggered by the user of the clipboard in order for the clipboard to move on to service another application’s requests, and consists of reading data past the end of file. The write end-of-file is indicated by use of the update command, which indicates to the clipboard that the previous write commands are completed.

Reading from the Clipboard Device

You read from the clipboard device by passing an IOClipReq to the device with CMD_READ set in io_Command, the number of bytes to be read set in io_Length and the address of the read buffer set in io_Data.

ClipIO->io_Command = CMD_READ;
ClipIO->io_Data    = (char *) read_data;
ClipIO->io_Length  = 20;

io_Offset must be set to zero for the first read of a paste sequence. An io_Actual that is less than the io_Length indicates that all the data has been read. After all the data has been read, a subsequent read must be performed (one whose io_Actual returns zero) to indicate to the clipboard device that all the data has been read. This allows random access of the clip while reading. Providing only valid reads are performed, your program can seek/read anywhere within the clip by setting the io_Offset field of the I/O request appropriately.

Tell The Clipboard You Are Finished Reading
Your application must perform an extra read (one whose io_Actual returns zero) to indicate to the clipboard device that all data has been read, if io_Actual is not already zero.

The data you read from the clipboard will be in IFF format. Conversion from IFF may be necessary depending on your application.

IFFParse That Data!
Keep in mind that the functions in the iffparse.library can be used to read data from the clipboard. See the IFFParse Library for more information.

Closing the Clipboard Device

Each OpenDevice() must eventually be matched by a call to CloseDevice().

IExec->CloseDevice(ClipIO);

When the last task closes a clipboard unit with CloseDevice(), the contents of the unit may be copied to a disk file in CLIPS: so that the clipboard device can be expunged.

Monitoring Clipboard Changes

Some applications require notification of changes to data on the clipboard. Typically, these applications will need to do some processing when this occurs. You can set up such an environment through the CBD_CHANGEHOOK command. CBD_CHANGEHOOK allows you to specify a hook to be called when the data on the clipboard changes.

For example, a “show clipboard” utility would need to know when the data on the clipboard is changed so that it can display the new data. The hook it would specify would read the new clipboard data and display it for the user.

You specify a hook for the clipboard device by initializing a Hook structure and then passing an IOClipReq to the device with CBD_CHANGEHOOK set in io_Command, 1 set in io_Length, and the address of the Hook structure set in io_Data.

struct IOClipReq *ClipIO;          /* Declare the IOClipReq */
struct Hook *ClipHook;             /* Declare the Hook */
 
/* Prepare the hook */
ClipHook->h_Entry = HookFunc;        /* Function to call when Hook is activated */
ClipHook->h_Data  = FindTask(NULL);  /* Set pointer to current task */
 
ClipIO->io_Data    = (char *) ClipHook; /* Point to hook struct */
ClipIO->io_Length  = 1;                 /* Add hook to clipboard */
ClipIO->io_Command = CBD_CHANGEHOOK;
IExec->DoIO(clipIO);

It also assumes that the function HookFunc() has been coded. One of the example programs at the end of this article has hook processing in it. See the include file utility/hooks.h and Utility Library for further information on hooks.

You remove a hook by passing an IOClipReq to the device with the address of the Hook structure set in io_Data, 0 set in io_Length and CBD_CHANGEHOOK set in io_Command.

ClipIO->io_Data    = (char *) ClipHook;   /* point to hook struct */
ClipIO->io_Length  = 0;                   /* Remove hook from clipboard */
ClipIO->io_Command = CBD_CHANGEHOOK;
IExec->DoIO(clipIO);

You must remove the hook or it will continue indefinitely.

Caveats for CBD_CHANGEHOOK

  • CBD_CHANGEHOOK should only be used by a special application, such as a clipboard viewing program. Most applications can check the contents of the clipboard when, and if, the user requests a paste.
  • Do not add system overhead by blindly reading and parsing the clipboard every time a user copies data to it. If all applications did this, the system could become intolerably slow whenever an application wrote to the clipboard. Only read and parse when it is necessary.

Example Clipboard Programs

Clipdemo.c

/*
 * Clipdemo.c
 *
 * Demonstrate use of clipboard I/O.  Uses general functions
 * provided in cbio.c
 *
 * Run from CLI only
 */
 
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/clipboard.h>
#include <libraries/dosextens.h>
#include <libraries/dos.h>
 
#include "cb.h"
 
#include <proto/dos.h>
#include <proto/exec.h>
 
#include <string.h>
 
#define FORGETIT 0
#define READIT   1
#define WRITEIT  2
#define POSTIT   3
 
/* prototypes */
 
int ReadClip( void );           /* Demonstrate reading clipboard data      */
int WriteClip( char * );        /* Demonstrate write to clipboard        */
int PostClip( char * );         /* Demonstrate posting data to clipboard */
 
char message[] = "\
\nPossible switches are:\n\n\
-r            Read, and output contents of clipboard.\n\
-w [string]   Write string to clipboard.\n\n\
-p [string]   Write string to clipboard using the clipboard POST mechanism.\n\n\
              The Post can be satisfied by reading data from\n\
              the clipboard.  Note that the message may never\n\
              be received if some other application posts, or\n\
              performs an immediate write to the clipboard.\n\n\
              To run this test you must run two copies of this example.\n\
              Use the -p switch with one to post data, and the -r switch\n\
              with another to read the data.\n\n\
              The process can be stopped by using the BREAK command,\n\
              in which case this example checks the CLIP write ID\n\
              to determine if it should write to the clipboard before\n\
              exiting.\n\n";
 
 
int main(int argc, char **argv)
{
int todo;
char *string;
 
todo = FORGETIT;
 
if (argc)     /* from CLI ? */
    {
 
    /* Very simple code to parse for arguments - will suffice for
     * the sake of this example
     */
 
    if (argc > 1)
       {
       if (!(strcmp(argv[1],"-r")))
           todo = READIT;
       if (!(strcmp(argv[1],"-w")))
           todo = WRITEIT;
       if (!(strcmp(argv[1],"-p")))
           todo = POSTIT;
 
       string = NULL;
 
       if (argc > 2)
           string=argv[2];
 
       }
 
    switch (todo)
            {
 
            case READIT:
 
                 ReadClip();
                 break;
 
            case POSTIT:
 
                 PostClip(string);
                 break;
 
            case WRITEIT:
 
                 WriteClip(string);
                 break;
 
            default:
 
                 IDOS->Printf("%s",message);
                 break;
 
            }
 
 
    }
}
 
/*
 * Read, and output FTXT in the clipboard.
 *
 */
 
int ReadClip(void)
{
struct IOClipReq *ior;
struct cbbuf *buf;
 
 
/* Open clipboard.device unit 0 */
 
if (ior=CBOpen(0L))
    {
 
    /* Look for FTXT in clipboard */
 
    if (CBQueryFTXT(ior))
        {
 
        /* Obtain a copy of the contents of each CHRS chunk */
 
        while (buf=CBReadCHRS(ior))
              {
              /* Process data */
 
              IDOS->Printf("%s\n",buf->mem);
 
              /* Free buffer allocated by CBReadCHRS() */
 
              CBFreeBuf(buf);
              }
 
        /* The next call is not really needed if you are sure */
        /* you read to the end of the clip.                   */
 
        CBReadDone(ior);
        }
    else
        {
        IDOS->PutStr("No FTXT in clipboard");
        }
 
    CBClose(ior);
    }
 
else
    {
    IDOS->PutStr("Error opening clipboard unit 0");
    }
 
return(0);
}
 
/*
 * Write a string to the clipboard
 *
 */
 
int WriteClip(char *string)
{
 
struct IOClipReq *ior;
 
if (string == NULL)
    {
    IDOS->PutStr("No string argument given");
    return(0L);
    }
 
/* Open clipboard.device unit 0 */
 
if (ior = CBOpen(0L))
    {
    if (!(CBWriteFTXT(ior,string)))
        {
        IDOS->Printf("Error writing to clipboard: io_Error = %ld\n",ior->io_Error);
        }
    CBClose(ior);
    }
else
    {
    IDOS->PutStr("Error opening clipboard.device");
    }
 
return(0);
}
 
 
/*
 * Write a string to the clipboard using the POST mechanism
 *
 * The POST mechanism can be used by applications which want to
 * defer writing text to the clipboard until another application
 * needs it (by attempting to read it via CMD_READ).  However
 * note that you still need to keep a copy of the data until you
 * receive a SatisfyMsg from the clipboard.device, or your program
 * exits.
 *
 * In most cases it is easier to write the data immediately.
 *
 * If your program receives the SatisfyMsg from the clipboard.device,
 * you MUST write some data.  This is also how you reply to the message.
 *
 * If your program wants to exit before it has received the SatisfyMsg,
 * you must check the io_ClipID field at the time of the post against
 * the current post ID which is obtained by sending the CBD_CURRENTWRITEID
 * command.
 *
 * If the value in io_ClipID (returned by CBD_CURRENTWRITEID) is greater
 * than your post ID, it means that some other application has performed
 * a post, or immediate write after your post, and that you're application
 * will never receive the SatisfyMsg.
 *
 * If the value in io_ClipID (returned by CBD_CURRENTWRITEID) is equal
 * to your post ID, then you must write your data, and send CMD_UPDATE
 * before exiting.
 *
 */
 
int PostClip(char *string)
{
 
struct MsgPort *satisfy;
struct SatisfyMsg *sm;
struct IOClipReq *ior;
int mustwrite;
uint32 postID;
 
if (string == NULL)
    {
    IDOS->PutStr("No string argument given");
    return(0L);
    }
 
if (satisfy = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END))
    {
 
    /* Open clipboard.device unit 0 */
 
    if (ior = CBOpen(0L))
        {
        mustwrite = FALSE;
 
        /* Notify clipboard we have data */
 
        ior->io_Data    = (STRPTR)satisfy;
        ior->io_ClipID  = 0L;
        ior->io_Command = CBD_POST;
        IExec->DoIO( (struct IORequest *) ior);
 
        postID = ior->io_ClipID;
 
        IDOS->Printf("\nClipID = %ld\n",postID);
 
        /* Wait for CTRL-C break, or message from clipboard */
        IExec->Wait(SIGBREAKF_CTRL_C|(1L << satisfy->mp_SigBit));
 
        /* see if we got a message, or a break */
        IDOS->PutStr("Woke up");
 
        if (sm = (struct SatisfyMsg *)GetMsg(satisfy))
            {
            IDOS->PutStr("Got a message from the clipboard\n");
 
            /* We got a message - we MUST write some data */
            mustwrite = TRUE;
            }
        else
            {
            /* Determine if we must write before exiting by
             * checking to see if our POST is still valid
             */
 
            ior->io_Command = CBD_CURRENTWRITEID;
            IExec->DoIO( (struct IORequest *) ior);
 
            IDOS->Printf("CURRENTWRITEID = %ld\n",ior->io_ClipID);
 
            if (postID >= ior->io_ClipID)
                mustwrite = TRUE;
 
            }
 
        /* Write the string of text */
 
        if (mustwrite)
            {
            if (!(CBWriteFTXT(ior,string)))
                IDOS->PutStr("Error writing to clipboard");
            }
        else
            {
            IDOS->PutStr("No need to write to clipboard");
            }
 
        CBClose(ior);
        }
    else
        {
        IDOS->PutStr("Error opening clipboard.device");
        }
 
    IExec->FreeSysObject(ASOT_PORT, satisfy);
    }
else
    {
    IDOS->PutStr("Error creating message port");
    }
 
return(0);
}

Changehook_Test.c

/*
 * Changehook_Test.c
 *
 * Demonstrate the use of CBD_CHANGEHOOK command.
 * The program will set a hook and wait for the clipboard data to change.
 * You must put something in the clipboard in order for it to return.
 *
 * Run from CLI only
 */
 
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/io.h>
#include <devices/clipboard.h>
#include <dos/dos.h>
#include <utility/hooks.h>
#include "cb.h"
 
#include <proto/exec.h>
#include <proto/dos.h>
 
int32 version = 1;
 
/* Data to pass around with the clipHook */
struct CHData
{
    struct Task *ch_Task;
    LONG ch_ClipID;
};
 
struct MsgPort *clip_port;
struct Hook hook;
struct CHData ch;
 
ULONG clipHook (struct Hook * h, VOID * o, struct ClipHookMsg * msg)
{
struct CHData *ch = (struct CHData *) h->h_Data;
 
if (ch)
   {
   /* Remember the ID of clip */
   ch->ch_ClipID = msg->chm_ClipID;
 
   /* Signal the task that started the hook */
   IExec->Signal (ch->ch_Task, SIGBREAKF_CTRL_E);
   }
 
return (0);
}
 
struct IOClipReq *OpenCB (LONG unit)
{
struct IOClipReq *clipIO;
 
/* Open clipboard unit 0 */
 
if (clipIO = CBOpen( 0L ))
    {
    /* Fill out the IORequest */
    clipIO->io_Data = (char *) &hook;
    clipIO->io_Length = 1;
    clipIO->io_Command = CBD_CHANGEHOOK;
 
    /* Set up the hook data */
    ch.ch_Task = IExec->FindTask (NULL);
 
    /* Prepare the hook */
    hook.h_Entry = clipHook;
    hook.h_Data = &ch;
 
    /* Start the hook */
    if (IExec->DoIO (clipIO))
        IDOS->Printf ("unable to set hook\n");
    else
        IDOS->Printf ("hook set\n");
 
    /* Return success */
    return ( clipIO );
    }
 
/* return failure */
return (NULL);
}
 
void CloseCB (struct IOClipReq *clipIO)
{
 
/* Fill out the IO request */
clipIO->io_Data = (char *) &hook;
clipIO->io_Length = 0;
clipIO->io_Command = CBD_CHANGEHOOK;
 
    /* Stop the hook */
if (IExec->DoIO (clipIO))
    IDOS->Printf ("unable to stop hook\n");
else
    /* Indicate success */
    IDOS->Printf ("hook is stopped\n");
 
CBClose(clipIO);
}
 
int main (int argc, char **argv)
{
struct IOClipReq *clipIO;
 
ULONG sig_rcvd;
 
IDOS->Printf ("Test v%ld\n", version);
 
if (clipIO=OpenCB (0L))
    {
    sig_rcvd = IExec->Wait ((SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E));
 
    if (sig_rcvd & SIGBREAKF_CTRL_C)
        IDOS->Printf ("^C received\n");
 
    if (sig_rcvd & SIGBREAKF_CTRL_E)
        IDOS->Printf ("clipboard change, current ID is %ld\n", ch.ch_ClipID);
 
    CloseCB(clipIO);
    }
    return 0;
}

Support Functions Called from Example Programs

/* Cbio.c
 *
 * Provide standard clipboard device interface routines
 *            such as Open, Close, Post, Read, Write, etc.
 *
 *  NOTE - These functions are useful for writing, and reading simple
 *         FTXT.  Writing, and reading complex FTXT, ILBM, etc.,
 *         requires more work - it is highly recommended that
 *         you use iffparse.library.
 */
 
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/clipboard.h>
 
#define CBIO 1
 
#include "cb.h"
 
#include <proto/exec.h>
 
#include <string.h>
 
/****** cbio/CBOpen *************************************************
*
*   NAME
*       CBOpen() -- Open the clipboard.device
*
*   SYNOPSIS
*       ior = CBOpen(unit)
*
*       struct IOClipReq *CBOpen( ULONG )
*
*   FUNCTION
*       Opens the clipboard.device.  A clipboard unit number
*       must be passed in as an argument.  By default, the unit
*       number should be 0 (currently valid unit numbers are
*       0-255).
*
*   RESULTS
*       A pointer to an initialized IOClipReq structure, or
*       a NULL pointer if the function fails.
*
*********************************************************************/
 
struct IOClipReq *CBOpen(uint32 unit)
{
struct MsgPort *mp = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
if (mp != NULL)
    {
    struct IOStdReq *ior = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_Size, sizeof(struct IOClipReq),
        ASOIOR_ReplyPort, mp,
        TAG_END);
 
    if (ior != NULL)
        {
        if (!(IExec->OpenDevice("clipboard.device", unit, ior, 0)))
            {
            return((struct IOClipReq *)ior);
            }
        IExec->FreeSysObject(ASOT_IOREQUEST, ior);
        }
    IExec->FreeSysObject(ASOT_PORT, mp);
    }
return(NULL);
}
 
/****** cbio/CBClose ************************************************
*
*   NAME
*       CBClose() -- Close the clipboard.device
*
*   SYNOPSIS
*       CBClose()
*
*       void CBClose()
*
*   FUNCTION
*       Close the clipboard.device unit which was opened via
*       CBOpen().
*
*********************************************************************/
 
void CBClose(struct IOClipReq *ior)
{
struct MsgPort *mp = ior->io_Message.mn_ReplyPort;
 
IExec->CloseDevice((struct IOStdReq *)ior);
IExec->FreeSysObject(ASOT_IOREQUEST, ior);
IExec->FreeSysObject(ASOT_PORT, mp);
}
 
/****** cbio/CBWriteFTXT *********************************************
*
*   NAME
*       CBWriteFTXT() -- Write a string of text to the clipboard.device
*
*   SYNOPSIS
*       success = CBWriteFTXT( ior, string)
*
*       int CBWriteFTXT(struct IOClipReq *, char *)
*
*   FUNCTION
*       Write a NULL terminated string of text to the clipboard.
*       The string will be written in simple FTXT format.
*
*       Note that this function pads odd length strings automatically
*       to conform to the IFF standard.
*
*   RESULTS
*       TRUE if the write succeeded, else FALSE.
*
*********************************************************************/
 
int CBWriteFTXT(struct IOClipReq *ior, STRPTR string)
{
uint32 length, slen;
BOOL odd;
int success;
 
slen = strlen(string);
odd = (slen & 1);               /* pad byte flag */
 
length = (odd) ? slen+1 : slen;
 
/* initial set-up for Offset, Error, and ClipID */
 
ior->io_Offset = 0;
ior->io_Error  = 0;
ior->io_ClipID = 0;
 
 
/* Create the IFF header information */
 
WriteLong(ior, (long *) "FORM");     /* "FORM"             */
length+=12L;                         /* + "[size]FTXTCHRS" */
WriteLong(ior, &length);             /* total length       */
WriteLong(ior, (long *) "FTXT");     /* "FTXT"             */
WriteLong(ior, (long *) "CHRS");     /* "CHRS"             */
WriteLong(ior, &slen);               /* string length      */
 
/* Write string */
ior->io_Data    = (STRPTR)string;
ior->io_Length  = slen;
ior->io_Command = CMD_WRITE;
IExec->DoIO( (struct IORequest *) ior);
 
/* Pad if needed */
if (odd)
    {
    ior->io_Data   = (STRPTR)"";
    ior->io_Length = 1L;
    IExec->DoIO( (struct IORequest *) ior);
    }
 
/* Tell the clipboard we are done writing */
 
ior->io_Command=CMD_UPDATE;
IExec->DoIO( (struct IORequest *) ior);
 
/* Check if io_Error was set by any of the preceding IO requests */
success = ior->io_Error ? FALSE : TRUE;
 
return(success);
}
 
WriteLong(struct IOClipReq *ior, long *ldata)
{
ior->io_Data    = (STRPTR)ldata;
ior->io_Length  = 4L;
ior->io_Command = CMD_WRITE;
IExec->DoIO( (struct IORequest *) ior);
 
if (ior->io_Actual == 4)
    {
    return( ior->io_Error ? FALSE : TRUE);
    }
 
return(FALSE);
}
 
 
/****** cbio/CBQueryFTXT **********************************************
*
*   NAME
*       CBQueryFTXT() -- Check to see if clipboard contains FTXT
*
*   SYNOPSIS
*       result = CBQueryFTXT( ior )
*
*       int CBQueryFTXT(struct IOClipReq *)
*
*   FUNCTION
*       Check to see if the clipboard contains FTXT.  If so,
*       call CBReadCHRS() one or more times until all CHRS
*       chunks have been read.
*
*   RESULTS
*       TRUE if the clipboard contains an FTXT chunk, else FALSE.
*
*   NOTES
*       If this function returns TRUE, you must either call
*       CBReadCHRS() until CBReadCHRS() returns FALSE, or
*       call CBReadDone() to tell the clipboard.device that
*       you are done reading.
*
*
*********************************************************************/
 
int CBQueryFTXT(struct IOClipReq *ior)
{
uint32 cbuff[4];
 
 
/* initial set-up for Offset, Error, and ClipID */
 
ior->io_Offset = 0;
ior->io_Error  = 0;
ior->io_ClipID = 0;
 
/* Look for "FORM[size]FTXT" */
 
ior->io_Command = CMD_READ;
ior->io_Data    = (STRPTR)cbuff;
ior->io_Length  = 12;
 
IExec->DoIO( (struct IORequest *) ior);
 
 
/* Check to see if we have at least 12 bytes */
 
if (ior->io_Actual == 12L)
    {
    /* Check to see if it starts with "FORM" */
    if (cbuff[0] == ID_FORM)
        {
        /* Check to see if its "FTXT" */
        if (cbuff[2] == ID_FTXT)
            return(TRUE);
        }
 
    /* It's not "FORM[size]FTXT", so tell clipboard we are done */
    }
 
CBReadDone(ior);
 
return(FALSE);
}
 
 
/****** cbio/CBReadCHRS **********************************************
*
*   NAME
*       CBReadCHRS() -- Reads the next CHRS chunk from clipboard
*
*   SYNOPSIS
*       cbbuf = CBReadCHRS( ior )
*
*       struct cbbuf *CBReadCHRS(struct IOClipReq * )
*
*   FUNCTION
*       Reads and returns the text in the next CHRS chunk
*       (if any) from the clipboard.
*
*       Allocates memory to hold data in next CHRS chunk.
*
*   RESULTS
*       Pointer to a cbbuf struct (see cb.h), or a NULL indicating
*       a failure (e.g., not enough memory, or no more CHRS chunks).
*
*       ***Important***
*
*       The caller must free the returned buffer when done with the
*       data by calling CBFreeBuf().
*
*   NOTES
*       This function strips NULL bytes, however, a full reader may
*       wish to perform more complete checking to verify that the
*       text conforms to the IFF standard (stripping data as required).
*
*       Under 2.0, the AllocVec() function could be used instead of
*       AllocMem() in which case the cbbuf structure may not be
*       needed.
*
*********************************************************************/
 
struct cbbuf *CBReadCHRS(struct IOClipReq *ior)
{
uint32 chunk,size;
struct cbbuf *buf;
int looking;
 
/* Find next CHRS chunk */
 
looking = TRUE;
buf = NULL;
 
while (looking)
      {
      looking = FALSE;
 
      if (ReadLong(ior,&chunk))
          {
          /* Is CHRS chunk ? */
          if (chunk == ID_CHRS)
              {
              /* Get size of chunk, and copy data */
              if (ReadLong(ior,&size))
                  {
                  if (size)
                      buf=FillCBData(ior,size);
                  }
              }
 
            /* If not, skip to next chunk */
          else
              {
              if (ReadLong(ior,&size))
                  {
                   looking = TRUE;
                   if (size & 1)
                       size++;    /* if odd size, add pad byte */
 
                    ior->io_Offset += size;
                  }
              }
          }
      }
 
if (buf == NULL)
    CBReadDone(ior);        /* tell clipboard we are done */
 
return(buf);
}
 
 
ReadLong(struct IOClipReq *ior, uint32 *ldata)
{
ior->io_Command = CMD_READ;
ior->io_Data    = (STRPTR)ldata;
ior->io_Length  = 4L;
 
IExec->DoIO( (struct IORequest *) ior);
 
if (ior->io_Actual == 4)
    {
    return( ior->io_Error ? FALSE : TRUE);
    }
 
return(FALSE);
}
 
struct cbbuf *FillCBData(struct IOClipReq *ior, uint32 size)
{
uint8 *to,*from;
uint32 x,count;
 
uint32 length;
struct cbbuf *buf,*success;
 
success = NULL;
 
if (buf = IExec->AllocVecTags(sizeof(struct cbbuf), TAG_END))
    {
 
    length = size;
    if (size & 1)
        length++;            /* if odd size, read 1 more */
 
    if (buf->mem = IExec->AllocVecTags(length+1, TAG_END))
        {
        buf->size = length+1L;
 
        ior->io_Command = CMD_READ;
        ior->io_Data    = (STRPTR)buf->mem;
        ior->io_Length  = length;
 
        to = buf->mem;
        count = 0L;
 
        if (!(DoIO( (struct IOStdReq *) ior)))
            {
            if (ior->io_Actual == length)
                {
                success = buf;      /* everything succeeded */
 
                /* strip NULL bytes */
                for (x=0, from=buf->mem ;x<size;x++)
                     {
                     if (*from)
                         {
                         *to = *from;
                         to++;
                         count++;
                         }
 
                     from++;
                     }
                *to=0x0;            /* Null terminate buffer */
                buf->count = count; /* cache count of chars in buf */
                }
            }
 
        if (!(success))
            IExec->FreeVec(buf->mem);
        }
    if (!(success))
        IExec->FreeVec(buf);
    }
 
return(success);
}
 
/****** cbio/CBReadDone **********************************************
*
*   NAME
*       CBReadDone() -- Tell clipboard we are done reading
*
*   SYNOPSIS
*       CBReadDone( ior )
*
*       void CBReadDone(struct IOClipReq * )
*
*   FUNCTION
*       Reads past end of clipboard file until io_Actual is equal to 0.
*       This is tells the clipboard that we are done reading.
*
*********************************************************************/
 
void CBReadDone(struct IOClipReq *ior)
{
char buffer[256];
 
ior->io_Command = CMD_READ;
ior->io_Data    = (STRPTR)buffer;
ior->io_Length  = 254;
 
 
/* falls through immediately if io_Actual == 0 */
 
while (ior->io_Actual)
      {
      if (IExec->DoIO( (struct IORequest *) ior))
          break;
      }
}
 
/****** cbio/CBFreeBuf **********************************************
*
*   NAME
*       CBFreeBuf() -- Free buffer allocated by CBReadCHRS()
*
*   SYNOPSIS
*       CBFreeBuf( buf )
*
*       void CBFreeBuf( struct cbbuf * )
*
*   FUNCTION
*       Frees a buffer allocated by CBReadCHRS().
*
*********************************************************************/
 
void CBFreeBuf(buf)
struct cbbuf *buf;
{
IExec->FreeVec(buf->mem);
IExec->FreeVec(buf);
}

Include File for the Example Programs

/***********************************************************************
 *
 * cb.h -- Include file used by clipdemo.c, changehook_test.c and cbio.c
 *
 ***********************************************************************/
 
struct cbbuf {
 
        uint32 size;     /* size of memory allocation            */
        uint32 count;    /* number of characters after stripping */
        uint8 *mem;      /* pointer to memory containing data    */
};
 
#define MAKE_ID(a,b,c,d) ((a<<24L) | (b<<16L) | (c<<8L) | d)
 
#define ID_FORM MAKE_ID('F','O','R','M')
#define ID_FTXT MAKE_ID('F','T','X','T')
#define ID_CHRS MAKE_ID('C','H','R','S')
 
#ifdef CBIO
 
/* prototypes */
 
struct IOClipReq        *CBOpen         ( uint32 );
void                    CBClose         (struct IOClipReq *);
int                     CBWriteFTXT     (struct IOClipReq *, char *);
int                     CBQueryFTXT     (struct IOClipReq *);
struct cbbuf            *CBReadCHRS     (struct IOClipReq *);
void                    CBReadDone      (struct IOClipReq *);
void                    CBFreeBuf       (struct cbbuf *);
 
 
/* routines which are meant to be used internally by routines in cbio */
 
int                     WriteLong       (struct IOClipReq *, long *);
int                     ReadLong        (struct IOClipReq *, uint32 *);
struct cbbuf            *FillCBData     (struct IOClipReq *, uint32);
 
#else
 
/* prototypes */
 
extern struct IOClipReq *CBOpen         ( uint32 );
extern void             CBClose         (struct IOClipReq *);
extern int              CBWriteFTXT     (struct IOClipReq *, char *);
extern int              CBQueryFTXT     (struct IOClipReq *);
extern struct cbbuf     *CBReadCHRS     (struct IOClipReq *);
extern void             CBReadDone      (struct IOClipReq *);
extern void             CBFreeBuf       (struct cbbuf *);
 
#endif

Additional Information on the Clipboard Device

Additional programming information on the clipboard device can be found in the include files for the clipboard device, iffparse library and utility library, and the Autodocs for all three. They are contained in the SDK.

Includes
devices/clipboard.h
libraries/iffparse.h
libraries/iffparse.h
AutoDocs
clipboard.doc
iffparse.doc
utility.doc