The only quirk remaining is that setting the scan area doesn't seem to have any effect: the resulting image MyScan.png is always a whole page although for testing I even changed the measurement unit to the (for me) more familiar centimeters and a smaller area...
Code: Select all
#ICDeviceLocationTypeMaskLocal = $00000100
#ICDeviceLocationTypeMaskRemote = $0000FE00
#ICDeviceTypeMaskScanner = $00000002
#ICDeviceTypeScanner = $00000002
ImportC ""
class_addMethod(Class.i, Selector.i, *Callback, Types.P-ASCII)
class_createInstance(Class.i, ExtraBytes.i)
class_addProtocol(Class.i, Protocol.i)
objc_allocateClassPair(ModelClass.i, NewClassName.P-ASCII, ExtraBytes.i)
objc_lookUpClass(ClassName.P-ASCII)
object_setClass(ObjectToModify.i, NewClass.i)
objc_registerClassPair(NewClass.i)
objc_getProtocol(ProtocolName.P-ASCII)
sel_registerName(MethodName.P-ASCII)
EndImport
ImportC "/System/Library/Frameworks/ImageCaptureCore.framework/ImageCaptureCore"
EndImport
ImportC "/System/Library/Frameworks/Quartz.framework/Quartz"
EndImport
Enumeration
#MainWindow
#scannerList
#scanButton
EndEnumeration
Global Dim scanners.i(0)
Global.i delegateClass, activeScanner, selectedScanner, scanning, selectedFunctionalUnit
Procedure scan()
Debug "initiating scan..."
scanning = 1
CocoaMessage(0, activeScanner, "requestScan")
EndProcedure
ProcedureC deviceDidBecomeReady(object.i, selector.i, device.i)
Protected FunctionalUnitArray.I
Protected NumFunctionalUnits.I
Debug "selected device is ready..."
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/Shardik/Scans/")
scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"MyScan")
; CocoaMessage(0, device, "requestSelectFunctionalUnit:", 0) ;0 for flatbed type
CocoaMessage(0, device, "requestSelectFunctionalUnit:", 3) ;3 for document feeder type
CocoaMessage(0, activeScanner, "setTransferMode:", 0) ;0 for file-mode
CocoaMessage(0, activeScanner, "setDownloadsDirectory:", scanFolder)
CocoaMessage(0, activeScanner, "setDocumentName:$", @"MyScan")
CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"public.png")
FunctionalUnitArray = CocoaMessage(0, device, "availableFunctionalUnitTypes")
If FunctionalUnitArray
NumFunctionalUnits = CocoaMessage(0, FunctionalUnitArray, "count")
If NumFunctionalUnits
Debug "Functional unit types:"
For i = 0 To NumFunctionalUnits - 1
Select CocoaMessage(0, CocoaMessage(0, FunctionalUnitArray,
"objectAtIndex:", i), "integerValue")
Case 0
Debug "- Flatbed"
Case 1
Debug "- Positive transparency"
Case 2
Debug "- Negative transparency"
Case 3
Debug "- Document feeder"
EndSelect
Next i
EndIf
EndIf
EndProcedure
ProcedureC didAddDevice(object.i, selector.i, deviceBrowser.i, addedDevice.i, moreComing.i)
Debug "device found and added..."
deviceType.i = CocoaMessage(0, addedDevice, "type")
deviceName.s = PeekS(CocoaMessage(0, CocoaMessage(0, addedDevice, "name"), "UTF8String"), -1, #PB_UTF8)
If moreComing = #NO And (deviceType & #ICDeviceTypeMaskScanner) = #ICDeviceTypeScanner
newIndex = ArraySize(scanners()) + 1
ReDim scanners(newIndex)
scanners(newIndex) = addedDevice
AddGadgetItem(#scannerList, -1, deviceName)
CocoaMessage(0, addedDevice, "setDelegate:",
class_createInstance(delegateClass, 0)) ;PureBasic app controls the scanner
EndIf
EndProcedure
ProcedureC didRemoveDevice(object.i, selector.i, removedDevice.i)
Debug "device removed"
EndProcedure
ProcedureC didRemoveDeviceInBrowser(object.i, selector.i, deviceBrowser.i, removedDevice.i, moreGoing.i)
Debug "device removed in browser..."
CocoaMessage(0, removedDevice, "requestCloseSession")
CocoaMessage(0, removedDevice, "setDelegate:", 0)
RemoveGadgetItem(#scannerList, selectedScanner - 1)
ReDim scanners(ArraySize(scanners()) - 1)
If activeScanner = removedDevice
activeScanner = 0
EndIf
EndProcedure
ProcedureC didOpenSessionWithError(object.i, selector.i, device.i, error.i)
Debug "session opened... errors: " + Str(error)
EndProcedure
ProcedureC didCloseSessionWithError(object.i, selector.i, device.i, error.i)
Debug "session closed... errors: " + Str(error)
EndProcedure
ProcedureC didSelectFunctionalUnit(object.i, selector.i, scannerDevice.i, functionalUnit.i, error.i)
Protected scanArea.NSRect
If error = 0
If functionalUnit <> 0
Debug "functional unit selected: " + functionalUnit
CocoaMessage(0, functionalUnit, "setMeasurementUnit:", 0)
Debug "MeasurementUnit set to Inches"
With scanArea
\origin\x = 0
\origin\y = 0
\size\width = 10
\size\height = 10
EndWith
CocoaMessage(0, functionalUnit, "setScanArea:@", @scanArea)
CocoaMessage(@scanArea, functionalUnit, "scanArea")
Debug "scanArea set to (" + Str(scanArea\origin\x) + "," +
Str(scanArea\origin\y) + ")-(" +
Str(scanArea\origin\x + scanArea\size\width) + "," +
Str(scanArea\origin\y + scanArea\size\height) + ")"
EndIf
Else
Debug "functional unit selected... errors: " + Str(error)
EndIf
EndProcedure
ProcedureC didReceiveStatusInformation(object.i, selector.i, device.i, StatusDictionary.i)
Debug "receiving status info..."
If StatusDictionary
Debug PeekS(CocoaMessage(0, CocoaMessage(0, StatusDictionary,
"valueForKey:$", @"ICLocalizedStatusNotificationKey"), "UTF8String"),
-1, #PB_UTF8)
EndIf
EndProcedure
ProcedureC didScanToURL(object.i, selector.i, scannerDevice.i, URL.i, *data)
Debug "scanned to " +
PeekS(CocoaMessage(0, CocoaMessage(0, URL, "absoluteString"), "UTF8String"),
-1, #PB_UTF8)
scanning = 0
EndProcedure
ProcedureC didCompleteScanWithError(object.i, selector.i, scannerDevice.i, error.i)
Debug "scan completed... errors: " + Str(error)
scanning = 0
EndProcedure
ProcedureC didEncounterError(object.i, selector.i, device.i, error.i)
Debug "encountered error: " + Str(error)
scanning = 0
EndProcedure
wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(#MainWindow, 100, 100, 800, 500, "Scanner Application", wFlags)
ListViewGadget(#scannerList, 0, 0, 200, 500)
ButtonGadget(#scanButton, 220, 10, 100, 30, "SCAN")
SetGadgetColor(#scannerList, #PB_Gadget_BackColor, RGB(200, 200, 255))
deviceBrowser = CocoaMessage(0, 0, "ICDeviceBrowser new")
delegateClass = objc_allocateClassPair(objc_lookUpClass("NSObject"), "PB_Delegate", 0) ;PureBasic app class
class_addMethod(delegateClass,
sel_registerName("didRemoveDevice:"), @didRemoveDevice(), "v@:@")
class_addMethod(delegateClass,
sel_registerName("deviceDidBecomeReady:"), @deviceDidBecomeReady(), "v@:@")
class_addMethod(delegateClass,
sel_registerName("device:didEncounterError:"), @didEncounterError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didOpenSessionWithError:"), @didOpenSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didCloseSessionWithError:"), @didCloseSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didReceiveStatusInformation:"), @didReceiveStatusInformation(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("deviceBrowser:didAddDevice:moreComing:"), @didAddDevice(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("deviceBrowser:didRemoveDevice:moreGoing:"), @didRemoveDeviceInBrowser(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didScanToURL:data:"), @didScanToURL(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didCompleteScanWithError:"), @didCompleteScanWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didSelectFunctionalUnit:error:"), @didSelectFunctionalUnit(), "v@:@@@")
objc_registerClassPair(delegateClass)
CocoaMessage(0, deviceBrowser, "setDelegate:", class_createInstance(delegateClass, 0)) ;PureBasic app controls the browser
CocoaMessage(0, deviceBrowser, "setBrowsedDeviceTypeMask:", #ICDeviceLocationTypeMaskLocal |
#ICDeviceLocationTypeMaskRemote |
#ICDeviceTypeMaskScanner)
CocoaMessage(0, deviceBrowser, "start")
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
appQuit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case #scanButton
scan()
Case #scannerList
Select EventType()
Case #PB_EventType_LeftDoubleClick
selectedScanner = GetGadgetState(#scannerList) + 1
activeScanner = scanners(selectedScanner)
CocoaMessage(0, activeScanner, "requestOpenSession")
EndSelect
EndSelect
EndSelect
Until appQuit = 1
If activeScanner
CocoaMessage(0, activeScanner, "release")
EndIf
CocoaMessage(0, deviceBrowser, "stop")
; CocoaMessage(0, deviceBrowser, "release")
Update 2: I have modified the deviceDidBecomeReady() callback to also display the available functional unit types.
Update 3: To prevent an invalid memory access (IMA) when closing the program after the scanner's power button had already been switched off, I have added
Code: Select all
If activeScanner = removedDevice
activeScanner = 0
EndIf
Code: Select all
CocoaMessage(0, activeScanner, "release")
Code: Select all
If activeScanner
CocoaMessage(0, activeScanner, "release")
EndIf