Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
CDEV.c
/*------------------------------------------------------------------------------ |
# |
# Macintosh Developer Technical Support |
# |
# Sample Control Panel Device and INIT Combination |
# |
# Program: INIT - CDEV |
# File: CDEV.c - C Source |
# |
# Copyright © 1990 Apple Computer, Inc. |
# All rights reserved. |
# |
------------------------------------------------------------------------------*/ |
#include <Common.h> |
#include <Quickdraw.h> |
#include <Files.h> |
#include <Memory.h> |
#include <OSUtils.h> |
#include <TextEdit.h> |
#include <Devices.h> |
#include <Dialogs.h> |
#include <Packages.h> |
#include <TextUtils.h> |
/*------------------------------------------------------------------------------ |
Constants |
------------------------------------------------------------------------------*/ |
#define kNoINIT -4048 /* Alert: INIT was not installed */ |
#define kCantWritePrefs -4047 /* Alert: error writing preferences */ |
#define kAboutText1 1 |
#define kAboutText2 2 |
#define kPrompt1 3 |
#define kPrompt2 4 |
#define kTimesToBeep 5 /* First editTExt item in cdev */ |
#define kBeepInterval 6 |
/*------------------------------------------------------------------------------ |
Types |
------------------------------------------------------------------------------*/ |
typedef struct CDEVRec { |
CommonGlobalsPtr commonGlobals; |
} CDEVRec, *CDEVPtr, **CDEVHnd; |
/*------------------------------------------------------------------------------ |
Procedure Prototypes and External Routines |
------------------------------------------------------------------------------*/ |
/* These procedures are from the orginal EditTextCDEV sample. */ |
pascal long TextCDEV(short message, short item, short numItems, |
short CPanelID, EventRecord *theEvent, |
Handle cdevStorage, DialogPtr CPDialog); |
void DoEditCommand(short message, DialogPtr CPDialog); |
/* Added for INIT - CDEV combo. */ |
void InitEditValues(CDEVHnd cdevStorage, short numItems, |
DialogPtr CPDialog); |
void UpdateINIT(CDEVHnd cdevStorage, short numItems, |
DialogPtr CPDialog); |
void UpdatePrefs(CDEVHnd cdevStorage); |
long GetItemValue(short itemNumber, DialogPtr CPDialog); |
void SetItemValue(short itemNumber, DialogPtr CPDialog, long value); |
void GetCommonStorage(CommonGlobalsPtr *commonGlobals); |
OSErr OpenAPort(short *refNum); |
pascal void StartCompProc(PPCParamBlockPtr ppb); |
/*------------------------------------------------------------------------------ |
pascal long TextCDEV(short message, short item, short numItems, |
short CPanelID, EventRecord *theEvent, |
Handle cdevStorage, DialogPtr CPDialog); |
This is the main dispatcher. It must be the first code in the cdev. |
EditCdevÕs dispatcher responds only to the following messages from the |
Control Panel: |
macDev - To indicate what machines it is available on. |
initDev - To set up some temporary storage and get the caret started. |
keyEvtDev - To check for an edit command and do the appropriate action. |
cutDev - To cut the current selection. |
copyDev - To copy the current selection. |
pasteDev - To paste the contents of the clipboard. |
clearDev - To delete the current selection. |
closeDev - To dispose of any data allocated in initDev. |
The Dialog ManagerÕs services are used to handle entry of text, selection |
of text, editing of text, and moving between the editText items via the |
tab key. Since the Dialog Manager handles the selection of text, we do not |
have to be concerned with hitDev messages for the editText items. The only |
things we have to take care of are calling the Dialog Manager editing |
routines in response to an edit command, and getting the caret to show up |
at the beginning. In response to an edit command that was the result of a |
command-key equivalent, we must also eliminate the event so that it does |
not get processed as a keyDown by the Dialog Manager. Otherwise, an ÔxÕ |
would show up in the editText item when the user did a command-x to cut |
the text. |
------------------------------------------------------------------------------*/ |
pascal long TextCDEV(short message, short item, short numItems, short CPanelID, |
EventRecord *theEvent, Handle cdevStorage, DialogPtr CPDialog) |
{ |
#pragma unused (item, CPanelID) /* unused formal parameters */ |
char tempChar; |
CommonGlobalsPtr commonGlobals; |
long result; |
if (message == macDev) { |
if (Gestalt(gestaltPPCToolboxAttr, &result)) |
return(0); /* No PPCToolbox; hide me */ |
else |
return(1); /* Have PPCToolBox; show me */ |
} |
else if (cdevStorage != nil) { |
switch (message) { |
case initDev: |
GetCommonStorage(&commonGlobals); |
if (commonGlobals) { |
/* create private storage */ |
cdevStorage = NewHandle(sizeof(CDEVRec)); |
if (cdevStorage) { |
(**(CDEVHnd)cdevStorage).commonGlobals = commonGlobals; |
InitEditValues( (CDEVHnd) cdevStorage, numItems, CPDialog); |
/* make caret show up */ |
SelectDialogItemText(CPDialog, numItems + kTimesToBeep, 0, 999); |
} |
} else { |
(void) StopAlert(kNoINIT, (ModalFilterProcPtr) nil); |
return(cdevGenErr); |
} |
break; |
case closeDev: /* clean up and dispose */ |
UpdateINIT((CDEVHnd) cdevStorage, numItems, CPDialog); |
UpdatePrefs((CDEVHnd) cdevStorage); |
DisposeHandle(cdevStorage); |
break; |
case hitDev: /* handle hit on item */ |
case nulDev: |
case updateDev: /* handle any update drawing */ |
case activDev: /* activate any needed items */ |
case deactivDev: /* deactivate any needed items */ |
break; |
case keyEvtDev: /* respond to keydown */ |
tempChar = theEvent->message & charCodeMask;/* get the character, and check */ |
if (theEvent->modifiers & cmdKey) { /* status of command key */ |
message = nulDev; /* start with no message */ |
theEvent->what = nullEvent; /* and empty event type */ |
switch (tempChar) { /* set appropriate message */ |
case 'X': |
case 'x': |
message = cutDev; |
break; |
case 'C': |
case 'c': |
message = copyDev; |
break; |
case 'V': |
case 'v': |
message = pasteDev; |
break; |
} |
DoEditCommand(message, CPDialog); /* Let edit command handler take it */ |
} |
break; |
case macDev: |
case undoDev: |
break; |
case cutDev: |
case copyDev: |
case pasteDev: |
case clearDev: |
DoEditCommand(message, CPDialog); /* respond to edit command */ |
break; |
} |
return ((long) cdevStorage); |
} /* cdevStorage != nil */ |
/* |
** We get here iff cdevStorage = NIL. Return 0 so that Control Panel |
** will put up "out of memory" error |
*/ |
return (0); |
} |
/*------------------------------------------------------------------------------ |
void DoEditCommand (short message, DialogPtr CPDialog) |
Call the appropriate Dialog Manager routine to handle an edit command for |
an editText item. It will do all the work regarding the TEScrap. |
------------------------------------------------------------------------------*/ |
void DoEditCommand (short message, DialogPtr CPDialog) |
{ |
switch (message) { |
case cutDev: |
DialogCut(CPDialog); |
break; |
case copyDev: |
DialogCopy(CPDialog); |
break; |
case pasteDev: |
DialogPaste(CPDialog); |
break; |
case clearDev: |
DialogDelete(CPDialog); |
break; |
} |
} |
/*------------------------------------------------------------------------------ |
void InitEditValues(CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog) |
Called when the CDEV is opened. It uses the pointer to the public globals |
that we got from the INIT to get the values to stuff into the EditText |
items. |
------------------------------------------------------------------------------*/ |
void InitEditValues (CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog) |
{ |
CommonGlobalsPtr commonGlobals; |
commonGlobals = (**cdevStorage).commonGlobals; |
SetItemValue(numItems + kTimesToBeep, /* item to set */ |
CPDialog, /* of this dialog */ |
commonGlobals->timesToBeep); /* set to this value */ |
SetItemValue(numItems + kBeepInterval, /* item to set */ |
CPDialog, /* of this dialog */ |
commonGlobals->beepInterval / 60); /* set to this value */ |
} |
/*------------------------------------------------------------------------------ |
void UpdateINIT(CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog) |
Called when the CDEV is about to close. It gets the values from the |
EditText items and stuffs them back into the INIT's public globals. |
------------------------------------------------------------------------------*/ |
void UpdateINIT (CDEVHnd cdevStorage, short numItems, DialogPtr CPDialog) |
{ |
CommonGlobalsPtr commonGlobals; |
commonGlobals = (**cdevStorage).commonGlobals; |
commonGlobals->timesToBeep = GetItemValue(numItems + kTimesToBeep, CPDialog); |
commonGlobals->beepInterval = GetItemValue(numItems + kBeepInterval, CPDialog) * 60; |
} |
/*------------------------------------------------------------------------------ |
void UpdatePrefs(CDEVHnd cdevStorage) |
Called when the CDEV is being closed. This routine writes to a preferences |
file, using the INIT's public globals as the output buffer. The data is |
written to the data fork; no resources are used at all. If the preferences |
file doesn't exist, it is created. Any errors are reported through an |
Alert saying that error number such-and-such occured. |
------------------------------------------------------------------------------*/ |
void UpdatePrefs(CDEVHnd cdevStorage) |
{ |
OSErr err; |
FSSpec spec; |
short refNum; |
long amountToWrite; |
Str255 errString; |
err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, |
&spec.vRefNum, &spec.parID); |
if (err == noErr) { |
(void) PLstrcpy(spec.name, kPrefsFileName); |
err = FSpCreate(&spec, kCreator, kDocKind, smSystemScript); |
if ((err == noErr) || (err == dupFNErr)) { |
err = FSpOpenDF(&spec, fsRdWrPerm, &refNum); |
if (err == noErr) { |
amountToWrite = sizeof(CommonGlobalsRec); |
err = FSWrite(refNum, &amountToWrite, |
(Ptr) (**cdevStorage).commonGlobals); |
(void) FSClose(refNum); |
} |
} |
} |
if (err) { |
NumToString(err, errString); |
ParamText(errString, nil, nil, nil); |
(void) StopAlert(kCantWritePrefs, (ModalFilterProcPtr) nil); |
} |
} |
/*------------------------------------------------------------------------------ |
long GetItemValue(short itemNumber, DialogPtr CPDialog) |
Utility routine used to turn the text in an EditText item into a long, |
and return it. |
------------------------------------------------------------------------------*/ |
long GetItemValue(short itemNumber, DialogPtr CPDialog) |
{ |
short itemType; |
Handle itemHandle; |
Rect itemRect; |
Str255 text; |
long theNum; |
GetDialogItem(CPDialog, itemNumber, &itemType, &itemHandle, &itemRect); |
GetDialogItemText(itemHandle, text); |
StringToNum(text, &theNum); |
return(theNum); |
} |
/*------------------------------------------------------------------------------ |
void SetItemValue(short itemNumber, DialogPtr CPDialog, long value) |
Utility routine used to turn a short into text, and stuff it into |
an EditText item |
------------------------------------------------------------------------------*/ |
void SetItemValue(short itemNumber, DialogPtr CPDialog, long value) |
{ |
short itemType; |
Handle itemHandle; |
Rect itemRect; |
Str255 text; |
NumToString(value, text); |
GetDialogItem(CPDialog, itemNumber, &itemType, &itemHandle, &itemRect); |
SetDialogItemText(itemHandle, text); |
} |
/*------------------------------------------------------------------------------ |
void GetCommonStorage(CommonGlobalsPtr *commonGlobals) |
This routine is called when the CDEV is opend to get a pointer to the |
INIT's public globals. During the life of the INIT, this pointer is saved |
in a block referenced by a handle. This handle is saved for us by the |
Control Panel. |
We get the pointer to the INIT's public globals by using the PPCToolbox. |
Because we wrote the INIT, we know that it is identifying itself to us by |
creator/type. So we fill out a portName with that information, and make an |
asynchronous PPCStart call. We make the call asynchronously so that the |
PPCToolbox can go an process all of the subsequent Read/Write/ |
End/Inform/etc. calls required in a PPC transaction. However, we have to |
hang around for the PPCStart call to finish, so we poll ioResult until it |
no longer equal 1 (ioResult = 1 is the international symbol for "I'm |
working on a task right now, come back later when I'm done."). |
When the PPCStart call completes, this means that the completion routine |
we installed, StartCompProc, has been called by the PPCToolbox. It is |
StartCompProc that was responsible for reading the data being sent to us |
by the INIT. Therefore, when PPCStart completes, our data buffer should |
have the location of the INIT's public globals. |
------------------------------------------------------------------------------*/ |
void GetCommonStorage(CommonGlobalsPtr *commonGlobals) |
{ |
OSErr err; |
SessionRecord sessionRecord; |
short refNum; |
*commonGlobals = nil; |
err = OpenAPort(&refNum); |
if (err) return; |
sessionRecord.portName.nameScript = GetScriptManagerVariable(smSysScript); |
(void) PLstrcpy(sessionRecord.portName.name, "\pBG Beeper"); |
sessionRecord.portName.portKindSelector = ppcByCreatorAndType; |
#if GENERATING68K |
// Universal Interfaces 2.0 |
sessionRecord.portName.u.port.portCreator = kCreator; |
sessionRecord.portName.u.port.portType = 'INIT'; |
#else |
sessionRecord.portName.u.port.creator = kCreator; |
sessionRecord.portName.u.port.type = 'INIT'; |
#endif |
sessionRecord.pb.startParam.ioCompletion = (PPCCompProcPtr) StartCompProc; |
sessionRecord.pb.startParam.portRefNum = refNum; |
sessionRecord.pb.startParam.serviceType = ppcServiceRealTime; |
sessionRecord.pb.startParam.resFlag = 0; |
sessionRecord.pb.startParam.portName = &sessionRecord.portName; |
sessionRecord.pb.startParam.locationName = nil; |
sessionRecord.pb.startParam.userData = kGetCommonGlobalsPtr; |
sessionRecord.pb.startParam.userRefNum = 0; /* guest access? */ |
err = PPCStartAsync(&sessionRecord.pb.startParam); |
/* Even though we make an asynchronous call, we have to wait |
around for the call to complete. One reason for this is because |
we can't really do anything until we get the information we are |
asking for from the INIT. Another good reason is because our |
parameter block is allocated on the stack. If we were to leave |
now, our parameter block would vanish in a puff of orange smoke, |
along with the rest of the Operating System... */ |
while (sessionRecord.pb.startParam.ioResult == 1) ; /* wait for the reply */ |
if (sessionRecord.pb.startParam.ioResult == noErr) |
*commonGlobals = |
((GetCommonGlobalsPtr) &sessionRecord.buffer)->commonGlobalsAddress; |
err = PPCEnd(&sessionRecord.pb.endParam, false); |
err = PPCClose(&sessionRecord.pb.closeParam, false); |
} |
/*------------------------------------------------------------------------------ |
OSErr OpenAPort(short *refNum) |
Used to open a PPC port. We identify ourselves using the portName record. |
First, we give ourselves a name that will show up in the PPCBrowser |
(portName.name). We then give ourselves an identify to other processes. We |
can do this either by name or by creator/type signatures. In our case, we |
choose creator/type ('INCD'/'CDEV'). |
We then set up for a PPCOpen call. We are making a synchronous call, so we |
don't need a completion routine. ServiceType and resFlag are set to |
required values (per Inside Mac). We make the CDEV not visible over the |
network. Next, we point to the name record we want to use for our port, |
and use the default location name (which identifies our computer to other |
computers on the network). |
Finally, we make the PPCOpen call synchronously, and return any errors. |
------------------------------------------------------------------------------*/ |
OSErr OpenAPort(short *refNum) |
{ |
PPCPortRec thePort; |
PPCOpenPBRec pb; |
OSErr err; |
thePort.nameScript = GetScriptManagerVariable(smSysScript); |
(void) PLstrcpy(thePort.name, "\pBG Beeper"); |
thePort.portKindSelector = ppcByCreatorAndType; |
#if GENERATING68K |
// Universal Interfaces 2.0 |
thePort.u.port.portCreator = kCreator; |
thePort.u.port.portType = 'CDEV'; |
#else |
thePort.u.port.creator = kCreator; |
thePort.u.port.type = 'CDEV'; |
#endif |
pb.ioCompletion = nil; |
pb.serviceType = ppcServiceRealTime; |
pb.resFlag = 0; |
pb.networkVisible = true; |
pb.portName = &thePort; |
pb.locationName = nil; // use the default location |
err = PPCOpen(&pb, false); |
if (err) return(err); |
*refNum = pb.portRefNum; |
return(noErr); |
} |
/*------------------------------------------------------------------------------ |
pascal void StartCompProc(PPCParamBlockPtr ppb) |
When the PPCStart call we made in GetCommonStorage completes, the |
PPCToolbox calls the completion routine we specified (this routine). At |
this point, our INIT has been contacted, because it had an outstanding |
PPCInform call. The INIT's PPCInform completion routine looked at what we |
stuffed in the userData field when we made the PPCStart call, and wrote |
back to us the location of its public globals with PPCWrite. Therefore, we |
have to set ourselves up to read that data with PPCRead. That's what we do |
here. |
Because this is a completion routine, it is declared as using the Pascal |
calling convention. |
------------------------------------------------------------------------------*/ |
pascal void StartCompProc(PPCParamBlockPtr ppb) |
{ |
OSErr err; |
GetCommonGlobalsPtr myBuffer; |
myBuffer = (GetCommonGlobalsPtr) ((SessionPtr)ppb)->buffer; |
ppb->readParam.ioCompletion = nil; |
ppb->readParam.bufferLength = sizeof(GetCommonGlobalsRecord); |
ppb->readParam.bufferPtr = (Ptr) myBuffer; |
ppb->readParam.more = false; |
err = PPCReadAsync(&ppb->readParam); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14