![]() |
PATH![]() |
![]() ![]() |
The ColorSync Manager provides for multiple concurrent accesses to a single profile through the use of a private data structure called a
profile reference.
When you call the
CMOpenProfile
function to open a profile or the
CMNewProfile
,
CWNewLinkProfile
, or
CMCopyProfile
functions to create or copy a profile, you pass a profile location and the function returns a profile reference. To specify the profile location, you use a structure of type
CMProfileLocation
, as described in
Opening a Profile and Obtaining a Reference to It
.
A ColorSync profile that you open or create is typically stored in one of the following locations:
The sample code in Listing 3-18 to Listing 3-29 demonstrates how to use a profile access procedure to provide access to a resource-based profile.
NoteWhile the following sample code includes some error handling, more complete error handling is left as an exercise for the reader.
The sample code listings that follow use the application-defined MyResourceLocRec data structure. It stores information to describe a resource-based profile, including
struct MyResourceLocRec {
FSSpec resFileSpec;
ResType resType;
short resID;
short resFileRef;
Handle resHandle;
CMProfileAccessUPP proc;
Str255 resName;
};
typedef struct MyResourceLocRec MyResourceLocRec, *MyResourceLocPtr;
The ColorSync Manager defines the CMProfileAccessUPP type as follows:
typedef UniversalProcPtr CMProfileAccessUPP;
The MyCreateProcedureProfileAccess routine shown in
Listing 3-18
sets up a
CMProfileLocation
structure for procedure access to a resource-based profile. The MyDisposeProcedureProfileAccess routine, shown in
Listing 3-19
, disposes of memory allocated by MyCreateProcedureProfileAccess. Your application uses these routines (or similar ones that you write) in the following way:
CMCopyProfile
, you call the MyCreateProcedureProfileAccess routine to set up a CMProfileLocation structure that you can pass to the ColorSync Manager routine. The location structure specifies your profile-access procedure and may provide other information as well. A sample profile-access procedure is shown in
Listing 3-20
.For the sample MyCreateProcedureProfileAccess routine shown in Listing 3-18 , you pass a pointer to a CMProfileLocation structure to fill in, a pointer to a file specification for the resource file containing the profile resource, the type of the resource, the ID for the resource, and optionally the name of the resource (stored as a Pascal string, where the first byte is a length byte for the string).
Note
Listing 3-18 assumes the profile access routine, MyCMProfileAccessProc, is within the scope of the MyCreateProcedureProfileAccess routine. Optionally, you could add a parameter to pass in a procedure pointer for the profile access routine.
Listing 3-18 Setting up a location structure for procedure access to a resource-based profile
OSErr MyCreateProcedureProfileAccess (
CMProfileLocation *profileLocation,
FSSpec *resourceSpec,
Str255 resourceName,
OSType resourceType,
short resourceID)
{
OSErr theErr = noErr;
MyResourceLocPtr resourceInfo;
/* Allocate memory for our private resource info structure. */
resourceInfo = (MyResourceLocPtr) NewPtrClear(sizeof(MyResourceLocRec));
if (!resourceInfo)
theErr = MemError();
if (!theErr)
{
/* Set up our private resource info structure. */
resourceInfo->resFileSpec = *resourceSpec;
resourceInfo->resType = resourceType;
resourceInfo->resID = resourceID;
resourceInfo->resFileRef = 0;
resourceInfo->resHandle = 0;
resourceInfo->proc = NewCMProfileAccessProc(MyCMProfileAccessProc);
/* If a resource name was passed in, copy it to the structure;
since it's a Pascal string, first byte is length;
note that BlockMoveData is faster than BlockMove for a
move that involves data only. */
if (resourceName)
BlockMoveData(resourceName, resourceInfo->resName,
resourceName[0]+1);
/* set up the profile location structure */
profileLocation->locType = cmProcedureBasedProfile;
profileLocation->u.procLoc.refCon = (void*) resourceInfo;
profileLocation->u.procLoc.proc = resourceInfo->proc;
}
return theErr;
}
If the MyCreateProcedureProfileAccess routine is able to set up the profile location pointer for procedure access to a resource-based profile, it returns a value of noErr .
Your application calls the MyDisposeProcedureProfileAccess routine ( Listing 3-19 ) to dispose of any memory allocated by the MyCreateProcedureProfileAccess routine ( Listing 3-18 ).
Listing 3-19 Disposing of a resource-based profile access structure
void MyDisposeProcedureProfileAccess (CMProfileLocation *profileLocation)
{
DisposeRoutineDescriptor(profileLocation->u.procLoc.proc);
/* Dispose of our private resource info structure. */
DisposePtr((Ptr)profileLocation->u.procLoc.refCon);
}
This routine first disposes of the universal procedure pointer to your profile access procedure, then disposes of the pointer used to store resource data in a MyResourceLocRec structure.
For information on the procedure declaration for a profile access procedure, see
MyCMProfileAccessProc
.
The ColorSync Manager calls your procedure when the profile is created, initialized, opened, read, updated, or closed, passing a command constant that specifies the current command. Your profile access procedure must be able to respond to each of the following command constants, which are described in
Profile Access Procedure Operation Codes
:
enum {
cmOpenReadAccess = 1,
cmOpenWriteAccess = 2,
cmReadAccess = 3,
cmWriteAccess = 4,
cmCloseAccess = 5,
cmCreateNewAccess = 6,
cmAbortWriteAccess = 7,
cmBeginAccess = 8,
cmEndAccess = 9
};
The profile access procedure shown in Listing 3-20 , MyCMProfileAccessProc , consists of a single switch statement, which calls the appropriate routine based on the value of the command parameter. Each of the nine routines called by MyCMProfileAccessProc is described and listed in the sections that follow Listing 3-20 , and each refers back to Listing 3-20 .
Listing 3-20 Responding to a procedure-based profile command
pascal OSErr MyCMProfileAccessProc (long command,
long offset,
long *sizePtr,
void *dataPtr,
void *refConPtr)
{
OSErr theErr = noErr;
switch (command)
{
case cmBeginAccess:
theErr = DoBeginAccess(refConPtr);
break;
case cmCreateNewAccess:
theErr = DoCreateNewAccess(refConPtr);
break;
case cmOpenReadAccess:
theErr = DoOpenReadAccess(refConPtr);
break;
case cmOpenWriteAccess:
theErr = DoOpenWriteAccess(sizePtr, refConPtr);
break;
case cmReadAccess:
theErr = DoReadAccess(offset, sizePtr, dataPtr, refConPtr);
break;
case cmWriteAccess:
theErr = DoWriteAccess(offset, sizePtr, dataPtr, refConPtr);
break;
case cmCloseAccess:
theErr = DoCloseAccess(refConPtr);
break;
case cmAbortWriteAccess:
theErr = DoAbortWriteAccess(refConPtr);
break;
case cmEndAccess:
theErr = DoEndAccess(refConPtr);
break;
default:
theErr = paramErr;
break;
}
return theErr;
}
Note that the MyCMProfileAccessProc routine passes its parameter data as necessary to the routines it calls. The parameters have the following values:
When your application calls the
CMOpenProfile
routine, specifying as a location a procedure-based profile, the ColorSync Manager invokes your specified profile access procedure with the cmBeginAccess command. This gives your procedure an opportunity to perform any required initialization or validation tasks, such as determining whether the data pointed to by the
refcon
parameter is valid. If your procedure returns an error (any value except
noErr
), the ColorSync Manager will not call your profile access procedure again.
For the cmBeginAccess command, the sample profile access procedure shown in Listing 3-20 calls the DoBeginAccess routine, shown in Listing 3-21 . DoBeginAccess interprets the refcon parameter as a MyResourceLocPtr type. If the parameter does not have a resource type of kProcResourceType, DoBeginAccess returns an invalid profile error, which effectively cancels the procedure-based profile access.
Listing 3-21 Handling the begin access command
static OSErr DoBeginAccess (void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
resourceInfo->resFileRef = 0;
if (resourceInfo->resType != kProcResourceType)
theErr = cmInvalidProfileLocation;
else
theErr = noErr;
return theErr;
}
When your application calls the
CMCopyProfile
or
CMUpdateProfile
routine, specifying as a location a procedure-based profile, the ColorSync Manager invokes the specified profile access procedure with the cmBeginAccess command, as described in
Handling the Begin Access Command
.
If your profile access procedure returns without error, ColorSync calls the procedure again with the cmCreateNewAccess command. Your procedure should create a new data stream for the actual physical location of the profile. The size of the profile is not known at this point.
For the cmCreateNewAccess command, the sample profile access procedure shown in Listing 3-20 calls the DoCreateNewAccess routine. DoCreateNewAccess interprets the refcon parameter as a MyResourceLocPtr type, and calls the Toolbox routine FSpCreateResFile to create an empty resource fork based on the file specification provided by the MyResourceLocPtr type. If the resource fork does not already exist and cannot be created, DoCreateNewAccess returns an error.
Note that for this example, the file type for a resource-based profile was chosen arbitrarily to be 'rprf' .
Listing 3-22 Handling the create new access command
OSErr DoCreateNewAccess (void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
FSpCreateResFile(&(resourceInfo->resFileSpec), '????', 'rprf', 0);
theErr = ResError();
if (theErr == dupFNErr)
theErr = noErr;
return theErr;
}
When your application calls a ColorSync Manager routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command. Then it calls your profile access routine once for each read session. The sample profile access procedure shown in Listing 3-20 calls the DoOpenReadAccess routine.
The DoOpenReadAccess routine shown in Listing 3-23 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read permission. If it can open the resource file, DoOpenReadAccess then attempts to load the profile resource.
The DoOpenReadAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.
Listing 3-23 Handling the open read access command
static OSErr DoOpenReadAccess (void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
short currentResFile;
/* Save current resource file. */
currentResFile = CurResFile();
/* Open the file's resource fork. */
resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec), fsRdPerm);
theErr = ResError();
/* Get the resource handle, but don't force it to be loaded into memory. */
if (!theErr)
{
SetResLoad(false);
resourceInfo->resHandle = GetResource(resourceInfo->resType,
resourceInfo->resID);
theErr = ResError();
SetResLoad(true);
}
/* Restore previous resource file. */
UseResFile(currentResFile);
return theErr;
}
When your application calls the
CMUpdateProfile
routine to update a procedure-based profile or the
CMCopyProfile
routine to copy a profile, the ColorSync Manager calls your profile access procedure with the cmOpenWriteAccess command. The sample profile access procedure shown in
Listing 3-20
calls the DoOpenWriteAccess routine.
The DoOpenWriteAccess routine shown in Listing 3-24 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read/write permission. If it can open the resource file, DoOpenWriteAccess then attempts to open the specified profile resource. If it can't open the resource, DoOpenWriteAccess creates a new resource. It then sets the size of the resource based on the passed setProfileSize pointer value and updates the resource file.
The DoOpenWriteAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.
NoteIf the cmOpenWriteAccess command succeeds, the ColorSync Manager guarantees an eventual call to the profile access procedure with the cmCloseAccess command, possibly after multiple cmWriteAccess commands, and possibly after a cmAbortWriteAccess command.
Listing 3-24 Handling the open write access command
static OSErr DoOpenWriteAccess (long *setProfileSize, void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
Size resourceSize;
short currentResFile;
/* Save current resource file. */
currentResFile = CurResFile();
/* Open the file's resource fork. */
resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec),
fsRdWrPerm);
theErr = ResError();
/* Get the resource handle, but don't force it to be loaded into memory. */
if (!theErr)
{
SetResLoad(false);
resourceInfo->resHandle = GetResource(resourceInfo->resType,
resourceInfo->resID);
theErr = ResError();
SetResLoad(true);
}
/* Call GetResourceSizeOnDisk to see if resource is already there. */
if (!theErr)
{
/* Get size of the resource. */
resourceSize = GetResourceSizeOnDisk(resourceInfo->resHandle);
theErr = ResError();
}
/* If the above call to GetResourceSizeOnDisk returns resNotFound,
then we need to create a new resource */
if (theErr == resNotFound)
{
/* Allocate a temporary handle just so that we can call AddResource. */
resourceInfo->resHandle = NewHandle(sizeof(long));
theErr = MemError();
/* Add resource to the file and release the temp handle. */
if (!theErr)
{
AddResource(resourceInfo->resHandle, resourceInfo->resType,
resourceInfo->resID, resourceInfo->resName);
theErr = ResError();
ReleaseResource(resourceInfo->resHandle);
}
/* Get the resource handle, but don't force it to be loaded into memory. */
if (!theErr)
{
SetResLoad(false);
resourceInfo->resHandle = GetResource(resourceInfo->resType,
resourceInfo->resID);
theErr = ResError();
SetResLoad(true);
}
}
/* Change the resource size to fit the profile. */
if (!theErr)
{
SetResourceSize(resourceInfo->resHandle, *setProfileSize);
theErr = ResError();
}
/* Force an update of the resource file. */
if (!theErr)
{
UpdateResFile(resourceInfo->resFileRef);
theErr = ResError();
}
/* Restore previous resource file. */
UseResFile(currentResFile);
return theErr;
}
When your application calls a ColorSync Manager routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command, as described in Handling the Open Read Access Command . Your profile access routine can be called with the cmReadAccess command at any time after the cmOpenReadAccess command is called. When the sample profile access procedure shown in Listing 3-20 receives the cmReadAccess command, it calls the DoReadAccess routine.
The DoReadAccess routine shown in Listing 3-25 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start reading, the number of bytes to read, and a pointer to a buffer in which to store the data that it reads. It then calls the Toolbox routine ReadPartialResource to do the actual reading.
If an error occurs while reading, DoReadAccess returns the error.
Listing 3-25 Handling the read access command
static OSErr DoReadAccess ( long offset,
long *sizePtr,
void *dataPtr,
void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
ReadPartialResource(resourceInfo->resHandle,
offset, dataPtr, *sizePtr);
theErr = ResError();
return theErr;
}
When your application calls the
CMUpdateProfile
routine to update a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenWriteAccess command. The DoOpenWriteAccess routine shown in
Listing 3-24
performs certain operations to prepare to write a resource-based profile.
Your profile access routine can be called with the cmWriteAccess command at any time after the cmOpenWriteAccess command is called. When the sample profile access procedure shown in Listing 3-20 receives the cmWriteAccess command, it calls the DoWriteAccess routine.
The DoWriteAccess routine shown in Listing 3-26 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start writing, the number of bytes to write, and a pointer to a buffer from which to get the data that it writes. It then calls the Toolbox routine WritePartialResource to do the actual writing.
If an error occurs while writing, DoWriteAccess returns the error.
NoteAfter ColorSync calls the profile access procedure with the cmWriteAccess command, ColorSync is guaranteed to eventually call the profile access procedure with the cmCloseAccess command--possibly after additional calls with the cmWriteAccess command, and possibly after a call with the cmAbortWriteAccess command.
Listing 3-26 Handling the write access command
static OSErr DoWriteAccess (long offset,
long *sizePtr,
void *dataPtr,
void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
WritePartialResource(resourceInfo->resHandle,
offset, dataPtr, *sizePtr);
theErr = ResError();
return theErr;
}
The ColorSync Manager calls your profile access procedure with the cmCloseAccess command to indicate that reading or writing is finished for the moment. A cmCloseAccess command can be followed by a cmOpenReadAccess command to begin reading again, a cmOpenWriteAccess command to begin writing again, or a cmEndAccess command to terminate the procedure-based profile access.
The sample profile access procedure shown in Listing 3-20 calls the DoCloseAccess routine.
The DoCloseAccess routine shown in Listing 3-27 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to close and update the resource file for the resource-based profile. If DoCloseAccess is unsuccessful, it returns an error value.
Listing 3-27 Handling the close access command
static OSErr DoCloseAccess (void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
/* Close and update resource file. */
if (resourceInfo->resFileRef)
{
CloseResFile(resourceInfo->resFileRef);
theErr = ResError();
resourceInfo->resFileRef = 0;
}
else theErr = paramErr;
return theErr;
}
If an error occurs between a cmOpenWriteAccess command and a cmCloseAccess command, the ColorSync Manager calls your profile access procedure with the cmAbortWriteAccess command. This allows your access procedure to perform any cleanup necessary for the partially written profile.
For the cmAbortWriteAccess command, the sample profile access procedure shown in Listing 3-20 calls the DoAbortWriteAccess routine.
The DoAbortWriteAccess routine shown in Listing 3-28 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to call the Toolbox routine RemoveResource to delete the partially written resource. If DoAbortWriteAccess is unsuccessful, it returns an error value.
Note
The ColorSync Manager will call your profile access procedure with the cmCloseAccess command after a cmAbortWriteAccess command.
Listing 3-28 Handling the abort write access command
static OSErr DoAbortWriteAccess (void *refcon)
{
OSErr theErr;
MyResourceLocPtr resourceInfo = refcon;
/* Delete the resource that we started. */
if (resourceInfo->resHandle)
{
RemoveResource(resourceInfo->resHandle);
theErr = ResError();
}
else theErr = paramErr;
return theErr;
}
When access to a procedure-based profile is complete, the ColorSync Manager calls your profile access procedure with the cmEndAccess command. This allows your procedure to do any final cleanup, such as freeing memory allocated by the procedure.
For the cmEndAccess command, the sample profile access procedure shown in Listing 3-20 calls the DoEndAccess routine. Because there is no additional memory to free or other cleanup to take care of, the DoEndAccess routine shown in Listing 3-29 does nothing.
Note
The MyCreateProcedureProfileAccess routine, shown in Listing 3-18 , does allocate memory, which is freed by a call to the MyDisposeProcedureProfileAccess routine, shown in Listing 3-19 . Your application calls the MyCreateProcedureProfileAccess routine before calling a ColorSync Manager routine such as CMCopyProfile with a procedure-based profile. After the copy is complete, your application calls the MyDisposeProcedureProfileAccess routine to perform any necessary deallocation.
Listing 3-29 Handling the end access command
pascal OSErr DoEndAccess (void *refcon)
{
OSErr theErr = noErr;
return theErr;
}