I am assisting another user in trying to access his microscope USB camera, and after first successes I am out of ideas:
The camera is a ToupTek ToupCam which comes with drivers and documentation we are trying a macOS version where the API is a dylib. The camera sends its replies via background threads, and yes I know these are not officially supported but there are workarounds that currently do work.
There is a hotPlug method which triggers a callback method each time a camera is connected or disconnected:
typedef void (*PTOUPCAM_HOTPLUG)(void* pCallbackCtx);
toupcam_ports(void) Toupcam_HotPlug(PTOUPCAM_HOTPLUG pHotPlugCallback, void* pCallbackCtx);
With an external method
Private Sub Toupcam_HotPlug(Callback as ptr, context as ptr)
and an instance method
Private Sub InsertHotPlug()
If Mid <> 0 Then
Dim id As UInteger = Me.ID
Toupcam_HotPlug AddressOf HotPlugCallBack, ptr(id)
InstanceDictionary.Value(Mid) = xojo.core.WeakRef.Create(Me)
xojo.core.timer.CallLater 0, WeakAddressOf Raiseconnected
End If
End Sub
I can register the shared method
Private Shared Sub HotPlugCallBack(context as Ptr)
#Pragma BackgroundTasks False
#Pragma StackOverflowChecking False
Dim instance As ToupCam.Camera = GetInstance(context)
If instance <> Nil Then
CallDelegateOnMainThreadMBS AddressOf instance.RaiseCamerasChanged
End If
End Sub
which uses
Private Shared Function GetInstance(context as ptr) as ToupCam.Camera
#Pragma BackgroundTasks False
#Pragma StackOverflowChecking False
Dim uid As UInt32 = Integer(context)
Dim w As Xojo.Core.WeakRef = InstanceDictionary.Lookup(uid, Nil)
If w <> Nil And w.Value <> Nil Then
return toupcam.Camera(w.Value)
End If
End Function
to retrieve the instance. I am using the user customisable ptr “context” to register the id of the camera which is an Unsigned in C terms.
The code above works nicely although using plugins is not advised when trying to cope with a background thread, but I hoped that this part of Christians plugins would be thread savvy (else it wouldnt make much sense) and it very much looks like that.
Now for the problem: There is another callback that is called when a variety of events occur which must be registered too:
typedef void (__stdcall* PTOUPCAM_EVENT_CALLBACK)(unsigned nEvent, void* pCallbackCtx);
toupcam_ports(HRESULT) Toupcam_StartPullModeWithCallback(HToupCam h, PTOUPCAM_EVENT_CALLBACK pEventCallback, void* pCallbackContext);
HResult is
#ifndef HRESULT
#define HRESULT int
#endif
Obviously, the differences are that the handle of the camera is passed to the StartPullMode method (the UInt32/unsigned I mentioned above), and that the callback method has two input parameters: an unsigned/UInt32 for the event definition and the custom ptr like above. And this method uses the stdCall calling convention. So here is the code:
Private Function Toupcam_StartPullModeWithCallback(id as uint32, callback as Ptr, context as ptr) as Int32
Public Function StartPullMode() as HResult
Dim id As UInteger = Me.ID
Return hresult(Toupcam_StartPullModeWithCallback (Mid, AddressOf EventCallBack, ptr(id)))
End Function
[code]Private Shared Sub EventCallBack(eventID as Uint32, context as Ptr)
#Pragma BackgroundTasks False
#Pragma StackOverflowChecking False
#Pragma X86CallingConvention StdCall
System.DebugLog “Event Callback start”
Dim instance As ToupCam.Camera = GetInstance(context)
If instance <> Nil Then
System.DebugLog “Instance <> Nil”
System.DebugLog "eventid: "+eventid.ToText
Select Case eventID
Case 1 // TOUPCAM_EVENT_EXPOSURE
CallDelegateOnMainThreadMBS addressof instance.RaiseexposureChanged
Case 2 // TOUPCAM_EVENT_TEMPTINT
CallDelegateOnMainThreadMBS AddressOf instance.RaiseTempTintModeChanged
Case 3 // TOUPCAM_EVENT_CHROME
System.DebugLog "Chrome eventID"
Case 4 // TOUPCAM_EVENT_IMAGE
CallDelegateOnMainThreadMBS AddressOf instance.RaiseImageArrived
Case 5 // TOUPCAM_EVENT_STILLIMAGE
CallDelegateOnMainThreadMBS AddressOf instance.RaiseStillimageArrived
Case 6 // TOUPCAM_EVENT_WBGAIN
CallDelegateOnMainThreadMBS AddressOf instance.RaiseRBGGainChanged
Case 128 // TOUPCAM_EVENT_ERROR
CallDelegateOnMainThreadMBS AddressOf instance.RaiseError
Case 129 // TOUPCAM_EVENT_DISCONNECTED
CallDelegateOnMainThreadMBS AddressOf instance.RaiseDisconnected
Else
System.DebugLog "Unknown eventID"
End Select
Else
System.DebugLog “Instance is Nil”
End If
End Sub[/code]
But calling StartPullMode always results in a segmentation error, the “Event Callback start” does not appear in the debug log.
Any ideas what I have wrong (except for trying to catch a background thread in Xojo at all)?
Sorry for the length of this entry, and thank you for reading it!