Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use void* in function signature? #380

Closed
hiaselhans opened this issue Aug 20, 2021 · 6 comments
Closed

use void* in function signature? #380

hiaselhans opened this issue Aug 20, 2021 · 6 comments

Comments

@hiaselhans
Copy link

Hello, thanks for the good library!

i want to make a delegate for an rfcomm connection. When i implement the corresponding function i end up with segfaults even when i'm doing nothing in the function call:

https://developer.apple.com/documentation/iobluetooth/iobluetoothrfcommchanneldelegate/1431822-rfcommchanneldata?language=objc

def rfcommChannelData_data_length_(self, channel, data, length):
    return

is there a decorator to transform a data-pointer with length to a buffer type? or similar?

thanks!

@ronaldoussoren
Copy link
Owner

The best way to accomplish this is through the metadata system, there is a decorator that can be used to change the default method signature but that interface is not rich enough in this case. The crash your getting is because the bridge by default assumes that all arguments are Objective-C objects (id).

With decorator:

class MyDelegate (NSObject):
    @objc.typedSelector(b"v@:@^vQ")
    def rfcommChannelData_data_length_(self, channel, data, length):
           pass

The problem with this approach is that the data argument will be passed in as an integer because the bridge doesn't know it is actually a buffer.

To fix that, drop the decorator and register some metadata before defining the class:

objc.registerMetaDataForSelector(
    b"NSObject",   # Name of the class, I use NSObject for selectors from protocols to automatically do the right thing
    b"rfcommChannelData:data:length:",   # Selector name
    {
       "arguments": {
         3: { "type": "b"n^v", "c_array_length_in_arg": 4 },  # 0, 1 are implicit arguments, 2 is ``channel``, 3 is ``data``
         4: { "type": b"Q" } # ``length``
    }
)

Note: I wrote the code fragments above directly in the browser, there may be typos.

BTW. I might implement full bindings for the. IOBluetooth framework in the future, although that depends on how much work that ends up being.

@hiaselhans
Copy link
Author

wow, thats responsive! and did the job :) thanks a lot!

now, another quick question:

i need to call writeSync

func writeSync(_ data: UnsafeMutableRawPointer!, 
        length: UInt16) -> IOReturn

i thought i need to make a pointer with objc.context, is that correct?

  def synchronouslyWriteData_toRFCOMMChannel_(self, data, channel):
      #     return [channel writeSync:(void *)[data bytes] length:[data length]];
      pointer =  objc.context.register(data)
      result = channel.writeSync_length_(pointer, len(data))

      objc.context.unregister(pointer)

      return result

here, data is a bytes object and the call is unfortionately not writing the right data.

@ronaldoussoren
Copy link
Owner

You don't want to use objc.context, that's a basic registry for passing "context info" pointers.

In this case you want to do something similar to the registration for the protocol method, but with slightly less information because PyObjC can extract the basic type information from the ObjC runtime.

objc.registerMetaDataForSelector(
    b"IOBluetoothRFCOMMChannel", 
    b"writeSync:length:",   # Selector name
    {
       "arguments": {
         2: { "type_modifier": "b"n", "c_array_length_in_arg": 4 },
       }
     }
)

You can also use objc._C_IN instead of b"n", that's pobably slightly clearer.

@hiaselhans
Copy link
Author

ok thanks!

still i seem to not have it right. I am cleaning up my fork of pybluez to get rid of objc-libraries included in the package:

https://github.com/hiaselhans/pybluez2/blob/master/bluetooth/macos/btsocket/delegate.py

i figured length: UInt16 being a half argument i had to use _C_USHT but that didn't help either.
i am getting a timeout error on the remote device so i guess not all / wrong data is sent

@hiaselhans
Copy link
Author

and actually data is of type <objective-c class _NSInlineData at 0x7fff8a9f12d0>

@hiaselhans
Copy link
Author

got it!

thanks a lot for all the help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants