TweetFollow Us on Twitter

Cubby VR Part II

Volume Number: 16 (2000)
Issue Number: 11
Column Tag: QuickDraw 3D Tricks

Cubby: Multiscreen Desktop VR Part II

by By Maarten Gribnau and Tom Djajadiningrat

How to create an Input Sprocket driver for a 3D input device

Summary

In this second part of our 'Cubby: Multiscreen Desktop VR' trilogy, we will introduce you to the art of creating a driver to read an Origin Instruments Dynasight input device. With the Dynasight, the position of the head of the user is established so that Cubby can display the correct images on its screens. The driver is created with InputSprocket, which is part of Apple's Game Sprockets API.

Introduction

In our previous articles about Desktop VR (Djajadiningrat & Gribnau, 1998; Gribnau & Djajadiningrat, 1998), we used the Pointing Device Manager (PDM) of QuickDraw 3D to serve our input needs. We promoted the PDM because it was intended to support 3D input devices. The PDM and InputSprocket have in common that they both separate the device dependent code from the application. They relieve application programmers from dealing with devices directly and provide an abstraction layer for input programming. The difference between the two is that InputSprocket is actually supported by device manufacturers. There are no QuickDraw 3D drivers available to our knowledge. For InputSprocket on the other hand, lots of drivers are available. In addition, InputSprocket has configuration management built in. A standard interface is provided for connecting devices to applications. For these reasons InputSprocket provides a better solution for Cubby's input needs than the PDM.

In this episode we cover programming with InputSprocket to create a device driver. In the next episode the application side will be covered. Together, the two articles cover the whole of InputSprocket. The driver we describe is for the Dynasight device. If you do not own one or plan to buy one, you might still be interested because the techniques explained here can be used for other input devices as well. Documentation on writing InputSprocket drivers is scarce, so you might pick up some facts here. To compile our code, you will probably need to download the latest version of InputSprocket from the Apple web site (see References section), which is currently version 1.7.3.

If you are familiar with programming drivers for InputSprocket, you can safely skip the next section and proceed with the following section about creating an InputSprocket driver for the Dynasight. If InputSprocket drivers are new to you, you can read the next section and be introduced to the basic concepts. If you have worked with InputSprocket before but not with drivers, you can also read the next section to learn how drivers and applications communicate.

Introduction to InputSprocket

The goal of InputSprocket is to make life easier for game programmers who deal with input devices. There are an immense number of devices on the market with different features. Trying to have a game support all these devices optimally can lead to severe headaches. Most games support input devices through emulation of the keyboard and mouse. But emulation does not suffice when supporting complex devices that have directional pads, levers, wheels, etc. For one, the game cannot take advantage of the extra input controls of complex devices and secondly, configuring the devices and connecting them to game controls is device specific. That means that game programmers should provide configuration capabilities for each device that they need to support.

This is where InputSprocket comes in. It solves these problems through an input device architecture that allows games developers to create games that can use a wide variety of input devices. Input device developers can use InputSprocket to build device drivers that provide a description of input device controls that the game can use to automatically configure its control options. The device driver can also provide a user interface that allows the user to change default control options. Therefore, game programming is made easier because a lot of device dependent code is moved from the application to the device driver.

The communication between InputSprocket drivers and games is based on elements. The element is a building block used to describe the capabilities of a device. Each control of a device is described with an element, so every device can be described with a set of elements. For example, a one-button mouse can be described with an element for the x-axis, an element for the y-axis and a button element. More complex devices may require more elements but are handled in the same way. An element is described by the following three pieces of data.

A human readable identifier is a string that the game can display to identify the element for the user during configuration. Examples of such identifiers are "trigger", "roll" or "move forward".

The element kind is a four-character sequence that indicates the type of data the element produces. For example, button elements will typically produce two-state values while axis elements produce continuous data. InputSprocket currently provides five basic element kinds:

  • Button elements produce two-state data.
  • Directional pad elements are nine state elements with an idle position and eight states corresponding to the eight directions on a typical directional game pad.
  • Axis elements produce continuous data, either with or without a meaningful center. A symmetrical axis has a meaningful center, like the axis of a joystick. Axis elements such as a gas pedal or a brake do not have a meaningful center.
  • Delta elements are like axis elements but instead of an absolute position or orientation, they produce Delta data, which indicates the distance moved relative to the previous position (e.g. mouse axes).
  • Movement elements produce movement data that is given both as x-y axis data and directional pad data, allowing the game to use whichever is suitable. Note that in general you should use axis data instead.

There is a sixth element kind, the virtual element, which we will encounter later.

The label of an element gives the suggested use for an element. For example, a button element may be intended as the start button or the firing button. During configuration, a game uses labels to find the elements it requires for play.

As was mentioned, elements are the way applications communicate with drivers. There are two interfaces to pass data from drivers to applications using elements: the low-level and the high-level interface. In principle, driver programmers can decide which interface to implement. It is tempting to implement the low-level interface only, since it is the easiest to implement. However, drivers should provide both the low and the high-level interface, so that game application programmers can decide which interface they want to use.

Low-level Interface

The low-level interface is the simplest to implement when building drivers. Figure 1 shows a diagram of both the low and the high-level interfaces. The lower parts of the Dynasight Driver and the InputSprocket Extension represent the low-level interface. Cubby uses only the high-level interface and therefore connects the cameras to the high-level axis elements (Head X, Head Y and Head Z) exclusively. When the driver is loaded, it creates elements for every control it has. The driver is responsible for reading the data from the device and, as soon as it is loaded, should update its elements every time the device reports new data. Applications can connect via InputSprocket directly to the elements of the device and update the game state accordingly.


Figure 1. Diagram of the low- and high-level interfaces.

High-level Interface

In Figure 1, the upper parts of the Dynasight Driver and the InputSprocket Extension represent the high-level interface. The high-level interface is somewhat more complicated than the low-level interface but it has all the advantages that InputSprocket was meant to offer. Instead of connecting to the device's elements directly, games read data from the devices through virtual elements. These virtual elements are created by InputSprocket from the needs that the game has. Drivers are responsible for the mapping of needs to virtual elements. The high-level interface is only valid between the driver's Init and Stop callbacks that InputSprocket calls. Only the driver knows exactly how it has been configured, and when it is active, it is responsible for pushing data to those virtual elements for each need for which it is configured.

Configuration

The majority of driver callbacks are related to the Configure dialog. These function calls will only happen while the high-level is valid (i.e. ISpInit has been called without a subsequent ISpStop). When the application calls ISpConfigure, a dialog is presented with a scrolling list of devices with one device selected. Figure 3 shows this dialog with our Dynasight device selected. The popup menus show the current connection of the Dynasight x- y- and z-axis to a game's roll, pitch and yaw controls. The selected device is responsible for the primary pane of the dialog, handling all events and drawing related to that area. The GetSize function is called to determine the preferred and minimum sizes of the pane area used by the device. If the pane area will not fit on any available display device, then that device is removed from the high-level list. The BeginConfiguration function is then called for each device. The configure dialog is resized and shown. The GetIcon function is called for each device to determine the icon to be shown in the scrolling list. Then the Show function is called for the selected device. This is the time to call AppendDITL for the resources to be displayed in the primary pane. All events returned in the dialog filter are passed to the HandleEvent function to give the device a chance to handle them. If update events are not handled (the recommended option to avoid extra flicker), InputSprocket will make the device Draw function call from within a BeginUpdate/EndUpdate pair. Unhandled mouse clicks will be passed to the Click function. If the device called AppendDITL to add items to the dialog and those items are returned by InputSprocket's ModalDialog call, then it will call the DialogItemHit function.

The device should maintain a 'dirty' variable that is set whenever any configuration information is changed, and returned and cleared when the Dirty call is made. When a different device is selected in the list, the old device receives a Hide function call and the new device a Show function call. When the dialog is closed, every device receives an EndConfiguration call.


Figure 2. InputSprockets's configuration dialog box with the Dynasight device selected.

Procedure

Figure 3 illustrates the sequence of events when a game is using InputSprocket. The initiating calls are shown on the left and the driver's responses are found on the right. There are four major activities. Low-level initialization is entered when an application calls ISpStartUp. InputSprocket will then load the driver and call its major entry point. In response, the driver should look for devices present on the system and create InputSprocket devices for each active device. If a device was created, InputSprocket will call the driver's Meta handler to find out where the driver's entry points are. If no InputSprocket device was created, the driver is unloaded. High-level initialization is entered when the application calls ISpInit. InputSprocket passes the needs and virtual elements to the driver. In response, the driver stores them and does an auto-configuration. This means that the driver will try to find an optimal match between the needs of the application and the elements the device has. When the high-level interface is valid, the application can call ISpConfigure. This starts the configuration activities explained in the previous section.

There are several guidelines that should be followed during auto-configuration. If a previous device fulfilled a need and the kISpNeedFlag_NoMultiConfig bit is set in the need structure for that need, the device should not attempt to auto-configure to the need. The driver should indicate that it is fulfilling a need so that devices queried later know that the need is taken. More guidelines can be found in Apple's documentation of InputSprocket (see References section). After auto-configuring, the device should immediately push initial values to the corresponding virtual elements and from that point push data to those virtual elements whenever data is pushed to the elements to which they are configured.

When the application calls ISpStop, the validity of the high-level interface ends. The drivers should stop pushing data to the virtual elements and dispose the needs and the virtual elements. Calling ISpShutdown within the application causes InputSprocket to call the driver's termination routine. In response, the driver should stop pushing low-level data and dispose the InputSprocket device.


Figure 3. The sequence of events in communication between driver and game.

Creating an InputSprocket Driver for the Dynasight

Now that we have a basic understanding of how InputSprocket works, we will dive into the coding of a driver and illustrate how we can put this knowledge to use. We take the Dynasight device as example and start with the real driver functionality: the initialization and transfer of data. The configuration of the driver will be covered in the next section. The Dynasight device is an infrared tracking device that is used in Cubby to track the head position of users. The 3D position data is made available on the serial port. In the following discussion, we will not go into the details of reading the position data from the serial port. If you are interested in that part of the driver, you can read our previous article about Desktop VR (Gribnau & Djajadiningrat, 1998) and/or you can have a look at the code accompanying this article.

Before we begin, we should make some general remarks about developing InputSprocket drivers. An InputSprocket driver is a shared library with a specific file type and creator ('shlb' and 'insp'). All drivers must be located in the same folder as the InputSprocket extension, which must be the Extensions folder. Being a shared library, it takes some extra effort to debug a driver. First of all, you should set the output directory of your development environment to the Extensions folder of the System folder on your startup disk, so that the compiled driver ends up there. Secondly, you should set a host application that is used to run the driver.

The Dynasight driver handles the six major tasks that all drivers should handle: shared library initialization, shared library termination, pushing data, 'high-level' Init, 'high-level' Stop, and the configure dialog user interface. The code for shared library initialization is shown in Listing 1. This routine is called when InputSprocket loads the driver. The important parts are that the driver checks whether a certain version of InputSprocket is available. If version 1.2 or greater is not available, the driver returns without creating any InputSprocket devices. In that case, InputSprocket unloads the driver. The other important point in this listing is that the driver retrieves the file specification of the shared library. The file specification is used in the driver initialization routine to locate the resource file.

Listing 1: ISpDriverDynasightMain.cp

__myintialize
OSErr __myinitialize(CFragInitBlockPtr ibp)
{
   OSErr   err = noErr;
   FSSpec fileSpec = {0,0,0};
   NumVersion version;
   UInt32 inputSprocketVersion;
   
#if __MWERKS__
   err = __initialize(ibp);
   if (err != noErr) {
      return err;
   }
#endif
   
   // Require InputSprocket 1.2.0
   version = ISpGetVersion();
   inputSprocketVersion = * (UInt32 *) &(version);   
   if (inputSprocketVersion < 0x01200000) {
      return err;
   }
   
   // Grab the file spec
   if (ibp->fragLocator.where == kDataForkCFragLocator) {
      // The shared library should always be located in the data fork
      fileSpec = *ibp->fragLocator.u.onDisk.fileSpec;
   }
   
   // Create IS device for each Dynasight device found on the system
   // If we don't create an IS device, IS will unload us
   DynasightInit(fileSpec);
   return err;   
}


In Listing 2, it is shown what happens inside the initialization routine. The routine looks at all available serial ports for a Dynasight. If one is found, the driver's resource file is opened and a structure in a global array (fDynas) is filled with all the information for the device on the current port. After setting some default values in the structure, the InputSprocket device is actually created (by calling CreateDevice) and the Dynasight is put into action. Finally, after all ports have been checked, the resource file of the driver is closed.

Listing 2: ISpDriverDynasight.cp

DynasightInit
OSErr DynasightInit(FSSpec fileSpec)
{
   OSErr err;
   short resourceRef = -1;                  // ref to our resource file
   Boolean resourceFileOpen = false;   // true if our resource file is open
   SerialPort port;
   UInt16 portID, numPortIDs;
   Boolean   portWithDynasight;
   
   fNumDynas = 0;
   numPortIDs = port.GetNumSerialPorts();
   for (portID = 0; portID < numPortIDs; portID++) {
      {
         Dynasight dynasight;
         portWithDynasight = 
            dynasight.GetDynasightFoundOnPort(portID);
      }
      if (portWithDynasight) {
         // Open the resource file and check for errors
         if (!resourceFileOpen) { 
            resourceRef   = FSpOpenResFile(&fileSpec, fsRdPerm);
            err = ResError();
            if (err != noErr) {
               return err;
            }
            resourceFileOpen = true;
         }
         fDynas[fNumDynas].dynasight = new Dynasight (portID);
         fDynas[fNumDynas].fileSpec = fileSpec;
         fDynas[fNumDynas].oldLLState.xAxis = kISpAxisMiddle;
         fDynas[fNumDynas].oldLLState.yAxis = kISpAxisMiddle;
         fDynas[fNumDynas].oldLLState.zAxis = kISpAxisMiddle;
         LowToHighLevelState(&fDynas[fNumDynas].oldLLState,
            &fDynas[fNumDynas].oldHLState);
         fDynas[fNumDynas].axisIndexToNeeds[kAxisIndex_XAxis]=
            kUnsetIndex;
         fDynas[fNumDynas].axisIndexToNeeds[kAxisIndex_YAxis]=
            kUnsetIndex;
         fDynas[fNumDynas].axisIndexToNeeds[kAxisIndex_ZAxis]=
            kUnsetIndex;
         fDynas[fNumDynas].virtualElementsValid = false;
         fDynas[fNumDynas].active = false;
         // Create the device and store its reference
         CreateDevice(&fDynas[fNumDynas]);
         fDynas[fNumDynas].active = true;
         fDynas[fNumDynas].dynasight->StartRunning(
            MyDynasightCompletionProc, &fDynas[fNumDynas]);
         fNumDynas++;
      }
   }
   // Close the resource file
   if (resourceFileOpen) {
      CloseResFile(resourceRef);
   }
   return err;
}


The CreateDevice routine actually creates the device and elements for the low-level interface. The driver provides three axis elements, corresponding to the x-, y- and z-axis of the Dynasight device. In our driver we chose to set up the device using a resource (of type 'isdv'), as shown in Listing 3. This resource contains all the information of an ISpDeviceDefinition structure that is passed to the InputSprocket. Listing 4 shows the creation of an axis element. Again, we chose to set up the axis elements from a resource (of type 'isel') that contains the information in an ISpElementDefinitionStruct structure which is needed to set up an element. After retrieving the element resource, yet another resource is retrieved. This is the element configuration resource (of type 'isei') that is used to configure the element. This part is optional. If the resource is not available or invalid, the element is still created in the subsequent ISpElement_New call.

Listing 3: ISpDriverDynasight.cp

CreateDeviceFromResource
OSErr CreateDeviceFromResource(
   short resId, 
   UInt32 refCon,
   ISpDeviceReference *device)
{
   Handle h;
   OSErr err;
   ISpDeviceDefinition def;
   
   // Read the device resource from the res file
   h = Get1Resource('isdv', resId);
   err = ResError();
   if (err != noErr) { return err; }
   if (h == nil) { err = -1; }
   if (err != noErr) { return err; }
   // Copy the handle to a structure and release the handle   
   BlockMoveData(*h, &def, sizeof(def));
   ReleaseResource(h);
   // Create the InputSprocket device
   err = ISpDevice_New(&def, (ISpDriverFunctionPtr_MetaHandler) DeviceMetaHandler, refCon, device);
   
   return err;
}


Listing 4: ISpDriverDynasight.cp

CreateElementFromResource
OSErr CreateElementFromResource(
   short elementResId,
   short configResID,
   ISpDeviceReference device,
   ISpElementReference *element)
{
   Handle h1;                  // handle to resource based element definition structure
   Handle h2;                  // handle to resource based element configuration info
   OSErr err;      
   ISpElementDefinitionStruct def;   // actual element definition structure
   
   // Read the element definition struct from a resource
   h1 = Get1Resource('isel', elementResId);
   err = ResError();
   if (err != noErr) { return err; }
   if (h1 == nil) { err = -1; }
   if (err != noErr) { return err; }
   
   // Copy the handle to a structure
   BlockMoveData(*h1, &def, sizeof(def));
   
   // Read the configuration information from a resource (this may fail)
   h2 = Get1Resource('isei', configResID);
   err = ResError();
   if (h2 && (err == noErr)) {
      HLock(h2);
      def.configInfo = *h2;
      def.configInfoLength = GetHandleSize(h2);
   }
   // Create the InputSprocket element
   def.device = device;
   err = ISpElement_New(&def, element);
   // Release resources
   HUnlock(h2);
   ReleaseResource(h1);
   ReleaseResource(h2);
   
   return err;
}



When creating the device in Listing 2, a pointer to DeviceMetaHandler was passed to the ISpDevice_New routine. This routine is called by InputSprocket to retrieve pointers to all the callbacks in our driver. It is called after the initialization of the driver. Listing 5 shows a part of the DeviceMetaHandler. InputSprocket calls the routine with a selector. The routine selects the appropriate callback and returns it. This example returns only the pointers to our Init and Stop callbacks. In our driver, the Meta handler returns pointers dealing with the configuration as well. These will be listed in the next section.

Listing 5: Example of a device Meta handler

DeviceMetaHandler
ISpDriverFunctionPtr_Generic DeviceMetaHandler(
   UInt32 refCon,
   ISpMetaHandlerSelector selector)
{
   ISpDriverFunctionPtr_Generic function = NULL;
   ISpDriverFunctionPtr_Init funcInit;
   ISpDriverFunctionPtr_Stop funcStop;
   
   switch(selector) {
      case kISpSelector_Init:
         funcInit = (ISpDriverFunctionPtr_Init) Init;
         function = (ISpDriverFunctionPtr_Generic) funcInit;
         break;
      
      case kISpSelector_Stop:
         funcStop = (ISpDriverFunctionPtr_Stop) Stop;
         function = (ISpDriverFunctionPtr_Generic) funcStop;
         break;
   }
   return function;
}

The beating hart of the driver is listed in Listing 6. This is the routine that is called whenever there is new data available from the Dynasight. We passed a pointer to this routine when we started the Dynasight in Listing 2. In the routine, it is first checked whether the low-level interface is active and whether the Dynasight is producing reliable data. Then, the position read from the Dynasight is scaled between 0 and 1 and subsequently scaled between InputSprocket axis values kISpAxisMinimum and kISpAxisMaximum. This is a requirement of InputSprocket. The convention is to scale coordinates of devices between those boundaries. For instance, with a joystick, kISpAxisMinimum should be sent when the joystick is fully rotated to one end and kISpAxisMaximum when it is fully rotated the other way.

The scaled coordinates are pushed to the low-level elements with the three ISpElement_PushSimpleData calls but only when a coordinate has changed. Then, the low-level data is converted to high-level data (in our case copied) and send to the virtual elements when the high-level interface is valid.

Listing 6: ISpDriverDynasight.cp

MyDynasightCompletionProc
void MyDynasightCompletionProc(
   DynasightCompletionProcData* data)
{
   OSStatus err;
   AbsoluteTime time;
   TDynasightRecPtr dyna = (TDynasightRecPtr)data->data;
   TISpLowLevelState llState;
   TISpHighLevelState hlState;
   float xf, yf, zf;
   
   if ((dyna->active) && (data->dynasightStatus ==
         dynasightStatus_Track)) {
      time = ISpUptime();

      // Scale Dynasight coordinates between 0 and 1
      NormalizeDynasightPosition(&data->position, &xf, &yf, &zf);
      // Put point between kISpAxisMinimum and kISpAxisMaximum
      scale = kISpAxisMaximum - kISpAxisMinimum;
      xf = kISpAxisMinimum + xf * scale;
      yf = kISpAxisMinimum + yf * scale;
      zf = kISpAxisMinimum + zf * scale;
      llState.xAxis = (UInt32)xf;
      llState.yAxis = (UInt32)yf;
      llState.zAxis = (UInt32)zf;
      
      // Push low-level data for low-level interface
      if (llState.xAxis != dyna->oldLLState.xAxis) {
         err = ISpElement_PushSimpleData(
                     dyna->deviceXAxis, llState.xAxis, &time);
      }
      if (llState.yAxis != dyna->oldLLState.yAxis) {
         err = ISpElement_PushSimpleData(
                     dyna->deviceYAxis, llState.yAxis, &time);
      }
      if (llState.zAxis != dyna->oldLLState.zAxis) {
         err = ISpElement_PushSimpleData(
                     dyna->deviceZAxis, llState.zAxis, &time);
      }
      
      // Push high-level data for high-level interface
      LowToHighLevelState(&llState, &hlState);
      if (dyna->virtualElementsValid) {
         SetVirtualsData(dyna, &hlState);
      }
      
      dyna->oldLLState = llState;
      dyna->oldHLState = hlState;
   }
}



InputSprocket Driver Configuration

We will now show the important parts of the driver relating to the configuration dialog. For our Dynasight driver this part is relatively easy since we have only three popup menus in the dialog box (see Figure 2). Each menu can be used to change the function of a Dynasight axis. For example, a user might select the menu and change the function of the x-axis from need 'pitch' to need 'move forward'. The driver maintains the list of total needs of the application and a mapping of needs to axes. A selection in the dialog might change this mapping.

As was mentioned above, the first routine InputSprocket calls when starting configuration is the GetSize routine. The driver returns the minimal and optimal size we need, as shown in Listing 7. The next routine called is the GetIcon routine shown in Listing 8. The driver returns the resource identifier of the icon suite of the Dynasight driver.

Listing 7: ISpDriverDynasight.cp

GetSize
OSStatus GetSize(
   UInt32 refCon,
   Point *minimum,
   Point *best)
{
   refCon;
   best->h = minimum->h = 200;
   best->v = minimum->v = 120;   
   return noErr;
}

Listing 8: ISpDriverDynasight.cp
GetIcon
OSStatus GetIcon(
   UInt32 refCon,
   short *iconSuiteResourceId)
{
   refCon;
   *iconSuiteResourceId = kIconSuiteDynasight;
   return noErr;
}

When the Dynasight device is selected is the dialog box, InputSprocket calls the driver's Show routine listed in Listing 9. This is the time to have our dialog items added to the main dialog pane. First, some information about the dialog is stored for later use (in the Hide routine for instance). Then, the 'DITL' resource is retrieved from the resource file and appended at the end. Listing 10 shows how the reverse takes place. The Hide routine is called when another device is selected in the dialog or when the dialog is closed. Then, the driver removes its dialog items from the 'DITL'.

Listing 9: ISpDriverDynasight.cp

Show
OSStatus Show(
   UInt32 refCon,
   DialogPtr theDialog, 
   short dialogItemNumber,
   Rect *r)
{
   OSStatus err;
   TDynasightRecPtr dyna = (TDynasightRecPtr) refCon;
   Handle ditl;
   
   dyna->dialogRect = *r;   
   dyna->dialogPtr = theDialog;
   dyna->dialogBaseDITLCount = CountDITL(theDialog);

   // Open the resource file, get our DITL and append it
   dyna->resFileRef = FSpOpenResFile(&dyna->fileSpec, 
      fsRdPerm);
   err = ResError();
   if (err != noErr) { return err; }
   ditl = Get1Resource('DITL', kDITLID_Config);
   err = ResError();
   if (err != noErr) {
      CloseResFile(dyna->resFileRef);
      return err;
   }
   AppendDITL(theDialog, ditl, -dialogItemNumber);
   
   // Free our DITL
   ReleaseResource(ditl);

   return noErr;
}

Listing 10: ISpDriverDynasight.cp

Hide
OSStatus Hide(
   UInt32 refCon)
{
   TDynasightRecPtr dyna = (TDynasightRecPtr) refCon;
   // Restore to original count of items
   ShortenDITL(dyna->dialogPtr, CountDITL(dyna->dialogPtr) - 
      dyna->dialogBaseDITLCount);
   dyna->dialogPtr = nil;
   CloseResFile(dyna->resFileRef);
   return noErr;
}


When the dialog is on the screen, the driver needs to respond to events to track whether there are changes in the configuration. In our case, the driver has only popup menus that it can handle by responding to mouse click events. Therefore, our driver ignores all other events that InputSprocket passes. In Listing 11, it is shown how the driver handles mouse clicks. First the position of the click is established. Then we iterate through our popup menus and call HandleAxisClick for each axis. This routine checks whether the current need of an axis changes as a result of the click.

Listing 11: ISpDriverDynasight.cp

Click
OSStatus Click(
   UInt32 refCon, 
   const EventRecord *event)
{
   refCon;
   TDynasightRecPtr dyna = (TDynasightRecPtr) refCon;
   Point where;
   UInt32 itr;
   short itemNo;
   UInt32 oldNeed;

   where = event->where;
   GlobalToLocal(&where);

   for (itr = kDialogItem_FirstPopup;
         itr <= kDialogItem_NumPopups;
         itr++) {
      itemNo = itr + dyna->dialogBaseDITLCount;
      oldNeed = dyna->axisIndexToNeeds[itr-1];
      switch (itr) {
         case kDialogItem_XAxis:
            HandleAxisClick(dyna, where, itemNo,
               kISpElementLabel_Axis_XAxis, kAxisIndex_XAxis,
               oldNeed);
            break;
         case kDialogItem_YAxis:
            HandleAxisClick(dyna, where, itemNo,
               kISpElementLabel_Axis_YAxis, kAxisIndex_YAxis,
               oldNeed);
            break;
         case kDialogItem_ZAxis:
            HandleAxisClick(dyna, where, itemNo,
               kISpElementLabel_Axis_ZAxis, kAxisIndex_ZAxis,
               oldNeed);
            break;
         default:
            break;
      }      
   }
   
   return noErr;
}



Listing 12 shows the HandleAxisClick routine. First, it is checked whether the popup is actually hit and if the need for the current axis changed as a result of the click. If the need changed, the need for that axis is first cleared. Then, the new need is activated. The current configuration is marked 'dirty' as InputSprocket requires. Finally, the popup menu is drawn with the new need setting to reflect the change in need for this axis.

Listing 12: ISpDriverDynasight.cp

HandleAxisClick
void HandleAxisClick(
   TDynasightRecPtr dyna,
   Point where,
   short itemNo,
   ISpElementLabel elementLabel,
   TAxisIndex axisIndex,
   UInt32 oldNeed)
{
   UInt32 itemIndex = itemNo - dyna->dialogBaseDITLCount;
   UInt32 newNeed;
   
   if (CheckPopUpHit(dyna, where, itemNo,
         kISpElementKind_Axis, elementLabel, validAxisKinds,
         validAxisKindsCount, oldNeed, newNeed) &&
         (oldNeed != newNeed)) {
      dynasightPtr->axisIndexToNeeds[axisIndex] = newNeed;
      dynasightPtr->configurationDirty = true;
      PlotPopupIcon(dynasightPtr,axisIndex + 1 + 
         dynasightPtr->dialogBaseDITLCount, ttNone);
   }
}


This concludes our coverage of the driver's code for the configuration dialog. There are some parts of the driver that we could not cover such as auto-configuration. The interested reader can look at the full source code of the driver to see how this is accomplished.

Conclusion

In this month's episode, you have discovered how InputSprocket can be used to create a driver for the Dynasight device. We hope that this episode has illustrated the power of InputSprocket. To connect Cubby with the Dynasight, we could have used parts of the driver code and pasted them directly into the Cubby application code. That would have saved the time to write and debug the driver. However, now that we have a driver, we can use the same device with other InputSprocket-savvy applications (imagine looking around in a game with the Dynasight as head-tracking device...). Moreover, making Cubby support InputSprocket, we can use any 3D device with an InputSprocket driver for Cubby.

In next month's episode, we will cover the integration of Cubby with InputSprocket. We will show you how Cubby uses InputSprocket to move the cameras from part I. In addition, we will cover calibrating Cubby so that Cubby positions the cameras such that the correct images are generated.

Bibliography and References

  • Apple Game Sprockets web site. The place to download Apple Game Sprockets software, documentation and example software. Available as:
    http://developer.apple.com/games/sprockets/index.html
  • Configuring Game Input Devices with InputSprocket. Available as: http://developer.apple.com/techpubs/macos8/pdf/InputSprocket1.7.pdf.
  • Djajadiningrat, J. P. & Gribnau, M. W. (1998). Desktop VR using QuickDraw 3D, Part I. MacTech Magazine 14(7), 32-43.
  • Gribnau, M. W. & Djajadiningrat, J. P. (1998). Desktop VR using QuickDraw 3D, Part II. MacTech Magazine 14(8), 26-34.
  • Djajadiningrat, J.P. & Gribnau, M.W. (2000). Cubby: Multiscreen Desktop VR Part I. MacTech Magazine 16(10).

Maarten is always jealous of his co-author. The ease with which Tom creates a funny 'about the author' piece starts to become almost traumatic. He is now on the verge of refusing to finish his contribution to the final episode. You might try to inspire him with an email at: M.W.Gribnau@io.tudelft.nl.

There are some striking similarities between Tom and his Wallstreet PoBo. You need to push them really hard to wake them from sleep, if you persist it still takes ages before they finally get into action, and sometimes it appears as if they've woken up but really all they do is zombie around in some undefined state in which it is impossible to get anything sensible out of them. If they're up and running you can reach them at J.P.Djajadiningrat@io.tudelft.nl.

 
AAPL
$97.19
Apple Inc.
+2.47
MSFT
$44.87
Microsoft Corpora
+0.04
GOOG
$595.98
Google Inc.
+1.24

MacTech Search:
Community Search:

Software Updates via MacUpdate

Firefox 31.0 - Fast, safe Web browser. (...
Firefox for Mac offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals... Read more
Little Snitch 3.3.3 - Alerts you to outg...
Little Snitch gives you control over your private outgoing data. Track background activityAs soon as your computer connects to the Internet, applications often have permission to send any... Read more
Thunderbird 31.0 - Email client from Moz...
As of July 2012, Thunderbird has transitioned to a new governance model, with new features being developed by the broader free software and open source community, and security fixes and improvements... Read more
Together 3.2 - Store and organize all of...
Together helps you organize your Mac, giving you the ability to store, edit and preview your files in a single clean, uncluttered interface. Smart storage. With simple drag-and-drop functionality,... Read more
Cyberduck 4.5 - FTP and SFTP browser. (F...
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
iExplorer 3.4 - View and transfer all th...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
Airmail 1.4 - Powerful, minimal email cl...
Airmail is a powerful, minimal mail client.It was designed to retain the same experience with a single or multiple accounts and provide a quick, modern and easy-to-use user experience. Airmail... Read more
Macs Fan Control 1.1.12 - Monitor and co...
Macs Fan Control allows you to monitor and control almost any aspect of your computer's fans, with support for controlling fan speed, temperature sensors pane, menu-bar icon, and autostart with... Read more
A Better Finder Rename 9.37 - File, phot...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
MacBook Air EFI Firmware Update 2.9 - Fo...
MacBook Air EFI Firmware Update is recommended for MacBook Air (Mid 2011) models. This update addresses an issue where systems may take longer to wake from sleep than expected and fixes a rare issue... Read more

Latest Forum Discussions

See All

Together for iOS (Productivity)
Together for iOS 1.0 Device: iOS Universal Category: Productivity Price: $9.99, Version: 1.0 (iTunes) Description: Together is an app for keeping things in one place. Notes, documents, images, movies, sounds, web pages and bookmarks... | Read more »
The Phantom PI Mission Apparition (Game...
The Phantom PI Mission Apparition 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: ** Release sale! 50% off for a limited time! ** The Phantom PI Mission Apparition is a spooky, puzzly, rock’... | Read more »
The Great Prank War (Games)
The Great Prank War 1.0.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.0 (iTunes) Description: Help Mordecai, Rigby, Muscle Man and Skips take the park back from Gene and his goons with a plethora of prank-related... | Read more »
Teenage Mutant Ninja Turtles (Games)
Teenage Mutant Ninja Turtles 1.0.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.0 (iTunes) Description: Download the all new Teenage Mutant Ninja Turtles Official Movie Game! | Read more »
Dream Revenant (Games)
Dream Revenant 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: EXCLUSIVE LAUNCH PRICE ! Dream Revenant is at $1.99 for a limited time ! | Read more »
Traps n' Gemstones (Games)
Traps n' Gemstones 1.00 Device: iOS Universal Category: Games Price: $2.99, Version: 1.00 (iTunes) Description: LAUNCH SALE! 40% off, JULY ONLY! TRAPS N' GEMSTONES is an adventurous platform game, among gamers typically known as the... | Read more »
Soccer Physics (Games)
Soccer Physics 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: One-button soccer game! So dumb it's fun. "Soccer Physics is probably the funniest football game you'll play on iOS" —... | Read more »
Ex-Angry Birds Developers Release Monsu...
Ex-Angry Birds Developers Release Monsu Teaser Trailer Posted by Jennifer Allen on July 23rd, 2014 [ permalink ] Finnish developer Boomlagoon has released a teaser trailer of their forthcoming side-scrolling action platformer, | Read more »
Dragons: Rise of Berk – Tips, Tricks, an...
Things have changed in Berk, the fantasy Viking village of DreamWorks’ How to Train Your Dragon series. Dragons and Vikings, once mortal enemies, now must learn to live together in peace. Dragons: Rise of Berk lets players manage dragon-Viking... | Read more »
Cowabunga! Teenage Mutant Ninja Turtles:...
Cowabunga! Teenage Mutant Ninja Turtles: Rooftop Run Is Currently Free Posted by Jennifer Allen on July 23rd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

What Should Apple’s Next MacBook Priority Be;...
Stabley Times’ Phil Moore says that after expanding its iMac lineup with a new low end model, Apple’s next Mac hardware decision will be how it wants to approach expanding its MacBook lineup as well... Read more
ArtRage For iPhone Painting App Free During C...
ArtRage for iPhone is currently being offered for free (regularly $1.99) during Comic-Con San Diego #SDCC, July 24-27, in celebration of the upcoming ArtRage 4.5 and other 64-bit versions of the... Read more
With The Apple/IBM Alliance, Is The iPad Now...
Almost since the iPad was rolled out in 2010, and especially after Apple made a 128 GB storage configuration available in 2012, there’s been debate over whether the iPad is a serious tool for... Read more
MacBook Airs on sale starting at $799, free s...
B&H Photo has the new 2014 MacBook Airs on sale for up to $100 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels... Read more
Apple 27″ Thunderbolt Display (refurbished) a...
The Apple Store has Apple Certified Refurbished 27″ Thunderbolt Displays available for $799 including free shipping. That’s $200 off the cost of new models. Read more
WaterField Designs Unveils Cycling Ride Pouch...
High end computer case and bag maker WaterField Designs of San Francisco now enters the cycling market with the introduction of the Cycling Ride Pouch – an upscale toolkit with a scratch-free iPhone... Read more
Kingston Digital Ships Large Capacity Near 1T...
Kingston Digital, Inc., the Flash memory affiliate of Kingston Technology Company, Inc.,has announced its latest addition to the SSDNow V300 series, the V310. The Kingston SSDNow V310 solid-state... Read more
Apple’s Fiscal Third Quarter Results; Record...
Apple has announced financial results for its fiscal 2014 third quarter ended June 28, 2014, racking up quarterly revenue of $37.4 billion and quarterly net profit of $7.7 billion, or $1.28 per... Read more
15-inch 2.0GHz MacBook Pro Retina on sale for...
B&H Photo has the 15″ 2.0GHz Retina MacBook Pro on sale for $1829 including free shipping plus NY sales tax only. Their price is $170 off MSRP. B&H will also include free copies of Parallels... Read more
Apple restocks refurbished Mac minis for up t...
The Apple Store has restocked Apple Certified Refurbished Mac minis for up to $150 off the cost of new models. Apple’s one-year warranty is included with each mini, and shipping is free: - 2.5GHz Mac... Read more

Jobs Board

Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
Senior Interaction Designer, *Apple* Online...
**Job Summary** Apple is looking for a hands on Senior…will be a key player in designing for the Apple Online Store. The ideal designer will have a Read more
*Apple* Sales Chat Rep - Apple (United State...
…is looking for motivated, outgoing, and tech savvy individuals who want to offer Apple Customers an unparalleled customer experience over chat. At Apple , we believe Read more
Mac Expert - *Apple* Online Store Mexico -...
…MUST be fluent in English and Spanish to be considered for this position At Apple , we believe that hard work, a fun environment, creativity and innovation fuel the Read more
*Apple* Industrial Design CAD Sculptor - App...
**Job Summary** The Apple Industrial Design team is looking for a CAD sculptor/Digital 3D modeler to create high quality CAD models used in the industrial design process Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.