Image Scanning on OS-X
Re: Image Scanning on OS-X
Sorry, but although having worked the whole weekend on that problem, unfortunately I haven't been successful...
My above code example is working like a charm on Snow Leopard, but won't work on Lion, Mountain Lion or Mavericks. On the latter three only an empty ScannerView is displayed.
In contrast to that an Objective-C code example in Xcode with no code but only the 2 views IKDeviceBrowserView and IKScannerDeviceView in a window is working fine on all 4 MacOS versions...
At first I thought that it might be a problem of the introduction of constraints in window layout beginning with Lion, but setting setTranslatesAutoresizingMaskIntoConstraints to #YES in both IKDeviceBrowserView and IKScannerDeviceView as described here didn't help (this setting is already the default setting). And changing several other contraints settings didn't help neither. Also compiling to an app on Snow Leopard and starting this app on Mountain Lion or Mavericks didn't work.
My second attempt was to try out different delegates with their respective callbacks for ICDeviceBrowser, IKDeviceBrowserView and IKScannerDeviceView which didn't work neither. I found out that the callback established for deviceBrowser:didAddDevice:moreComing: was always working fine on all 4 MacOS versions but the callback for deviceBrowser:didRemoveDevice:moreGoing: was only called on SnowLeopard (when closing the app), but never on Lion, Mountain Lion and Mavericks...
By the way I found out that for detecting my scanner Brother ADS-2100, it was essential that at least one printer had been connected once and its drivers had been installed. Otherwise the scanner was not detected...
Sorry, TI-994A, for not being able to present the good news you were waiting for.
My above code example is working like a charm on Snow Leopard, but won't work on Lion, Mountain Lion or Mavericks. On the latter three only an empty ScannerView is displayed.
In contrast to that an Objective-C code example in Xcode with no code but only the 2 views IKDeviceBrowserView and IKScannerDeviceView in a window is working fine on all 4 MacOS versions...
At first I thought that it might be a problem of the introduction of constraints in window layout beginning with Lion, but setting setTranslatesAutoresizingMaskIntoConstraints to #YES in both IKDeviceBrowserView and IKScannerDeviceView as described here didn't help (this setting is already the default setting). And changing several other contraints settings didn't help neither. Also compiling to an app on Snow Leopard and starting this app on Mountain Lion or Mavericks didn't work.
My second attempt was to try out different delegates with their respective callbacks for ICDeviceBrowser, IKDeviceBrowserView and IKScannerDeviceView which didn't work neither. I found out that the callback established for deviceBrowser:didAddDevice:moreComing: was always working fine on all 4 MacOS versions but the callback for deviceBrowser:didRemoveDevice:moreGoing: was only called on SnowLeopard (when closing the app), but never on Lion, Mountain Lion and Mavericks...
By the way I found out that for detecting my scanner Brother ADS-2100, it was essential that at least one printer had been connected once and its drivers had been installed. Otherwise the scanner was not detected...
Sorry, TI-994A, for not being able to present the good news you were waiting for.
Re: Image Scanning on OS-X
Hello Shardik, and thank you for your tireless help - I truly do appreciate it.
I find it strange that there's so little on the topic, almost like no one uses scanners on Macs.
Anyway, for the time being, my Xcode version of the scanner browser would have to suffice, called with PureBasic's RunProgram() function; at least I get title customisation, although it would be even better if I could set the scan destination and file name as well.
You mentioned trying out different delegates: I know that delegate protocols are added with the class_addMethod API function, but how do you set the delegates themselves? And how do you set multiple delegates? For example the ICScannerDeviceDelegate and IKScannerDeviceViewDelegate? Could you help me with a small code illustration?
I'll still be chipping away at this, and will keep you updated if there's any progress.
Once again, thank you Shardik.
I find it strange that there's so little on the topic, almost like no one uses scanners on Macs.
Anyway, for the time being, my Xcode version of the scanner browser would have to suffice, called with PureBasic's RunProgram() function; at least I get title customisation, although it would be even better if I could set the scan destination and file name as well.
You mentioned trying out different delegates: I know that delegate protocols are added with the class_addMethod API function, but how do you set the delegates themselves? And how do you set multiple delegates? For example the ICScannerDeviceDelegate and IKScannerDeviceViewDelegate? Could you help me with a small code illustration?
I'll still be chipping away at this, and will keep you updated if there's any progress.
Once again, thank you Shardik.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
Hi Shardik. I've been working on the code this past week, and I've gotten some progress. Instead of using the ImageKit browsers and views, I've tried to implement the more native functions of the ImageCaptureCore library. That way, the application is not relegated to using the built-in UI, and could perhaps get a little more control.
So far, I've been able to set the various delegate methods up, and the application is responding to devices being added and removed, and the devices are responding to open session requests through the proper protocol methods.
However, when a requestScan function is invoked, no actual scan is performed, although the proper response is received through the didCompleteScanWithError() method.
Here's the code:
I was hoping that you could take a look at it and see where I've gone wrong. I'm sure that it must be littered with syntactical errors, although I wouldn't know even if it hit me on the head. I've also not been able to set the measurement area or scan area of the functional unit, as it keeps throwing an Object is nil error.
If anyone else might have any ideas as well, I'd appreciate your help. It would be a pretty useful utility to have in the PureBasic toolbox; if we could get it to work.
Thanks!
So far, I've been able to set the various delegate methods up, and the application is responding to devices being added and removed, and the devices are responding to open session requests through the proper protocol methods.
However, when a requestScan function is invoked, no actual scan is performed, although the proper response is received through the didCompleteScanWithError() method.
Here's the code:
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
Procedure scan()
Debug "initiating scan..."
scanning = 1
CocoaMessage(0, activeScanner, "requestScan")
EndProcedure
ProcedureC deviceDidBecomeReady(object.i, selector.i, device.i)
Debug "selected device is ready..."
Protected scanArea.NSRect
With scanArea
\origin\x = 0
\origin\y = 0
\size\width = 10
\size\height = 10
EndWith
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/myFolder")
scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"myFile")
CocoaMessage(0, device, "requestSelectFunctionalUnit:", 0) ;0 for flatbed type
CocoaMessage(0, activeScanner, "setTransferMode:", 0) ;0 for file-mode
CocoaMessage(0, activeScanner, "setDownloadsDirectory:", scanFolder)
CocoaMessage(0, activeScanner, "setDocumentName:$", @"MyScan")
CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"kUTTypePNG")
CocoaMessage(0, CocoaMessage(0, activeScanner,
"selectedFunctionalUnit"), "setMeasurementUnit:", 0)
CocoaMessage(0, CocoaMessage(0, activeScanner,
"selectedFunctionalUnit"), "setScanArea:@", @scanArea)
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, deviceBrowser.i, removedDevice.i, moreGoing.i)
Debug "device removed..."
CocoaMessage(0, removedDevice, "requestCloseSession")
CocoaMessage(0, removedDevice, "setDelegate:", 0)
RemoveGadgetItem(#scannerList, selectedScanner - 1)
ReDim scanners(ArraySize(scanners()) - 1)
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)
Debug "functional unit selected... errors: " + Str(error)
EndProcedure
ProcedureC didReceiveStatusInformation(object.i, selector.i, device.i, status.i)
Debug "receiving status info..."
EndProcedure
ProcedureC didScanToURL(object.i, selector.i, scannerDevice.i, URL.i)
Debug "scanned to file: "+ PeekS(URL, -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("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:"), @didRemoveDevice(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didScanToURL:"), @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
CocoaMessage(0, activeScanner, "release")
CocoaMessage(0, deviceBrowser, "stop")
CocoaMessage(0, deviceBrowser, "release")
If anyone else might have any ideas as well, I'd appreciate your help. It would be a pretty useful utility to have in the PureBasic toolbox; if we could get it to work.
Thanks!
Last edited by TI-994A on Thu Apr 30, 2015 7:16 am, edited 1 time in total.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
TI-994A,
unfortunately I don't have a scanner anymore for testing (only two scanners which aren't recognized neither by Apple's image capture app nor by ICDeviceBrowser), so I am "blind" again. It would be helpful if you could post your debug log, so that we are able to see which callbacks are called. Is "requestOpenSession" executed when selecting your scanner in #scannerList? If this is the case, your callback didOpenSessionWithError() should be called (even if no error occurred) otherwise the delegate isn't set correctly:
unfortunately I don't have a scanner anymore for testing (only two scanners which aren't recognized neither by Apple's image capture app nor by ICDeviceBrowser), so I am "blind" again. It would be helpful if you could post your debug log, so that we are able to see which callbacks are called. Is "requestOpenSession" executed when selecting your scanner in #scannerList? If this is the case, your callback didOpenSessionWithError() should be called (even if no error occurred) otherwise the delegate isn't set correctly:
ICDevice.h wrote:@method requestOpenSession:
@abstract This message requests to open a session on the device. A client MUST open a session on a device in order to use the device.
@discussion Make sure the receiver's delegate is set prior to sending this message; otherwise this message will be ignored. This request is completed when the delegate receives a "device:didOpenSessionWithError:" message. No more messages will be sent to the delegate if this request fails.
Re: Image Scanning on OS-X
Hi Shardik. Thank you for your reply, and my apologies for my late reply; I was away on project for a few days and I didn't have access to my dev systems.Shardik wrote:...It would be helpful if you could post your debug log, so that we are able to see which callbacks are called...
Anyway, as you had requested, here are the output logs for the code that I had posted. As you can see, the method responses correspond to the API requirements, with each called in succession, in the proper sequence.
Hope you might have some insight.debug wrote:on connecting a scanner:
1. device found and added...
on selecting the device from the list gadget:
2. session opened...
3. selected device is ready...
4. functional unit selected... errors: 0
5. functional unit selected... errors: 0
on clicking the SCAN button:
6. initiating scan...
7. encountered error: 429xxxxxxx
8. scan completed... errors: 429xxxxxxx
on disconnecting the scanner:
9. device removed...
on re-connecting the scanner:
10. device found and added...
Further to this, did you find any implementation errors in my code? I'm not very familiar with the CocoaMessage() function, and as such am not sure if I've called half of the functions correctly.
In particular are the scanner's setDownloadsDirectory, setDocumentName, and setDocumentUTI functions - is the file name and download directory set correctly, I mean using the fileURLWithPath and stringWithString types? And is the kUTTypePNG an actual string identifier or is it meant to be a constant?
As for the selected functional unit's setMeasurementUnit and setScanArea functions - is that the correct method to reference the selected functional unit?
Code: Select all
CocoaMessage(0, CocoaMessage(0, activeScanner, "selectedFunctionalUnit"), "setScanArea:@", @scanArea)
Thank you Shardik.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
It's a NSString constant "public.png" so that line should beTI-994A wrote:And is the kUTTypePNG an actual string identifier or is it meant to be a constant?
Code: Select all
CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"public.png")
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: Image Scanning on OS-X
Hi wilbert, and thank you for your quick reply. I'll give that a try.wilbert wrote:It's a NSString constant "public.png" so that line should beCode: Select all
CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"public.png")
Would you also know if the fileURLWithPath and stringWithString types are being used correctly to reference the file name and path? Is the current implementation correct:
Code: Select all
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/myFile")
scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"myFile")
Thank you.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
To set the scanFolder correctly you have to use a trailing slash. These declarations are working for me:TI-994A wrote:Would you also know if the fileURLWithPath and stringWithString types are being used correctly to reference the file name and path? Is the current implementation correct:Are the file separators [/] placed correctly and in the right places?Code: Select all
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/myFile") scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"myFile")
Code: Select all
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/Shardik/Scans/")
scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"MyScan")
My first test of your code example on Snow Leopard displayed the following debug and error messages:
- The first error message after selecting the functional unit originated from your selection of a flatbed type scanner. My Brother ADS-2100 is a document feeder type, so I had to change 0 to 3 (the structure ICScannerFunctionalUnitType is defined in ICScannerFunctionalUnits.h).Snow Leopard:
-------------
After starting the program:
- device found and added...
After double-click onto scanner Brother ADS-2100:
- session opened... errors: 0
- selected device is ready...
- functional unit selected... errors: 4322294496
- functional unit selected... errors: 0
After clicking onto "Scan":
- initiating scan...
- receiving status info...
- receiving status info...
- scan completed... errors: 0
After closing the program by clicking onto the red dot:
- [ERROR] ScanDemo.PB (Line: 181)
- [ERROR] -[PB_Delegate didRemoveDevice:]: unrecognized selector sent to instance 0x101a0b5d0
- The second error on exiting the program caused a crash because you defined the callback for deviceBrowser:didRemoveDevice:moreGoing: but not the callback for didRemoveDevice:. After defining the missing callback your code example closed gracefully calling first didRemoveDevice: and afterwards deviceBrowser:didRemoveDevice:moreGoing: (on Snow Leopard).
- A third bug was the missing data: parameter in scannerDevice:didScanToURL:data: which resulted in not calling that callback.
- I have changed the PNG definiton as wilbert had proposed
There are still some quirks though (on which I am still working):
- On Mavericks the last line (release of the device browser) has to be commented out because otherwise the program will terminate with the error "Program terminated (by an external library)"
- On both Snow Leopard and Mavericks the lines with setMeasurementUnit: and setScanArea: throw a warning with "Object is nil."
But I absolutely wanted to let you know that at last we are nearly through...
This is your modified code example which runs on Snow Leopard and Mavericks and saves a complete scanned page as MyScan.png in /Users/Shardik/Scans on my iMac:
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
Procedure scan()
Debug "initiating scan..."
scanning = 1
CocoaMessage(0, activeScanner, "requestScan")
EndProcedure
ProcedureC deviceDidBecomeReady(object.i, selector.i, device.i)
Debug "selected device is ready..."
Protected scanArea.NSRect
With scanArea
\origin\x = 0
\origin\y = 0
\size\width = 10
\size\height = 10
EndWith
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")
CocoaMessage(0, CocoaMessage(0, activeScanner,
"selectedFunctionalUnit"), "setMeasurementUnit:", 0)
CocoaMessage(0, CocoaMessage(0, activeScanner,
"selectedFunctionalUnit"), "setScanArea:@", @scanArea)
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)
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)
Debug "functional unit selected... errors: " + Str(error)
EndProcedure
ProcedureC didReceiveStatusInformation(object.i, selector.i, device.i, status.i)
Debug "receiving status info..."
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
CocoaMessage(0, activeScanner, "release")
CocoaMessage(0, deviceBrowser, "stop")
; CocoaMessage(0, deviceBrowser, "release")
Re: Image Scanning on OS-X
Thank you Shardik - YOU DA MAN!.
It never occurred to me that the issue was with the functional unit all the while. It didn't work when left on default, and it didn't work when the flatbed unit (functional unit 0) was selected. But for some reason, scanning through the document feeder (functional unit 3) works, and so does selecting functional units 2 or 3 (transparency-type units).
Now, with the above settings, the program scans and saves the image to the specified file perfectly, and all is well with the world!
To solve this functional-unit issue, I thought it best to retrieve the available functional units from the device itself, and use the results to make the selection. I tried translating this Objective-C code into PureBasic, but it seems to be returning some arbitrary numbers that don't work:
Code: Select all
NSArray *availableFuncUnits = scanner.availableFunctionalUnitTypes;
Code: Select all
availableFuncUnits = CocoaMessage(0, 0, activeScanner, "availableFunctionalUnitTypes")
availableFuncUnitsSize = CocoaMessage(0, availableFuncUnits, "count")
While i < availableFuncUnitsSize
funcUnit = CocoaMessage(0, availableFuncUnits, "objectAtIndex:", i)
Debug funcUnit
i + 1
Wend
I also tried getting the status information from the didReceiveStatusInformation() method like this, but was unsuccessful:
Code: Select all
statusMsg.s = PeekS(status, -1, #PB_UTF8)
And finally, I am still unable to figure out how to format the setMeasurementUnit and setScanArea settings for the functional unit. As you said, it keeps returning an Object is nil error.
If you could kindly steer me in the right direction on these issues, that would pretty much wrap things up here (I hope!)
Once again, my sincere thanks for all your help.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
In the meantime I have also solved the errors with setting measurement unit and scan area ("Object is nil."). The setting of measurement unit and scan area had to be moved to the callback didSelectFunctionalUnit(). Now the modified code example below works like a charm on both Snow Leopard and Mavericks.
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...
Update 1: I have modified the didReceiveStatusInformation() callback to also display the status which is contained in the status dictionary passed as a parameter to this callback.
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 addedin procedure didRemoveDeviceInBrowser() and changedtoat the end of the code example. I have tested this code successfully with PB 5.44 x86 in both ASCII and Unicode mode on MacOS 10.6.8 (Snow Leopard) and with PB 5.44 x86 and x64 in both ASCII and Unicode mode on MacOS 10.7.5 (Lion).
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
Last edited by Shardik on Wed Dec 28, 2016 9:30 pm, edited 5 times in total.
Re: Image Scanning on OS-X
You're a true genius. But more importantly, how did you figure that out? I didn't find that anywhere in the documentation or in any of the tonnes of examples that I had pored over.Shardik wrote:...I have also solved the errors with setting measurement unit and scan area ("Object is nil."). The setting of measurement unit and scan area had to be moved to the callback didSelectFunctionalUnit()...
No; it works! I've tested it with both inches and centimetres settings, and it scans precisely the given area. Amazing!Shardik wrote: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...
The reason that you're not getting it is because the Brother ADS-2100 that you're using is a document-feeder type, which ignores the scan area setting.
This is wonderful! We're really getting this thing to work.
I bow to you, GURU.
But I still await updates on reading the availableFunctionalUnitTypes property array, as well as the status message returned by the didReceiveStatusInformation() method.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
TI-994A,
thank you very much for your kind words. But unfortunately you are massively exaggerating about my capabilities. All PB Mac programmers can't be grateful enough to Wilbert who is a true genius, a Cocoa framework magician and assembler guru. Without Wilbert's incredible work and Fred's help on the former oMsg() library which finally became the native CocoaMessage() function and the multitude of Wilbert's excellent code examples my help for your scanning program would not have been possible. And I bet that if Wilbert would have had a proper scanner at home, he would have had worked out a scanning solution for you in minutes while I needed many hours...
Of course it's technically not possible to use a sheet feeder type scanner for scanning only a given area...
thank you very much for your kind words. But unfortunately you are massively exaggerating about my capabilities. All PB Mac programmers can't be grateful enough to Wilbert who is a true genius, a Cocoa framework magician and assembler guru. Without Wilbert's incredible work and Fred's help on the former oMsg() library which finally became the native CocoaMessage() function and the multitude of Wilbert's excellent code examples my help for your scanning program would not have been possible. And I bet that if Wilbert would have had a proper scanner at home, he would have had worked out a scanning solution for you in minutes while I needed many hours...
That's another proof of my silliness...TI-994A wrote:The reason that you're not getting it is because the Brother ADS-2100 that you're using is a document-feeder type, which ignores the scan area setting.
Of course it's technically not possible to use a sheet feeder type scanner for scanning only a given area...
I have modified the didReceiveStatusInformation() callback in my last code posting to also display the status contained in the status dictionary and the deviceDidBecomeReady() callback to report the available functional unit types.TI-994A wrote:But I still await updates on reading the availableFunctionalUnitTypes property array, as well as the status message returned by the didReceiveStatusInformation() method.
Re: Image Scanning on OS-X
SHARDIK! YOU DID IT MAN!
But you went out of your way to help me, and to borrow a quote from John McClane (Bruce Willis, Die Hard 4.0), "That's what makes you that guy."
Bottom-line: I can't thank you enough. Danke Vielmals!
Just FYI, in the didSelectFunctionalUnit() method, I was able to get the selected functional unit type with this CocoaMessage():
I truly believe that this is one for the PureBasic books, and warrants an addition into the Mac OSX Tricks 'n' Tips section. Please do the honours, Shardik; this is definitely your baby.
Thanks again, and hope you have a wonderful weekend. I know I will!
Yes; I too have studied his many code examples intently, and yours too, and would not have imagined to approach this topic without them. Like you said, if only he had a scanner.Shardik wrote:...you are massively exaggerating about my capabilities. All PB Mac programmers can't be grateful enough to Wilbert who is a true genius ... and the multitude of Wilbert's excellent code examples...
But you went out of your way to help me, and to borrow a quote from John McClane (Bruce Willis, Die Hard 4.0), "That's what makes you that guy."
Bottom-line: I can't thank you enough. Danke Vielmals!
Just FYI, in the didSelectFunctionalUnit() method, I was able to get the selected functional unit type with this CocoaMessage():
Code: Select all
CocoaMessage(0, functionalUnit, "type")
It should also be noted that all my tests were done on Mac OSX Lion v10.7.5. So, it clearly works on Snow Leopard, Lion, and MavericksShardik wrote:...the modified code example below works like a charm on both Snow Leopard and Mavericks.
I truly believe that this is one for the PureBasic books, and warrants an addition into the Mac OSX Tricks 'n' Tips section. Please do the honours, Shardik; this is definitely your baby.
Thanks again, and hope you have a wonderful weekend. I know I will!
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel
Re: Image Scanning on OS-X
I added a link to the source in the first post of that threadTI-994A wrote:I truly believe that this is one for the PureBasic books, and warrants an addition into the Mac OSX Tricks 'n' Tips section.
It's indeed a very nice contribution
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: Image Scanning on OS-X
Hi wilbert. I just saw the new link in the Mac OSX Tricks 'n' Tips section; a very nice contribution indeed. Thank you.wilbert wrote:I added a link to the source in the first post of that thread
It's indeed a very nice contribution
And like Shardik said, it wouldn't have been possible without your contributions. I'd have to say that the techniques implemented in this example were most certainly derived from the many API examples that you, and the other OSX gurus, had posted in these forums over the years. Truly educational stuff.
So, dank u wel.
I just realised that it took 36 days to get this thing off the ground. But it is well worth every minute!
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel