Vala プログラミング

GNUstep プログラミング

おなが@京都先端科学大

Camlkit & GNUstep

Cocoa frameworksのOCaml bindingsに、Camlkitがあります。
今回このCamlkitを試してみました。

[ANN] Camlkit
https://discuss.ocaml.org/t/ann-camlkit-macos-ios-gnustep-toolkit-for-ocaml/14722
Camlkit project page
https://github.com/dboris/camlkit
camlkit-examples
https://github.com/dboris/camlkit-examples

macOS example ( camlkit-examples/2-default-app )

2-default-appフォルダ内で dune exec ./main.exe を実行
Info.plistが要求されるので、b-document-app からコピーし、
MSMainNibFileは空にする。

GNUstep example on GhostBSD


CamlkitのOverviewを見ると、FoundationとAppKitはGNUstep
でも使えそうなので、試してみました。
CamlkitはLinuxにはインストールできないので、FreeBSD系のGhostBSDを
使用します。
dune プロジェクトを以下の様に作成します。

test (プロジェクト名)
   test.opam (そのまま)
   dune-project (そのまま)
   /bin
      dune
      nain.ml
   /lib (使用しない)
   /test (使用しない)

dune

(executable
  (public_name test)
  (name main)
  ;(flags :standard -ccopt "-framework AppKit")
  ; for FreeBSD
  (flags :standard -ccopt -L/usr/local/GNUstep/System/Library/Libraries  -cclib -lgnustep-gui)
  (libraries camlkit-base.foundation camlkit-gui.appkit camlkit.camlkit))

main.ml

(* camlkit app
   create Window + create Menu
*)
open Foundation
open Appkit
open Runtime

let _NSWindowStyleMaskTitled = 1
let _NSWindowStyleMaskClosable = 2
let _NSBackingStoreBuffered = 2

let create_menu () =
	let main_menu = _new_ NSMenu._class_
	and app_menu = _new_ NSMenu._class_
	in
	main_menu
		|> NSMenu.setSubmenu app_menu
			~forItem: (
				main_menu
      			|> NSMenu.addItemWithTitle (new_string "2-default-app")
        		~action: (to_selector nil)
        		~keyEquivalent: (new_string "")
    		);

    app_menu
  		|> NSMenu.addItemWithTitle (new_string "Quit")
    		~action: (selector "terminate:")
    		~keyEquivalent: (new_string "q")
  		|> ignore;

  		main_menu
;;

let create_window () =
	let w = 300. and h = 200. in
  		let win =
    		alloc NSWindow._class_
    			|> NSWindow.initWithContentRect
      				(CGRect.make ~x: 0. ~y: 0. ~width: w ~height: h)
      				~styleMask: (combine_options [
        				_NSWindowStyleMaskTitled; _NSWindowStyleMaskClosable])
      				~backing: _NSBackingStoreBuffered
      				~defer: false
      	in
  		win |> NSWindow.setTitle (new_string "2-default-app");
  		win
;;

let main () =
	let app = NSApplication._class_ |> NSApplication.C.sharedApplication in
	let menu = create_menu () in
		app |> NSApplication.setMainMenu menu;
	let win = create_window () in
       win |> NSWindow.cascadeTopLeftFromPoint (CGPoint.make ~x: 150. ~y: 850.)
              |> ignore;
       win |> NSWindow.makeKeyAndOrderFront nil;

	NSApplication.run app
;;

let () = main ()


GhostBSD
ライブラリ等のインストール
opam, GNUstep: Software Stationからインストール
dune: opam install duneでインストール
 clangが見つからないというエラーが出る。(clangのファイルはclang15
 になっている。)
 devtools (os-generic-userland-devtools)をインストールする。
camlkit: opam install camlkitでインストール
 途中にpkgconfのインストールを問われるので、インストールする。

Swift Vulkan & GNUStepSwiftBridge (2)

前回につづき、Swift Vulkan & GNUStepSwiftBridgeについての記事です。
今回は、Vulkanウィンドウを表示しながらGNUstepウィンドウやメニューの操作が
出来るようにしました。

[ 実行結果 ]

(起動時の状態から、NSWindow上のボタンとメニューを操作しているところ)

Vulkanウィンドウ(glfw window)は、NSWindowに貼り付けています。
Vulkan表示のプログラムは、DispatchQueue.global.asyncを使って
非同期処理をしています。

[プログラム]
ディレクトリ配置、ライブラリの利用は前回同様です。

1 VulkanウィンドウのNSWindowへの貼付け
VulkanWindow.swift

// The Swift Programming Language

import AppKit
import ObjCSwiftInterop

import CGLFW
import CVulkan
import Vulkan

public class VulkanWindow: NSWindow {
    var glfwWindow: OpaquePointer!
    var instance: Instance? = nil
    var surface: SurfaceKHR? = nil

    public override init(_ rect: CGRect) {
        super.init()

        /// GLFW
        glfwInit()
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API)
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE)
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)
        
        glfwWindow = glfwCreateWindow(Int32(rect.width), Int32(rect.height), "GLFW Window", nil, nil)
        glfwSetWindowPos(glfwWindow, Int32(rect.x), Int32(rect.y))
        glfwShowWindow(glfwWindow)
	var xwindow = glfwGetX11Window(glfwWindow)

        let nsWindowClass =  objc_getClass("NSWindow")
        var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
	allocatedObject = forSwift_objcSendMessage1(&allocatedObject!.pointee, sel_registerName("initWithWindowRef:"), &xwindow)

	self._nsobjptr = allocatedObject
	self.setTitle(NSString(string: "Untitled Window"))
    }

    public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
        super.init(nsobjptr: nsobjptr)
    }

    public func getInstance() -> Instance {
        var count: UInt32 = 0
        let extensionsPtr = glfwGetRequiredInstanceExtensions(&count)
        var extensions = UnsafeBufferPointer(start: extensionsPtr, count: Int(count)).map{ String(cString: $0!) }
        var layers: [String] = []
        
	let createInfo = InstanceCreateInfo(
            flags: [],
            applicationInfo: nil,
            enabledLayerNames: layers,
            enabledExtensionNames: extensions
        )

	try createInfo.withCStruct { ptr_createInfo in
            var out: VkInstance!
            vkCreateInstance(ptr_createInfo, nil, &out)
	    self.instance = Instance(rawInstance: out)
        }

        return self.instance!
    }

    public func getSurface() -> SurfaceKHR {
        var cSurface: VkSurfaceKHR!
        glfwCreateWindowSurface(instance!.pointer, self.glfwWindow, nil, &cSurface)
        self.surface = SurfaceKHR(pointer: cSurface, instance: self.instance!)

        return self.surface!
    }
}

Vulkan表示のglfwウィンドウをNSWindowに貼り付けているのが、
以下の箇所です。
glfwWindowからxwindowを取得し、 NSWindowに貼り付けます。

var xwindow = glfwGetX11Window(glfwWindow)

let nsWindowClass =  objc_getClass("NSWindow")
var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
allocatedObject = forSwift_objcSendMessage1(&allocatedObject!.pointee, sel_registerName("initWithWindowRef:"), &xwindow)

また、Vulkan InstanceとSurfaceの生成も行なっています。

2 GUIとVulkanウィンドウの表示
AppDelegate.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import Foundation
import AppKit

let AppWindowRect = AppKit.CGRect(x: 250, y: 100, width: 500, height: 500)

let fileManager = FileManager.default

class AppDelegate: NSApplicationDelegate {
    lazy var window = NSWindow(CGRect(x: 0, y: 500, width: 200, height: 200))
    lazy var view = UIView()
    lazy var button  = NSButton()
    lazy var button2 = NSButton()
    lazy var button3 = NSButton()
    lazy var vulkanWindow = VulkanWindow(AppWindowRect)

    override func applicationDidFinishLaunching(notification: Any?) {
        window.orderFront(sender: self)
        window.setTitle(NSString(string: "Vulkan Control"))

        button.setTitle(NSString(string: "ClearColor"))
	button.frame = .init(x: 30, y: 10, width: 140, height: 32)
	button2.setTitle(NSString(string: "Triangle"))
	button2.frame = .init(x: 30, y: 50, width: 140, height: 32)
	button3.setTitle(NSString(string: "UniformBuffer"))
	button3.frame = .init(x: 30, y: 90, width: 140, height: 32)

	view.frame = .init(x: 0, y: 0, width: 300, height: 300)
	view.addSubview(button)
	view.addSubview(button2)
	view.addSubview(button3)
	window.setContentView(view)

	vulkanWindow.orderFront(sender: self)
	vulkanWindow.setTitle(NSString(string: "Vulkan Window"))

	let instance = vulkanWindow.getInstance()
	let surface = vulkanWindow.getSurface()

	let appClear = AppClearColor(instance: instance, surface: surface)
	let appTri = AppTriangle(instance: instance, surface: surface)
	let appUniform = AppUniformBuffer(instance: instance, surface: surface)

	button.onAction = { button in
	    if appTri.isRun {
	        appTri.stop()
	    }
	    if appUniform.isRun {
	        appUniform.stop()
	    }
	    if !appClear.isRun {
	        DispatchQueue.global(qos: .userInteractive).async {
	            print("task appClear start")
	            appClear.run()
	            print("task appClear end")
	        }
	    }
	}

	button2.onAction = { button in
	    if appClear.isRun {
	        appClear.stop()
	    }
	    if appUniform.isRun {
	        appUniform.stop()
	    }
	    if !appTri.isRun {
	        DispatchQueue.global(qos: .userInteractive).async {
	            print("task appTri start")
	            appTri.run()
	            print("task appTri end")
	        }
	    }
        }

	button3.onAction = { button in
	    if appClear.isRun {
	        appClear.stop()
	    }
	    if appTri.isRun {
	        appTri.stop()
	    }
	    if !appUniform.isRun {
	        DispatchQueue.global(qos: .userInteractive).async {
	            print("task appUniform start")
	            appUniform.run()
	            print("task appUniform end")
	        }
	    }
	}

	DispatchQueue.global(qos: .userInteractive).async {
	    appClear.run()
	    print("init appCear end")
	}
    }
}

Vulkanのwindow、instance、surfaceは共通で利用するので、その生成部分はAppClearColor、AppTriangle、AppUniformBufferの各プログラムから削除します。

AppTriangleの例

// The Swift Programming Language

import CGLFW
import Vulkan

struct Vector2 {
    let x: Float
    let y: Float
}

struct Vector3 {
    let x: Float
    let y: Float
    let z: Float
}

struct Vertex {
    let position: Vector2
    let color: Vector3
}

class AppTriangle {
    let windowSize = Extent2D(width: UInt32(AppWindowRect.width), height: UInt32(AppWindowRect.height))
    var isRun: Bool

    var instance: Instance!
    var surface: SurfaceKHR!

    public init(instance: Instance, surface: SurfaceKHR) {
        self.instance = instance
        self.surface = surface
        self.isRun = false

        print("App initialized")
    }

    /// A Vulkan physical device.
    var physicalDevice: PhysicalDevice!
    /// The index of a graphics queue family.
    var graphicsFamilyIndex: UInt32!
    /// The index of a presentation queue family.
    var presentationFamilyIndex: UInt32!
    /// Selects a suitable physical device, as well as the graphics and presentation queue families.
    func selectPhysicalDevice() throws {
        let physicalDevices = try instance.getPhysicalDevices()
        
        for physicalDevice in physicalDevices {
            /// Device must support the VK_KHR_swapchain extension
            let extensions = try physicalDevice.getDeviceExtensionProperties(layerName: nil)
            guard extensions.contains(where: { $0.extensionName == VK_KHR_SWAPCHAIN_EXTENSION_NAME }) else {
                continue
            }
            
            /// Device must support the b8g8r8a8Srgb / srgbNonLinear surface format.
            let surfaceFormats = try physicalDevice.getSurfaceFormatsKHR(surface: surface)
            guard surfaceFormats.contains(where: { $0.format == .b8g8r8a8Srgb && $0.colorSpace == .srgbNonlinear }) else {
                continue
            }
            
            /// Device must have a graphics queue family.
            let queueFamilies = physicalDevice.getQueueFamilyProperties()
            guard let graphicsFamilyIndex = queueFamilies.firstIndex(where: { $0.queueFlags.contains(.graphics) }) else {
                continue
            }
            
            /// Device must have a presentation queue family that is compatible with the surface.
            let queueFamilyIndices = 0..<UInt32(queueFamilies.count)
            guard let presentationFamilyIndex = try queueFamilyIndices.first(where: {
                try physicalDevice.getSurfaceSupportKHR(queueFamilyIndex: $0, surface: surface)
            }) else {
                continue
            }
            
            self.graphicsFamilyIndex = UInt32(graphicsFamilyIndex)
            self.presentationFamilyIndex = presentationFamilyIndex
            self.physicalDevice = physicalDevice
            break
        }
        
        guard physicalDevice != nil else {
            throw AppError(description: "Could not find a suitable device.")
        }
    }

    /// A Vulkan device.
    var device: Device!
    /// A Vulkan queue that supports graphics commands.
    var graphicsQueue: Queue!
    /// A Vulkan queue that supports presentation commands.
    var presentationQueue: Queue!
    /// Creates a logical device and retrieves its queues.
    func createDevice() throws {
        let families: Set = [graphicsFamilyIndex!, presentationFamilyIndex!]
        
        let queueInfos = families.map {
            DeviceQueueCreateInfo(
                flags: [],
                queueFamilyIndex: UInt32($0),
                queuePriorities: [1.0]
            )
        }
        
        device = try physicalDevice.createDevice(
            createInfo: .init(
                flags: [],
                queueCreateInfos: queueInfos,
                enabledLayerNames: [],
                enabledExtensionNames: [VK_KHR_SWAPCHAIN_EXTENSION_NAME],
                enabledFeatures: nil
            )
        )
        
        graphicsQueue = device.getQueue(queueFamilyIndex: graphicsFamilyIndex, queueIndex: 0)
        presentationQueue = device.getQueue(queueFamilyIndex: presentationFamilyIndex, queueIndex: 0)
    }

    /// A Vulkan swapchain.
    var swapchain: SwapchainKHR!
    /// The size of the swapchain images.
    var swapchainExtent: Extent2D!
    /// An array of Vulkan swapchain images.
    var swapchainImages: [Image] = []
    /// Creates a swapchain and retrieves its images.
    func createSwapchain() throws {
        let capabilities = try physicalDevice.getSurfaceCapabilitiesKHR(surface: surface)
        
        var imageCount = capabilities.minImageCount + 1
        if capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount {
            imageCount = capabilities.maxImageCount
        }
        
        if capabilities.currentExtent.width != UInt32.max {
            swapchainExtent = capabilities.currentExtent
        } else {
            let width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, windowSize.width))
            let height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, windowSize.height))
            swapchainExtent = Extent2D(width: width, height: height)
        }
        
        swapchain = try device.createSwapchainKHR(
            createInfo: .init(
                flags: [],
                surface: surface,
                minImageCount: imageCount,
                imageFormat: .b8g8r8a8Srgb,
                imageColorSpace: .srgbNonlinear,
                imageExtent: swapchainExtent,
                imageArrayLayers: 1,
                imageUsage: .colorAttachment,
                imageSharingMode: graphicsFamilyIndex == presentationFamilyIndex ? .exclusive : .concurrent,
                queueFamilyIndices: [graphicsFamilyIndex, presentationFamilyIndex],
                preTransform: capabilities.currentTransform,
                compositeAlpha: .opaque,
                presentMode: .fifo,
                clipped: true,
                oldSwapchain: nil
            )
        )
        
        swapchainImages = try swapchain.getImagesKHR()
    }
    
    /// An array of Vulkan image views.
    var imageViews: [ImageView] = []
    /// Creates an image view for each of the swapchain images.
    func createImageViews() throws {
        for image in swapchainImages {
            let imageView = try device.createImageView(
                createInfo: .init(
                    flags: [],
                    image: image,
                    viewType: .type2d,
                    format: .b8g8r8a8Srgb,
                    components: .init(r: .r, g: .g, b: .b, a: .a),
                    subresourceRange: .init(
                        aspectMask: .color,
                        baseMipLevel: 0,
                        levelCount: 1,
                        baseArrayLayer: 0,
                        layerCount: 1
                    )
                )
            )
            imageViews.append(imageView)
        }
    }
    
    /// A Vulkan render pass.
    var renderPass: RenderPass!
    /// Creates a Vulkan render pass.
    func createRenderPass() throws {
        let colorAttachment = AttachmentDescription(
            flags: [],
            format: .b8g8r8a8Srgb,
            samples: .type1,
            loadOp: .clear,
            storeOp: .store,
            stencilLoadOp: .dontCare,
            stencilStoreOp: .dontCare,
            initialLayout: .undefined,
            finalLayout: .presentSrcKHR
        )
        
        let colorAttachmentRef = AttachmentReference(attachment: 0, layout: .colorAttachmentOptimal)
        
        let subpass = SubpassDescription(
            flags: [],
            pipelineBindPoint: .graphics,
            inputAttachments: [],
            colorAttachments: [colorAttachmentRef],
            resolveAttachments: nil,
            depthStencilAttachment: nil,
            preserveAttachments: []
        )
        
        let dependency = SubpassDependency(
            srcSubpass: VK_SUBPASS_EXTERNAL,
            dstSubpass: 0,
            srcStageMask: .colorAttachmentOutput,
            dstStageMask: .colorAttachmentOutput,
            srcAccessMask: [],
            dstAccessMask: .colorAttachmentWrite,
            dependencyFlags: []
        )
        
        renderPass = try device.createRenderPass(
            createInfo: .init(
                flags: [],
                attachments: [colorAttachment],
                subpasses: [subpass],
                dependencies: [dependency]
            )
        )
    }
    
    /// Creates a Vulkan shader module from the source file at the given path.
    func createShaderModule(path: String) throws -> ShaderModule {
        guard let buffer = readFile(path: path) else {
            throw AppError(description: "Failed to read file: \(path)")
        }
        
        return try buffer.withUnsafeBytes { bytes in
            try device.createShaderModule(
                createInfo: .init(
                    flags: [],
                    codeSize: buffer.count,
                    code: bytes.bindMemory(to: UInt32.self).baseAddress!
                )
            )
        }
    }
    
    /// A Vulkan pipeline layout.
    var pipelineLayout: PipelineLayout!
    /// A Vulkan graphics pipeline.
    var pipeline: Pipeline!
    /// Creates a Vulkan graphics pipeline.
    func createGraphicsPipeline() throws {
        let vertexShader = try createShaderModule(path: "./Sources/Main/shaders/vert.spv")
        let fragmentShader = try createShaderModule(path: "./Sources/Main/shaders/frag.spv")
        defer {
            vertexShader.destroy()
            fragmentShader.destroy()
        }
        
        let vertexStage = PipelineShaderStageCreateInfo(
            flags: [],
            stage: .vertex,
            module: vertexShader,
            name: "main",
            specializationInfo: nil
        )
        
        let fragmentStage = PipelineShaderStageCreateInfo(
            flags: [],
            stage: .fragment,
            module: fragmentShader,
            name: "main",
            specializationInfo: nil
        )
        
        let vertexBinding = VertexInputBindingDescription(
            binding: 0,
            stride: UInt32(MemoryLayout<Vertex>.stride),
            inputRate: .vertex
        )
        
        let positionAttribute = VertexInputAttributeDescription(
            location: 0,
            binding: 0,
            format: .r32g32Sfloat,
            offset: UInt32(MemoryLayout.offset(of: \Vertex.position)!)
        )
        
        let colorAttribute = VertexInputAttributeDescription(
            location: 1,
            binding: 0,
            format: .r32g32b32Sfloat,
            offset: UInt32(MemoryLayout.offset(of: \Vertex.color)!)
        )
        
        let vertexInputState = PipelineVertexInputStateCreateInfo(
            flags: [],
            vertexBindingDescriptions: [vertexBinding],
            vertexAttributeDescriptions: [positionAttribute, colorAttribute]
        )
        
        let inputAssemblyState = PipelineInputAssemblyStateCreateInfo(
            flags: [],
            topology: .triangleList,
            primitiveRestartEnable: false
        )
        
        let viewport = Viewport(
            x: 0,
            y: 0,
            width: Float(swapchainExtent.width),
            height: Float(swapchainExtent.height),
            minDepth: 0,
            maxDepth: 1
        )
        
        let scissor = Rect2D(offset: .init(x: 0, y: 0), extent: swapchainExtent)
        
        let viewportState = PipelineViewportStateCreateInfo(
            flags: [],
            viewports: [viewport],
            scissors: [scissor]
        )
        
        let rasterizationState = PipelineRasterizationStateCreateInfo(
            flags: [],
            depthClampEnable: false,
            rasterizerDiscardEnable: false,
            polygonMode: .fill,
            cullMode: .back,
            frontFace: .clockwise,
            depthBiasEnable: false,
            depthBiasConstantFactor: 0,
            depthBiasClamp: 0,
            depthBiasSlopeFactor: 0,
            lineWidth: 1
        )
        
        let multisampleState = PipelineMultisampleStateCreateInfo(
            flags: [],
            rasterizationSamples: .type1,
            sampleShadingEnable: false,
            minSampleShading: 0,
            sampleMask: nil,
            alphaToCoverageEnable: false,
            alphaToOneEnable: false
        )
        
        let colorBlendAttachmentState = PipelineColorBlendAttachmentState(
            blendEnable: false,
            srcColorBlendFactor: .zero,
            dstColorBlendFactor: .zero,
            colorBlendOp: .add,
            srcAlphaBlendFactor: .zero,
            dstAlphaBlendFactor: .zero,
            alphaBlendOp: .add,
            colorWriteMask: [.r, .g, .b, .a]
        )
        
        let colorBlendState = PipelineColorBlendStateCreateInfo(
            flags: [],
            logicOpEnable: false,
            logicOp: .copy,
            attachments: [colorBlendAttachmentState],
            blendConstants: (0, 0, 0, 0)
        )
        
        pipelineLayout = try device.createPipelineLayout(
            createInfo: .init(
                flags: [],
                setLayouts: [],
                pushConstantRanges: []
            )
        )
        
        let pipelineInfo = GraphicsPipelineCreateInfo(
            flags: [],
            stages: [vertexStage, fragmentStage],
            vertexInputState: vertexInputState,
            inputAssemblyState: inputAssemblyState,
            tessellationState: nil,
            viewportState: viewportState,
            rasterizationState: rasterizationState,
            multisampleState: multisampleState,
            depthStencilState: nil,
            colorBlendState: colorBlendState,
            dynamicState: nil,
            layout: pipelineLayout,
            renderPass: renderPass,
            subpass: 0,
            basePipelineHandle: nil,
            basePipelineIndex: 0
        )
        
        pipeline = try device.createGraphicsPipelines(
            pipelineCache: nil,
            createInfos: [pipelineInfo]
        )[0]
    }
    
    /// An array of Vulkan framebuffers.
    var framebuffers: [Framebuffer] = []
    /// Creates a Vulkan framebuffer for each of the swapchain image views.
    func createFramebuffers() throws {
        for imageView in imageViews {
            let framebuffer = try device.createFramebuffer(
                createInfo: .init(
                    flags: [],
                    renderPass: renderPass,
                    attachments: [imageView],
                    width: swapchainExtent.width,
                    height: swapchainExtent.height,
                    layers: 1
                )
            )
            framebuffers.append(framebuffer)
        }
    }
    
    /// Creates a Vulkan buffer and allocates memory for it, with the given properties.
    func createBuffer(size: VkDeviceSize, usage: BufferUsageFlags, properties: MemoryPropertyFlags) throws -> (Buffer, DeviceMemory) {
        let buffer = try device.createBuffer(
            createInfo: .init(
                flags: [],
                size: size,
                usage: usage,
                sharingMode: .exclusive,
                queueFamilyIndices: []
            )
        )
        
        let memoryRequirements = buffer.getMemoryRequirements()
        let memoryProperties = physicalDevice.getMemoryProperties()
        
        guard let memoryType = memoryProperties.memoryTypes.enumerated().first(where: { index, type in
            (memoryRequirements.memoryTypeBits & (1 << index) != 0) &&
                type.propertyFlags.contains(properties)
        }) else {
            throw AppError(description: "Could not find suitable memory type.")
        }
        
        let memory = try device.allocateMemory(
            allocateInfo: .init(
                allocationSize: memoryRequirements.size,
                memoryTypeIndex: UInt32(memoryType.offset)
            )
        )
        
        try buffer.bindMemory(memory: memory, memoryOffset: 0)
        
        return (buffer, memory)
    }
    
    /// An array of vertices that describe a triangle.
    let vertices = [
        Vertex(position: .init(x: 0, y: -0.5), color: .init(x: 1, y: 0, z: 0)),
        Vertex(position: .init(x: 0.5, y: 0.5), color: .init(x: 0, y: 1, z: 0)),
        Vertex(position: .init(x: -0.5, y: 0.5), color: .init(x: 0, y: 0, z: 1))
    ]
    
    /// The size, in bytes, of the vertices.
    var bufferSize: VkDeviceSize!
    /// A Vulkan buffer used to stage the vertices before transfering them to the GPU.
    var stagingBuffer: Buffer!
    /// Host visible memory that is bound to the staging buffer.
    var stagingBufferMemory: DeviceMemory!
    /// A Vulkan buffer used to store the vertices that will be accessed by the GPU.
    var vertexBuffer: Buffer!
    /// Device local memory that is bound to the vertex buffer.
    var vertexBufferMemory: DeviceMemory!
    /// Creates the staging and vertex buffers.
    func createVertexBuffer() throws {
        bufferSize = VkDeviceSize(MemoryLayout<Vertex>.stride * vertices.count)
        
        (stagingBuffer, stagingBufferMemory) = try createBuffer(size: bufferSize, usage: .transferSrc, properties: [.hostVisible, .hostCoherent])
        
        (vertexBuffer, vertexBufferMemory) = try createBuffer(size: bufferSize, usage: [.vertexBuffer, .transferDst], properties: .deviceLocal)
    }

    /// A Vulkan command pool.
    var commandPool: CommandPool!
    /// Creates a command pool.
    func createCommandPool() throws {
        commandPool = try device.createCommandPool(
            createInfo: .init(
                flags: [.resetCommandBuffer],
                queueFamilyIndex: graphicsFamilyIndex
            )
        )
    }
    
    /// Copies the vertices to the staging buffer, then transfers the contents of the staging buffer to the vertex buffer.
    func copyVertexBuffer() throws {
        let ptr = try stagingBufferMemory.mapMemory(offset: 0, size: bufferSize, flags: [])
        vertices.withUnsafeBytes { bytes in
            ptr.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
        }
        stagingBufferMemory.unmapMemory()
        
        let commandBuffer = try device.allocateCommandBuffers(
            allocateInfo: .init(
                commandPool: commandPool,
                level: .primary,
                commandBufferCount: 1
            )
        )[0]
        defer {
            commandPool.freeCommandBuffers(commandBuffers: [commandBuffer])
        }
        
        try commandBuffer.begin(
            beginInfo: .init(
                flags: .oneTimeSubmit,
                inheritanceInfo: nil
            )
        )
        
        let copyRegion = BufferCopy(srcOffset: 0, dstOffset: 0, size: bufferSize)
        commandBuffer.cmdCopyBuffer(srcBuffer: stagingBuffer, dstBuffer: vertexBuffer, regions: [copyRegion])
        
        try commandBuffer.end()
        
        let submitInfo = SubmitInfo(
            waitSemaphores: [],
            waitDstStageMask: [],
            commandBuffers: [commandBuffer],
            signalSemaphores: []
        )
        try graphicsQueue.submit(submits: [submitInfo], fence: nil)
        try graphicsQueue.waitIdle()
    }

    /// A Vulkan command buffer.
    var commandBuffers: [CommandBuffer] = []
    
    /// Allocates a command buffer for each of the swapchain images.
    func createCommandBuffers() throws {
        commandBuffers = try device.allocateCommandBuffers(
            allocateInfo: .init(
                commandPool: commandPool,
                level: .primary,
                commandBufferCount: UInt32(swapchainImages.count)
            )
        )
    }
    
    /// Records a set of commands for each command buffer.
    func recordCommandBuffers() throws {
        for (commandBuffer, framebuffer) in zip(commandBuffers, framebuffers) {
            try commandBuffer.begin(beginInfo: .init(flags: [], inheritanceInfo: nil))
            
            let clearColor = VkClearValue(color: .init(float32: (0, 0, 0, 1)))
            commandBuffer.cmdBeginRenderPass(
                renderPassBegin: .init(
                    renderPass: renderPass,
                    framebuffer: framebuffer,
                    renderArea: .init(offset: .init(x: 0, y: 0), extent: swapchainExtent),
                    clearValues: [clearColor]),
                contents: .inline
            )
            
            commandBuffer.cmdBindPipeline(pipelineBindPoint: .graphics, pipeline: pipeline)
            commandBuffer.cmdBindVertexBuffers(firstBinding: 0, buffers: [vertexBuffer], offsets: [0])
            commandBuffer.cmdDraw(vertexCount: UInt32(vertices.count), instanceCount: 1, firstVertex: 0, firstInstance: 0)
            
            commandBuffer.cmdEndRenderPass()
            
            try commandBuffer.end()
        }
    }

    /// A semaphore to signal when an image is available for rendering.
    var imageAvailable: Semaphore!
    /// A semaphore to signal when rendering is complete.
    var renderComplete: Semaphore!
    /// Creates any objects required for synchronization.
    func createSync() throws {
        imageAvailable = try device.createSemaphore(createInfo: .init(flags: []))
        renderComplete = try device.createSemaphore(createInfo: .init(flags: []))
    }

    /// Aquires an image from the swapchain, submits the associated command buffer, and finally presents the image.
    func drawFrame() throws {
        let index = try swapchain.acquireNextImageKHR(timeout: UInt64.max, semaphore: imageAvailable, fence: nil)
        
        let graphicsSubmit = SubmitInfo(
            waitSemaphores: [imageAvailable],
            waitDstStageMask: [.colorAttachmentOutput],
            commandBuffers: [commandBuffers[Int(index)]],
            signalSemaphores: [renderComplete]
        )
        try graphicsQueue.submit(submits: [graphicsSubmit], fence: nil)
        
        try presentationQueue.presentKHR(
            presentInfo: .init(
                waitSemaphores: [renderComplete],
                swapchains: [swapchain],
                imageIndices: [index],
                results: nil
            )
        )
        
        /// Note: Don't use this in the real world. It is not an efficient method of synchronization.
        try presentationQueue.waitIdle()
    }

    func mainLoop() throws {
        while self.isRun {
            //glfwPollEvents()
            try drawFrame()
        }
        try device.waitIdle()
    }

    func run() {
        do {
            print("appTri run")
            self.isRun = true

            try selectPhysicalDevice()
            try createDevice()
            try createSwapchain()
            try createImageViews()
            try createRenderPass()
            try createGraphicsPipeline()
            try createFramebuffers()
            try createVertexBuffer()
            try createCommandPool()
            try copyVertexBuffer()
            try createCommandBuffers()
            try recordCommandBuffers()
            try createSync()
            try mainLoop()
        } catch let e as Result {
            print("Encountered Vulkan error: \(e)")
        } catch let e {
            print(e)
        }
    }

    func stop() {
        self.isRun = false
    }

    /// Cleans up any resources that were created.
    deinit {
        imageAvailable?.destroy()
        renderComplete?.destroy()
        commandPool?.destroy()
        vertexBuffer?.destroy()
        vertexBufferMemory?.freeMemory()
        stagingBuffer?.destroy()
        stagingBufferMemory?.freeMemory()
        for framebuffer in framebuffers { framebuffer.destroy() }
        pipeline?.destroy()
        pipelineLayout?.destroy()
        renderPass?.destroy()
        for imageView in imageViews { imageView.destroy() }
        swapchain?.destroyKHR()
        device?.destroy()
    }
}

Swift Vulkan & GNUStepSwiftBridge

Swif言語を用いてVulkanによる描画にトライしました。
GNUStepSwiftBridgeと組合せて、NSWindow上のボタンから実行できるように
しています。Vulkanの描画は、glfwを使ってXWindowに描画します。

[ 実行結果 ]
Triangle

UniformBuffer

ネット検索すると、Swift言語で書かれたVulkanのライブラリやサンプルはいくつか
見つかりますが、エラー無しに実行できるものは少ししかありません。
その中で、
  henrybetts/swift-vulkan: A natural Swift interface for Vulkan
  https://github.com/henrybetts/swift-vulkan
を参考にしました。

[ サンプルプログラムの実行結果 ]
DemoClearColor

(/swift-vulkan/Demos/swift-vulkan-demosディレクトリで
 swift run DemoClearColorを実行)

DemoTriangle

(shaderのパスを "./Sources/DemoTriangle/shaders/vert.spv" のように変更)
glfwを使って、XWindowに表示しています。

今回は、このサンプルプログラムとGNUStepSwiftBridgeを組合せました。
また、UniformBufferによる描画を追加しました。

ディレクトリ配置

AppVulkan
   Package.swift
   /AppKit
   /Sources
       /CGLFW
       /CVulkan
       /Vulkan
       /SGLMath
       /Main
           /Resources
           /shaders
           /shaders_ubo
           CheckResource.swift
           main.swift
           AppDelegate.swift
           AppError.swift
           AppClearColor.swift
           AppTriangle.swift
           AppUniformBuffer.swift
           color.swift
           file.swift

AppKitの配置を変更しています。内容は前回同様です。

AppKit
   Package.swift
   /Sources
      /Headers
      /AppKit
      /AppKitGNUStep
      /FoubdationGNUStep
      /libobjc2
      /ObjCSwiftInterop

Package.swift
   // swift-tools-version: 5.8
   // The swift-tools-version declares the minimum version of Swift required to build this package.

   import PackageDescription

   let package = Package(
      name: "AppKit",
      products: [
         // Products define the executables and libraries a package produces, making them visible to other packages.
         .library(name: "libobjc", targets: ["libobjc2"]),
         .library(name: "AppKitGNUStep", targets: ["AppKitGNUStep"]),
	 .library(name: "FoundationGNUStep", targets: ["FoundationGNUStep"]),
	 .library(name: "ObjCSwiftInterop",  targets: ["ObjCSwiftInterop"]),
         .library(name: "AppKit", targets: ["AppKit"]),
      ],
      targets: [
         // Targets are the basic building blocks of a package, defining a module or a test suite.
         // Targets can depend on other targets in this package and products from dependencies.
         .target(name: "ObjCSwiftInterop"),
         .target(
            name: "AppKit",
	    dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop", "FoundationGNUStep"]),
	 .systemLibrary(name: "libobjc2"),
	 .systemLibrary(name: "AppKitGNUStep"),
	 .systemLibrary(name: "FoundationGNUStep"),
      ]
   )

パッケージのPackage.swift

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
   name: "AppVulkan",
   products: [
      // Products define the executables and libraries a package produces, making them visible to other packages
      .library(name: "Vulkan", targets: ["Vulkan"]),
      .library(name: "SGLMath", targets: ["SGLMath"]),
   ],
   dependencies: [
      .package(path: "./AppKit")
   ],
   targets: [
      // Targets are the basic building blocks of a package, defining a module or a test suite.
      // Targets can depend on other targets in this package and products from dependencies.
      .executableTarget(
         name: "Main",
	 dependencies: ["AppKit", "CGLFW", "Vulkan", "SGLMath"]
      ),

      .target(name: "Vulkan",
         dependencies: ["CVulkan"]),
      .target(name: "SGLMath"),
		
      .systemLibrary(name: "CVulkan"),
      .systemLibrary(name: "CGLFW"),
   ]
)

/CGLFW, /Cvulkan, /Vulkan はVulkanのサンプルプログラムをそのまま利用して
います。
/SGLMathは、SwiftGL Math Library(https://github.com/SwiftGL/Math)を利用
します。

AppError.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

struct AppError: Error, CustomStringConvertible {
    let description: String
}

AppDelegate.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import Foundation
import AppKit
import ObjCSwiftInterop

let AppWindowRect = CGRect(x: 250, y: 100, width: 600, height: 500)

let fileManager = FileManager.default

class AppDelegate: NSApplicationDelegate {
	lazy var window = NSWindow(CGRect(x: 0, y: 500, width: 200, height: 200))
	lazy var view = UIView()
	lazy var button  = NSButton()
	lazy var button2 = NSButton()
	lazy var button3 = NSButton()

	override func applicationDidFinishLaunching(notification: Any?) {
		window.orderFront(sender: self)
		window.setTitle(NSString(string: "AppKit Test"))

		button.setTitle(NSString(string: "ClearColor"))
		button.frame = .init(x: 30, y: 10, width: 140, height: 32)
		button.onAction = { button in
			AppClearColor().run()
		}
		button2.setTitle(NSString(string: "Triangle"))
		button2.frame = .init(x: 30, y: 50, width: 140, height: 32)
		button2.onAction = { button in
			AppTriangle().run()
		}
		button3.setTitle(NSString(string: "UniformBuffer"))
		button3.frame = .init(x: 30, y: 90, width: 140, height: 32)
		button3.onAction = { button in
			AppUniformBuffer().run()
		}

		view.frame = .init(x: 0, y: 0, width: 300, height: 300)
		view.addSubview(button)
		view.addSubview(button2)
		view.addSubview(button3)
		window.setContentView(view)
	}
}

AppUniformBuffer.swift

import Foundation
import CVulkan
import CGLFW
import Vulkan
import SGLMath

//struct Vector2
//struct Vector3
//struct Vertex
// defined in AppTriangle

class AppUniformBuffer {
    /// A GLFW window handle.
    var window: OpaquePointer!

    /// The size of the window.
    let windowSize = Extent2D(width: UInt32(AppWindowRect.size.width), height: UInt32(AppWindowRect.size.height))
    
    /// Initializes GLFW and creates a window.
    func createWindow() throws {
        guard glfwInit() == GLFW_TRUE else {
            throw AppError(description: "Failed to initialize GLFW.")
        }
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API)
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE)
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)
        guard let window = glfwCreateWindow(Int32(windowSize.width), Int32(windowSize.height), "Vulkan Triangle", nil, nil) else {
            throw AppError(description: "Failed to create window.")
        }
        glfwSetWindowPos(window, Int32(AppWindowRect.origin.x), Int32(AppWindowRect.origin.y))
	    glfwShowWindow(window)

        self.window = window

        glfwSetKeyCallback(window, key_callback)
    }

    /// A Vulkan instance.
    var instance: Instance!
    /// Creates a Vulkan instance with the extensions required by GLFW.
    func createInstance() throws {
        var count: UInt32 = 0
        let extensionsPtr = glfwGetRequiredInstanceExtensions(&count)
        var extensions = UnsafeBufferPointer(start: extensionsPtr, count: Int(count)).map{ String(cString: $0!) }
        var layers: [String] = []
        
        #if DEBUG
        extensions.append(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)
        layers.append("VK_LAYER_KHRONOS_validation")
        #endif

        let entry = try Entry()
        instance = try entry.createInstance(
            createInfo: .init(
                flags: [],
                applicationInfo: nil,
                enabledLayerNames: layers,
                enabledExtensionNames: extensions
            )
        )
    }

    /// A Vulkan surface.
    var surface: SurfaceKHR!
    /// Creates a surface, via GLFW.
    func createSurface() throws {
        var cSurface: VkSurfaceKHR!
        let result = instance.withHandle { handle in
            glfwCreateWindowSurface(handle, window, nil, &cSurface)
        }
        guard result.rawValue >= 0 else {
            throw Result(rawValue: result.rawValue)!
        }
        surface = SurfaceKHR(handle: cSurface, instance: instance)
    }

    /// A Vulkan physical device.
    var physicalDevice: PhysicalDevice!
    /// The index of a graphics queue family.
    var graphicsFamilyIndex: UInt32!
    /// The index of a presentation queue family.
    var presentationFamilyIndex: UInt32!
    /// Selects a suitable physical device, as well as the graphics and presentation queue families.
    func selectPhysicalDevice() throws {
        let physicalDevices = try instance.getPhysicalDevices()
        
        for physicalDevice in physicalDevices {
            // Device must support the VK_KHR_swapchain extension
            let extensions = try physicalDevice.getDeviceExtensionProperties(layerName: nil)
            guard extensions.contains(where: { $0.extensionName == VK_KHR_SWAPCHAIN_EXTENSION_NAME }) else {
                continue
            }
            
            // Device must support the b8g8r8a8Srgb / srgbNonLinear surface format.
            let surfaceFormats = try physicalDevice.getSurfaceFormatsKHR(surface: surface)
            guard surfaceFormats.contains(where: { $0.format == .b8g8r8a8Srgb && $0.colorSpace == .srgbNonlinear }) else {
                continue
            }
            
            // Device must have a graphics queue family.
            let queueFamilies = physicalDevice.getQueueFamilyProperties()
            guard let graphicsFamilyIndex = queueFamilies.firstIndex(where: { $0.queueFlags.contains(.graphics) }) else {
                continue
            }
            
            // Device must have a presentation queue family that is compatible with the surface.
            let queueFamilyIndices = 0..<UInt32(queueFamilies.count)
            guard let presentationFamilyIndex = try queueFamilyIndices.first(where: {
                try physicalDevice.getSurfaceSupportKHR(queueFamilyIndex: $0, surface: surface)
            }) else {
                continue
            }
            
            self.graphicsFamilyIndex = UInt32(graphicsFamilyIndex)
            self.presentationFamilyIndex = presentationFamilyIndex
            self.physicalDevice = physicalDevice
            break
        }
        
        guard physicalDevice != nil else {
            throw AppError(description: "Could not find a suitable device.")
        }
    }

    /// A Vulkan device.
    var device: Device!
    /// A Vulkan queue that supports graphics commands.
    var graphicsQueue: Queue!
    /// A Vulkan queue that supports presentation commands.
    var presentationQueue: Queue!
    /// Creates a logical device and retrieves its queues.
    func createDevice() throws {
        let families: Set = [graphicsFamilyIndex!, presentationFamilyIndex!]
        
        let queueInfos = families.map {
            DeviceQueueCreateInfo(
                flags: [],
                queueFamilyIndex: UInt32($0),
                queuePriorities: [1.0]
            )
        }
        
        device = try physicalDevice.createDevice(
            createInfo: .init(
                flags: [],
                queueCreateInfos: queueInfos,
                enabledLayerNames: [],
                enabledExtensionNames: [VK_KHR_SWAPCHAIN_EXTENSION_NAME],
                enabledFeatures: nil
            )
        )
        
        graphicsQueue = device.getQueue(queueFamilyIndex: graphicsFamilyIndex, queueIndex: 0)
        presentationQueue = device.getQueue(queueFamilyIndex: presentationFamilyIndex, queueIndex: 0)
    }

    /// A Vulkan swapchain.
    var swapchain: SwapchainKHR!
    /// The size of the swapchain images.
    var swapchainExtent: Extent2D!
    /// An array of Vulkan swapchain images.
    var swapchainImages: [Image] = []
    /// Creates a swapchain and retrieves its images.
    func createSwapchain() throws {
        let capabilities = try physicalDevice.getSurfaceCapabilitiesKHR(surface: surface)
        
        var imageCount = capabilities.minImageCount + 1
        if capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount {
            imageCount = capabilities.maxImageCount
        }
        
        if capabilities.currentExtent.width != UInt32.max {
            swapchainExtent = capabilities.currentExtent
        } else {
            let width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, windowSize.width))
            let height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, windowSize.height))
            swapchainExtent = Extent2D(width: width, height: height)
        }
        
        swapchain = try device.createSwapchainKHR(
            createInfo: .init(
                flags: [],
                surface: surface,
                minImageCount: imageCount,
                imageFormat: .b8g8r8a8Srgb,
                imageColorSpace: .srgbNonlinear,
                imageExtent: swapchainExtent,
                imageArrayLayers: 1,
                imageUsage: .colorAttachment,
                imageSharingMode: graphicsFamilyIndex == presentationFamilyIndex ? .exclusive : .concurrent,
                queueFamilyIndices: [graphicsFamilyIndex, presentationFamilyIndex],
                preTransform: capabilities.currentTransform,
                compositeAlpha: .opaque,
                presentMode: .fifo,
                clipped: true,
                oldSwapchain: nil
            )
        )
        
        swapchainImages = try swapchain.getImagesKHR()
    }
    
    /// An array of Vulkan image views.
    var imageViews: [ImageView] = []
    /// Creates an image view for each of the swapchain images.
    func createImageViews() throws {
        for image in swapchainImages {
            let imageView = try device.createImageView(
                createInfo: .init(
                    flags: [],
                    image: image,
                    viewType: .type2d,
                    format: .b8g8r8a8Srgb,
                    components: .init(r: .r, g: .g, b: .b, a: .a),
                    subresourceRange: .init(
                        aspectMask: .color,
                        baseMipLevel: 0,
                        levelCount: 1,
                        baseArrayLayer: 0,
                        layerCount: 1
                    )
                )
            )
            imageViews.append(imageView)
        }
    }
    
    /// A Vulkan render pass.
    var renderPass: RenderPass!
    /// Creates a Vulkan render pass.
    func createRenderPass() throws {
        let colorAttachment = AttachmentDescription(
            flags: [],
            format: .b8g8r8a8Srgb,
            samples: .type1,
            loadOp: .clear,
            storeOp: .store,
            stencilLoadOp: .dontCare,
            stencilStoreOp: .dontCare,
            initialLayout: .undefined,
            finalLayout: .presentSrcKHR
        )
        
        let colorAttachmentRef = AttachmentReference(attachment: 0, layout: .colorAttachmentOptimal)
        
        let subpass = SubpassDescription(
            flags: [],
            pipelineBindPoint: .graphics,
            inputAttachments: [],
            colorAttachments: [colorAttachmentRef],
            resolveAttachments: nil,
            depthStencilAttachment: nil,
            preserveAttachments: []
        )
        
        let dependency = SubpassDependency(
            srcSubpass: VK_SUBPASS_EXTERNAL,
            dstSubpass: 0,
            srcStageMask: .colorAttachmentOutput,
            dstStageMask: .colorAttachmentOutput,
            srcAccessMask: [],
            dstAccessMask: .colorAttachmentWrite,
            dependencyFlags: []
        )
        
        renderPass = try device.createRenderPass(
            createInfo: .init(
                flags: [],
                attachments: [colorAttachment],
                subpasses: [subpass],
                dependencies: [dependency]
            )
        )
    }
    
    /// Creates a Vulkan shader module from the source file at the given path.
    func createShaderModule(path: String) throws -> ShaderModule {
        guard let buffer = readFile(path: path) else {
            throw AppError(description: "Failed to read file: \(path)")
        }
        
        return try buffer.withUnsafeBytes { bytes in
            try device.createShaderModule(
                createInfo: .init(
                    flags: [],
                    codeSize: buffer.count,
                    code: bytes.bindMemory(to: UInt32.self).baseAddress!
                )
            )
        }
    }
    
    /// DescriptorSetLayout
    var descriptorSetLayout: DescriptorSetLayout!
    func createDescriptorSetLayout() throws {
        let uboLayoutBinding = DescriptorSetLayoutBinding(
            binding: 0,
            descriptorType: .uniformBuffer,
            descriptorCount: 1,
            stageFlags: .vertex,
            immutableSamplers: []
        )

        descriptorSetLayout = try self.device.createDescriptorSetLayout(
            createInfo: .init(
                flags: [],
                bindings: [uboLayoutBinding]
            )
        )
    }

    /// A Vulkan pipeline layout.
    var pipelineLayout: PipelineLayout!
    /// A Vulkan graphics pipeline.
    var pipeline: Pipeline!
    /// Creates a Vulkan graphics pipeline.
    func createGraphicsPipeline() throws {
        let vertexShader = try createShaderModule(path: "./Sources/Main/shaders_ubo/vert.spv")
        let fragmentShader = try createShaderModule(path: "./Sources/Main/shaders_ubo/frag.spv")
        defer {
            vertexShader.destroy()
            fragmentShader.destroy()
        }
        
        let vertexStage = PipelineShaderStageCreateInfo(
            flags: [],
            stage: .vertex,
            module: vertexShader,
            name: "main",
            specializationInfo: nil
        )
        
        let fragmentStage = PipelineShaderStageCreateInfo(
            flags: [],
            stage: .fragment,
            module: fragmentShader,
            name: "main",
            specializationInfo: nil
        )
        
        let vertexBinding = VertexInputBindingDescription(
            binding: 0,
            stride: UInt32(MemoryLayout<Vertex>.stride),
            inputRate: .vertex
        )
        
        let positionAttribute = VertexInputAttributeDescription(
            location: 0,
            binding: 0,
            format: .r32g32Sfloat,
            offset: UInt32(MemoryLayout.offset(of: \Vertex.position)!)
        )
        
        let colorAttribute = VertexInputAttributeDescription(
            location: 1,
            binding: 0,
            format: .r32g32b32Sfloat,
            offset: UInt32(MemoryLayout.offset(of: \Vertex.color)!)
        )
        
        let vertexInputState = PipelineVertexInputStateCreateInfo(
            flags: [],
            vertexBindingDescriptions: [vertexBinding],
            vertexAttributeDescriptions: [positionAttribute, colorAttribute]
        )
        
        let inputAssemblyState = PipelineInputAssemblyStateCreateInfo(
            flags: [],
            topology: .triangleList,
            primitiveRestartEnable: false
        )
        
        let viewport = Viewport(
            x: 0,
            y: 0,
            width: Float(swapchainExtent.width),
            height: Float(swapchainExtent.height),
            minDepth: 0,
            maxDepth: 1
        )
        
        let scissor = Rect2D(offset: .init(x: 0, y: 0), extent: swapchainExtent)
        
        let viewportState = PipelineViewportStateCreateInfo(
            flags: [],
            viewports: [viewport],
            scissors: [scissor]
        )
        
        let rasterizationState = PipelineRasterizationStateCreateInfo(
            flags: [],
            depthClampEnable: false,
            rasterizerDiscardEnable: false,
            polygonMode: .fill,
            cullMode: .back,
            frontFace: .clockwise,
            depthBiasEnable: false,
            depthBiasConstantFactor: 0,
            depthBiasClamp: 0,
            depthBiasSlopeFactor: 0,
            lineWidth: 1
        )
        
        let multisampleState = PipelineMultisampleStateCreateInfo(
            flags: [],
            rasterizationSamples: .type1,
            sampleShadingEnable: false,
            minSampleShading: 0,
            sampleMask: nil,
            alphaToCoverageEnable: false,
            alphaToOneEnable: false
        )
        
        let colorBlendAttachmentState = PipelineColorBlendAttachmentState(
            blendEnable: false,
            srcColorBlendFactor: .zero,
            dstColorBlendFactor: .zero,
            colorBlendOp: .add,
            srcAlphaBlendFactor: .zero,
            dstAlphaBlendFactor: .zero,
            alphaBlendOp: .add,
            colorWriteMask: [.r, .g, .b, .a]
        )
        
        let colorBlendState = PipelineColorBlendStateCreateInfo(
            flags: [],
            logicOpEnable: false,
            logicOp: .copy,
            attachments: [colorBlendAttachmentState],
            blendConstants: (0, 0, 0, 0)
        )
        
        pipelineLayout = try device.createPipelineLayout(
            createInfo: .init(
                flags: [],
                setLayouts: [self.descriptorSetLayout],
                pushConstantRanges: []
            )
        )
        
        let pipelineInfo = GraphicsPipelineCreateInfo(
            flags: [],
            stages: [vertexStage, fragmentStage],
            vertexInputState: vertexInputState,
            inputAssemblyState: inputAssemblyState,
            tessellationState: nil,
            viewportState: viewportState,
            rasterizationState: rasterizationState,
            multisampleState: multisampleState,
            depthStencilState: nil,
            colorBlendState: colorBlendState,
            dynamicState: nil,
            layout: pipelineLayout,
            renderPass: renderPass,
            subpass: 0,
            basePipelineHandle: nil,
            basePipelineIndex: 0
        )
        
        pipeline = try device.createGraphicsPipelines(
            pipelineCache: nil,
            createInfos: [pipelineInfo]
        )[0]
    }
    
    /// An array of Vulkan framebuffers.
    var framebuffers: [Framebuffer] = []
    /// Creates a Vulkan framebuffer for each of the swapchain image views.
    func createFramebuffers() throws {
        for imageView in imageViews {
            let framebuffer = try device.createFramebuffer(
                createInfo: .init(
                    flags: [],
                    renderPass: renderPass,
                    attachments: [imageView],
                    width: swapchainExtent.width,
                    height: swapchainExtent.height,
                    layers: 1
                )
            )
            framebuffers.append(framebuffer)
        }
    }
    
    /// Creates a Vulkan buffer and allocates memory for it, with the given properties.
    func createBuffer(size: VkDeviceSize, usage: BufferUsageFlags, properties: MemoryPropertyFlags) throws -> (Buffer, DeviceMemory) {
        let buffer = try device.createBuffer(
            createInfo: .init(
                flags: [],
                size: size,
                usage: usage,
                sharingMode: .exclusive,
                queueFamilyIndices: []
            )
        )
        
        let memoryRequirements = buffer.getMemoryRequirements()
        let memoryProperties = physicalDevice.getMemoryProperties()
        
        guard let memoryType = memoryProperties.memoryTypes.enumerated().first(where: { index, type in
            (memoryRequirements.memoryTypeBits & (1 << index) != 0) &&
                type.propertyFlags.contains(properties)
        }) else {
            throw AppError(description: "Could not find suitable memory type.")
        }
        
        let memory = try device.allocateMemory(
            allocateInfo: .init(
                allocationSize: memoryRequirements.size,
                memoryTypeIndex: UInt32(memoryType.offset)
            )
        )
        
        try buffer.bindMemory(memory: memory, memoryOffset: 0)
        
        return (buffer, memory)
    }
    
    /// An array of vertices that describe a triangle.
    let vertices = [
        Vertex(position: .init(x: 0, y: -0.5), color: .init(x: 1, y: 0, z: 0)),
        Vertex(position: .init(x: 0.5, y: 0.5), color: .init(x: 0, y: 1, z: 0)),
        Vertex(position: .init(x: -0.5, y: 0.5), color: .init(x: 0, y: 0, z: 1))
    ]
    
    /// The size, in bytes, of the vertices.
    var bufferSize: VkDeviceSize!
    /// A Vulkan buffer used to stage the vertices before transfering them to the GPU.
    var stagingBuffer: Buffer!
    /// Host visible memory that is bound to the staging buffer.
    var stagingBufferMemory: DeviceMemory!
    /// A Vulkan buffer used to store the vertices that will be accessed by the GPU.
    var vertexBuffer: Buffer!
    /// Device local memory that is bound to the vertex buffer.
    var vertexBufferMemory: DeviceMemory!
    /// Creates the staging and vertex buffers.
    func createVertexBuffer() throws {
        bufferSize = VkDeviceSize(MemoryLayout<Vertex>.stride * vertices.count)
        
        (stagingBuffer, stagingBufferMemory) = try createBuffer(size: bufferSize, usage: .transferSrc, properties: [.hostVisible, .hostCoherent])
        
        (vertexBuffer, vertexBufferMemory) = try createBuffer(size: bufferSize, usage: [.vertexBuffer, .transferDst], properties: .deviceLocal)
    }

    /// create UniformData
    var model: mat4 = mat4(0.0)
    var view: mat4 = mat4(0.0)
    var proj: mat4 = mat4(0.0)
    func createUniformData() {
        self.proj = SGLMath.perspective(radians(45), Float(1.0), Float(0.1), Float(10))
        self.view = SGLMath.lookAt(
            vec3(2.0, 2.0, 1.0),
            vec3(0, 0, 0),
            vec3(0, 0, 1))

    //    let angle = Float.pi * 0.5
        self.model = SGLMath.rotate(mat4(1.0), 0.0, vec3(0, 0, 1))
    }

    /// add UniformBuffer DescriptorSet
    struct UniformBufferObject{
        var model: mat4
        var view: mat4
        var proj: mat4
    }

    var uboBufferSize: VkDeviceSize!
    var uniformBuffer: Buffer!
    var uniformBufferMemory: DeviceMemory!
    func createUniformBuffer() throws {
        //struct UniformBufferObject{
        //    var model: mat4
        //    var view: mat4
        //    var proj: mat4
        //}
        let ubo = UniformBufferObject(model: self.model, view: self.view, proj: self.proj)
        print("ubo", ubo)

        uboBufferSize = VkDeviceSize(MemoryLayout.size(ofValue: ubo))
        print("uniform bufferSize", uboBufferSize)
        (uniformBuffer, uniformBufferMemory) = try createBuffer(size: uboBufferSize, usage: .uniformBuffer, properties: [.hostVisible, .hostCoherent])
        //print("uniformBuffer", uniformBuffer)

        //let ptr = try uniformBufferMemory.mapMemory(offset: 0, size: uboBufferSize, flags: [])
        //withUnsafeBytes(of: ubo) { bytes in
        //    ptr.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
        //}
        //uniformBufferMemory.unmapMemory()
    }

    var counter: Float = 0
    func updateUniformBuffer() throws {
        //struct UniformBufferObject{
        //    var model: mat4
        //    var view: mat4
        //    var proj: mat4
        //}

        //var model: mat4 = mat4(0.0)
        //var view: mat4 = mat4(0.0)
        //var proj: mat4 = mat4(0.0)
    
        //let proj = SGLMath.perspective(radians(45), Float(1.0), Float(0.1), Float(10))
        //let view = SGLMath.lookAt(
        //    vec3(2.0, 2.0, 2.0),
        //    vec3(0, 0, 0),
        //    vec3(0, 0, 1))

        counter += 0.1
        var angle = counter
        let model = SGLMath.rotate(mat4(1.0), angle, vec3(0, 0, 1))

        let ubo = UniformBufferObject(model: model, view: self.view, proj: self.proj)
        //print("ubo", ubo)

        //uboBufferSize = VkDeviceSize(MemoryLayout.size(ofValue: ubo))
        //print("uniform bufferSize", uboBufferSize)
        //(uniformBuffer, uniformBufferMemory) = try createBuffer(size: uboBufferSize, usage: .uniformBuffer, properties: [.hostVisible, .hostCoherent])
        //print("uniformBuffer", uniformBuffer)

        let ptr = try uniformBufferMemory.mapMemory(offset: 0, size: uboBufferSize, flags: [])
        withUnsafeBytes(of: ubo) { bytes in
            ptr.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
        }
        uniformBufferMemory.unmapMemory()
    }

    /// DescriptorPool, DescriptorSet
    var descriptorPool: DescriptorPool!
    var descriptorSets: [DescriptorSet] = []
    func createDescriptorSet() throws {
        let poolSize = DescriptorPoolSize(
                type: .uniformBuffer,
                descriptorCount: 1
        )

        let info = DescriptorPoolCreateInfo(
                flags: [],
                maxSets: 1,
                poolSizes: [poolSize]
        )

        descriptorPool = try self.device.createDescriptorPool(createInfo: info)

        let allocInfo = DescriptorSetAllocateInfo(
            descriptorPool: descriptorPool,
            setLayouts: [self.descriptorSetLayout]
        )

        descriptorSets = try self.device.allocateDescriptorSets(allocateInfo: allocInfo)
        print("descriptorSets", descriptorSets)

        let bufferInfo = DescriptorBufferInfo(
            buffer: uniformBuffer,
            offset: 0,
            range: UInt64(self.uboBufferSize)
        )

        let wds = WriteDescriptorSet(
            dstSet: descriptorSets[0],
            dstBinding: 0,
            dstArrayElement: 0,
            descriptorType: .uniformBuffer,
            descriptorCount: 1,
            imageInfo: [],
            bufferInfo: [bufferInfo],
            texelBufferView: []
        )

        self.device.updateDescriptorSets(descriptorWrites: [wds], descriptorCopies: [])
    }

    /// A Vulkan command pool.
    var commandPool: CommandPool!
    /// Creates a command pool.
    func createCommandPool() throws {
        commandPool = try device.createCommandPool(
            createInfo: .init(
                flags: [.resetCommandBuffer],
                queueFamilyIndex: graphicsFamilyIndex
            )
        )
    }
    
    /// Copies the vertices to the staging buffer, then transfers the contents of the staging buffer to the vertex buffer.
    func copyVertexBuffer() throws {
        let ptr = try stagingBufferMemory.mapMemory(offset: 0, size: bufferSize, flags: [])
        vertices.withUnsafeBytes { bytes in
            ptr.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
        }
        stagingBufferMemory.unmapMemory()
        
        let commandBuffer = try device.allocateCommandBuffers(
            allocateInfo: .init(
                commandPool: commandPool,
                level: .primary,
                commandBufferCount: 1
            )
        )[0]
        defer {
            commandPool.freeCommandBuffers(commandBuffers: [commandBuffer])
        }
        
        try commandBuffer.begin(
            beginInfo: .init(
                flags: .oneTimeSubmit,
                inheritanceInfo: nil
            )
        )
        
        let copyRegion = BufferCopy(srcOffset: 0, dstOffset: 0, size: bufferSize)
        commandBuffer.cmdCopyBuffer(srcBuffer: stagingBuffer, dstBuffer: vertexBuffer, regions: [copyRegion])
        
        try commandBuffer.end()
        
        let submitInfo = SubmitInfo(
            waitSemaphores: [],
            waitDstStageMask: [],
            commandBuffers: [commandBuffer],
            signalSemaphores: []
        )
        try graphicsQueue.submit(submits: [submitInfo], fence: nil)
        try graphicsQueue.waitIdle()
    }

    /// A Vulkan command buffer.
    var commandBuffers: [CommandBuffer] = []
    /// Allocates a command buffer for each of the swapchain images.
    func createCommandBuffers() throws {
        commandBuffers = try device.allocateCommandBuffers(
            allocateInfo: .init(
                commandPool: commandPool,
                level: .primary,
                commandBufferCount: UInt32(swapchainImages.count)
            )
        )
    }
    
    /// Records a set of commands for each command buffer.
    func recordCommandBuffers() throws {
        for (commandBuffer, framebuffer) in zip(commandBuffers, framebuffers) {
            try commandBuffer.begin(beginInfo: .init(flags: [], inheritanceInfo: nil))
            
            let clearColor = VkClearValue(color: .init(float32: (0, 0, 0, 1)))
            commandBuffer.cmdBeginRenderPass(
                renderPassBegin: .init(
                    renderPass: renderPass,
                    framebuffer: framebuffer,
                    renderArea: .init(offset: .init(x: 0, y: 0), extent: swapchainExtent),
                    clearValues: [clearColor]),
                contents: .inline
            )
            
            commandBuffer.cmdBindPipeline(pipelineBindPoint: .graphics, pipeline: pipeline)
            //
            commandBuffer.cmdBindDescriptorSets(pipelineBindPoint: .graphics, layout: self.pipelineLayout, firstSet: 0, descriptorSets: self.descriptorSets, dynamicOffsets: [])
            //
            commandBuffer.cmdBindVertexBuffers(firstBinding: 0, buffers: [vertexBuffer], offsets: [0])
            commandBuffer.cmdDraw(vertexCount: UInt32(vertices.count), instanceCount: 1, firstVertex: 0, firstInstance: 0)
            
            commandBuffer.cmdEndRenderPass()
            
            try commandBuffer.end()
        }
    }

    /// A semaphore to signal when an image is available for rendering.
    var imageAvailable: Vulkan.Semaphore!
    /// A semaphore to signal when rendering is complete.
    var renderComplete: Vulkan.Semaphore!
    /// Creates any objects required for synchronization.
    func createSync() throws {
        imageAvailable = try device.createSemaphore(createInfo: .init(flags: []))
        renderComplete = try device.createSemaphore(createInfo: .init(flags: []))
    }
    
    /// Aquires an image from the swapchain, submits the associated command buffer, and finally presents the image.
    func drawFrame() throws {
        try updateUniformBuffer()

        let index = try swapchain.acquireNextImageKHR(timeout: UInt64.max, semaphore: imageAvailable, fence: nil)
        
        let graphicsSubmit = SubmitInfo(
            waitSemaphores: [imageAvailable],
            waitDstStageMask: [.colorAttachmentOutput],
            commandBuffers: [commandBuffers[Int(index)]],
            signalSemaphores: [renderComplete]
        )
        try graphicsQueue.submit(submits: [graphicsSubmit], fence: nil)
        
        try presentationQueue.presentKHR(
            presentInfo: .init(
                waitSemaphores: [renderComplete],
                swapchains: [swapchain],
                imageIndices: [index],
                results: nil
            )
        )
        
        // Note: Don't use this in the real world. It is not an efficient method of synchronization.
        try presentationQueue.waitIdle()
    }

    /// Runs the main draw loop until the window is closed.
    func mainLoop() throws {
        while glfwWindowShouldClose(window) == GLFW_FALSE {
            glfwPollEvents()
            try drawFrame()
        }
        try device.waitIdle()
    }
    
    /// Initializes and runs the application.
    func run() {
        do {
            try createWindow()
            try createInstance()
            try createSurface()
            try selectPhysicalDevice()
            try createDevice()
            try createSwapchain()
            try createImageViews()
            try createRenderPass()
            try createDescriptorSetLayout()
            try createGraphicsPipeline()
            try createFramebuffers()
            try createVertexBuffer()
            try createCommandPool()
            try copyVertexBuffer()
            createUniformData()
            try createUniformBuffer()
            try createDescriptorSet()
            try createCommandBuffers()
            try recordCommandBuffers()
            try createSync()
            try mainLoop()
        } catch let e as Result {
            print("Encountered Vulkan error: \(e)")
        } catch let e {
            print(e)
        }
    }

    /// Cleans up any resources that were created.
    deinit {
        imageAvailable?.destroy()
        renderComplete?.destroy()
        commandPool?.destroy()
        vertexBuffer?.destroy()
        vertexBufferMemory?.freeMemory()
        stagingBuffer?.destroy()
        stagingBufferMemory?.freeMemory()
        //
        uniformBuffer?.destroy()
        uniformBufferMemory?.freeMemory()
        //
        for framebuffer in framebuffers { framebuffer.destroy() }
        pipeline?.destroy()
        pipelineLayout?.destroy()
        //
        descriptorSetLayout?.destroy()
        descriptorPool?.destroy()
        //
        renderPass?.destroy()
        for imageView in imageViews { imageView.destroy() }
        swapchain?.destroyKHR()
        device?.destroy()
        surface?.destroyKHR()
        instance?.destroy()
        glfwDestroyWindow(window)
        glfwTerminate()
    }
}

/// GLFW key callback
// defined in AppClearColor

shaderは次のようになります。

/shaders_ubo
shader.vert
#version 450

layout (binding = 0) uniform bufferVals {
    mat4 model;
    mat4 view;
    mat4 proj;
} ubo;

layout (location = 0) in vec2 inPosition;
layout (location = 1) in vec3 inColor;

layout (location = 0) out vec3 fragColor;

void main() {
   vec4 pos = vec4(inPosition, 0.0, 1.0);

   gl_Position = ubo.proj * ubo.view * ubo.model * pos;
   fragColor = inColor;
}

shader.frag
#version 450

layout (location = 0) in vec3 fragColor;
layout (location = 0) out vec4 outColor;
void main() {
   outColor = vec4(fragColor, 1.0);
}

glslang でspvに変換します。

GNUStepSwiftBridge & OpenGL

GNUStepSwiftBridge を使って NSOpenGLView への描画にトライしました。

[ 実行結果 ]

Swift で OpenGL による描画のサンプルがあります。
GitHub - sakrist/Swift_OpenGL_Example: :star: Swift OpenGL Example written with swift (for Ubuntu, macOS, iOS and Android)
shader を使って描画しています。
これは、macOS, iOS, Linux, Android で動作するようです。
下図は Linux で実行したものです。XWindow に描画しています。
mouse による操作ができます。

今回は、これを参考にして、GNUStepSwiftBridge で NSOpenGLView への描画にトライしました。

前回の AppKit.swift にNSOpenGLView を追加しました。
AppKit.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book
// add
// 2022-11-15
// NSImage public static func imageWithPath(_ path: String)

import ObjCSwiftInterop

////NSApplication
open class NSApplication: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSApplication"
	}
	public var shared: NSApplication {
		get {
			var sharedApp =  forSwift_objcSendMessage(&self._nsobjptr!.pointee, sel_registerName("sharedApplication"))
			return NSApplication()
		}
	}
}

open class NSApplicationDelegate: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSApplicationDelegateForSwift"
	}

	static var didFinishLaunchingIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL,id?) -> (UInt8) = { first, second, third in
		let SELF: NSApplicationDelegate? = smart_swift_lookupIvarWithType(_nsobjptr: first, name: "___swiftPtr")
		SELF?.applicationDidFinishLaunching(notification: nil)
		return 0
	}

	open func applicationDidFinishLaunching(notification: Any?) {

	}

	static var _objcClass = GNUStepNSObjectSubclassConstructor(name: "NSApplicationDelegateForSwift", superName: "NSObject", create: { ptr in
		var types = "i@:@"
		let imp = imp_implementationWithBlock(unsafeBitCast(didFinishLaunchingIMP, to: id.self))
		class_addMethod(ptr, sel_registerName("applicationDidFinishLaunching:"),imp, types)
	})

	public override init() {
		_ = NSApplicationDelegate._objcClass

		super.init()

		var nclass = objc_getClass("NSApplicationDelegateForSwift")

		var initalizedObject = forSwift_objcSendMessage(&nclass!.pointee, sel_getUid("alloc"))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("init"))

		self._nsobjptr = initalizedObject

		var cast = Unmanaged.passUnretained(self).toOpaque()
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func didFinishLaunching(_ OBJ: Any, notification: Any?) -> Void {

	}
}

open class UIView: NSView {
	public override var _nsclassName: String {
		return "UIView"
	}

	static var isFlippedIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL) -> (UInt8) = { first, second in
		print("!!!!!!!!!!!!!!!!!!!!!!!!asked if is flipped!")
		return 1
	}

	static var _objcClass = GNUStepNSObjectSubclassConstructor(
		name: "UIView", superName: "NSScrollView", create: { ptr in }
	)

	public override init() {
		_ = UIView._objcClass

		super.init()
		let nclass = objc_getClass("UIView")

		let types = "c"
		let imp = imp_implementationWithBlock(unsafeBitCast(UIView.isFlippedIMP, to: id.self))
		class_replaceMethod(UIView._objcClass._nsobjptr!, sel_registerName("isFlipped"),imp, types)

		var initalizedObject = forSwift_objcSendMessage(&nclass!.pointee, sel_getUid("alloc"))
		initalizedObject = forSwift_objcSendMessage1NSRect(&initalizedObject!.pointee, sel_getUid("initWithFrame:"), CGRect(x: 0, y: 0, width: 100, height: 100))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("retain"))
		self._nsobjptr = initalizedObject

		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("_rebuildCoordinates"))
		
		let cast = Unmanaged.passUnretained(self).toOpaque()
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}
}


public class NSFont: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSFont"
	}

	public override init() {
		super.init()
	}

	public init?(name: String, size: Float) {
		super.init()
		var nsColorClass = objc_getClass("NSFont")
		var size = size
		var string = NSString(string: name)
		var imp:  (@convention(c) (id, SEL, id, Float) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "fontWithName:size:")
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("fontWithName:size:"), &string._nsobjptr!.pointee, size) {
			self._nsobjptr = rtn
			print("FOUND FONT")
			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
		} else {
			print("FONT NOT FOUND")
			return nil
		}
	}

	public static func boldSystemFontOfSize(size: Double) -> NSFont {
		var nsColorClass = objc_getClass("NSFont")
		var size = size

		var imp:  (@convention(c) (id, SEL, Double) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "boldSystemFontOfSize:")
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("boldSystemFontOfSize:"), size) {
			print("FOUND boldSystemFontOfSize")

			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
			return NSFont(nsobjptr: rtn)

		} else {
			return NSFont()
		}
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}
}

public class NSColor: GNUStepNSObjectWrapper {

	public override var _nsclassName: String {
		return "NSColor"
	}

	public override init() {
		super.init()
		let nsColorClass =  objc_getClass(self._nsclassName)
		var initalizedObject = forSwift_objcSendMessage(&nsColorClass!.pointee, sel_registerName("blueColor"))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = initalizedObject
	}

	public init(red: Float, green: Float, blue: Float, alpha: Float) {
		super.init()
		let nsColorClass =  objc_getClass(self._nsclassName)
		let red = red
		let green = green
		let blue = blue
		let alpha = alpha
		var initalizedObject = forSwift_objcSendMessage4Floats(&nsColorClass!.pointee, sel_registerName("colorWithCalibratedRed:green:blue:alpha:"), red, green, blue, alpha)
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = initalizedObject
	}

	public static var clear: NSColor {
		return .init(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0)
	}

	public static var blue: NSColor {
		return .init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
	}

	public static var black: NSColor {
		return .init(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
	}

	public static var white: NSColor {
		return .init(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
	}

	public static var grey: NSColor {
		return .init(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
	}

	public static var darkPurple: NSColor {
		return NSColor.init(red: 0.1422559619, green: 0.05977959186, blue: 0.2294596732, alpha: 1)
	}

	public static var purple: NSColor {
		let c =    NSColor.init(red: 0.2726541758, green: 0.1163508371, blue: 0.435738951, alpha: 1)
		return c
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}
}

open class NSImage: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSImage"
	}

	public static func image(named: String) -> NSImage? {
		print("finding image named \(named)")
		var nsColorClass = objc_getClass("NSImage")
		var nsColorClassAsClass =  object_getClass(objc_getClass("NSImage"))

		var string = NSString(string: named)
		var imp:  (@convention(c) (id, SEL, id) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "imageNamed:")
		print(imp)
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("imageNamed:"), &string._nsobjptr!.pointee) {
			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
			let v = NSImage(nsobjptr: &rtn.pointee)
			print("image is named \(v.name)")
			return v
		}
		return nil
	}
	
	public static func imageWithPath(_ path: String) -> NSImage? {
		print("file path:", path)
		let nsfilePath = NSString(string: path)
		var nsImageClass = objc_getClass("NSImage")
		var initializedObject = forSwift_objcSendMessage(&nsImageClass!.pointee, sel_getUid("alloc"))
		var vimage = forSwift_objcSendMessage1(&initializedObject!.pointee, sel_getUid("initWithContentsOfFile:"), nsfilePath._nsobjptr)
        let v = NSImage(nsobjptr: &vimage!.pointee)
        return v
    }

	public var name: String {
		get {
			print("getting name")
			if var ptr = self._nsobjptr {
				var imp: (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "name")
				if let rtn = imp?(&ptr.pointee, sel_getUid("name")) {
					if let rtn = objc_convertToSwift_NSObject(value: rtn) as? NSString {
						return rtn.string
					}
				}
			}

			return ""
		}
	}
}

open class NSView: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSView"
	}

	public var subviews: [Any] = []
	public func addSubview(_ subview: NSView) {
		subviews.append(subview)
		guard let selfPtr = self._nsobjptr else {return}
		if var ptr = subview._nsobjptr{
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("addSubview:"), &ptr.pointee)

		}
	}

	public func setBackgroundColor(_ color:  NSColor) {
		guard let colorPtr = color._nsobjptr else {return}
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setBackgroundColor:"), &colorPtr.pointee)
	}

	public var frame: CGRect {
		get {
			let imp:  (@convention(c) (id, SEL) -> (CGRect))? = objc_smart_getIMP(object: self, selector: "frame")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("frame")) {
				return rtn
			}
			return .init(x: 0, y: 0, width: 0, height: 0)
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, CGRect) -> (Void))? = objc_smart_getIMP(object: self, selector: "setFrame:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setFrame:"), newValue)
		}
	}

	public func setFrame(_ rect: CGRect) {
		print("set frame: \(rect)")
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1NSRect(&selfPtr.pointee, sel_registerName("setFrame:"), rect)
	}

}

public class NSWindow: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSWindow"
	}

	public override init() {
		super.init()
		let nsWindowClass =  objc_getClass(_nsclassName)
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		let styleMask: UInt = 1 + 2 + 4 + 8
		let backingStoreType: UInt = 0
		let deferr: Int8 = 0
		let rect = CGRect(x: 200, y: 200, width: 500, height: 500)
		allocatedObject = initWithContentRect_styleMask_backing_defer(&allocatedObject!.pointee, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, styleMask, backingStoreType, deferr)

		self._nsobjptr = allocatedObject
		self.setTitle(NSString(string: "Untitled Window"))

	}

	public init(_ rect: CGRect) {
		super.init()
		let nsWindowClass =  objc_getClass(_nsclassName)
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		let styleMask: UInt = 1 + 2 + 4 + 8
		let backingStoreType: UInt = 0
		let deferr: Int8 = 0
		allocatedObject = initWithContentRect_styleMask_backing_defer(&allocatedObject!.pointee, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, styleMask, backingStoreType, deferr)

		self._nsobjptr = allocatedObject
		self.setTitle(NSString(string: "Untitled Window"))
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func orderFront(sender:  Any?) {
		guard var ptr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("orderFront:"), nil)
	}

	public func setBackgroundColor(_ color:  NSColor) {
		guard let colorPtr = color._nsobjptr else {return}
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setBackgroundColor:"), &colorPtr.pointee)
	}

	var title: String {
		set {
			var title = NSString(string: newValue)
			if var ptr = title._nsobjptr, var selfPtr = self._nsobjptr {
				_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setTitle:"), &ptr.pointee)
			}
		}

		get {
			if let x: NSString = objc_smart_sendMessage(object: self, selector: "title", value1: StopVariable(), value2: StopVariable(), value3: StopVariable(), value4: StopVariable(), value5: StopVariable(), value6: StopVariable(), value7: StopVariable(), value8: StopVariable(), value9: StopVariable()) {
				return x.string
			}
			return ""
		}
	}

	public func setTitle(_ title: NSString) {
		if var ptr = title._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setTitle:"), &ptr.pointee)
		}
	}

	public var subviews: [Any] = []
	public func addSubview(_ subview: GNUStepNSObjectWrapper) {
		subviews.append(subview)
		guard let selfPtr = self._nsobjptr else {return}
		var contentViewPtr =  forSwift_objcSendMessage(&selfPtr.pointee, sel_registerName("contentView"))
		if var ptr = subview._nsobjptr, var contentViewPtr = contentViewPtr {
			_ = forSwift_objcSendMessage1(&contentViewPtr.pointee, sel_registerName("addSubview:"), &ptr.pointee)
		}
	}

	var contentView: NSView?
	public func setContentView(_ view: NSView) {
		guard let selfPtr = self._nsobjptr else {return}
		self.contentView = view
		if var ptr = view._nsobjptr {
			var _ =  forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setContentView:"), &ptr.pointee)
			_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("_invalidateCoordinates"), &ptr.pointee)
			_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("_rebuildCoordinates"), &ptr.pointee)
		}
	}

	public func printWindow() {
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("print:"), nil)
	}

	public func setFrameOrigin(_ origin: ObjCSwiftInterop.NSPoint) {
		guard var ptr = self._nsobjptr else {return}
		var origin = origin

		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setFrameOrigin:"), &origin)
	}
}

public class NSControl: NSView {
	public func setEnabled(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setEnabled:"), &bool)
	}

	public var stringValue: String {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "stringValue")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("stringValue")) {
				if let rtn = objc_convertToSwift_NSObject(value: rtn) as? NSString {
					return rtn.string
				}
			}
			return ""
		}
		set {			
			guard var selfPtr = self._nsobjptr else {return}
			let ns = NSString(string: newValue)
			guard var stringPTR = ns._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setStringValue:")
			let rtn = imp?(&selfPtr.pointee, sel_getUid("setStringValue:"), &stringPTR.pointee)
		}
	}
}

public class NSImageView: NSView {
	public override var _nsclassName: String {
		return "NSImageView"
	}

	public override init() {
		super.init()

		var rect = ObjCSwiftInterop.CGRect(x: 0, y: 0, width: 200, height: 22) //(x: Double(50), y: Double(50), width:  Double(50), height: Double(50))
		let  nsWindowClass =  objc_getClass("NSImageView")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		allocatedObject = forSwift_objcSendMessage1NSRect(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), rect)
		_ =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))

		self._nsobjptr = allocatedObject
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}

	public func setImage(_ image: NSImage) {
		print("Setting Image 1")
		guard var ptr = image._nsobjptr else {print("Setting Image image ptr == nil"); return}
		guard var selfPtr = self._nsobjptr else {print("Setting Image 1.2"); return}
		if var ptr = image._nsobjptr, var selfPtr = self._nsobjptr {
			print("Setting Image 2")
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setImage:"), ptr)
		}
	}
}

public class NSOpenGLView: NSView {
	public override var _nsclassName: String {
		return "NSOpenGLView"
	}

	public override init() {
		super.init()

		var rect = ObjCSwiftInterop.CGRect(x: 0, y: 0, width: 300, height: 300)
		let  nsViewClass =  objc_getClass("NSOpenGLView")
		
		var allocatedObject = forSwift_objcSendMessage(&nsViewClass!.pointee, sel_registerName("alloc"))
		allocatedObject = forSwift_objcSendMessage1NSRect(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), rect)
		_ =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))

		self._nsobjptr = allocatedObject
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}

	public func contextMakeCurrent() {
		let glcontext = forSwift_objcSendMessage(&self._nsobjptr!.pointee, sel_registerName("openGLContext"))
		forSwift_objcSendMessage(&glcontext!.pointee, sel_registerName("makeCurrentContext"))
	}

	public func display() {
		let glcontext = forSwift_objcSendMessage(&self._nsobjptr!.pointee, sel_registerName("openGLContext"))
		forSwift_objcSendMessage(&glcontext!.pointee, sel_registerName("flushBuffer"))
	}
}

public class NSButton: NSControl {
	static var _objcClass = GNUStepNSObjectSubclassConstructor(name: "NSButtonForSwift", superName: "NSButton", create: { ptr in})

	public override var _nsclassName: String {
		return "NSButton"
	}

	static var ___private_actionIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL,id?) -> (Void) = { first, second, third in
		let SELF: NSButton? = smart_swift_lookupIvarWithType(_nsobjptr: first, name: "___swiftPtr")
		if let SELF = SELF {
			SELF.onAction?(SELF)
		}
	}

	public lazy var onAction: ((NSButton) -> (Void))? = { first in }

	public override init() {
		super.init()
		_ = Self._objcClass
		var rect = ObjCSwiftInterop.CGRect(x: 0, y: 0, width: 200, height: 22) //(x: Double(50), y: Double(50), width:  Double(50), height: Double(50))
		let  nsWindowClass =  objc_getClass("NSButtonForSwift")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
		allocatedObject = forSwift_objcSendMessage1NSRect(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), rect)
		_ =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))

		var types = "@"
		let imp = imp_implementationWithBlock(unsafeBitCast(NSButton.___private_actionIMP, to: id.self))
		class_addMethod(object_getClass(&allocatedObject!.pointee), sel_registerName("___private_action:"),imp, types)

		_ = forSwift_objcSendMessage1ID(&allocatedObject!.pointee, sel_registerName("setTarget:"), allocatedObject!)
		var sel = sel_getUid("___private_action:")
		print("SEL = \(sel!)")
		_ = forSwift_objcSendMessage1SEL(&allocatedObject!.pointee, sel_registerName("setAction:"), sel!)

		self._nsobjptr = allocatedObject
		
		var cast = Unmanaged.passUnretained(self).toOpaque()
		print("in NSButton about to cast \(cast)")
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func setTitle(_ text: NSString) {
		if var ptr = text._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setTitle:"), ptr)
		}
	}

	public func setImage(_ image: NSImage) {
		if var ptr = image._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setImage:"), ptr)
		}
	}

}

public class NSTextField: NSControl {
	public override var _nsclassName: String {
		return "NSTextField"
	}

	public override init() {
		super.init()
		var rect = CGRect(x: 10, y: 10, width: 200, height: 50)
		let  nsWindowClass =  objc_getClass("NSTextField")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
		allocatedObject = forSwift_objcSendMessage1(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), &rect)
		allocatedObject =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = allocatedObject
		self.setText(NSString(string: "This is me!"))
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}

	public func setText(_ text: NSString) {
		if var ptr = text._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setStringValue:"), &ptr.pointee)
		}
	}

	public var font: NSFont? {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "font")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("font")) {
				return NSFont(nsobjptr: rtn)
			}
			return nil
		}

		set {
			print("SETTING FONT")
			guard let selfPtr = self._nsobjptr else {print("selfPtr = nil"); return}
			guard let newValuePtr = newValue?._nsobjptr else {print("newValue = nil \(newValue)"); return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setFont:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setFont:"), &newValuePtr.pointee)
		}
	}

	public func setEditable(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setEditable:"), &bool)
	}

	public func setSelectable(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setSelectable:"), &bool)
	}

	public func selectText() {
		guard var ptr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage(&ptr.pointee, sel_registerName("selectText"))
	}

	public var isSelectable: Bool {
		get {
			var imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isSelectable")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isSelectable")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setSelectable:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setSelectable:"), newValue ? 1 : 0)
		}
	}

	public var isEditable: Bool {
		get {
			var imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isEditable")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isEditable")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setEditable:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setEditable:"), newValue ? 1 : 0)
		}
	}

	public var textColor: NSColor {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "textColor")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("textColor")) {
				return NSColor(nsobjptr: rtn)
			}
			return .blue
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			guard let newValuePtr = newValue._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setTextColor:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setTextColor:"), &newValuePtr.pointee)
		}
	}

	public var isBordered: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isBordered")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isBordered")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setBordered:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setBordered:"), newValue ? 1 : 0)
		}
	}

	public var isBezeled: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isBezeled")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isBezeled")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setBezeled:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setBezeled:"), newValue ? 1 : 0)
		}
	}

	public var drawsBackground: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "drawsBackground")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("drawsBackground")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setDrawsBackground:")
			let _rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setDrawsBackground:"), newValue ? 1 : 0)
		}
	}
}


application package のディレクトリ配置

AppOpenGL
    /Headers ( GNUStepSwiftBridge と同じ)
    /Sources
         /AppKit
             AppKit.swift ( 編集した AppKit.swift)
             Foundation.swift ( 元ファイルの6行目import AppKitGNUStep 行をコメント)
             SwiftForGNUStep.swift ( 元ファイルの14行目import AppKitGNUStep 行をコメント)
         /AppKitGNUStep ( GNUStepSwiftBridge と同じ)
         /COpenGL
             module.modulemap
         /FoubdationGNUStep ( GNUStepSwiftBridge と同じ)
         /libobjc2 ( GNUStepSwiftBridge と同じ)
         /Main
             Resources ( 前回と同じ)
             AppDelegate.swift
             CheckResource.swift ( 前回と同じ)
             Cube.swift ( /sakrist/Swift_OpenGL_Example/Sources/app 内ファイルと同じ)
             Geometry.swift ( /sakrist/Swift_OpenGL_Example/Sources/app 内ファイルと同じ)
             main.swift
             RenderObject.swift ( /sakrist/GLApplication/Sources/GLApplication 内ファイルと同じ)
             Sene.swift ( /sakrist/Swift_OpenGL_Example/Sources/app 内ファイルと同じ)
             Shader.swift ( /sakrist/Swift_OpenGL_Example/Sources/app 内ファイルと同じ)
             String_extension.swift ( /sakrist/Swift_OpenGL_Example/Sources/app 内ファイルと同じ)
        /ObjCSwiftInterop ( GNUStepSwiftBridge と同じ)
        /SwiftMath ( /sakrist/SwiftMath/Sources  と同じ)
    Package.swift

OpenGL ライブラリ名は、COpenGL に変更。

COpenGL

module.modulemap
module COpenGL [system] {
     header "/usr/include/GL/gl.h"
     link "GL"
     export *
}


main.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import AppKitGNUStep
import ObjCSwiftInterop

// check ResourcesFile
CheckResource()

// main
let delegate = AppDelegate()

let napClass =  objc_getClass("NSApplication")
var sharedApp =  forSwift_objcSendMessage(&napClass!.pointee, sel_registerName("sharedApplication"))
print("Just created NSApplication")

let v = forSwift_objcSendMessage1ID(&sharedApp!.pointee, sel_getUid("setDelegate:"), delegate._nsobjptr!)
print("made it!")

NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv.pointee)


AppDelegate.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import Foundation
import AppKit
import SwiftMath

let fileManager = FileManager.default

let width  = 500.0
let height = 400.0

class AppDelegate: NSApplicationDelegate {
	lazy var window = NSWindow(CGRect(x: 150, y: 400, width: width, height: height + 50.0))
	lazy var view = UIView()
	lazy var glView = NSOpenGLView()
	lazy var button1 = NSButton()
	lazy var button2 = NSButton()
	lazy var button3 = NSButton()
	lazy var button4 = NSButton()
	lazy var textField1 = NSTextField()
	lazy var textField2 = NSTextField()

	override func applicationDidFinishLaunching(notification: Any?) {
		window.orderFront(sender: self)
		window.setTitle(NSString(string: "OpenGL Test"))

		var pitch:Float = 0.0
    	var yaw:Float = 0.0

		textField1.setBackgroundColor(.clear)
		textField1.textColor = .black
		textField1.isSelectable = false
		textField1.isBordered = false
		textField1.isBezeled = false
		textField1.drawsBackground = false
		textField1.frame = CGRect(x: 20, y: height + 20, width: 40, height: 30)
		textField1.stringValue = "pitch"

		button1.setTitle(NSString(string: "+"))
		button1.frame = .init(x: 70, y: height + 10, width: 30, height: 30)
		button2.setTitle(NSString(string: "-"))
		button2.frame = .init(x: 110, y: height + 10, width: 30, height: 30)

		textField2.setBackgroundColor(.clear)
		textField2.textColor = .black
		textField2.isSelectable = false
		textField2.isBordered = false
		textField2.isBezeled = false
		textField2.drawsBackground = false
		textField2.frame = CGRect(x: width / 2, y: height + 20, width: 40, height: 30)
		textField2.stringValue = "yaw"

		button3.setTitle(NSString(string: "+"))
		button3.frame = .init(x: width/2 + 50, y: height + 10, width: 30, height: 30)
		button4.setTitle(NSString(string: "-"))
		button4.frame = .init(x: width/2 + 90, y: height + 10, width: 30, height: 30)

		view.frame = .init(x: 0, y: 0, width: width, height: height)
		view.setBackgroundColor(NSColor(red: 0.8, green: 0.8, blue: 0.8, alpha: 1.0))

		glView.frame = .init(x: 0, y: 0, width: width, height: height)
		view.addSubview(glView)
		view.addSubview(textField1)
		view.addSubview(button1)
		view.addSubview(button2)
		view.addSubview(textField2)
		view.addSubview(button3)
		view.addSubview(button4)
		window.setContentView(view)

		glView.contextMakeCurrent()
		
		var renderObject: RenderObject?
    
    	var scene:Scene { 
        	get { 
            	return renderObject as! Scene
        	} 
        	set(scene) { 
            	renderObject = scene
        	}        
    	}

		scene = Scene()
		scene.size = Size(Int(width), Int(height))
        let rotateX = Matrix4x4f.rotate(x: Angle.init(radians: pitch))
        let rotateY = Matrix4x4f.rotate(y: Angle.init(radians: yaw))
        scene.modelViewMatrix = scene.modelViewMatrix * (rotateX * rotateY)
        scene.geometries.append(Cube())
		
		needsDisplay()

		button1.onAction = { button in
			pitch += 0.2
			//print("pitch yaw", pitch, yaw)
			let rotateX = Matrix4x4f.rotate(x: Angle.init(radians: pitch))
        	let rotateY = Matrix4x4f.rotate(y: Angle.init(radians: yaw))
        	scene.modelViewMatrix = Matrix4x4f.translate(tx: 0, ty: 0, tz: -4) * (rotateX * rotateY)
			needsDisplay()
		}
		button2.onAction = { button in
			pitch -= 0.2
			//print("pitch yaw", pitch, yaw)
			let rotateX = Matrix4x4f.rotate(x: Angle.init(radians: pitch))
        	let rotateY = Matrix4x4f.rotate(y: Angle.init(radians: yaw))
        	scene.modelViewMatrix = Matrix4x4f.translate(tx: 0, ty: 0, tz: -4) * (rotateX * rotateY)
			needsDisplay()
		}
		button3.onAction = { button in
			yaw += 0.2
			//print("pitch yaw", pitch, yaw)
			let rotateX = Matrix4x4f.rotate(x: Angle.init(radians: pitch))
        	let rotateY = Matrix4x4f.rotate(y: Angle.init(radians: yaw))
        	scene.modelViewMatrix = Matrix4x4f.translate(tx: 0, ty: 0, tz: -4) * (rotateX * rotateY)
			needsDisplay()
		}
		button4.onAction = { button in
			yaw -= 0.2
			//print("pitch yaw", pitch, yaw)
			let rotateX = Matrix4x4f.rotate(x: Angle.init(radians: pitch))
        	let rotateY = Matrix4x4f.rotate(y: Angle.init(radians: yaw))
        	scene.modelViewMatrix = Matrix4x4f.translate(tx: 0, ty: 0, tz: -4) * (rotateX * rotateY)
			needsDisplay()
		}
		
		func needsDisplay() {
			scene.render()
			glView.display()
		} 
	}
}


Package.swift

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
	name: "AppOpenGL",
	products: [
		// Products define the executables and libraries a package produces, making them visible to other packages.
		.executable(
			name: "Main",
			targets: ["Main"]),
	
		.library(name: "libobjc", targets: ["libobjc2"]),
		.library(name: "AppKitGNUStep", targets: ["AppKitGNUStep"]),
		.library(name: "FoundationGNUStep", targets: ["FoundationGNUStep"]),
		.library(name: "ObjCSwiftInterop",  targets: ["ObjCSwiftInterop"]),
		.library(name: "AppKit", targets: ["AppKit"]),
		.library(name: "SwiftMath", type: .static, targets: ["SwiftMath"]),
	],
	targets: [
		// Targets are the basic building blocks of a package, defining a module or a test suite.
		// Targets can depend on other targets in this package and products from dependencies.
		.executableTarget(
			name: "Main",
			dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop", "FoundationGNUStep", "AppKit", "COpenGL", "SwiftMath"],
			cSettings: [.define("GL_GLEXT_PROTOTYPES")]
		),
		
		.target(name: "ObjCSwiftInterop"),
		.target(name: "AppKit",
			dependencies: ["libobjc2", "ObjCSwiftInterop", "FoundationGNUStep"]),
		.target(name: "SwiftMath",
			swiftSettings: [ .define("NOSIMD", .when(platforms: [.linux, .android, .windows, .wasi, ])),]),
		
		.systemLibrary(name: "libobjc2"),
		.systemLibrary(name: "AppKitGNUStep"),
		.systemLibrary(name: "FoundationGNUStep"),
		.systemLibrary(name: "COpenGL"),
	]
)

AppKit ライブラリを application 内のライブラリにしました。
これはライブラリの編集をしやすくするためです。

全体のファイルは以下にあります。
https://drive.google.com/file/d/1G5q0Rq74WN9jZBkfHBN4FY0szGTishro/view?usp=sharing

GNUStepSwiftBridge & CairoGraphics

前回の GNUStepSwiftBridge を利用して、CairoGraphics を行いました。
実行結果
起動時

メニューアイテム(AItem)をクリック時

InfoPanel の表示

前回からの変更点
Resources ファイルの .build/debug ディレクトリへのコピー
 前回はResouces を手動で コピーしていましたが、これをプログラム内で行うようにしました。FileManager を使用します。
CheckResource.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

// check Resources Files
import Foundation

func CheckResource() {
    let fileManager = FileManager.default
    let currentPath = fileManager.currentDirectoryPath
    let filePath:String =  currentPath + "/Sources/Resources"
    let copyPath:String = ".build/debug/Resources"

    let exist = fileManager.fileExists(atPath: copyPath)
    if !exist {
        do {
            try fileManager.copyItem(atPath: filePath, toPath: copyPath)
    	} catch {
            print("can not copy")
    	}
    }
}

AppKit ライブラリ
 前回はAppKit ライブラリを application project と同じフォルダに置いていましたが、これをAppKit 単体のライブラリにしました。
方法は以下の通り。

1 mkdir AppKit
2 cd AppKit
   swift package init --type library
3 copy AppKitGNUStep , FoundationGNUStep, libobjc2 , ObjCSwiftInterop in Sources dir
   copy Headers in AppKit dir
4 edit Sources/AppKit/AppKit.swift
   copy Foundation.swift , SwiftForGNUStep.swift
5 edit Package.swift
   // swift-tools-version: 5.8
   // The swift-tools-version declares the minimum version of Swift required to build
   this package.

   import PackageDescription

   let package = Package(
       name: "AppKit",
       products: [
           // Products define the executables and libraries a package produces, 
                 making them visible to other packages.
           .library(name: "libobjc", targets: ["libobjc2"]),
           .library(name: "AppKitGNUStep", targets: ["AppKitGNUStep"]),
           .library(name: "FoundationGNUStep", targets: ["FoundationGNUStep"]),
           .library(name: "ObjCSwiftInterop",  targets: ["ObjCSwiftInterop"]),
           .library(
               name: "AppKit",
               targets: ["AppKit"]),
       ],
       targets: [
           // Targets are the basic building blocks of a package,
                 defining a module or a test suite.
           // Targets can depend on other targets in this package and products
                 from dependencies.
           .target(name: "ObjCSwiftInterop"),
           .target(
                name: "AppKit",
                dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop",
                                         "FoundationGNUStep"]),
           .systemLibrary(name: "libobjc2"),
           .systemLibrary(name: "AppKitGNUStep"),
           .systemLibrary(name: "FoundationGNUStep"),
           .testTarget(
                name: "AppKitTests",
                dependencies: ["AppKit"]),
       ]
   )
5 set git info
   in AppKit dir
       git init
       git add .
       git commit -m "Initial Commit"
       git tag 1.0.0

application (AppCG) ディレクトリ配置

    AppKit (上記ライブラリ)
    AppCG
       Sources
            CCairo
                 module.modulemap
            Resources
                 MainMenu.gorm
                 Info-gnustep.plist
                 ( tiff files )
            main.swift
            AppDelegate.swift
            Image.swift
            CGContext.swift
            CheckResource.swift
       Package.swift

MainMenu.gorm
Gorm を使って編集します。
 1 InfoPanel の表示
  メニューの InfoPanel を NSFirst に接続
  target から orderFrontStandardInfoPanel: を選択
 2 メニューアイテムの追加
  パレットウィンドウのメニューから、 Item をドラッグ

CCairo
module.modulemap

module CCairo [system] {
    module CairoXlib {
        header "/usr/include/cairo/cairo-xlib.h"
    }
    module Cairo {
        header "/usr/include/cairo/cairo.h"
    }
    link "cairo"
    export *
}

Package.swift

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AppCG",
    dependencies: [
        .package(url: "../AppKit", from: Version(1,0,0))
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "AppCG",
            dependencies: ["AppKit", "CCairo"],
            path: "Sources"
         ),
         .systemLibrary(
            name: "CCairo"),
    ]
)

main.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import AppKitGNUStep
import ObjCSwiftInterop
import Foundation

// check ResourcesFile
CheckResource()

// main
let fileManager = FileManager.default

let delegate = AppDelegate()

let napClass =  objc_getClass("NSApplication")
var sharedApp =  forSwift_objcSendMessage(&napClass!.pointee, sel_registerName("sharedApplication"))
print("Just created NSApplication")

let v = forSwift_objcSendMessage1ID(&sharedApp!.pointee, sel_getUid("setDelegate:"), delegate._nsobjptr!)
print("made it!")

NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv.pointee)

AppDelegate.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import FoundationGNUStep
import ObjCSwiftInterop
import AppKit
import CCairo.Cairo

let width = 640
let height = 480
var drawFlag = 1

func draw(_ context: CGContext, _ width: Float, _ height: Float) -> Void {
   // background
   context.setColor(1, 1, 1, 1)
   context.addRect(CGRect(x:0, y:0, width: Double(width), height: Double(height)))
   context.fillPath()

   switch (drawFlag) {
      case 1:
      // Draw random rects (some stroked, some filled)
      for i in 0...19 {
         if (i % 2 == 0) {
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.addRect(CGRect(
               x:Double.random(in: 0...1)*Double(width),
               y:Double.random(in: 0...1)*Double(height),
               width:Double.random(in: 0...1)*Double(width),
               height:Double.random(in: 0...1)*Double(height)))
            context.fillPath()
         }
         else {
            context.lineWidth = Float.random(in: 0...10) + 2
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.addRect(CGRect(
               x:Double.random(in: 0...1)*Double(width),
               y:Double.random(in: 0...1)*Double(height),
               width:Double.random(in: 0...1)*Double(width),
               height:Double.random(in: 0...1)*Double(height)))
            context.strokePath()
         }
      }

      case 2:
      // Draw random circles (some stroked, some filled)
      for i in 0...19 {
         context.addArc(
            center: CGPoint(x:Double.random(in: 0...1)*Double(width),
                            y:Double.random(in: 0...1)*Double(height)),
            radius: Float.random(in: 0...1)*((width>height) ? Float(height) : Float(width)),
            startAngle: 0, endAngle: 2*Float.pi, clockwise: false)
      
         if (i % 2 == 0) {
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.fillPath()
         }
         else {
            context.lineWidth = Float.random(in: 0...10) + 2
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.strokePath()
         }
      }

      default:
         break
   }
}

func display() {
	let cs = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, Int32(width), Int32(height) )
	let c = CGContext(surface: cs, width: width, height: height)

   // draw
   draw(c, Float(width), Float(height))
   cairo_surface_write_to_png( cs, "test.png" )
}

class AppDelegate: NSApplicationDelegate {
	lazy var window = NSWindow(CGRect(x: 150, y: 380, width: 500, height: 450))
	lazy var imageView = NSImageView()
	lazy var view = UIView()

	override func applicationDidFinishLaunching(notification: Any?) {
		print("\nget mainMenu")
		let menu =  forSwift_objcSendMessage(&sharedApp!.pointee, sel_registerName("mainMenu"))
		// menu: add Action
		print("item at index")
		var aindex : Int = 1
		print("item index:", aindex)
		let aindexNSInt: UnsafeMutablePointer<objc_object> = unsafeBitCast(aindex, to: UnsafeMutablePointer<objc_object>.self)
		let aitem = forSwift_objcSendMessage1ID(&menu!.pointee, sel_getUid("itemAtIndex:"), aindexNSInt)
		print("index \(aindex) item:", aitem)
		let aitem2 = objc_convertToSwift_NSObject(value: aitem) as? NSString
		let aitemtitle = forSwift_objcSendMessage(&aitem!.pointee, sel_registerName("title"))
		let aitemtitle2 = objc_convertToSwift_NSObject(value: aitemtitle) as? NSString
		print("index \(aindex) item:", aitemtitle2!.string)
		
		let my_actionIMP: @convention(block) () -> () = {
			print("print from menu")
			drawFlag = 2
            display()

			let currentPath = fileManager.currentDirectoryPath
			let filePath:String =  currentPath + "/test.png"
        	let image2 = Image.imageWithPath(filePath)
        	self.imageView.setImage(image2!)
		}
		let types = "@"
		let imp = imp_implementationWithBlock(unsafeBitCast(my_actionIMP, to: id.self))
		class_addMethod(object_getClass(&aitem!.pointee), sel_registerName("my_action:"),imp, types)
		
		_ = forSwift_objcSendMessage1ID(&aitem!.pointee, sel_registerName("setTarget:"), aitem!)
		let sel = sel_getUid("my_action:")
		_ = forSwift_objcSendMessage1SEL(&aitem!.pointee, sel_registerName("setAction:"), sel!)
		//
        print("end menu\n")
        
        display()
        
		let currentPath = fileManager.currentDirectoryPath
		let filePath:String =  currentPath + "/test.png"
    	let image = Image.imageWithPath(filePath)
        
		window.orderFront(sender: self)
		window.setTitle(NSString(string: "Image Test"))

		view.frame = .init(x: 0, y: 0, width: 500, height: 450)

		imageView.frame = .init(x: 0, y: 0, width: 500, height: 450)
		
		imageView.setImage(image!)
		
		view.addSubview(imageView)

		window.setContentView(view)
	}
}

Image.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import FoundationGNUStep
import ObjCSwiftInterop
import AppKit

class Image {
	static func imageWithPath(_ path: String) -> NSImage? {
		print("file path:", path)
		let nsfilePath = NSString(string: path)
		var nsImageClass = objc_getClass("NSImage")
		var initializedObject = forSwift_objcSendMessage(&nsImageClass!.pointee, sel_getUid("alloc"))
		var vimage = forSwift_objcSendMessage1(&initializedObject!.pointee, sel_getUid("initWithContentsOfFile:"), nsfilePath._nsobjptr)
        //print("vimage type:", type(of: vimage))
        let v = NSImage(nsobjptr: &vimage!.pointee)
        return v
    }
}

CGContext.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book
//
//  CGContext.swift
//  CairoGraphics
//

import ObjCSwiftInterop
import CCairo.Cairo

open class CGContext {
    public var surface: Optional<OpaquePointer>
    public var context: Optional<OpaquePointer>
    
    public internal(set) var height: Int = 0
    public internal(set) var width: Int = 0

    public init(surface: Optional<OpaquePointer>, width: Int, height: Int) {
        self.context = cairo_create(surface)
        self.surface = surface
        self.width = width
        self.height = height
    }
}

public extension CGContext {
    func flush() {
        cairo_surface_flush(surface)
    }
}

public extension CGContext {
    func beginPath() {
        cairo_new_path(context)
    }
    
    func closePath() {
        cairo_close_path(context)
    }
    
    var currentPointOfPath: CGPoint {
        var x: Double = .zero
        var y: Double = .zero
        cairo_get_current_point(context, &x, &y)
        return CGPoint(x: x, y: y)
    }
    
    var boundingBoxOfPath: CGRect {
        var x1: Double = .zero
        var y1: Double = .zero
        var x2: Double = .zero
        var y2: Double = .zero
        
        cairo_path_extents(context, &x1, &y1, &x2, &y2)
        
        if x1.isZero && y1.isZero && x2.isZero && y2.isZero {
            //return .null
            return CGRect(x: x1, y: y1, width: x2, height: y2)
        } else {
            return CGRect(x: min(x1, x2), y: min(y1, y2), width: max(x1, x2) - min(x1, x2), height: max(y1, y2) - min(y1, y2))
        }
    }
}

public extension CGContext {
    func move(to point: CGPoint) {
        cairo_move_to(context, Double(point.x), Double(point.y))
    }
    
    func addLine(to point: CGPoint) {
        cairo_line_to(context, Double(point.x), Double(point.y))
    }
    
    func addRect(_ rect: CGRect) {
        cairo_rectangle(context, Double(rect.x), Double(rect.y), Double(rect.width), Double(rect.height))
    }
    
    func addCurve(to end: CGPoint, control1: CGPoint, control2: CGPoint) {
        cairo_curve_to(context,
                       Double(control1.x), Double(control1.y),
                       Double(control2.x), Double(control2.y),
                       Double(end.x), Double(end.y))
    }
    
    func addQuadCurve(to end: CGPoint, control: CGPoint) {
        let current = currentPointOfPath
        
        let control1 = CGPoint(x: (current.x / 3.0) + (2.0 * control.x / 3.0), y: (current.y / 3.0) + (2.0 * control.y / 3.0))
        let control2 = CGPoint(x: (2.0 * control.x / 3.0) + (end.x / 3.0), y: (2.0 * control.y / 3.0) + (end.y / 3.0))
        
        addCurve(to: end, control1: control1, control2: control2)
    }
    
    func addLines(between points: [CGPoint]) {
        if points.count == 0 { return }
        
        move(to: points[0])
        
        for i in 1..<points.count {
            addLine(to: points[i])
        }
    }
    
    func addRects(_ rects: [CGRect]) {
        for rect in rects {
            addRect(rect)
        }
    }
    
    func addArc(center: CGPoint, radius: Float, startAngle: Float, endAngle: Float, clockwise: Bool) {
        if clockwise {
            cairo_arc_negative(context, Double(center.x), Double(center.y), Double(radius), Double(startAngle), Double(endAngle))
        } else {
            cairo_arc(context, Double(center.x), Double(center.y), Double(radius), Double(startAngle), Double(endAngle))
        }
    }
}

public extension CGContext {
    func fillPath() {
        cairo_fill(context)
    }

    func clip() {
        cairo_clip(context)
    }
    
    func resetClip() {
        cairo_reset_clip(context)
    }
    
    func strokePath() {
        cairo_stroke(context)
    }
}

public extension CGContext {
    func fill(_ rect: CGRect) {
        beginPath()
        addRect(rect)
        closePath()
    }
    
    func stroke(_ rect: CGRect) {
        beginPath()
        addRect(rect)
        closePath()
        strokePath()
    }
}

public extension CGContext {
    func setColor(_ r:Double, _ g:Double, _ b:Double, _ a:Double) {
        cairo_set_source_rgba(context, r, g, b, a);
    }
    
    var lineWidth: Float {
        get {
            return Float(cairo_get_line_width(context))
        }
        set {
            cairo_set_line_width(context, Double(newValue))
        }
    }    
}

GNUStepSwiftBridge

discuss-gnustep mailing list(https://mail.gnu.org/archive/html/discuss-gnustep/)
の8月のリストに、以下のような投稿がありました。
Swift calling into GNUStep Progress
github: https://github.com/austintatiousness/GNUStepSwiftBridge

このスレッドに中に、swift から GNUstep gui を呼び出した画像があります。

この gui 呼出しにトライしてみました。
実行結果

(ウィンドウサイズ等は変えてあります)

以下がその方法です。

GNUstep
GNUStepSwiftBridge project には、
 This project is a Swift interface for GNUStep's version of AppKit.
 This project assumes that you are running OnFlapp's GNUStep Desktop project.
とあります。
そこで、 OnFlapp's GNUStep Desktop をインストールしておきます。
OnFlapp's GNUStep Desktop https://github.com/onflapp/gs-desktop

インストールは、記された手順通りにすれば、問題なく行えます。
Lubuntuの repo からのインストールと異なり、システムのルートディレクトリに
/Applications , /Developer , /Library , /System が作成され、そこにインストール
される。

Swift
Linux のシステムは、これまで使用している Lubuntu をそのまま利用します。
Swiftは、バージョンを5.8.1 にして、前回同様インストールします。

GNUStepSwiftBridge
上記の github からダウンロードし、任意の場所に保存します。


準備が整ったら、GNUStepSwiftBridge のルートディレクトリから、
 swift run NSWindowTest
と実行します。import AppKit のエラーが出ます。
これは、AppKit ライブラリを呼び出していますが、このプロジェクト内に
AppKit ライブラリが定義されていません。ライブラリが期待される場所(Sources/AppKit)に AppKit.swift がありますが、内容がライブラリと異なっています。

その関連を調べていたら、update メッセージの中にそれらしきものがありました。
それをもとに書き直した AppKit.swift です。
AppKit.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

import FoundationGNUStep
import libobjc2
import AppKitGNUStep
import ObjCSwiftInterop

//NSApplication
open class NSApplication: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSApplication"
	}
	public var shared: NSApplication {
		get {
			var sharedApp =  forSwift_objcSendMessage(&self._nsobjptr!.pointee, sel_registerName("sharedApplication"))
			return NSApplication()
		}
	}
}

open class NSApplicationDelegate: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSApplicationDelegateForSwift"
	}

	static var didFinishLaunchingIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL,id?) -> (UInt8) = { first, second, third in
		let SELF: NSApplicationDelegate? = smart_swift_lookupIvarWithType(_nsobjptr: first, name: "___swiftPtr")
		SELF?.applicationDidFinishLaunching(notification: nil)
		return 0
	}

	open func applicationDidFinishLaunching(notification: Any?) {

	}

	static var _objcClass = GNUStepNSObjectSubclassConstructor(name: "NSApplicationDelegateForSwift", superName: "NSObject", create: { ptr in
		var types = "i@:@"
		let imp = imp_implementationWithBlock(unsafeBitCast(didFinishLaunchingIMP, to: id.self))
		class_addMethod(ptr, sel_registerName("applicationDidFinishLaunching:"),imp, types)
	})

	public override init() {
		_ = NSApplicationDelegate._objcClass
		super.init()

		var nclass = objc_getClass("NSApplicationDelegateForSwift")
		var initalizedObject = forSwift_objcSendMessage(&nclass!.pointee, sel_getUid("alloc"))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("init"))
		self._nsobjptr = initalizedObject

		var cast = Unmanaged.passUnretained(self).toOpaque()
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func didFinishLaunching(_ OBJ: Any, notification: Any?) -> Void {

	}
}

open class UIView: NSView {
	public override var _nsclassName: String {
		return "UIView"
	}

	static var isFlippedIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL) -> (UInt8) = { first, second in
		print("!!!!!!!!!!!!!!!!!!!!!!!!asked if is flipped!")
		return 1
	}

	static var _objcClass = GNUStepNSObjectSubclassConstructor(
		name: "UIView", superName: "NSScrollView", create: { ptr in }
	)

	public override init() {
		_ = UIView._objcClass
		super.init()

		let nclass = objc_getClass("UIView")

		let types = "c"
		let imp = imp_implementationWithBlock(unsafeBitCast(UIView.isFlippedIMP, to: id.self))
		class_replaceMethod(UIView._objcClass._nsobjptr!, sel_registerName("isFlipped"),imp, types)

		var initalizedObject = forSwift_objcSendMessage(&nclass!.pointee, sel_getUid("alloc"))
		initalizedObject = forSwift_objcSendMessage1NSRect(&initalizedObject!.pointee, sel_getUid("initWithFrame:"), CGRect(x: 0, y: 0, width: 100, height: 100))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("retain"))
		self._nsobjptr = initalizedObject

		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_getUid("_rebuildCoordinates"))
		
		let cast = Unmanaged.passUnretained(self).toOpaque()
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}
}


public class NSFont: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSFont"
	}

	public override init() {
		super.init()
	}

	public init?(name: String, size: Float) {
		super.init()

		var nsColorClass = objc_getClass("NSFont")
		var size = size
		var string = NSString(string: name)
		var imp:  (@convention(c) (id, SEL, id, Float) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "fontWithName:size:")
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("fontWithName:size:"), &string._nsobjptr!.pointee, size) {
			self._nsobjptr = rtn
			print("FOUND FONT")
			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
		} else {
			print("FONT NOT FOUND")
			return nil
		}
	}

	public static func boldSystemFontOfSize(size: Double) -> NSFont {
		var nsColorClass = objc_getClass("NSFont")
		var size = size

		var imp:  (@convention(c) (id, SEL, Double) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "boldSystemFontOfSize:")
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("boldSystemFontOfSize:"), size) {
			print("FOUND boldSystemFontOfSize")

			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
			return NSFont(nsobjptr: rtn)

		} else {
			return NSFont()
		}
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}
}

public class NSColor: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSColor"
	}

	public override init() {
		super.init()

		let nsColorClass =  objc_getClass(self._nsclassName)
		var initalizedObject = forSwift_objcSendMessage(&nsColorClass!.pointee, sel_registerName("blueColor"))
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = initalizedObject
	}

	public init(red: Float, green: Float, blue: Float, alpha: Float) {
		super.init()
		let nsColorClass =  objc_getClass(self._nsclassName)
		let red = red
		let green = green
		let blue = blue
		let alpha = alpha
		var initalizedObject = forSwift_objcSendMessage4Floats(&nsColorClass!.pointee, sel_registerName("colorWithCalibratedRed:green:blue:alpha:"), red, green, blue, alpha)
		initalizedObject = forSwift_objcSendMessage(&initalizedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = initalizedObject
	}

	public static var clear: NSColor {
		return .init(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0)
	}

	public static var blue: NSColor {
		return .init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
	}

	public static var black: NSColor {
		return .init(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
	}

	public static var white: NSColor {
		return .init(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
	}

	public static var grey: NSColor {
		return .init(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
	}

	public static var darkPurple: NSColor {
		return NSColor.init(red: 0.1422559619, green: 0.05977959186, blue: 0.2294596732, alpha: 1)
	}

	public static var purple: NSColor {
		let c =    NSColor.init(red: 0.2726541758, green: 0.1163508371, blue: 0.435738951, alpha: 1)
		return c
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}
}


open class NSImage: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSImage"
	}

	public static func image(named: String) -> NSImage? {
		print("finding image named \(named)")
		var nsColorClass = objc_getClass("NSImage")
		var nsColorClassAsClass =  object_getClass(objc_getClass("NSImage"))

		var string = NSString(string: named)
		var imp:  (@convention(c) (id, SEL, id) -> (id?))? = objc_smart_getIMP(id: &nsColorClass!.pointee, selector: "imageNamed:")
		print(imp)
		if var rtn = imp?(&nsColorClass!.pointee, sel_getUid("imageNamed:"), &string._nsobjptr!.pointee) {
			var initalizedObject = forSwift_objcSendMessage(&rtn.pointee, sel_registerName("retain"))
			let v = NSImage(nsobjptr: &rtn.pointee)
			print("image is named \(v.name)")
			return v
		}
		return nil
	}

	public var name: String {
		get {
			print("getting name")
			if var ptr = self._nsobjptr {
				var imp: (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "name")
				if let rtn = imp?(&ptr.pointee, sel_getUid("name")) {
					if let rtn = objc_convertToSwift_NSObject(value: rtn) as? NSString {
						return rtn.string
					}
				}
			}

			return ""
		}
	}
}

open class NSView: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSView"
	}

	public var subviews: [Any] = []
	public func addSubview(_ subview: NSView) {
		subviews.append(subview)
		guard let selfPtr = self._nsobjptr else {return}
		if var ptr = subview._nsobjptr{
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("addSubview:"), &ptr.pointee)

		}
	}

	public func setBackgroundColor(_ color:  NSColor) {
		guard let colorPtr = color._nsobjptr else {return}
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setBackgroundColor:"), &colorPtr.pointee)
	}

	public var frame: CGRect {
		get {
			let imp:  (@convention(c) (id, SEL) -> (CGRect))? = objc_smart_getIMP(object: self, selector: "frame")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("frame")) {
				return rtn
			}
			return .init(x: 0, y: 0, width: 0, height: 0)
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, CGRect) -> (Void))? = objc_smart_getIMP(object: self, selector: "setFrame:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setFrame:"), newValue)
		}
	}

	public func setFrame(_ rect: CGRect) {
		print("set frame: \(rect)")
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1NSRect(&selfPtr.pointee, sel_registerName("setFrame:"), rect)
	}

}

public class NSWindow: GNUStepNSObjectWrapper {
	public override var _nsclassName: String {
		return "NSWindow"
	}

	public override init() {
		super.init()

		let nsWindowClass =  objc_getClass(_nsclassName)
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		let styleMask: UInt = 1 + 2 + 4 + 8
		let backingStoreType: UInt = 0
		let deferr: Int8 = 0
		let rect = CGRect(x: 200, y: 200, width: 500, height: 500)
		allocatedObject = initWithContentRect_styleMask_backing_defer(&allocatedObject!.pointee, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, styleMask, backingStoreType, deferr)
		self._nsobjptr = allocatedObject
		self.setTitle(NSString(string: "Untitled Window"))
	}

	public init(_ rect: CGRect) {
		super.init()
		let nsWindowClass =  objc_getClass(_nsclassName)
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		let styleMask: UInt = 1 + 2 + 4 + 8
		let backingStoreType: UInt = 0
		let deferr: Int8 = 0
		allocatedObject = initWithContentRect_styleMask_backing_defer(&allocatedObject!.pointee, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, styleMask, backingStoreType, deferr)
		self._nsobjptr = allocatedObject
		self.setTitle(NSString(string: "Untitled Window"))
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func orderFront(sender:  Any?) {
		guard var ptr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("orderFront:"), nil)
	}


	public func setBackgroundColor(_ color:  NSColor) {
		guard let colorPtr = color._nsobjptr else {return}
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setBackgroundColor:"), &colorPtr.pointee)
	}

	var title: String {
		set {
			var title = NSString(string: newValue)
			if var ptr = title._nsobjptr, var selfPtr = self._nsobjptr {
				_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setTitle:"), &ptr.pointee)
			}
		}

		get {
			if let x: NSString = objc_smart_sendMessage(object: self, selector: "title", value1: StopVariable(), value2: StopVariable(), value3: StopVariable(), value4: StopVariable(), value5: StopVariable(), value6: StopVariable(), value7: StopVariable(), value8: StopVariable(), value9: StopVariable()) {
				return x.string
			}
			return ""
		}
	}

	public func setTitle(_ title: NSString) {
		if var ptr = title._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setTitle:"), &ptr.pointee)
		}
	}

	public var subviews: [Any] = []
	public func addSubview(_ subview: GNUStepNSObjectWrapper) {
		subviews.append(subview)
		guard let selfPtr = self._nsobjptr else {return}
		var contentViewPtr =  forSwift_objcSendMessage(&selfPtr.pointee, sel_registerName("contentView"))
		if var ptr = subview._nsobjptr, var contentViewPtr = contentViewPtr {
			_ = forSwift_objcSendMessage1(&contentViewPtr.pointee, sel_registerName("addSubview:"), &ptr.pointee)
		}
	}

	var contentView: NSView?
	public func setContentView(_ view: NSView) {
		guard let selfPtr = self._nsobjptr else {return}
		self.contentView = view
		if var ptr = view._nsobjptr {
			var _ =  forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setContentView:"), &ptr.pointee)
			_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("_invalidateCoordinates"), &ptr.pointee)
			_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("_rebuildCoordinates"), &ptr.pointee)
		}
	}

	public func printWindow() {
		guard let selfPtr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("print:"), nil)
	}

	public func setFrameOrigin(_ origin: ObjCSwiftInterop.NSPoint) {
		guard var ptr = self._nsobjptr else {return}
		var origin = origin

		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setFrameOrigin:"), &origin)
	}
}

public class NSControl: NSView {
	public func setEnabled(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setEnabled:"), &bool)
	}

	public var stringValue: String {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "stringValue")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("stringValue")) {
				if let rtn = objc_convertToSwift_NSObject(value: rtn) as? NSString {
					return rtn.string
				}
			}
			return ""
		}
		set {			
			guard var selfPtr = self._nsobjptr else {return}
			let ns = NSString(string: newValue)
			guard var stringPTR = ns._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setStringValue:")
			let rtn = imp?(&selfPtr.pointee, sel_getUid("setStringValue:"), &stringPTR.pointee)
		}
	}
}


public class NSImageView: NSView {
	public override var _nsclassName: String {
		return "NSImageView"
	}

	public override init() {
		super.init()

		var rect = ObjCSwiftInterop.CGRect(x: 0, y: 0, width: 200, height: 22) //(x: Double(50), y: Double(50), width:  Double(50), height: Double(50))
		let  nsWindowClass =  objc_getClass("NSImageView")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))

		allocatedObject = forSwift_objcSendMessage1NSRect(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), rect)
		_ =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))
		
		self._nsobjptr = allocatedObject
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}

	public func setImage(_ image: NSImage) {
		print("Setting Image 1")
		guard var ptr = image._nsobjptr else {print("Setting Image image ptr == nil"); return}
		guard var selfPtr = self._nsobjptr else {print("Setting Image 1.2"); return}
		if var ptr = image._nsobjptr, var selfPtr = self._nsobjptr {
			print("Setting Image 2")
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setImage:"), ptr)
		}
	}
}

public class NSButton: NSControl {
	static var _objcClass = GNUStepNSObjectSubclassConstructor(name: "NSButtonForSwift", superName: "NSButton", create: { ptr in})

	public override var _nsclassName: String {
		return "NSButton"
	}

	static var ___private_actionIMP: @convention(block) (UnsafeMutablePointer<objc_object>?,SEL,id?) -> (Void) = { first, second, third in
		let SELF: NSButton? = smart_swift_lookupIvarWithType(_nsobjptr: first, name: "___swiftPtr")
		if let SELF = SELF {
			SELF.onAction?(SELF)
		}
	}

	public lazy var onAction: ((NSButton) -> (Void))? = { first in }

	public override init() {
		super.init()
		_ = Self._objcClass
		var rect = ObjCSwiftInterop.CGRect(x: 0, y: 0, width: 200, height: 22) //(x: Double(50), y: Double(50), width:  Double(50), height: Double(50))
		let  nsWindowClass =  objc_getClass("NSButtonForSwift")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
		allocatedObject = forSwift_objcSendMessage1NSRect(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), rect)
		_ =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))

		var types = "@"
		let imp = imp_implementationWithBlock(unsafeBitCast(NSButton.___private_actionIMP, to: id.self))
		class_addMethod(object_getClass(&allocatedObject!.pointee), sel_registerName("___private_action:"),imp, types)

		_ = forSwift_objcSendMessage1ID(&allocatedObject!.pointee, sel_registerName("setTarget:"), allocatedObject!)
		var sel = sel_getUid("___private_action:")
		print("SEL = \(sel!)")
		_ = forSwift_objcSendMessage1SEL(&allocatedObject!.pointee, sel_registerName("setAction:"), sel!)

		self._nsobjptr = allocatedObject
		
		var cast = Unmanaged.passUnretained(self).toOpaque()
		print("in NSButton about to cast \(cast)")
		smart_swift_setIvar(_nsobjptr: self._nsobjptr, name: "___swiftPtr", value: cast)
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init(nsobjptr: nsobjptr)
	}

	public func setTitle(_ text: NSString) {
		if var ptr = text._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setTitle:"), ptr)
		}
	}

	public func setImage(_ image: NSImage) {
		if var ptr = image._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1ID(&selfPtr.pointee, sel_registerName("setImage:"), ptr)
		}
	}
}

public class NSTextField: NSControl {
	public override var _nsclassName: String {
		return "NSTextField"
	}

	public override init() {
		super.init()

		var rect = CGRect(x: 10, y: 10, width: 200, height: 50)
		let  nsWindowClass =  objc_getClass("NSTextField")
		var allocatedObject = forSwift_objcSendMessage(&nsWindowClass!.pointee, sel_registerName("alloc"))
		allocatedObject = forSwift_objcSendMessage1(&allocatedObject!.pointee, sel_registerName("initWithFrame:"), &rect)
		allocatedObject =  forSwift_objcSendMessage(&allocatedObject!.pointee, sel_registerName("retain"))
		self._nsobjptr = allocatedObject
		self.setText(NSString(string: "This is me!"))
	}

	public required init(nsobjptr: UnsafeMutablePointer<objc_object>?) {
		super.init()
		self._nsobjptr = nsobjptr
		var initalizedObject = forSwift_objcSendMessage(&nsobjptr!.pointee, sel_registerName("retain"))
	}

	public func setText(_ text: NSString) {
		if var ptr = text._nsobjptr, var selfPtr = self._nsobjptr {
			_ = forSwift_objcSendMessage1(&selfPtr.pointee, sel_registerName("setStringValue:"), &ptr.pointee)
		}
	}

	public var font: NSFont? {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "font")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("font")) {
				return NSFont(nsobjptr: rtn)
			}
			return nil
		}

		set {
			print("SETTING FONT")
			guard let selfPtr = self._nsobjptr else {print("selfPtr = nil"); return}
			guard let newValuePtr = newValue?._nsobjptr else {print("newValue = nil \(newValue)"); return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setFont:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setFont:"), &newValuePtr.pointee)
		}
	}

	public func setEditable(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setEditable:"), &bool)
	}

	public func setSelectable(_ flag: Bool) {
		guard var ptr = self._nsobjptr else {return}
		var bool = flag
		_ = forSwift_objcSendMessage1(&ptr.pointee, sel_registerName("setSelectable:"), &bool)
	}

	public func selectText() {
		guard var ptr = self._nsobjptr else {return}
		_ = forSwift_objcSendMessage(&ptr.pointee, sel_registerName("selectText"))
	}

	public var isSelectable: Bool {
		get {
			var imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isSelectable")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isSelectable")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setSelectable:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setSelectable:"), newValue ? 1 : 0)
		}
	}

	public var isEditable: Bool {
		get {
			var imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isEditable")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isEditable")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setEditable:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setEditable:"), newValue ? 1 : 0)
		}
	}

	public var textColor: NSColor {
		get {
			var imp:  (@convention(c) (id, SEL) -> (id))? = objc_smart_getIMP(object: self, selector: "textColor")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("textColor")) {
				return NSColor(nsobjptr: rtn)
			}
			return .blue
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			guard let newValuePtr = newValue._nsobjptr else {return}
			var imp:  (@convention(c) (id, SEL, id) -> (Void))? = objc_smart_getIMP(object: self, selector: "setTextColor:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setTextColor:"), &newValuePtr.pointee)
		}
	}

	public var isBordered: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isBordered")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isBordered")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setBordered:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setBordered:"), newValue ? 1 : 0)
		}
	}

	public var isBezeled: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "isBezeled")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("isBezeled")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setBezeled:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setBezeled:"), newValue ? 1 : 0)
		}
	}

	public var drawsBackground: Bool {
		get {
			let imp:  (@convention(c) (id, SEL) -> (UInt8))? = objc_smart_getIMP(object: self, selector: "drawsBackground")
			if let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("drawsBackground")) {
				return rtn == 0 ? false : true
			}
			return true
		}

		set {
			guard let selfPtr = self._nsobjptr else {return}
			let imp:  (@convention(c) (id, SEL, UInt8) -> (Void))? = objc_smart_getIMP(object: self, selector: "setDrawsBackground:")
			let rtn = imp?(&self._nsobjptr!.pointee, sel_getUid("setDrawsBackground:"), newValue ? 1 : 0)
		}
	}
}


この AppKit.swift をライブラリとして利用できるように、Package.swift を編集
します。
Package.swift

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
	name: "HelloWorld",
	products: [
		// Products define the executables and libraries a package produces, making them visible to other packages.
		.executable(
			name: "HelloWorld",
			targets: ["HelloWorld"]),
		
		.executable(
			name: "NSWindowTest",
			targets: ["NSWindowTest"]),
		
		.library(name: "libobjc", targets: ["libobjc2"]),
		
		.library(name: "AppKitGNUStep", targets: ["AppKitGNUStep"]),
		
		.library(name: "FoundationGNUStep", targets: ["FoundationGNUStep"]),
		.library(name: "ObjCSwiftInterop",  targets: ["ObjCSwiftInterop"]),

		.library(name: "AppKit", targets: ["AppKit"]),
	],
	targets: [
		// Targets are the basic building blocks of a package, defining a module or a test suite.
		// Targets can depend on other targets in this package and products from dependencies.
		.executableTarget(
			name: "HelloWorld",
			dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop", "FoundationGNUStep", "AppKit"]
          //resources: [.copy("Resources")]
      ),

		.executableTarget(
			name: "NSWindowTest",
			dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop", "FoundationGNUStep", "AppKit"]
			//resources: [.copy("Resources")]
		),
		
		.target(name: "ObjCSwiftInterop"),

		.target(
                                      name: "AppKit",
			dependencies: ["libobjc2", "AppKitGNUStep", "ObjCSwiftInterop", "FoundationGNUStep"]),
		
		.systemLibrary(name: "libobjc2"),
		.systemLibrary(name: "AppKitGNUStep"),
		.systemLibrary(name: "FoundationGNUStep"),

		.testTarget(
			name: "HelloWorldTests",
			dependencies: ["HelloWorld"]),
	]
)

次の部分を追加しています。
 products .library 文
 targets .executableTarget dependencies
 targets .target 文

NSWindowTest、HolloWorld の main.swift を以下のように編集します。
どちらも数行程、編集や追加を行っています。
NSWindowTest
main.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
import FoundationGNUStep
import libobjc2
import AppKitGNUStep
import ObjCSwiftInterop
import AppKit

@main
struct App {
	static var window = NSWindow()
	static var window2 = NSWindow()

	//static var label = NSLabel()
	static var label = NSTextField()
	static var button = NSButton()

	static func main() {
		print("Hello World Times Two")

		//var poolClass = objc_getClass("NSAutoreleasePool")
		//var poolClassAllocated =  forSwift_objcSendMessage(&objc_getClass("NSAutoreleasePool")!.pointee, sel_registerName("alloc"))
		//_ =  forSwift_objcSendMessage(&poolClassAllocated!.pointee, sel_registerName("init"))

		let napClass =  objc_getClass("NSApplication")
		var sharedApp =  forSwift_objcSendMessage(&napClass!.pointee, sel_registerName("sharedApplication"))
		
		App.window.orderFront(sender: nil)
		App.window.setBackgroundColor(NSColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.5))

		App.label.frame = .init(x: 20, y: 20, width: 200, height: 32)
		
		App.label.setBackgroundColor(NSColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0))
		App.window.addSubview(App.label)
		
		let image = NSImage.image(named: "Dummy")

		App.button.frame = .init(x: 50, y: 100, width: 100, height: 30)

		App.window2.orderFront(sender: nil)
		App.window2.setFrameOrigin(NSPoint(x: 300, y: 300))
		App.window2.setTitle(NSString(string: "Window 2"))
		App.window2.addSubview(App.button)
		
		//App.window.printWindow()
		

		//window.setBackgroundColor(NSColor())

//'UnsafeMutablePointer<Int8>
//UnsafeMutablePointer<UnsafePointer<CChar>?>
		//return
		return NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv.pointee)		
	}
}

下の2行を追加しています。
 1 App.label.frame = .init(x: 20, y: 20, width: 200, height: 32)
  この行がないと、TextField が表示されない。
 2 let image = NSImage.image(named: "Dummy")
  この行がないと、Button 生成でコアダンプが起きる。
  App.button の前に必要。

HolloWorld
main.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
import FoundationGNUStep
import libobjc2
import AppKitGNUStep
import ObjCSwiftInterop
import AppKit

class AppDelegate: NSApplicationDelegate {
	//lazy var window = NSWindow()
	//lazy var newWindow = NSWindow()
	lazy var window = NSWindow(CGRect(x: 150, y: 300, width: 550, height: 450))
	lazy var newWindow = NSWindow(CGRect(x: 100, y: 100, width: 400, height: 400))
	lazy var button = NSButton()
	lazy var imageView = NSImageView()
	lazy var button2 = NSButton()
	lazy var button3 = NSButton()
	lazy var newWindowButton = NSButton()
	lazy var textField = NSTextField()
	lazy var image = NSImage.image(named: "Terminal.tiff")
	lazy var textField2 = NSTextField()
	lazy var view = UIView()
	override func applicationDidFinishLaunching(notification: Any?) {
		window.orderFront(sender: self)
		window.setTitle(NSString(string: "Hello Window"))

		view.frame = .init(x: 0, y: 0, width: 500, height: 450)

		newWindowButton.setTitle(NSString(string: "Show Window"))
		newWindowButton.frame = .init(x: 10, y: 22, width: 100, height: 32)
		newWindowButton.onAction = { button in
			self.newWindow.orderFront(sender: self)
			self.newWindow.setBackgroundColor(.purple)
		}
		view.addSubview(newWindowButton)
		
		imageView.frame = .init(x: 340, y: 22, width: 32, height: 32)
		imageView.setImage(self.image!)
		view.addSubview(imageView)
		
		button.setTitle(NSString(string: "Get Frame"))
		button.frame = .init(x: 120, y: 22, width: 100, height: 32)
		button.onAction = { button in
			self.textField.stringValue = "\(self.view.frame)"
		}
		view.addSubview(button)
		
		button2.setTitle(NSString(string: "Set Other Window"))
		button2.frame = .init(x: 230, y: 22, width: 100, height: 32)
		button2.onAction = { button in
			//self.view.frame = .init(x: 0, y: 32, width: 300, height: 300)
			self.newWindow.setBackgroundColor(.black)
		}
		view.addSubview(button2)
			
		button3.setImage(image!)
		button3.frame = .init(x: 430, y: 22, width: 100, height: 32)
		button3.onAction = { button in
			//self.view.frame = .init(x: 0, y: 32, width: 300, height: 300)
			//self.newWindow.setBackgroundColor(.black)
			self.newWindow.setBackgroundColor(.grey)
		}
		view.addSubview(button3)	
		
		textField.setBackgroundColor(.clear)
		//textField.textColor = .init(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.5)
		textField.textColor = .blue
		textField.isSelectable = false
		textField.isBordered = false
		textField.isBezeled = false
		textField.drawsBackground = false
		
		textField.frame = CGRect(x: 10, y: 100, width: 300, height: 32)
		//textField.text = "Click 'Get Frame'"
		textField.stringValue = "Click 'Get Frame'"
		textField.font = .boldSystemFontOfSize(size: 30)
		view.addSubview(textField)

		textField2.frame = CGRect(x: 10, y: 140, width: 300, height: 32)
		//textField2.text = "Some Cool Text"
		textField2.stringValue = "Some Cool Text"
		textField2.textColor = .white
		textField2.setBackgroundColor(.grey)
		view.addSubview(textField2)
		
		view.setBackgroundColor(.init(red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0))
		window.setContentView(view)
	}
}

@main
struct App {
	//static var window = NSWindow()
	static var delegate = AppDelegate()
	//static var window2 = NSWindow()

	//static var label = NSTextField()
	static func main() {
		print("Hello World Times Two")

		App.delegate

		//var poolClass = objc_getClass("NSAutoreleasePool")
		//var poolClassAllocated =  forSwift_objcSendMessage(&objc_getClass("NSAutoreleasePool")!.pointee, sel_registerName("alloc"))
		//_ =  forSwift_objcSendMessage(&poolClassAllocated!.pointee, sel_registerName("init"))

		
		//https://stackoverflow.com/questions/24662864/swift-how-to-use-sizeof
			
		let napClass =  objc_getClass("NSApplication")
		var sharedApp =  forSwift_objcSendMessage(&napClass!.pointee, sel_registerName("sharedApplication"))
		print("Just created NSApplication")
		
		let v = forSwift_objcSendMessage1ID(&sharedApp!.pointee, sel_getUid("setDelegate:"), delegate._nsobjptr!)
		print("made it!")

		//window.setBackgroundColor(NSColor())

//'UnsafeMutablePointer<Int8>
//UnsafeMutablePointer<UnsafePointer<CChar>?>
		//return
		return NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv.pointee)		
	}
}

1 TextField や Button は frame 設定がないと表示されない。
2 TextField の文字は text では表示されない。stringValue を用いる。


これで準備が整ったので、NSWindowTest と HelloWorld を実行します。
 swift run NSWindowTest
 swift run HelloWorld
NSWindowTest は実行できますが、HelloWorld ではコアダンプが起きます。
これは、ビルドされた実行ファイルがある場所に、gorm ファイルを含むResources
がないために起こっています。
この project site の最初に書いてあるように、Resources(NSWindowTest か HelloWorld のいずれか) を .build/debug にコピーします。(一度 swift run を実行しないと .build ディレクトリは作成されない。)今のところ、プログラムの中でコピーする方法が分かりません。

これで、 swift run を実行をすれば、正しく動作するはずです。
gorm ファイルのメニューも表示されます。

HolloWorld では、ボールドフォントのサイズ設定を行っています。
Lubuntu のシェルからの実行では、フォントが見つかりません。
/System/bin/startgsde を実行し、GWorkspace の Terminal から実行します。

Swift CairoGraphics

前回報告した GNUstep CoreGraphics は、「今更 GNUstep ?」感がありますので、同様な事を Swift で行ってみました。
Linux(Lubuntu 22.04) の Swift(v5.8) を使用しています。
(Swift のインストールは、UbuntuでSwiftの環境構築を行う方法を参照。
 バージョンは Ubuntu バージョンに揃えます。)

cairo ライブラリで描画した画像を、OpenGLで texture として描画します。
OpenGLは glut ライブラリを用い、
Use a C library in Swift on Linux - Stack Overflow
に記述されている方法で利用します。
cairo ライブラリは、
AppKid
GitHub - smumriak/AppKid: UI toolkit for Linux in Swift. Powered by Vulkan
にあるCairoGraphics 関連を参照しました。

実行結果
起動時の画面 (Draw Rects) とメニュー(右クリックで表示)

Draw Circles

Draw Paths

Draw Clip

プログラム
ディレクトリ構成

TestCG
    Package.swift
    Sources
        CCairo
            module.modulemap
        CFreeGLUT
            module.modulemap
        COpenGL
             module.modulemap
        COpenGLU
             module.modulemap
        main.swift
        CGContext.swift

TestCG ディレクトリで、swift package init --type executable を実行する。
CCairo 等の各 module は、ディレクトリを作り modulemap ファイルを作成する。

CCairo
module.modulemap

module CCairo [system] {
    module CairoXlib {
        header "/usr/include/cairo/cairo-xlib.h"
    }
    module Cairo {
        header "/usr/include/cairo/cairo.h"
    }
    link "cairo"
    export *
}

CFreeGLUT
module.modulemap

module CFreeGLUT [system] {
    header "/usr/include/GL/freeglut.h"
    link "glut"
    export *
}

COpenGL
module.modulemap

module COpenGL [system] {
    header "/usr/include/GL/gl.h"
    link "GL"
    export *
}

COpenGLU
module.modulemap

module COpenGLU [system] {
    header "/usr/include/GL/glu.h"
    link "GLU"
    export *
}

Package.swift

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "TestCG",
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "TestCG",
            dependencies: ["COpenGL", "COpenGLU", "CFreeGLUT", "CCairo"],
            path: "Sources"),
        .systemLibrary(
            name: "COpenGL"),
        .systemLibrary(
            name: "COpenGLU"),
        .systemLibrary(
            name: "CFreeGLUT"),
        .systemLibrary(
            name: "CCairo"),
    ]
)

main.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book

//print("Hello, world!")
import COpenGL
import COpenGLU
import CFreeGLUT
import CCairo.Cairo
import Foundation

let width = 640
let height = 480
var drawFlag = 1

func drawRandomPaths(_ context: CGContext, _ width: CGFloat, _ height: CGFloat) -> Void {
   // Draw random paths (some stroked, some filled)
   for i in 0...19 {
      let numberOfSegments = Int.random(in: 0...7)
      let sx = CGFloat.random(in: 0...1) * width
      let sy = CGFloat.random(in: 0...1) * height
      context.move(to: CGPoint(
         x:CGFloat.random(in: 0...1)*width,
         y:CGFloat.random(in: 0...1)*height
      ))

      for j in 0...numberOfSegments {
         if (j % 2 == 0) {
            context.addLine(to: CGPoint(
               x:CGFloat.random(in: 0...1)*width,
               y:CGFloat.random(in: 0...1)*height
            ))
         }
         else {
            context.addCurve(
               to: CGPoint(
                  x:CGFloat.random(in: 0...1)*height,
                  y:CGFloat.random(in: 0...1)*height
               ),
               control1: CGPoint(
                  x:CGFloat.random(in: 0...1)*width,
                  y:CGFloat.random(in: 0...1)*height
               ),
               control2: CGPoint(
                  x:CGFloat.random(in: 0...1)*width,
                  y:CGFloat.random(in: 0...1)*height
               )
            )
         }
      }

      if (i % 2 == 0) {
         context.addCurve(
            to: CGPoint(x:sx, y:sy),
            control1: CGPoint(
               x:CGFloat.random(in: 0...1)*width,
               y:CGFloat.random(in: 0...1)*height
            ),
            control2: CGPoint(
               x:CGFloat.random(in: 0...1)*width,
               y:CGFloat.random(in: 0...1)*height
            )
         )
         context.closePath()
         context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
            Double.random(in: 0...1),  Double.random(in: 0...1))
         context.fillPath()
            
      }
      else {
            context.lineWidth = CGFloat.random(in: 0...10) + 2
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
                Double.random(in: 0...1),  Double.random(in: 0...1))
            context.strokePath()
      }
   }
}

func draw(_ context: CGContext, _ width: CGFloat, _ height: CGFloat) -> Void {
   // background
   context.setColor(1, 1, 1, 1)
   context.addRect(CGRect(x:0, y:0, width:width, height:height))
   context.fillPath()

   switch (drawFlag) {
      case 1:
      // Draw random rects (some stroked, some filled)
      for i in 0...19 {
         if (i % 2 == 0) {
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.addRect(CGRect(
               x:CGFloat.random(in: 0...1)*width,
               y:CGFloat.random(in: 0...1)*height,
               width:CGFloat.random(in: 0...1)*width,
               height:CGFloat.random(in: 0...1)*height))
            context.fillPath()
         }
         else {
            context.lineWidth = CGFloat.random(in: 0...10) + 2
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.addRect(CGRect(
               x:CGFloat.random(in: 0...1)*width,
               y:CGFloat.random(in: 0...1)*height,
               width:CGFloat.random(in: 0...1)*width,
               height:CGFloat.random(in: 0...1)*height))
            context.strokePath()
         }
      }

      case 2:
      // Draw random circles (some stroked, some filled)
      for i in 0...19 {
         context.addArc(
            center: CGPoint(x:CGFloat.random(in: 0...1)*CGFloat(width),
                            y:CGFloat.random(in: 0...1)*CGFloat(height)),
            radius: CGFloat.random(in: 0...1)*((width>height) ? CGFloat(height) : CGFloat(width)),
            startAngle: 0, endAngle: 2*CGFloat.pi, clockwise: false)
      
         if (i % 2 == 0) {
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.fillPath()
         }
         else {
            context.lineWidth = CGFloat.random(in: 0...10) + 2
            context.setColor( Double.random(in: 0...1), Double.random(in: 0...1),
               Double.random(in: 0...1),  Double.random(in: 0...1))
            context.strokePath()
         }
      }

      case 3:
      // Draw random paths (some stroked, some filled)
      drawRandomPaths(context, width, height)

      case 4:
      // Cliped: Draw random paths (some stroked, some filled)
      context.addArc(
         center: CGPoint(x:width/2, y:height/2),
         radius: (width>height) ? height/2 : width/2,
         startAngle: 0, endAngle: 2*CGFloat.pi, clockwise: false)
      context.closePath()
      context.clip()

      drawRandomPaths(context, width, height)

      context.lineWidth = 1
      context.setColor(0, 0, 0, 1)
      context.strokePath()

      default:
         break
   }
}

// Menu
func showMenuItem(_ val: Int32) {
   switch (val) {
      case 1:
         drawFlag = 1
      case 2:
         drawFlag = 2
      case 3:
         drawFlag = 3
      case 4:
         drawFlag = 4
      case 999:
         glutLeaveMainLoop()
      default:
         break
   }
}

func setupMenus() {
   glutCreateMenu(showMenuItem)

   glutAddMenuEntry("Draw Rects", 1)
   glutAddMenuEntry("Draw Circles", 2)
   glutAddMenuEntry("Draw Paths", 3)
   glutAddMenuEntry("Draw Clip", 4)
   glutAddMenuEntry("Exit", 999)

   glutAttachMenu(GLUT_RIGHT_BUTTON)
}

func display() {
   let cs = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, Int32(width), Int32(height) )
   let c = CGContext(surface: cs, width: width, height: height)


   // draw
   draw(c, CGFloat(width), CGFloat(height))

   var _texture: GLuint = 0
   glGenTextures(1, &_texture);
   glBindTexture(UInt32(GL_TEXTURE_2D), _texture);
   glPixelStorei(UInt32(GL_UNPACK_ALIGNMENT), 1);

   let data = cairo_image_surface_get_data(cs);
   gluBuild2DMipmaps(UInt32(GL_TEXTURE_2D), Int32(GL_RGBA), Int32(width), Int32(height), UInt32(GL_RGBA), UInt32(GL_UNSIGNED_BYTE), data);
   
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClear(UInt32(GL_COLOR_BUFFER_BIT))
   glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)
   
   glEnable(UInt32(GL_TEXTURE_2D));
   glBindTexture(UInt32(GL_TEXTURE_2D), _texture);
   glColor3f(1,1,1);

   glBegin(UInt32(GL_QUADS));
      glTexCoord2f(0.0, 1.0);
      glVertex2f(-1.0, -1.0);

      glTexCoord2f(0.0, 0.0);
      glVertex2f(-1.0, 1.0);

      glTexCoord2f(1.0, 0.0);
      glVertex2f(1.0, 1.0);

      glTexCoord2f(1.0, 1.0);
      glVertex2f(1.0, -1.0);
   glEnd();

   glFlush()
}

var localArgc = CommandLine.argc
glutInit(&localArgc, CommandLine.unsafeArgv)
glutInitDisplayMode(UInt32(GLUT_SINGLE | GLUT_RGB))
glutInitWindowPosition(80, 80)
glutInitWindowSize(Int32(width), Int32(height))
glutCreateWindow("CairoGraphics")
glutDisplayFunc(display)

setupMenus()
glutMainLoop()

CGContext.swift

// The Swift Programming Language
// https://docs.swift.org/swift-book
//
//  CGContext.swift
//  CairoGraphics
//
//  Created by Serhii Mumriak on 03.02.2020.
//

import Foundation
import CCairo.Cairo

open class CGContext {
    public var surface: Optional<OpaquePointer>
    public var context: Optional<OpaquePointer>
    
    public internal(set) var height: Int = 0
    public internal(set) var width: Int = 0

    public init(surface: Optional<OpaquePointer>, width: Int, height: Int) {
        self.context = cairo_create(surface)
        self.surface = surface
        self.width = width
        self.height = height
    }
}

public extension CGContext {
    func flush() {
        cairo_surface_flush(surface)
    }
}

public extension CGContext {
    func beginPath() {
        cairo_new_path(context)
    }
    
    func closePath() {
        cairo_close_path(context)
    }
    
    var currentPointOfPath: CGPoint {
        var x: Double = .zero
        var y: Double = .zero
        cairo_get_current_point(context, &x, &y)
        return CGPoint(x: x, y: y)
    }
    
    var boundingBoxOfPath: CGRect {
        var x1: Double = .zero
        var y1: Double = .zero
        var x2: Double = .zero
        var y2: Double = .zero
        
        cairo_path_extents(context, &x1, &y1, &x2, &y2)
        
        if x1.isZero && y1.isZero && x2.isZero && y2.isZero {
            return .null
        } else {
            return CGRect(x: min(x1, x2), y: min(y1, y2), width: max(x1, x2) - min(x1, x2), height: max(y1, y2) - min(y1, y2))
        }
    }
}

public extension CGContext {
    func move(to point: CGPoint) {
        cairo_move_to(context, Double(point.x), Double(point.y))
    }
    
    func addLine(to point: CGPoint) {
        cairo_line_to(context, Double(point.x), Double(point.y))
    }
    
    func addRect(_ rect: CGRect) {
        cairo_rectangle(context, Double(rect.origin.x), Double(rect.origin.y), Double(rect.width), Double(rect.height))
    }
    
    func addCurve(to end: CGPoint, control1: CGPoint, control2: CGPoint) {
        cairo_curve_to(context,
                       Double(control1.x), Double(control1.y),
                       Double(control2.x), Double(control2.y),
                       Double(end.x), Double(end.y))
    }
    
    func addQuadCurve(to end: CGPoint, control: CGPoint) {
        let current = currentPointOfPath
        
        let control1 = CGPoint(x: (current.x / 3.0) + (2.0 * control.x / 3.0), y: (current.y / 3.0) + (2.0 * control.y / 3.0))
        let control2 = CGPoint(x: (2.0 * control.x / 3.0) + (end.x / 3.0), y: (2.0 * control.y / 3.0) + (end.y / 3.0))
        
        addCurve(to: end, control1: control1, control2: control2)
    }
    
    func addLines(between points: [CGPoint]) {
        if points.count == 0 { return }
        
        move(to: points[0])
        
        for i in 1..<points.count {
            addLine(to: points[i])
        }
    }
    
    func addRects(_ rects: [CGRect]) {
        for rect in rects {
            addRect(rect)
        }
    }
    
    func addArc(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) {
        if clockwise {
            cairo_arc_negative(context, Double(center.x), Double(center.y), Double(radius), Double(startAngle), Double(endAngle))
        } else {
            cairo_arc(context, Double(center.x), Double(center.y), Double(radius), Double(startAngle), Double(endAngle))
        }
    }
}

public extension CGContext {
    func fillPath() {
        cairo_fill(context)
    }

    func clip() {
        cairo_clip(context)
    }
    
    func resetClip() {
        cairo_reset_clip(context)
    }
    
    func strokePath() {
        cairo_stroke(context)
    }
}

public extension CGContext {
    func fill(_ rect: CGRect) {
        beginPath()
        addRect(rect)
        closePath()
    }
    
    func stroke(_ rect: CGRect) {
        beginPath()
        addRect(rect)
        closePath()
        strokePath()
    }
}

public extension CGContext {
    func setColor(_ r:Double, _ g:Double, _ b:Double, _ a:Double) {
        cairo_set_source_rgba(context, r, g, b, a);
    }
    
    var lineWidth: CGFloat {
        get {
            return CGFloat(cairo_get_line_width(context))
        }
        set {
            cairo_set_line_width(context, Double(newValue))
        }
    }    
}