rhx / SwiftGtk

A Swift wrapper around gtk-3.x and gtk-4.x that is largely auto-generated from gobject-introspection
https://rhx.github.io/SwiftGtk/
BSD 2-Clause "Simplified" License
317 stars 26 forks source link

Cannot call some methods from AboutDialogProtocol (GTK 3) using Array of Strings as a parameter #61

Open jasaldivara opened 1 week ago

jasaldivara commented 1 week ago

I want to call some AboutDialogProtocol methods like set(artists:) and set(authors:) Both methods have declared a parameter of the type UnsafeMutablePointer<UnsafePointer<gchar>?>! but I currently cannot call them from Swift.

On the GTK C API documentation those parameters appear as NULL-terminated array of strings (That is a pointer to an array of pointers to char, where the last element is a NULL pointer):

gtk_about_dialog_set_artists (GtkAboutDialog *about,
                              const gchar **artists);

If I call the method with an array of Strings, it doesn't compile:

about.set(artists: [
    "Catalog Icons from Twemoji",
    "Desktop Icons from <a href='https://icons8.com/'>Icons8</a>",
    "Other Icons by Jesús Abelardo Saldívar Aguilar"
    ])
error: cannot convert value of type '[String]' to expected argument type 'UnsafeMutablePointer<UnsafePointer<gchar>?>?' (aka 'Optional<UnsafeMutablePointer<Optional<UnsafePointer<Int8>>>>')
        about.set(artists: [
                           ^

I have tried a snippet from Swift Forums for similar situations (Converting a Swift Array of Strings to a C Array of Strings) with minor modifications, but haven't been successful. The code compiles, but crashes on run time, possibly because the last element of the array is not null/nil (In the online example, the Vulkan API does not require the last element to be NULL):

https://forums.swift.org/t/create-c-string-array-from-swift/31149/2

extension Collection {
    /// Creates an UnsafeMutableBufferPointer with enough space to hold the elements of self.
    public func unsafeCopy() -> UnsafeMutableBufferPointer<Self.Element> {
        let copy = UnsafeMutableBufferPointer<Self.Element>.allocate(capacity: self.count)
        _ = copy.initialize(from: self)
        return copy
    }
}
extension String {
    /// Create UnsafeMutableBufferPointer holding a null-terminated UTF8 copy of the string
    public func unsafeUTF8Copy() -> UnsafeMutableBufferPointer<gchar> { self.utf8CString.unsafeCopy() }
}
...
let about = AboutDialog();
let artistsCredits = [
    "Iconos del catálogo por <a href='https://github.com/twitter/twemoji'>Twemoji</a>",
    "Otros iconos por Jesús Abelardo Saldívar Aguilar",
    "Iconos de escritorio por <a href='https://icons8.com/'>Icons8</a>"
    ].map({ UnsafePointer($0.unsafeUTF8Copy().baseAddress) }).unsafeCopy().baseAddress
about.set(artists: artistsCredits)
'swift run ChickenPOS' terminated by signal SIGSEGV (Address boundary error)

I haven't yet figured how to add a nil last element.

I think SwiftGtk functions or methods accepting UnsafeMutablePointer<UnsafePointer<gchar>?>! as a parameter sould instead accept an array of strings [String] to make them easier to use.

I'm currently running this on Fedora 40 aarch64, Swift 5.8.1