Sources/EntryPoints.m
// File: EntryPoints.m |
// Abstract: n/a |
// Version: 1.2 |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
// Inc. ("Apple") in consideration of your agreement to the following |
// terms, and your use, installation, modification or redistribution of |
// this Apple software constitutes acceptance of these terms. If you do |
// not agree with these terms, please do not use, install, modify or |
// redistribute this Apple software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non-exclusive |
// license, under Apple's copyrights in this original Apple software (the |
// "Apple Software"), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and/or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following |
// text and disclaimers in all such redistributions of the Apple Software. |
// Neither the name, trademarks, service marks or logos of Apple Inc. may |
// be used to endorse or promote products derived from the Apple Software |
// without specific prior written permission from Apple. Except as |
// expressly stated in this notice, no other rights or licenses, express or |
// implied, are granted by Apple herein, including but not limited to any |
// patent rights that may be infringed by your derivative works or by other |
// works in which the Apple Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
// POSSIBILITY OF SUCH DAMAGE. |
// |
// Copyright (C) 2012 Apple Inc. All Rights Reserved. |
// |
#import <sys/stat.h> |
#import <pthread.h> |
#import "EntryPoints.h" |
#import "VirtualScanner.h" |
extern DASessionRef _gDiskArbSession; |
extern pthread_mutex_t _gScannersMutex; |
extern NSMutableDictionary* _gScannersDictionary; |
extern BOOL _gIsVirtualScanner; |
extern int gArgc; |
extern char* gArgv[10]; |
UInt32 LocationIDOfMatchingUSBDevice( void ); |
#pragma mark - |
//------------------------------------------------------------------------------------- GetLocationIDOfMatchingUSBDevice |
// Utility function to find a relevant USB device connected to the Mac |
UInt32 |
LocationIDOfMatchingUSBDevice() |
{ |
UInt32 locationID = 0; |
io_iterator_t iterator = 0; |
if ( IOServiceGetMatchingServices( kIOMasterPortDefault, IOServiceMatching( kIOUSBDeviceClassName ), &iterator ) == kIOReturnSuccess ) |
{ |
BOOL found = NO; |
io_service_t interface = 0; |
while ( ( interface = IOIteratorNext( iterator ) ) && !found ) |
{ |
NSMutableDictionary* objProps = NULL; |
if ( IORegistryEntryCreateCFProperties( interface, (CFMutableDictionaryRef*)&objProps, kCFAllocatorDefault, 0 ) == kIOReturnSuccess ) |
{ |
// Check to see if this is your device |
/* |
unsigned short idVendor = [[objProps objectForKey:@"idVendor"] unsignedShortValue]; |
unsigned short idProduct = [[objProps objectForKey:@"idProduct"] unsignedShortValue]; |
if ( ( idVendor == yourDeviceVendorID ) && ( idProduct == yourDeviceProductID ) ) |
{ |
locationID = [[objProps objectForKey:@"locationID"] unsignedIntValue]; |
found = YES; |
} |
*/ |
} |
[objProps release]; |
IOObjectRelease( interface ); |
} |
IOObjectRelease( iterator ); |
} |
return locationID; |
} |
//----------------------------------------------------------------------------------------------- _ICD_ScannerOpenDevice |
/* |
This function is usually called when a USB device is attached to the Macintosh. |
This function is also called when the device module is executed via Xcode. In the latter case, |
the value of locationID passed to this function is set to 0. |
*/ |
ICAError |
_ICD_ScannerOpenUSBDevice( |
UInt32 locationID, |
ScannerObjectInfo* newDeviceObjectInfo |
) |
{ |
Log("--> _ICD_ScannerOpenUSBDevice\n"); |
ICAError err = paramErr; |
if ( locationID == 0 ) |
{ |
// Special Case - we were launched by XCode and hence locationID is 0. |
if ( _gIsVirtualScanner ) |
{ |
UInt32 fakeLocation = 0; |
if ( gArgc == 2 ) |
{ |
int l = strlen( gArgv[1] ); |
if ( l <= 4 ) |
{ |
char* temp = (char*)&fakeLocation; |
int c = 0; |
for ( c = 0; c < l; ++c ) |
temp[c] = gArgv[1][c]; |
} |
} |
else |
fakeLocation = 0xDEADBEEF; |
err = ICDConnectUSBDevice( fakeLocation ); |
} |
else |
{ |
UInt32 newLocationID = LocationIDOfMatchingUSBDevice(); |
if ( newLocationID ) |
err = ICDConnectUSBDevice( newLocationID ); |
} |
} |
else |
{ |
NSNumber* locIDNum = [NSNumber numberWithUnsignedInt:locationID]; |
BOOL newDevice = YES; |
pthread_mutex_lock( &_gScannersMutex ); |
newDevice = ( [_gScannersDictionary objectForKey:locIDNum] == NULL ); |
pthread_mutex_unlock( &_gScannersMutex ); |
if ( newDevice ) |
{ |
VirtualScanner* vScanner = NULL; |
if ( _gIsVirtualScanner ) |
{ |
if( locationID == 0xDEADBEEF ) |
{ |
vScanner = [[VirtualScanner alloc] init]; |
} |
else |
{ |
// Construct the full BSD name for the mounted virtual scanner disk image and use it to create |
// an instance of VirtualScanner. |
char name[5] = {0}; |
NSString* bsdName = NULL; |
memcpy( name, &locationID, sizeof(locationID) ); |
bsdName = [NSString stringWithFormat:@"disk%s",name]; |
Log( " Opening Device %s\n", [bsdName fileSystemRepresentation] ); |
vScanner = [[VirtualScanner alloc] initWithDiskBSDName:bsdName andDASession:_gDiskArbSession]; |
} |
} |
else |
{ |
vScanner = [[VirtualScanner alloc] initWithLocationID:locationID]; |
// add code here to setup the USB device |
} |
if ( vScanner ) |
{ |
newDeviceObjectInfo->privateData = (Ptr)vScanner; |
newDeviceObjectInfo->flags = 0; |
newDeviceObjectInfo->thumbnailSize = 1; // All scanner clients are based on ImageCaptureCore framework, which dynamically determines size of thumbnails. So, we do not need to know the exact size of the thumbnail. Set this to 1 if we have a thumbnail. |
newDeviceObjectInfo->dataSize = 0; |
newDeviceObjectInfo->icaObjectInfo.objectType = kICADevice; |
newDeviceObjectInfo->icaObjectInfo.objectSubtype = kICADeviceScanner; |
strlcpy( (char*)newDeviceObjectInfo->name, [vScanner.name UTF8String], sizeof(newDeviceObjectInfo->name) ); |
NSDateFormatter* df = [[NSDateFormatter alloc] initWithDateFormat:@"%Y:%m:%d %H:%M:%S" allowNaturalLanguage:YES]; |
NSDate* d = [NSDate date]; |
NSString* ds = [df stringFromDate:d]; |
if ( ds ) |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), [ds UTF8String], sizeof(newDeviceObjectInfo->creationDate) ); |
else |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), "0000:00:00 00:00:00", sizeof(newDeviceObjectInfo->creationDate) ); |
[df release]; |
// Cache device object info in our device object |
vScanner.deviceObjectInfo = newDeviceObjectInfo; |
pthread_mutex_lock( &_gScannersMutex ); |
[_gScannersDictionary setObject:vScanner forKey:locIDNum]; |
pthread_mutex_unlock( &_gScannersMutex ); |
err = noErr; |
} |
} |
} |
Log("<-- _ICD_ScannerOpenUSBDevice\n"); |
return err; |
} |
//--------------------------------------------------------------------------------------------- _ICD_ScannerOpenFWDevice |
/* |
This function is called when a FireWire device is attached to the Macintosh. |
The value of guid is the FireWire GUID of the device and the value of ioRegPath can be used to obtain |
the IOKit object corresponding to the IOService that triggered the launch of this device module. |
*/ |
ICAError |
_ICD_ScannerOpenFWDevice( |
UInt64 guid, |
io_string_t ioRegPath, |
ScannerObjectInfo* newDeviceObjectInfo |
) |
{ |
Log("--> _ICD_ScannerOpenFWDevice\n"); |
ICAError err = paramErr; |
NSNumber* guidNum = [NSNumber numberWithUnsignedLongLong:guid]; |
BOOL newDevice = YES; |
pthread_mutex_lock( &_gScannersMutex ); |
newDevice = ( [_gScannersDictionary objectForKey:guidNum] == NULL ); |
pthread_mutex_unlock( &_gScannersMutex ); |
if ( newDevice ) |
{ |
VirtualScanner* vScanner = [[VirtualScanner alloc] initWithGUID:guid andIORegPath:ioRegPath]; |
if ( vScanner ) |
{ |
newDeviceObjectInfo->privateData = (Ptr)vScanner; |
newDeviceObjectInfo->flags = 0; |
newDeviceObjectInfo->thumbnailSize = 1; // All scanner clients are based on ImageCaptureCore framework, which dynamically determines size of thumbnails. So, we do not need to know the exact size of the thumbnail. Set this to 1 if we have a thumbnail. |
newDeviceObjectInfo->dataSize = 0; |
newDeviceObjectInfo->icaObjectInfo.objectType = kICADevice; |
newDeviceObjectInfo->icaObjectInfo.objectSubtype = kICADeviceScanner; |
strlcpy( (char*)newDeviceObjectInfo->name, [vScanner.name UTF8String], sizeof(newDeviceObjectInfo->name) ); |
NSDateFormatter* df = [[NSDateFormatter alloc] initWithDateFormat:@"%Y:%m:%d %H:%M:%S" allowNaturalLanguage:YES]; |
NSDate* d = [NSDate date]; |
NSString* ds = [df stringFromDate:d]; |
if ( ds ) |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), [ds UTF8String], sizeof(newDeviceObjectInfo->creationDate) ); |
else |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), "0000:00:00 00:00:00", sizeof(newDeviceObjectInfo->creationDate) ); |
[df release]; |
// Cache device object info in our device object |
vScanner.deviceObjectInfo = newDeviceObjectInfo; |
pthread_mutex_lock( &_gScannersMutex ); |
[_gScannersDictionary setObject:vScanner forKey:guidNum]; |
pthread_mutex_unlock( &_gScannersMutex ); |
err= noErr; |
} |
} |
Log("<-- _ICD_ScannerOpenFWDevice\n"); |
return err; |
} |
//------------------------------------------------------------------------------------------ _ICD_ScannerOpenTCPIPDevice |
/* |
This function is called when a Bonjour service supported by this device module is selected by the user. |
The parameter dictionary 'params' contains information needed to conntect to the network device using |
TCP/IP protocol. Here is a sample of the 'params' dictionary: |
{ |
ICABonjourDeviceLocationKey = "In the magic ether"; |
ICABonjourServiceNameKey = "Virtual Scanner Bonjour"; |
ICABonjourServiceTypeKey = "_scanner._tcp."; |
ICABonjourTXTRecordKey = { |
mdl = <56697274 75616c20 5363616e 6e6572>; |
mfg = <4170706c 65>; |
note = <496e2074 6865206d 61676963 20657468 6572>; |
scannerAvailable = <31>; |
txtvers = <31>; |
ty = <4170706c 65205669 72747561 6c205363 616e6e65 72>; |
}; |
ICADeviceBrowserDeviceRefKey = 1; |
UUIDString = "1D279211-1D27-9211-1D27-92111D279211"; |
deviceModulePath = "/System/Library/Image Capture/Devices/VirtualScanner.app"; |
deviceModuleVersion = 16809984; |
deviceType = scanner; |
hostGUID = "C933C548-A19F-4084-BF09-528438D6D581"; |
hostName = "Baskarans-MBP-UniMP"; |
ipAddress = "192.168.2.9"; |
"ipAddress_v6" = "fe80::3615:9eff:fe8a:9f9c"; |
ipGUID = ""; |
ipPort = 9500; |
"ipPort_v6" = 9500; |
name = "Apple Virtual Scanner"; |
persistentIDString = "1D279211-1D27-9211-1D27-92111D279211"; |
transportType = "TCP/IP"; |
} |
*/ |
ICAError |
_ICD_ScannerOpenTCPIPDevice( |
CFDictionaryRef params, |
ScannerObjectInfo* newDeviceObjectInfo |
) |
{ |
Log("--> _ICD_ScannerOpenTCPIPDevice\n"); |
ICAError err = paramErr; |
BOOL newDevice = YES; |
pthread_mutex_lock( &_gScannersMutex ); |
newDevice = ( [_gScannersDictionary objectForKey:[(NSDictionary*)params objectForKey:(id)kICABonjourServiceNameKey]] == NULL ); |
pthread_mutex_unlock( &_gScannersMutex ); |
if ( newDevice ) |
{ |
VirtualScanner* vScanner = NULL; |
if ( _gIsVirtualScanner ) |
{ |
vScanner = [[VirtualScanner alloc] init]; |
} |
else |
{ |
vScanner = [[VirtualScanner alloc] initWithParameters:(NSDictionary*)params]; |
} |
if( vScanner ) |
{ |
newDeviceObjectInfo->privateData = (Ptr)vScanner; |
newDeviceObjectInfo->flags = 0; |
newDeviceObjectInfo->thumbnailSize = 1; // All scanner clients are based on ImageCaptureCore framework, which dynamically determines size of thumbnails. So, we do not need to know the exact size of the thumbnail. Set this to 1 if we have a thumbnail. |
newDeviceObjectInfo->dataSize = 0; |
newDeviceObjectInfo->icaObjectInfo.objectType = kICADevice; |
newDeviceObjectInfo->icaObjectInfo.objectSubtype = kICADeviceScanner; |
strlcpy( (char*)newDeviceObjectInfo->name, [vScanner.name UTF8String], sizeof(newDeviceObjectInfo->name) ); |
NSDateFormatter* df = [[NSDateFormatter alloc] initWithDateFormat:@"%Y:%m:%d %H:%M:%S" allowNaturalLanguage:YES]; |
NSDate* d = [NSDate date]; |
NSString* ds = [df stringFromDate:d]; |
if ( ds ) |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), [ds UTF8String], sizeof(newDeviceObjectInfo->creationDate) ); |
else |
strlcpy( (char*)(newDeviceObjectInfo->creationDate), "0000:00:00 00:00:00", sizeof(newDeviceObjectInfo->creationDate) ); |
[df release]; |
// Cache device object info in our device object |
vScanner.deviceObjectInfo = newDeviceObjectInfo; |
pthread_mutex_lock( &_gScannersMutex ); |
[_gScannersDictionary setObject:vScanner forKey:(id)kICABonjourServiceNameKey]; |
pthread_mutex_unlock( &_gScannersMutex ); |
err = noErr; |
} |
} |
Log("<-- _ICD_ScannerOpenTCPIPDevice\n"); |
return err; |
} |
//---------------------------------------------------------------------------------------------- _ICD_ScannerCloseDevice |
ICAError |
_ICD_ScannerCloseDevice( |
ScannerObjectInfo* deviceObjectInfo |
) |
{ |
Log("--> _ICD_ScannerCloseDevice\n"); |
ICAError err = noErr; |
VirtualScanner* vScanner = NULL; |
require_action( deviceObjectInfo, bail, err = paramErr ); |
vScanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action( vScanner, bail, err = paramErr ); |
// Add code here to perform any clean up operations |
[vScanner release]; |
pthread_mutex_lock( &_gScannersMutex ); |
if ( _gIsVirtualScanner ) |
{ |
NSString* bsdName = vScanner.bsdName; |
UInt32 locationID = 0; |
if ( bsdName ) |
{ |
NSString* temp = [bsdName substringFromIndex:4]; |
char tempCStr[5] = {}; |
[temp getCString:tempCStr maxLength:5 encoding:NSUTF8StringEncoding]; |
strlcpy( (char*)&locationID, tempCStr, 4 ); |
} |
else |
locationID = 0xDEADBEEF; |
[_gScannersDictionary removeObjectForKey:[NSNumber numberWithUnsignedInt:locationID]]; |
} |
else |
{ |
if ( vScanner.usbLocationID != 0 ) |
{ |
[_gScannersDictionary removeObjectForKey:[NSNumber numberWithUnsignedInt:vScanner.usbLocationID]]; |
} |
else if ( vScanner.firewireGuid != 0 ) |
{ |
[_gScannersDictionary removeObjectForKey:[NSNumber numberWithUnsignedLongLong:vScanner.firewireGuid]]; |
} |
else if ( vScanner.networkParams != NULL ) |
{ |
[_gScannersDictionary removeObjectForKey:[vScanner.networkParams objectForKey:(id)kICABonjourServiceNameKey]]; |
} |
} |
pthread_mutex_unlock( &_gScannersMutex ); |
bail: |
Log("<-- _ICD_ScannerCloseDevice\n"); |
return err; |
} |
//-------------------------------------------------------------------------------------------------- _ICD_ScannerCleanup |
/* |
This function is called once for each object created by the device module. The objects include the scanner objects |
and any file objects created as children of the scanner objects. The file objects are created when scans are requested |
by a remote client. |
*/ |
ICAError |
_ICD_ScannerCleanup( |
ScannerObjectInfo* objectInfo |
) |
{ |
Log("--> _ICD_ScannerCleanup\n"); |
Log("<-- _ICD_ScannerCleanup\n"); |
return noErr; |
} |
//--------------------------------------------------------------------------------------------- _ICD_ScannerPeriodicTask |
/* |
This function is called periodically to allow the device module to perform any periodic activity. Any code that polls |
the device for its status should go here. |
*/ |
ICAError |
_ICD_ScannerPeriodicTask( |
ScannerObjectInfo * deviceObjectInfo |
) |
{ |
//Log("--> _ICD_ScannerPeriodicTask\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
// Acquire the scanner button status |
[scanner acquireButtonPress]; |
bail: |
//Log("<-- _ICD_ScannerPeriodicTask\n"); |
return err; |
} |
//-------------------------------------------------------------------------------------------- _ICD_ScannerGetObjectInfo |
/* |
This function is called when file objects are created as children of a scanner object. These files are created |
when when scans are requested by a remote client. |
*/ |
ICAError |
_ICD_ScannerGetObjectInfo( |
const ScannerObjectInfo* parentObjectInfo, |
UInt32 index, |
ScannerObjectInfo* newObjectInfo |
) |
{ |
Log("--> _ICD_ScannerGetObjectInfo\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
ICAObjectInfo parentICAObjectInfo; |
// Files are presented as children of the device object using a flat hierarchy. Therefore, the parentObjectInfo |
// should belong to the device object. The following code checks for this. |
require_action(parentObjectInfo, bail, err = paramErr); |
parentICAObjectInfo = parentObjectInfo->icaObjectInfo; |
require_action(parentICAObjectInfo.objectType == kICADevice, bail, err = paramErr); |
scanner = (VirtualScanner*)parentObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
if ( index >= scanner.numberOfScannedImages ) |
return kICAIndexOutOfRangeErr; |
switch ( parentICAObjectInfo.objectType ) |
{ |
case kICADevice: |
err = [scanner updateObjectInfo:newObjectInfo forScannedImageAtIndex:index]; |
break; |
default: |
err = paramErr; |
break; |
} |
bail: |
Log("<-- _ICD_ScannerGetObjectInfo\n"); |
return err; |
} |
//--------------------------------------------------------------------------------------------- _ICD_ScannerReadFileData |
/* |
This function is called to retrieve the image data of scanned image cached for remote client. |
*/ |
ICAError |
_ICD_ScannerReadFileData( |
const ScannerObjectInfo* objectInfo, |
UInt32 dataType, |
Ptr buffer, |
UInt32 offset, |
UInt32* length |
) |
{ |
Log("--> _ICD_ScannerReadFileData\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(objectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)objectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
[scanner readFileDataWithObjectInfo:objectInfo intoBuffer:(char*)buffer withOffset:offset andLength:length]; |
bail: |
Log("<-- _ICD_ScannerReadFileData\n"); |
return 0; |
} |
//---------------------------------------------------------------------------------------------- _ICD_ScannerSendMessage |
/* |
SendMessage is a powerful mechanism for supporting a lot of system and device specific features. |
Here we demonstrate a few common messages: |
- a message to delete a cached image object |
- a message to retrieve the last button pressed |
- a message to inform the current selection rectangle position on the overview scan |
*/ |
ICAError |
_ICD_ScannerSendMessage( |
const ScannerObjectInfo* objectInfo, |
ICD_ScannerObjectSendMessagePB* pb, |
ICDCompletion completion |
) |
{ |
Log("--> _ICD_ScannerSendMessage\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
ICAObjectInfo icaObjectInfo; |
require_action(objectInfo, bail, err = paramErr); |
icaObjectInfo = objectInfo->icaObjectInfo; |
scanner = (VirtualScanner*)objectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
switch ( pb->message.messageType ) |
{ |
case kICAMessageCameraDeleteOne: |
// ICADevices framework will send this message after successfully retrieving the object |
// data by calling _ICD_ScannerReadFileData(). |
err = [scanner removeObject:objectInfo]; |
break; |
case kICAMessageGetLastButtonPressed: |
{ |
// The scanner client sends this message to retrieve information about the last pressed button |
// on the scanner. Return the last button that was pressed on the device so that a custom work-flow |
// could be initiated based on this information. |
// check to make sure that the message received is for a device object |
require_action( icaObjectInfo.objectType == kICADevice, bail, err = paramErr ); |
// check to make sure message.dataPtr != nil, and message.dataSize == 4 |
require_action( pb->message.dataPtr && pb->message.dataSize == 4, bail, err = paramErr ); |
*((OSType*)(pb->message.dataPtr)) = scanner.lastButtonPressed; |
pb->message.dataType = scanner.lastButtonPressed; |
} |
break; |
case kICAMessageScannerOverviewSelectionChanged: |
{ |
/* |
The scanner client sends this message whenever the selection area is changed in the scanner UI. |
The device module could respond to this message using a notification that updates the image content of the |
selected area. For example, if the currently used functional unit is negative film scanner, then the |
device module could send a notification with "positive" image data for the selected area. |
If the user has selected a vendor-feature like dust-removal, then the device could send a dust-removed |
version of the image for the selected area. |
The image data sent through the notification should be an RGB buffer. |
The client application (e.g. the Image Capture application) uses this RGB buffer to update the |
selection area. |
The device module need not send any notification if there is nothing to do in response to this message. |
You get this message whenever the user changes the selection / target rectangle. We have a corresponding |
message where you can pass an 8 bit RGB buffer back. You do not have to handle this message. However, |
this is the mechanism where you could implement advanced UI interaction like converting from negative to |
positive, repairing scratch, etc. The client application ( e.g. the Image Capture application ) uses |
the RGB buffer you sent back to update the target rect in real time ( assuming that you have buffer the |
overview image data for processing so you can generate the RGB data for the update ). |
A pointer to NSMutableDictionary instance that contains the selection area is found at pb->message.dataPtr. |
Here is an example of this dictionary: (All the numbers are in pixels) |
{ |
ICAP_XRESOLUTION : { |
type. : TWON_ONEVALUE |
value : 150 |
} |
ICAP_YRESOLUTION : { |
type. : TWON_ONEVALUE |
value : 150 |
} |
height.......... : 208 |
offsetX......... : 59 |
offsetY......... : 92 |
width........... : 345 |
} |
If the device chose to respond to this message, then it should send this dictionary back along with three |
key-value pairs: |
key value |
--- ----- |
kICANotificationDataKey NSData* containing the RGB buffer for the selection area |
kICANotificationICAObjectKey device ICAObject |
kICANotificationTypeKey kICANotificationTypeScannerOverviewOverlayAvailable |
Here is an example of the notification dictionary: |
ICANotificationDictionaryKey : { |
ICANotificationDataKey........... : <NSData> |
ICANotificationICAObjectKey...... : <device ica object> |
ICANotificationTypeKey........... : kICANotificationTypeScannerOverviewOverlayAvailable |
ICAP_XRESOLUTION................. : { |
type. : TWON_ONEVALUE |
value : 150 |
} |
ICAP_YRESOLUTION................. : { |
type. : TWON_ONEVALUE |
value : 150 |
} |
height........................... : 208 |
offsetX.......................... : 59 |
offsetY.......................... : 92 |
width............................ : 345 |
} |
*/ |
// Below is an example of asking the scanner to send an inverted rectangle corresponding to the selection |
// Because we have also added a vendor specific button, the rectangle will only be updated if the box |
// is selected to enable inversion. |
NSMutableDictionary* paramDict = (NSMutableDictionary*)(pb->message.dataPtr); |
NSData* data = NULL; |
// Scanner module function that creates an RGB data object for the inverted rectangle that has been selected |
data = [scanner newInvertedRect: paramDict]; |
// Only if we actually got an updated value for the RGB buffer shall we send a notification update. If no |
// notification is received, the selection will appear unhandled. |
if( data ) |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithDictionary:paramDict]; |
[notification setObject:[NSNumber numberWithUnsignedInt:objectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:data forKey:(id)kICANotificationDataKey]; |
[notification setObject:(id)kICANotificationTypeScannerOverviewOverlayAvailable forKey:(id)kICANotificationTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
err = ICDSendNotification( ¬ePB ); |
[notification release]; |
[data release]; |
} |
} |
break; |
default: |
{ |
// Unknown message! |
err = paramErr; |
pb->result = err; |
} |
break; |
} |
bail: |
pb->header.err = err; |
#if ( SCANNER_DEBUG == 1 ) |
if ( err ) |
Log( " _ICD_ScannerSendMessage error %d occured.\n", err ); |
#endif |
if ( (err == noErr) && completion ) |
completion((ICDHeader*)pb); |
Log("<-- _ICD_ScannerSendMessage\n"); |
return err; |
} |
//------------------------------------------------------------------------------ _ICD_ScannerAddPropertiesToCFDictionary |
/* |
Add properties to dictionary for the device object. |
*/ |
ICAError |
_ICD_ScannerAddPropertiesToCFDictionary( |
ScannerObjectInfo* objectInfo, |
CFMutableDictionaryRef dict |
) |
{ |
Log("--> _ICD_ScannerAddPropertiesToCFDictionary\n"); |
ICAError err = paramErr; |
VirtualScanner* scanner = NULL; |
NSMutableDictionary* propDict = (NSMutableDictionary*)dict; |
require_action(objectInfo, bail, err = paramErr); |
if ( objectInfo->icaObjectInfo.objectType == kICADevice ) |
{ |
scanner = (VirtualScanner*)objectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
[scanner addPropertiesToDictionary:propDict]; |
err = noErr; |
} |
bail: |
Log("<-- _ICD_ScannerAddPropertiesToCFDictionary\n"); |
return err; |
} |
//---------------------------------------------------------------------------------------------- _ICD_ScannerOpenSession |
/* |
This gives a client exclusive access to the scanner. |
*/ |
ICAError |
_ICD_ScannerOpenSession( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerOpenSessionPB* pb |
) |
{ |
Log("--> _ICD_ScannerOpenSession\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
err = [scanner openSessionWithParams:pb]; |
bail: |
Log("<-- _ICD_ScannerOpenSession\n"); |
return err; |
} |
//--------------------------------------------------------------------------------------------- _ICD_ScannerCloseSession |
/* |
Remove exclusive access to the scanner. |
*/ |
ICAError |
_ICD_ScannerCloseSession( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerCloseSessionPB* pb |
) |
{ |
Log("--> _ICD_ScannerOpenSession\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
err = [scanner closeSessionWithParams:pb]; |
bail: |
Log("<-- _ICD_ScannerOpenSession\n"); |
return err; |
} |
//-------------------------------------------------------------------------------------------- _ICD_ScannerGetParameters |
/* |
_ICD_ScannerGetParameters returns the setting of our scanner and its current functional unit |
that is selected -- not a particular scan. We are using TWAIN keys in our dictionary whenever |
possible. |
*/ |
ICAError |
_ICD_ScannerGetParameters( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerGetParametersPB* pb |
) |
{ |
Log("--> _ICD_ScannerGetParameters\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
err = [scanner getSelectedFunctionalUnitParams:pb]; |
bail: |
Log("<-- _ICD_ScannerGetParameters\n"); |
return err; |
} |
//-------------------------------------------------------------------------------------------- _ICD_ScannerSetParameters |
/* |
_ICD_ScannerSetParameters sets up the scanner for a particular scan. The keys we passed are very |
different from the those passed in _ICD_ScannerGetParameters. We are using TWAIN keys in our dictionary |
whenever possible. |
Note that offsets are in native resolutions. |
*/ |
ICAError |
_ICD_ScannerSetParameters( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerSetParametersPB* pb |
) |
{ |
Log("--> _ICD_ScannerSetParameters\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*) deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
err = [scanner setSelectedFunctionalUnitParams:pb]; |
bail: |
Log("<-- _ICD_ScannerSetParameters\n"); |
return err; |
} |
//--------------------------------------------------------------------------------------------------- _ICD_ScannerStatus |
/* |
_ICD_ScannerStatus is not yet implemented in Image Capture Architecture. Therefore, this function will not |
be called. |
*/ |
ICAError |
_ICD_ScannerStatus( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerStatusPB* pb |
) |
{ |
ICAError err = noErr; |
Log("--> _ICD_ScannerStatus\n"); |
Log("<-- _ICD_ScannerStatus\n"); |
return err; |
} |
//---------------------------------------------------------------------------------------------------- _ICD_ScannerStart |
/* |
_ICD_ScannerStart issues a scan with the current settings and functional unit selected. |
*/ |
ICAError |
_ICD_ScannerStart( |
const ScannerObjectInfo* deviceObjectInfo, |
ICD_ScannerStartPB* pb |
) |
{ |
Log("--> _ICD_ScannerScannerStart\n"); |
ICAError err = noErr; |
VirtualScanner* scanner = NULL; |
require_action(deviceObjectInfo, bail, err = paramErr); |
scanner = (VirtualScanner*)deviceObjectInfo->privateData; |
require_action(scanner, bail, err = paramErr); |
err = [scanner startScanningWithParams:pb]; |
bail: |
Log("<-- _ICD_ScannerScannerStart\n"); |
return err; |
} |
//---------------------------------------------------------------------------------------------------------------------- |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-06-12