Plug-ins

API for Videoconferencing / VFMI / Function Modules

version 1.2, 15 November 1994
written by Aaron Giles
copyright (c) 1993-94, Cornell University Medical College

 

What it is

 

The Videoconferencing Function Module Interface (VFMI) is a runtime "plug-in" architecture designed to enable third-party developers to add functionality on top of a videoconferencing application. Although VFMI was originally designed to work in conjunction with CU-SeeMe, its interface is public, and it is expected that this interface will be easily portable to other videoconferencing applications in the future.

 

The VFMI itself deals with the concept of a function module; that is, an external piece of code which is loaded and executed by the VFMI at runtime. These function modules (FM's) come in two flavors: embedded modules and external modules.

 

Embedded modules live in the application's resource fork, and are often distributed as an integral part of the application, making them useful for in-house developers creating self-contained programs with specific local applications. All of the modules' resources must also be included in the application's resource fork; thus, it is important to avoid using common resource ID's in your modules.

 

External modules are modules that are self-contained in a file separate from the VFMI application. In order to be properly found by the VFMI, this file must reside either in the same folder as the application or in a subfolder of the system's Extensions folder called "Videoconferencing Modules". The VFMI searches these two folders for any files with a file type of `vVFM', and looks inside those files for a `cVFM' resource of ID 0, which identifies the locations and properties of all the modules contained in that file. Additional details on the format of the `cVFM' resource will be given later in this document.

 

Any given module, embedded or external, can contain code for 68000 family Macintoshes, PowerPC Macintoshes, or both. All modules are called from the VFMI through a single entry point, with a selector passed as the first parameter to indicate which action the function module should take. Thus, the main entry point will essentially be a dispatch function which jumps to the routine appropriate to the current selector.

 

Modules written for 68000 Macintoshes must be built as code resources of type `vVFM', and the main entry point must reside at the very beginning of the resource. This means that any global variables used by the module must be accessed through some mechanism other than reliance on the current A5 world. See the documentation for your particular development environment for an explanation of how globals are supported in code resources. 68000 modules operate perfectly well under emulation on Power Macintoshes.

 

Modules that are compiled native for PowerPC Macintoshes must be built as shared libraries, with the main entry point for the library set to point to the dispatch function in the code. Thus, all PowerPC code is expected to reside in the data fork of the file, meaning it can be file mapped by the MacOS when running virtual memory. Multiple modules in a single file can be constructed with the proper `cfrg' resources pointing to different locations within the data fork where each module's code resides. PowerPC modules cannot be used on a 68000 Macintosh.

 

Once a module is located by the VFMI, its code is loaded and hooked into the main application's event loop. Whenever an event is received for a window or menu item associated with the module, the dispatch function is called with the appropriate selector and data to allow the module to act on the event in whatever way it chooses.

 

How it works

 

Function modules are designed to be called through a single entry point, which is expected to be a function defined like this:

extern pascal OSErr
     MyFMEntryPoint(short selector,FMParamsPtr params,CallbacksPtr cb);

This function has three input parameters:

 

selector--this is an integer value which specifies the type of event being passed to the module. The VFMI can pass a number of different types of events; each type is fully described later in the section titled VFMI Event Selectors.

 

params--this is a pointer to a predefined structure of type FMParams which contains all the data needed to describe the event. See the VFMI Data Structures section of this document for details on what exactly all the fields mean.

 

cb--this is a pointer to a constant list of callback functions: functions built into the VFMI which your module can call to compatibly perform certain operations involving the user interface or data transmission. A complete list of the supported callback functions is given in the section VFMI Callbacks.

 

After processing the event passed in the selector parameter, your module should return an OSErr to the VFMI to indicate what happened. In addition to all the standard error codes, you can also pass the special error code errKillMe, which tells the VFMI to close up your module and remove it from memory. At this point your module will likely receive a number of new messages (window close messages and a clean up message) before it is actually removed.

 

When you return an error to the VFMI, it will attempt to report the error to the user in some sort of generic fashion. However, it may be more informative to the user if the module instead reports its own errors, describing the context and possible resolutions of the problem. If you wish to handle any errors internally in your module, you can report the error directly to the user and then return the value noErr to the VFMI.

 

VFMI Event Selectors

 

The events which get passed from the VFMI to your module can be roughly divided into six groups:

 

* Set up/Clean up group: basic initialization and clean up events

* Core event group: standard user interface events

* File menu group: events pertaining to user selections in the File menu

* Edit menu group: events pertaining to user selections in the Edit menu

* Other menu group: events pertaining to user selections in other menus

* Data transmission group: events relating to data transmission

 

Set up/Clean up group

 

FMInitialize

This selector is called immediately after the module is loaded. It is designed to allow the module a chance to initialize global variables, load resources, register data transmission handlers, or do whatever else it needs to do. If you are writing a module for 68000 Macintoshes and you need a place to store a handle to your global variables, you can store it in the privates member of the parameter block, and it will be passed back to your module on all subsequent calls.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

 

FMCleanUp

This selector is called either when the application quits or when the module asks the VFMI to kill it. You should use this opportunity to free any memory, unload extra code, or do whatever you need to do to free up resources. Remember: you are not guaranteed that you will only get called this way at application quit time, so don't be lazy about freeing memory. It's quite possible that the VFMI is telling you to clean up because it is an emergency!

 

If, for some reason, you absolutely cannot quit at this time, you are permitted to return an error code other than noErr. This can happen, for instance, if the user has forgotten to save his data, and answers "Cancel" in response to a standard "Save data before quitting?" alert. Returning a non-zero error code will cause the host application to cease its cleanup and return to the event loop.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

 

Core event group

 

FMHandleNull

This selector is called once every time through the event loop, to allow your module to do idle time processing. You can use this function to track the cursor when your window is frontmost, to animate selections, or to do any necessary background processing.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window if and only if that window belongs to the module; otherwise, this value will be nil

 

FMHandleActivate

This selector is called whenever one of the module's windows receives an activate event. It will also be called if the application is brought to the front and one of the application's windows is frontmost. Use this opportunity to activate all controls and redisplay any selections in the newly active window.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window that has just become frontmost

 

FMHandleDeactivate

This selector is called whenever one of the module's windows receives a deactivate event. It will also be called if the application is sent to the background while one of the module's windows is frontmost. When you receive this call, you should deactivate any controls and hide selections in the

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window that was previously frontmost

 

FMHandleUpdate

This selector is called whenever one of the module's windows receives an update event. You should respond to this selector by calling BeginUpdate(), redrawing the contents of the window, and then calling EndUpdate().

 

Important: It is your responsibility at the very least to call BeginUpdate() and EndUpdate() on the window in order to clear the pending update; otherwise, no other windows will be updated and you will tie up the system in an infinite updating loop!

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the window that needs to be updated

 

FMHandleContentClick

This selector is called whenever the user clicks in the content area of one of the module's windows.

 

Important: When this selector is called, you are not guaranteed that your window will be frontmost. You must check for this explicitly by comparing the window parameter with the current FMFrontNonFloatingWindow(). If the window was not frontmost, you must bring it to the front yourself (via FMSelectReferencedWindow()) if appropriate. This was done so that drag and drop from windows which are not frontmost can still work within a VFMI application.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the window where the click occurred

 

FMHandleDragClick

This selector is called whenever the user clicks in the title bar of one of the module's windows. The usual action here is simply to call FMDragReferencedWindow(), unless your module needs to take special action if, say, one of the modifier keys is held down. Note that unless the command key is held down, the window to be dragged is guaranteed to be frontmost at the time of this call.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the window whose titlebar was clicked

 

FMHandleGrowClick

This selector is called whenever the user clicks in the size box of one of the module's windows. The usual action here is to call GrowWindow().

 

Important: When this selector is called, you are not guaranteed that your window will be frontmost. You must check for this explicitly by comparing the window parameter with the current FMFrontNonFloatingWindow(). If the window was not frontmost, you must bring it to the front yourself (via FMSelectReferencedWindow()) if appropriate. This was done so that drag and drop from windows which are not frontmost can still work within a VFMI application.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the window that has just become frontmost

 

FMHandleWindowZoom

This selector is called whenver the user clicks in the zoom box of one of the module's windows. When you receive this selector, you should handle whatever zooming action you have defined for the window.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the window that has just become frontmost

* part--contains the part code (either inZoomIn or inZoomOut) returned by FindWindow()

 

FMHandleKeyDown

This selector is called whenever a key down event is detected while one of the module's nonfloating windows is frontmost.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call

* window--points to the frontmost window

 

File Menu group

 

FMDisplayAboutBox

This selector is called when the About MyModule item is selected from the menu. Typically this item is placed into a hierarchical menu, a la Adobe Photoshop. Your module should preferably put up either a movable modal dialog box or a modeless window in order to allow the underlying application time to continue handling incoming data.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

 

FMHandleOpenFile

This selector is called when a file of the appropriate type (see FMIsValidFileType) is opened by the user, either by selecting Open from the File menu and choosing a file, or by receiving an Open Documents AppleEvent from another application. Your application should respond by attempting to open and display the file.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* fileSpec--an FSSpec pointing to the file you have been requested to open

 

FMIsValidFileType

This selector is called from within the file filter of the Open dialog to determine if the given file type is one which the module can handle. It is also used after a file has been chosen in order to determine whether your module can handle the file.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* fileType--the 4 character type code of the file in question

* fileCreator--the 4 character creator code of the file in question

 

FMHandleCloseWindow

This selector is called when the user selects Close Window from the File menu, and the frontmost window belongs to the module. It is also called when the user clicks in the window's close box. Finally, this selector will be called for all active windows before the application quits. If the user has not saved the contents of the given window, you should prompt him to do so before closing; if the user wishes to abort, return a non-zero error code.

 

Important: All windows are required to be created and handled through the callback routines given below, so that the application can register any new windows internally as they are created.

 

Important: At application quit time, you will receive FMHandleCloseWindow calls for all of your open windows. It is very important that, unless the user aborts the quit process, you can properly close every window you open through this selector. Failing to do so will likely leave the application hanging in an infinite loop as it repeatedly asks you to close your window.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* event--contains a copy of the event record which triggered this call, if any; check the event.what field for a non-zero value to confirm that this call is actually the direct result of an event

* window--points to the window to be closed

 

FMHandleSave

This selector is called when the user selects Save from the File menu and the frontmost window belongs to the module. You should save the contents of the window in whatever format is appropriate for your document.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window whose contents you should save

 

FMHandleSaveAs

This selector is called when the user selects Save As from the File menu and the frontmost window belongs to the module. You should prompt the user for a new filename and save the contents of the window to that file in whatever format is appropriate for your document.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window whose contents you should save

 

FMHandlePageSetup

This selector is called when the user selects Page Setup from the File menu and the frontmost window belongs to the module. You should open the current printer driver and present the user with the standard page setup dialog for your document. When the user saves the document, you should also save the resulting print record along with it.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window whose contents you should get a page setup for

 

FMHandlePrint

This selector is called when the user selects Print from the File menu and the frontmost window belongs to the module. You should open the current printer driver and present the user with the standard print dialog for your document. Once the user has clicked OK, you should then proceed to print the document, returning control to CU-SeeMe only when printing is finished.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the window whose contents you should print

 

Edit menu group

 

FMHandleUndo

This selector is called when Undo is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

FMHandleCut

This selector is called when Cut is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

FMHandleCopy

This selector is called when Copy is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

FMHandlePaste

This selector is called when Paste is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

FMHandleClear

This selector is called when Clear is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

FMHandleSelectAll

This selector is called when Select All is selected from the Edit menu, and the frontmost window belongs to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window

 

Other menu group

 

FMMenuSelect

This selector is called when an item is selected from a menu that belongs to the module. Any menu owned by the module must be created through the callback routines listed below, so that the application can internally register the menu as belonging to the module.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* window--points to the frontmost window if and only if that window belongs to the module; otherwise, this value will be nil

* menuItem--menu item number selected

* menuID--menu ID of the menu from which menuItem was selected

 

FMAdjustMenus

This selector is called whenever CU-SeeMe updates the status of the menus. You should directly enable or disable any items in your own menus, using system calls. In addition, you should fill in the enableFlags field of the parameter block to indicate which of the standard File and Edit menu items should be enabled.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

Parameters expected on output:

* enableFlags--a structure containing Boolean flags specifying the active state of the standard menu items

 

Data transmission group

 

FMIncomingOpen

This selector is called whenever the first part of an item of data is received that can be handled by the module. Your code should do whatever processing is necessary to handle the incoming data; typically, a new handle is allocated in preparation for the arrival of the rest of the data item.

 

Important: In order to begin receiving data, your module must explicitly register with the application all the different types of data it can accept. See the list of data transmission callbacks below for information on how to register data types.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* dataReceived--structure containing information about the status of the incoming data item

 

FMIncomingData

This selector is called for each succeeding part of an data item that is appropriate to the module. Your module should continue its processing of the data, typically by appending the new data to any previously recevied data.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* dataReceived--structure containing information about the status of the incoming data item

 

FMIncomingClose

This selector is called to signal the end of an incoming piece of data. When this message is received, your module should finish processing the incoming data.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* dataReceived--structure containing information about the status of the incoming data item

 

FMIncomingError

This selector is called when an error occurs during the transmission of data. In response to this selector, your module should notify the user of the error and do whatever cleanup is necessary.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* dataReceived--structure containing information about the status of the incoming data item

 

FMDataSendComplete

This selector is called immediately after a given item of data has finished being sent. You can use this call to identify when it is appropriate to dispose of the handle you passed to the FMSendData.

 

Parameters defined on input:

* qd--a pointer to the application's QuickDraw globals

* privates--handle to private storage used by your application

* dataSent--structure containing information about the status of the outgoing data item; this is the same as the status block returned from the FMSendData call

 

Callback Functions

 

The VFMI defines a number of callback functions which are designed to make it easier for function modules to integrate seamlessly into the videoconferencing application. Callbacks, like event selectors, can be organized into groups:

 

* Data transmission group: callbacks relating to data transmission

* Videoconferencing group: callbacks controlling or getting information about the current videoconference

* User interface group: callbacks for controlling other unified user interface items

* Window group: callbacks for controlling windows in a floating window environment

 

Data transmission group

 

FMRegisterTransmission

In order to send or receive data using the data transmission functions, you must first register your intentions with the videoconferencing application. This involves telling the application three things: the type of data that will be transmitted, the connection ID of the participant you wish to communicate with, and whether you will be sending or receiving data (or both).

 

The type of data you are sending is specified, like file types in the MacOS, by a unique 4-character signature. A module can register to send or receive any number of data types; however, it is recommended that you stick to one data type and embed a subtype code in your data to extend your options.

 

The connectionID you specify controls exactly from whom you will be receiving data. In the most general case, where you wish to received data of the given type with anyone who happens to be connected, you will just pass a 0 here, enabling tramissions with everyone. To receive data from one particular participant, you can simply pass his or her connection ID (as obtained from the FMGetConnectionInfo call). To receive data from several participants (but not all of them), you must call FMRegisterTransmission several times, passing the ID of each party.

 

To register to receive data of the given type, pass a value of true for the receive parameter. (Once again, a false value here does not unregister you for receiving the data; use the FMUnregisterTransmission call for accomplishing that). Once you're done this, any data arriving with that type from the specified connection will result in a call through the module's dispatch function with one of the FMDataOpenItem, FMDataNewData, or FMDataCloseItem selectors. Your module can then act on the incoming data in any way it chooses.

 

To register to send data of the given type, pass a value of true for the send parameter. Unlike registering to receive data, a send registration does not allow you to control to whom you will be sending. (Note that passing a value of false does not turn off sending; you must use the FMUnregisterTransmission call to do this). Once this is done, you will be able to send data to anyone connected via the FMSendData call.

 

Important: The VFMI requires that the types your module registers be unique. That is, if a module has already registered to accept data of type `ABCD', no other module can request that type. For this reason, it is strongly suggested that developers stay away from the use of "standard" types, such as `PICT', `TEXT', etc.

 

Function description:

pascal OSErr FMRegisterTransmission(long theType, long srcID,
	Boolean receive, Boolean send);

 

theType -- this is the unique 4-character signature identifying the type of data whose arrival you wish to be notified of

 

srcID -- this is the ID of the connection from whom you wish to accept data of this type; if you want to receive this type of data from any connected person, pass a 0 for the srcID

 

receive -- this flag specifies whether or not you are registering to receive data of the specified type. Pass true here to indicate that you are; pass false to leave the current receiving state of this type of data alone. In order to unregister yourself for receiving this type of data, use the FMUnregisterTransmission call.

 

send -- this is a flag which specifies whether or not you are registering to send data of the specified type. Pass true here to indicate that you are; pass false to leave the current sending state of this type of data alone. In order to unregister yourself for sending this type of data, use the FMUnregisterTransmission call.

 

 

FMUnregisterTransmission

This callback is used to notify the videoconferencing application that you will no longer be sending or receiving the specified data type from a particular connection. This function is called in a completely analogous way to the FMRegisterTransmission call, and is used to specify that you will not be either sending or receiving a given data type.

 

Function description:

pascal OSErr FMUnregisterTransmission(long theType, long srcID,
	Boolean receive, Boolean send);

 

theType -- this is the 4-character signature identifying the type of data you wish to ignore

 

srcID -- this is the ID of the connection from whom you wish to ignore data of this type; if you want to stop receiving this type of data from all connected parties, pass a 0 for the srcID

 

receive -- this flag specifies whether or not you wish to stop receiving data of the specified type. Pass true here to indicate that you do; pass false to leave the current receiving state of this type of data alone.

 

send -- this is a flag which specifies whether or not you wish to stop sending data of the specified type. Pass true here to indicate that you do; pass false to leave the current sending state of this type of data alone.

 

 

FMGetTransmissionState

This callback is used to notify the videoconferencing application that you will no longer be sending or receiving the specified data type from a particular connection. This function is called in a completely analogous way to the FMRegisterTransmission call, and is used to specify that you will not be either sending or receiving a given data type.

 

Function description:

pascal OSErr FMGetTransmissionState(long theType, long srcID,
	Boolean *othersReceiving, Boolean *othersSending,
	Boolean *imReceiving, Boolean *imSending);

 

theType -- this is the 4-character signature identifying the type of data whose state you wish to query

 

srcID -- this is the ID of the client you are requesting transmission information for; if you pass a 0 for the ID, this function will return true in othersReceiving and othersSending if any client is sending or receiving the given data type

 

othersReceiving -- this should point to a Boolean variable which will, on return, indicate whether or not the specified client is ready to receive the given data type; pass nil here if you do not want this information

 

othersSending -- this should point to a Boolean variable which will, on return, indicate whether or not the specified client has registered to send the given data type; pass nil here if you do not want this information

 

imReceiving -- this should point to a Boolean variable which will, on return, indicate whether or not you have registered to receive the given data type from the specified client; pass nil here if you do not want this information

 

imSending -- this should point to a Boolean variable which will, on return, indicate whether or not you have registered to send the given data type; pass nil here if you do not want this information

 

 

FMSendData

This function is the meat of the data transmission callback group. Essentially, it allows you to send a handle's worth of arbitrary data simultaneously with the video and audio stream. When the VFMI has finished processing your request, you will receive a call through the FMDataSendComplete selector, at which point you can dispose of the data handle you pass to this callback.

 

Important: Before sending data of a given type, you must register to do so by calling FMRegisterTransmission, passing in the appropriate type and true for the sending parameter.

 

Function description:

pascal OSErr FMSendData(char opcode, char priority, char rate, 
	long destID, long type, long itemID, Handle data, 
	FMDataSentPtr *status);

opcode -- this "opcode" is used to specify the exact method of data transmission you wish to use for this piece of data. Currently, data can be sent in one of three ways, indicated by one of the following opcodes:

* kDataSendPassive: this type of transmission, which can only be used when sending to multiple recipients, uses passive error correction to allow a multicast data send; because it relies on the receiving end to report errors, it is not 100% reliable, but should work most of the time

* kDataSendActive: this type of transmission can only be used when sending to a single destination; it is more reliable than the kDataSendPassive method, using active error correction to ensure delivery of all the data

* kDataSendOnce: this mechanism provides a one-shot attempt at sending the data; if it doesn't make it, no attempt is made to recover any missing parts

 

priority -- this parameter describes the priority of the data being sent, using a signed number between -127 and 127. It is used internally by the sending mechanism to appropriately schedule transmissions when several modules are vying for the transmission bandwidth. To simplify the use of this parameter, the constants kDataPriorityNormal, kDataPriorityLow, and kDataPriorityHigh have been provided.

 

rate -- this parameter is designed to control the data transmission rate, indicating what percentage of the available bandwidth this data should attempt to use. Note that even if you request 100% of the bandwidth, you are not guaranteed it; the actual bandwidth you receive is highly dependent upon the bandwidth requested by another other transmitting modules. Note that requesting 100% of the bandwidth will bring the video stream to a halt for the duration of sending; this may or may not be desirable.

 

destID -- this is the ID of the person to whom you are sending the data (as determined by the FMGetConnectionInfo callback). To send to everyone who is currently connected, specify an ID of 0.

 

type -- this is the unique 4-character signature identifying the type of data you are sending. Only videoconferencing clients which have registered to receive this type of data from you will actually see anything.

 

itemID -- this number serves to identify the data numerically to the destination; it is strongly recommended that this number be monotonically increasing, in order to enable the detection of missing data items

 

data -- this is a handle to the data to be sent

 

status -- this is a pointer to an address which, upon return, will point to an FMDataSent structure containing information about the status of the transmission; in the future, this structure can be polled to find out how far along the transmission is

 

Data streaming group

 

FMRegisterStreamSender

In addition to the standard data transmission group, function modules can also send up to 32 bytes periodically as stream data mixed in with the audiovisual streams of the videoconferencing application. There are also a couple of important limitations on the use of this stream data. Most importantly, a module's stream data is limited to 32 bytes total, and can only be directed at the same type of module on the receiving end. There is no concept of data types or availability, as with the standard data transmission.

 

In order to begin sending stream data, a module must register a sending routine with the videoconferencing application. Because this routine may get called as often as 30 times a second, you will not get called through the overhead of the main dispatch function as with other types of data. Instead, the application will call the function you registered directly whenever it is about to send out the data stream. It is thus imperative that you keep the amount of work done by this function to a minimum, to keep the responsiveness of the videoconference high.

 

The operation of the sender function is very similar to that of a VBL task. It must be of the form:

pascal long FMRegisterStreamSender(Ptr data, void *refCon);

The data parameter points to a 32-byte area of memory which will be transmitted with the next video packet. (Be careful not to overstep the bounds of the 32 bytes!) The refCon parameter is the same refCon you pass in the FMRegisterStreamSender call. After it is finished, it should return a value which indicates, in 1/60th of a second increments, the minimum time between now and the next call. Since this function is being called directly, you must be careful about setting up your globals. Also, you are not guaranteed to get your next calling within the time you specified. The application is required to respect the minimum delay you specify, but can actually call your function at any arbitrary time in the future after that.

 

To unregister a sender function, simply call FMRegisterStreamSender with a nil function pointer. To register a different function, pass the new address instead and it will be changed; each function module can have but one registered sender function, so the new function will override the previous one.

 

Function description:

pascal OSErr FMRegisterStreamSender(FMStreamSenderUPP sender,
	long delay, void *refCon);

sender -- this is a pointer to your sender function; PowerPC native modules should pass a pointer to a RoutineDescriptor here for compatibility in mixed mode environments.

 

delay -- this is the minimal delay, in 1/60th of a second increments, between now and the first call to the sender function. Note that this only specifies a lower bound to the time; the actual delay is never guaranteed.

 

refCon -- this is a user-supplied value which gets passed to your callback; use it to point to some useful area in memory

 

 

FMRegisterStreamReceiver

In order to begin receiving stream data, you must also register a receiving routine with the videoconferencing application. Like the sending routine, this routine may get called as often as 30 times a second, and you will therefore not get called through the overhead of the main dispatch function as with other types of data. Instead, the application will call the function you registered directly whenever it receives and update to the data stream. Again, it is imperative that you keep the amount of work done by this function to a minimum, to keep the responsiveness of the videoconference high.

 

The receiver function must be of the form:

pascal void FMRegisterStreamReceiver(Ptr data, void *refCon);

 

The data parameter points to a 32-byte area of memory containing the most recently received stream packet. The refCon parameter is the same refCon you pass in the FMRegisterStreamReceiver call. In order to return quickly, it is suggested that you merely copy out the data into some other area of memory, and then process it on your next call through the standard dispatch function.

 

To unregister a receiver function, simply call FMRegisterStreamReceiver with a nil function pointer. To register a different function, pass the new address instead and it will be changed; each function module can have but one registered receiver function, so the new function will override the previous one.

 

Function description:

pascal OSErr FMRegisterStreamReceiver(FMStreamReceiverUPP receiver,
	void *refCon);

receiver -- this is a pointer to your receiver function; PowerPC native modules should pass a pointer to a RoutineDescriptor here for compatibility in mixed mode environments.

 

refCon -- this is a user-supplied value which gets passed to your callback; use it to point to some useful area in memory

 

Videoconferencing group

 

FMAcquireAV

Allows the module to gain control of the video and/or audio system currently being used by the videoconferencing application. Note that common courtesy dictates that control be restored eventually by making a call to FMGetAV(nil, nil).

 

Function description:

pascal OSErr FMAcquireAV(VideoDigitizerComponent *vdig, long *sdev);

 

vdig -- this should point to an address which will receive the VideoDigitizerComponent reference currently used in the videoconferencing application. The application will then stop using the video digitizer until you return control. To return control, simply pass nil for this parameter

 

sdev -- this should point to an address which will receive the device reference number for the sound input device currently used in the videoconferencing application. The application will then stop using the sound input device until you return control. To return control, simply pass nil for this parameter

 

FMGetConnectionInfo

Use this function to retrieve information about all parties to whom you are currently connected. See the section on Data Types for a description of the information returned in the FMConnectionInfo structure.

 

Function description:

pascal OSErr FMGetConnectionInfo(short index, 
	FMConnectionInfo *info);

 

index -- this should indicate, as an index, which connection you wish to get information about. Pass a 0 here to get information about your own status. Pass values greater than 1 to get information about other people connected to you. To find out all the active connections, start at 1 and call FMGetConnectionInfo() repeatedly, incrementing the index until you get an errNoSuchConnection error

 

info -- this should point to an address which will receive the information block returned by this function

 

User interface group

 

AddNewMenu

Adds the given menu onto the end of the menu bar. Note that by using this callback, the VFMI gets the opporunity to register the menu internally, so that it knows when it is appropriate to call the module with the FMMenuSelect selector. In order to allow a large number of modules to be supported on a small-sized monitor, the VFMI may, at its discretion, place all menus associated with external modules into a single Externals menu. This means that wide-screen people can see:

 

File Edit Connection Participants Images Pointer Text

 

(where Images, Pointer, and Text are menus added through this call), whereas a narrow-screen person would see:

 

File Edit Connection Participants Externals

 

and the Externals menu would look like this:

 

Externals

Images >

Pointer >

Text >

 

where each item becomes a hierarchical menu where the commands are actually located.

 

Function description:

pascal OSErr FMAddNewMenu(MenuHandle theMenu);

theMenu -- a menu handle describing the menu to be added to the end of the menubar

 

Window group

 

The VFMI calls for the use of Apple's floating window API. This means that both the videoconferencing application as well as the external modules can create and maintain floating windows, provided everybody follows the rules! Rather than go into all the gory details of the floating window API here, it is suggested that you consult d e v e l o p, issue number 15.

 

Important: The window group provides alternative functions for most of the standard Window Manager. It is vitally important that, wherever applicable, you use the replacement functions rather than the Window Manager. If you fail to do this, very bad things will happen to the state of the videoconferencing application's windows.

 

NewWindowReference, GetNewWindowReference

These functions create new windows according to Apple's floating window API, and also registers the new windows internally, so that the VFMI will know when to send events to the function module.

 

DisposeWindowReference

This function is a simple analog to the Toolbox's DisposeWindow() call.

 

SelectReferencedWindow

This function is a simple analog to the Toolbox's SelectWindow() call.

 

HideReferencedWindow, ShowReferencedWindow

These functions are simple analogs to the Toolbox's HideWindow() and ShowWindow() calls.

 

DragReferencedWindow

This function is a simple analog to the Toolbox's DragWindow() call.

 

FrontNonFloatingWindow

This function is a simple analog to the Toolbox's FrontWindow() call.

 

LastFloatingWindow

Returns a pointer to the bottommost floating window.

 

DeactivateFloatersAndFirstDocumentWindow

Prepares the activation of windows for a modal dialog or alert. You should call this before putting up any sort of modal window.

 

ActivateFloatersAndFirstDocumentWindow

Reactivates the frontmost windows after showing a dialog or alert. You should call this after removing any sort of modal window.

 

Data Structures

 

A number of data structures are defined for various purposes in the VFMI specification. This section goes through each structure in detail and describes the purpose of all the fields.

 

 

FMParamBlock

 

This structure is the central parameter block which is passed to your module anytime it is called through the dispatch function. Depending on the selector that gets passed along with it, this structure will only have certain fields filled in. Check the description of the selector in the VFMI Event Selectors section for an exact description of which fields you can expect to be filled in.

typedef struct FMParamBlock {
	Handle privates;
	EventRecord event;
	WindowRef window;
	short part;
	QDGlobals *qd;
	FSSpec fileSpec;
	OSType fileType;
	OSType fileCreator;
	short menuItem;
	short menuID;
	FMMenuEnableFlags enableFlags;
	FMDataReceivedPtr dataReceived;
	FMDataSentPtr dataSent;
} FMParamBlock, *FMParamBlockPtr, **FMParamBlockHandle;

privates -- this is a handle which is maintained across calls to your module; you are welcome to store any data here in the knowledge that it will be preserved intact for future calls. On 68k machines, this could be used as a handle containing the module's global variables.

 

event -- this is a copy of the event record associated with a given event call; used, for example, in FMHandleContentClick and FMHandleKeyDown events

 

window -- this is the window associated with a given event; in general, it is the window to be acted upon (e.g., the window to update or the frontmost window when a key is pressed)

 

part -- this is the part code associated with a mouse down in a window; used only for a FMHandleWindowZoom event

 

qd -- this is a pointer to the application's QuickDraw globals

 

fileSpec -- this is a copy of the file associated with a given event; currently only the FMHandleOpenFile event uses this parameter

 

fileType, fileCreator -- this is the file type and creator given by the Finder for a given file; used only for the FMIsValidFileType event

 

menuItem, menuID -- this is the item number and menu ID of the menu item selected; used only for the FMMenuSelect event

 

enableFlags -- this is a structure containing a series of Boolean values used to control the enabling of the standard menu items: save, undo, cut, copy, paste, clear, and select all

 

dataReceived -- this is a pointer to a data received structure containing information about incoming data; used by the FMIncomingOpen, FMIncomingData, and FMIncomingClose events

 

dataSent -- this is a pointer to a data sent structure containing information about a recently sent block of data; used by the FMDataSendComplete event

 

FMMenuEnableFlags

 

This structure serves as part of the central FMParamBlock structure, and holds a set of Boolean flags which indicate whether the videoconferencing application should enable several standard menu items. You are expected to change these values in the FMParamBlock structure to reflect your current state whenver you receive an FMAdjustMenus event.

typedef struct FMMenuEnableFlags {
	Boolean
		save,
		saveAs,
		pageSetup,
		print,
		undo,
		cut,
		copy,
		paste,
		clear,
		selectAll;
} FMMenuEnableFlags, *FMMenuEnableFlagsPtr;

save -- set this true to enable the Save item in the File menu

 

saveAs -- set this true to enable the Save As item in the File menu

 

pageSetup -- set this true to enable the Page Setup item in the File menu

 

print -- set this true to enable the Print item in the File menu

 

undo -- set this true to enable the Undo item in the Edit menu

 

cut -- set this true to enable the Cut item in the Edit menu

 

copy -- set this true to enable the Copy item in the Edit menu

 

paste -- set this true to enable the Paste item in the Edit menu

 

clear -- set this true to enable the Clear item in the Edit menu

 

selectAll -- set this true to enable the Select All item in the Edit menu

 

 

FMConnectionInfo

 

This structure contains information returned from the FMGetConnectionInfo call.

typedef struct FMConnectionInfo {
	long srcID;
	long srcIP;
	char name[32];
	Boolean sending;
	Boolean receiving;
} FMConnectionInfo, *FMConnectionInfoPtr;

srcID -- this is the unique identification number which you will use to communicate with the connected party

 

srcIP -- this is the actual IP address of the connected party; note that, although this value may in some cases be identical to the srcID field, you should not count on it

 

name -- this is the name given by the connected party, up to 32-characters, as a pascal formatted string

 

sending -- this flag is true if the party is currently sending audio or video data

 

receiving -- this flag is true if the party is currently receiving audio or video data

 

 

FMDataReceived

typedef struct FMDataReceived {
	long type;
	char selector; 
	char opcode;
	char status;
	char priority;
	short segmentLength;
	long segmentOffset;
	Ptr	segmentPtr;	
	long itemNumber;
	long itemID;
	long itemLength;
	long holesLength;
	void *reserved;
	long srcID;
	long userWord1;
	long userWord2;
} FMDataReceived, *FMDataReceivedPtr;

FMDataSent


typedef struct FMDataSent {
	long bytesLeftToSend;
	long retryBytesLeftToSend;
	long completionCode;
	long reserved;
	long userWord;
	long userWord2;
	short state;
	Boolean reserved2;
} FMDataSent, *FMDataSentPtr;