Vala プログラミング

WebGPU プログラミング

おなが@京都先端科学大

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 として描画します。
OpenGLglut ライブラリを用い、
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))
        }
    }    
}

GNUstep CoreGraphics (2)

前回報告した ProjectCenter を使って作成した CoreGraphics のプログラムです。

実行結果
起動時の画面 (Draw1)

Draw2(メニューから Draw → Draw2 を選択)

Draw3

Draw4

プログラム
(プロジェクト生成時以降、編集したプログラム)
Headers
AppController.h

#ifndef _PCAPPPROJ_APPCONTROLLER_H
#define _PCAPPPROJ_APPCONTROLLER_H

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "QEB2OpenGLView.h"

@interface AppController : NSObject
{
    NSWindow *window;
    QEB2OpenGLView * openGLView;
}

+ (void)  initialize;

- (id) init;
- (void) dealloc;

- (void) awakeFromNib;

- (void) applicationDidFinishLaunching: (NSNotification *)aNotif;
- (BOOL) applicationShouldTerminate: (id)sender;
- (void) applicationWillTerminate: (NSNotification *)aNotif;
- (BOOL) application: (NSApplication *)application
              openFile: (NSString *)fileName;

- (void) showPrefPanel: (id)sender;
- (void) drawAction1: (id)sender;
- (void) drawAction2: (id)sender;
- (void) drawAction3: (id)sender;
- (void) drawAction4: (id)sender;

@end

#endif

QEB2OpenGLView.h (新しく生成)

#import <GL/gl.h>
#import <GL/glu.h>
#import <cairo/cairo.h>

#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

@interface QEB2OpenGLView : NSOpenGLView
{
   cairo_surface_t * _cairoSurface;
   CGContextRef _opalContext;
   GLuint _texture;
   CGRect rootRect;
}

- (void) draw1;
- (void) draw2;
- (void) draw3;
- (void) draw4;
- (void) reDraw;
- (void) clearContextWithRect:(CGRect)rect;
@end

Classes
AppController.m

#import "AppController.h"

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

@implementation AppController

+ (void) initialize
{
   NSMutableDictionary *defaults = [NSMutableDictionary dictionary];

   /*
    * Register your app's defaults here by adding objects to the
    * dictionary, eg
    *
    * [defaults setObject:anObject forKey:keyForThatObject];
    *
    */
  
   [[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
   [[NSUserDefaults standardUserDefaults] synchronize];
}

- (id) init
{
   if ((self = [super init]))
   {
   }
      return self;
}

- (void) dealloc
{
   RELEASE (openGLView);
   RELEASE (window);
   [super dealloc];
}

- (void) awakeFromNib
{
}

- (void) applicationDidFinishLaunching: (NSNotification *)aNotif
{ 
   openGLView = [[QEB2OpenGLView alloc]
               initWithFrame: [[window contentView] frame]
                   pixelFormat: [QEB2OpenGLView defaultPixelFormat]];
  
   [window setContentView: openGLView];
  
   [openGLView reDraw];
}

- (BOOL) applicationShouldTerminate: (id)sender
{
   return YES;
}

- (void) applicationWillTerminate: (NSNotification *)aNotif
{
}

- (BOOL) application: (NSApplication *)application
       	      openFile: (NSString *)fileName
{
   return NO;
}

- (void) showPrefPanel: (id)sender
{
}

- (void) drawAction1: (id)sender
{
   NSLog(@"drawAction1");
   [openGLView draw1];
}

- (void) drawAction2: (id)sender
{
   NSLog(@"drawAction2");
   [openGLView draw2];
}

- (void) drawAction3: (id)sender
{
   NSLog(@"drawAction3");
   [openGLView draw3];
}

- (void) drawAction4: (id)sender
{
   NSLog(@"drawAction4");
   [openGLView draw4];
}

@end

QEB2OpenGLView.m (新しく生成)

#import "QEB2OpenGLView.h"

#define TEXTURE_TARGET GL_TEXTURE_2D

#define PI 3.14159265358979323846

CGContextRef opal_new_CGContext(cairo_surface_t *target, CGSize device_size);

void drawRandomPaths(CGContextRef context, int w, int h)
{
   for (int i = 0; i < 20; i++) {
      int numberOfSegments = rand() % 8;
      int j;
      CGFloat sx, sy;
        
      CGContextBeginPath(context);
      sx = rand()%w; sy = rand()%h;
      CGContextMoveToPoint(context, rand()%w, rand()%h);
      for (j = 0; j < numberOfSegments; j++) {
         if (j % 2) {
            CGContextAddLineToPoint(context, rand()%w, rand()%h);
         }
         else {
            CGContextAddCurveToPoint(context, rand()%w, rand()%h,  
               rand()%w, rand()%h,  rand()%h, rand()%h);
         }
      }
      if(i % 2) {
         CGContextAddCurveToPoint(context, rand()%w, rand()%h,
            rand()%w, rand()%h,  sx, sy);
         CGContextClosePath(context);
         CGContextSetRGBFillColor(context, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextFillPath(context);
      }
      else {
         CGContextSetLineWidth(context, (rand()%10)+2);
         CGContextSetRGBStrokeColor(context, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextStrokePath(context);
      }
   }
}

@implementation QEB2OpenGLView

- (void) draw1
{
   NSLog(@"GLView draw1");
   [self clearContextWithRect:rootRect];
   
   int width  = rootRect.size.width;
   int height = rootRect.size.height;
  
   for (int i = 0; i < 20; i++) {
      if(i % 2) {
         CGContextSetRGBFillColor(_opalContext, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextFillRect(_opalContext, CGRectMake(rand()%width,
            rand()%height, rand()%width, rand()%height));
      } 
      else {
         CGContextSetLineWidth(_opalContext, (rand()%10)+2);
            CGContextSetRGBStrokeColor(_opalContext, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextStrokeRect(_opalContext, CGRectMake(rand()%width,
            rand()%height, rand()%width, rand()%height));
      }
   }

   [self reDraw];
}

- (void) draw2
{
   NSLog(@"GLView draw2");
   [self clearContextWithRect:rootRect];
   
   int width  = rootRect.size.width;
   int height = rootRect.size.height;
  
   // Draw random circles (some stroked, some filled)
   for (int i = 0; i < 20; i++) {
      CGContextBeginPath(_opalContext);
      CGContextAddArc(_opalContext, rand()%width, rand()%height,
         rand()%((width>height) ? height : width), 0, 2*PI, 0);
      CGContextClosePath(_opalContext);

      if(i % 2) {
         CGContextSetRGBFillColor(_opalContext,
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255,
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255);
         CGContextFillPath(_opalContext);
      }
      else {
         CGContextSetLineWidth(_opalContext, (rand()%10)+2);
         CGContextSetRGBStrokeColor(_opalContext,
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255,
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255);
         CGContextStrokePath(_opalContext);
      }
   }
   
   [self reDraw];
}

- (void) draw3
{
   NSLog(@"GLView draw3");
  [self clearContextWithRect:rootRect];
   
   drawRandomPaths(_opalContext, rootRect.size.width,
      rootRect.size.height);
      
   [self reDraw];
}

- (void) draw4
{
   NSLog(@"GLView draw4");
   [self clearContextWithRect:rootRect];
   
   int width  = rootRect.size.width;
   int height = rootRect.size.height;
  
   /* Clipping example - draw random path through a circular clip */
   CGContextBeginPath(_opalContext);
   CGContextAddArc(_opalContext, width/2, height/2,
      ((width>height) ? height : width)/2, 0, 2*PI, 0);
   CGContextClosePath(_opalContext);
   CGContextClip(_opalContext);
        
   // Draw something into the clip
   drawRandomPaths(_opalContext, width, height);
   
   // Draw an clip path on top as a black stroked circle.
   CGContextBeginPath(_opalContext);
   CGContextAddArc(_opalContext, width/2, height/2,
      ((width>height) ? height : width)/2, 0, 2*PI, 0);
   CGContextClosePath(_opalContext);
   CGContextSetLineWidth(_opalContext, 1);
   CGContextSetRGBStrokeColor(_opalContext, 0, 0, 0, 1);
   CGContextStrokePath(_opalContext);
   
   [self reDraw];
   
   OPContextResetClip(_opalContext);
}

-(void) reDraw
{  
   [self reshape];
}

- (void) clearContextWithRect:(CGRect)rect
{
   CGContextSetRGBFillColor(_opalContext, 1, 1, 1, 1);
   CGContextFillRect(_opalContext, rect);
}

- (void) prepareOpenGL
{
   [super prepareOpenGL];
   int width  = [self frame].size.width;
   int height = [self frame].size.height;

   _cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
   _opalContext = opal_new_CGContext(_cairoSurface, CGSizeMake(width, height));
  
   /* Draw some content into the context */
   rootRect = CGRectMake(0, 0, width, height);
   [self clearContextWithRect:rootRect];
   
   // Draw random rectangles (some stroked some filled)
   for (int i = 0; i < 20; i++) {
      if(i % 2) {
         CGContextSetRGBFillColor(_opalContext, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextFillRect(_opalContext, CGRectMake(rand()%width,
            rand()%height, rand()%width, rand()%height));
      } 
      else {
         CGContextSetLineWidth(_opalContext, (rand()%10)+2);
            CGContextSetRGBStrokeColor(_opalContext, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, 
            (CGFloat)(rand()%256)/255);
         CGContextStrokeRect(_opalContext, CGRectMake(rand()%width,
            rand()%height, rand()%width, rand()%height));
      }
  }
}

- (void) dealloc
{
   cairo_surface_finish(_cairoSurface);
   CGContextRelease(_opalContext);

   [super dealloc];
}

-(void) reshape
{
   NSLog(@"reshaping");
   NSRect rect = [self bounds];
   glViewport(0, 0, rect.size.width, rect.size.height);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(60.0, rect.size.width/rect.size.height, 0.2, 7);

   [self setNeedsDisplay:YES];
}

- (void) drawRect: (NSRect)r
{
   [[self openGLContext] makeCurrentContext];
   
   int width = [self frame].size.width;
   int height = [self frame].size.height;
   
   glGenTextures(1, &_texture);
   glBindTexture(TEXTURE_TARGET, _texture);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   unsigned char * data = cairo_image_surface_get_data(_cairoSurface);
   gluBuild2DMipmaps(TEXTURE_TARGET, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
   
   glViewport(0, 0, [self frame].size.width, [self frame].size.height);
 
   glClear(GL_COLOR_BUFFER_BIT);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glEnable(GL_TEXTURE_2D);
   glEnable(TEXTURE_TARGET);
   glBindTexture(TEXTURE_TARGET, _texture);
   glColor3f(1,1,1);

   glBegin(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();

   [[self openGLContext] flushBuffer];
}

@end

Supporting Files
GNUmakefile.preamble(変更箇所のみ)

# Additional flags to pass to Objective C compiler
ADDITIONAL_OBJCFLAGS += -std=gnu99

# Additional flags to pass to C compiler
ADDITIONAL_CFLAGS += -std=gnu99

# Additional GUI libraries to link
ADDITIONAL_GUI_LIBS += -lm -lGL -lGLU -lopal -lcairo

Interfaces
(プロジェクト名).gorm
変更は下参照


ProjectCenter と Gorm によるプログラム作成
1 ProjectCenter でプロジェクトを作成
 Project Types は Application を選択
 ビルドとランを実行
 [Main Menu] が表示される。
2 window を追加
 Interfaces の (プロジェクト名).gorm をダブルクリック
 Gorm が起動する。
 Gorm のControls window -> Windows から [Window] を
 Resources window の Objects にドロップ
 Window(0) インスタンスが生成される。
 size, attributes (Visual at launch をチェック)を設定する。

 設定を保存する場合
 Gorm メインメニュー -> Document -> Save

 AppController と Window の接続
 Window の View の設定は AppController で行うので、AppController と
 Window を接続する。
(1)AppController Outlet の作成
  (作成した Outlet 名が winsow instance の名前になる、Action は要らない)
  Resources window の Classes で、NSObject -> AppController を選択
  Inspector window の Outlet add ボタンで作成(Outlet名: window)
 (AppController が表示されていない場合は、下のスクロールバーを
  左にする)
(2)接続
  Resources window の Objects で、 AppController(S) から Window(0)(T) に接続
  接続は、[Ctrl]キーを押しながらドラッグする。
  Inspector window で、Outlet window を選択し接続[Connect] する。

3 Draw menu の追加
 interfaces の gorm file をダブルクリックし、 Gorm を起動
 ・[Main Menu](作成プログラムのメニュー)に [Submenu] を追加
  Controls window -> Menus から、 [Submenu>] を [Main Menu] の目的の
  場所にドラッグする。
  [Submanu] が追加され、メニューに [item] が1個できる。
  Item 名を変更する。(Draw1 など)
  ( 2つ目い以降は、 Menus の [item] をドッラグして追加する)
 ・Outlet/Action の追加と接続
  [Submenu] のコントロールは AppController で行うようにするので、
  [Submenu] を AppController に接続する。
  *Outlet/Action の追加
  AppController に
   Outlet: draw1, draw2, draw3, draw4
   Action: drawAction1:, drawAction2:, drawAction3:, drawAction4
  を追加する。
  *接続
  [Main Menu] の Draw1(draw1) を AppController(drawAction1:) に接続
  接続方法は window の場合と同様。(残りのメニューも同様に接続する)

4 プログラムの生成と編集
 QEB2OpenGLView.h, QEB2OpenGLView.m の生成と編集
 (新しく作成したクラスファイルのプロジェクトへの追加は、Classes を
  選択しメニューから Project -> Add Files を実行する。)
 AppController.h, AppController.m の編集
 GNUmakefile.preamble の編集

GNUstep CoreGraphics (1)

今回は、ブログタイトルから離れて、GNUstep について書いています。

最近 GNUstepObjective-C についての記事が、2、3報告されています。
今更のObjective-C | 貯え 小道具 興味ない?
M1 Mac-Ubuntu20.04でGNUstep | 貯え 小道具 興味ない?
GNUstepをWindowsで試すならWSLを使う-Ubuntu 20.04 LTS+WSL2へのインストール - Technically Impossible

当方 2007年に「GNUstep 入門」(ふくろう出版、友野印刷)を出版しています。
Objective-C プログラミングや ProjectCenter、Gorm の使用方法について記してい
ます。

GNUstep は、それ以降もライブラリの追加やアップデートが行われています。
今回は、CoreGraphics(libs-opal) について調べてみました。
CoreGraphics による描画

ProjectCenter を使ってプログラムの編集、ビルド、実行を行っています。

OSは Lubuntu22.04 Linux を使用しています。GNUstep のベースシステムは
Lubuntu の repo からインストールします。
プログラミングを行うので、
・make, gnustep-make, gcc, gobjc などの build tools
・ProjectCenter, Gorm のプログラミングツール
もインストールしておきます。

*日本語の表示
スタートアップメニューの設定から System Preferences を起動し、Preferences
ウインドウから「Font」を選択する。
最初は、gnustep default の DejaVuSansMono を選択し、その後
NotoSans CJK jp Regular等(Lubuntu の日本語フォント)に変更する。

*CoreGraphics(libs-opal)、CoreFoundation(libs-corebase, 依存ライブラリ) の
ビルドとインストール
libs-corebase とlibs-opalはバイナリーが無いので、ソースからビルドし
インストールします。ソースはGNUstepGithub からダウンロードします。
・libs-corebase
 1依存ライブラリ libicu-dev をインストール(lubuntu repo から)
 2./configure
 3make
 4sudo make install

・libs-opal
 1依存ライブラリ libfontconfig-dev、libcairo2-dev、liblcms2-dev、
   libjpeg-turbo8-dev、libtiff-devをインストール(lubuntu repo から)
 2make
 3sudo make install
 libファイルlibopal.so.o は /usr/local/lib にインストールされるので、
 フォルダを設定します。
  export LD_LIBRARY_PATH=/usr/local/lib

・libs-QuartzCore
 libs-opal では、XWindow に表示するようになっています。これをNSWindow に
 表示するため、libs-QuartzCore を利用します。NSOpenGLView に表示
 します。
 1依存ライブラリ libgl-dev、libglu1-mesa-devをインストール
  (lubuntu repo から)
 2Tests の make で、cairo reference error が出るので、
  Tests/GNUmakefile の ADDITIONAL_TOOL_LIBS に -lcairo を追加
 3make
 4sudo make install

*ProjectCenter のビルドとインストール
CoreGraphics では gnustep 以外に OpenGL、opal、cairo等のライブラリも使用します。これらが利用出来るように、プログラムのビルド時にGNUmakefile.preamble
に設定します。
  ADDITIONAL_GUI_LIBS += -lm -lGL -lGLU -lopal -lcairo

しかし、1つ問題があります。ProjectCenter では、プロジェクトの保存や
再オープン時に GNUmakefile.preamble に設定した内容が、リセット されます。
これを避けるため、ProjectCenter のソースファイルを修正しインストールします。
ソース(apps-projectcenter)はGNUstepGithub からダウンロードします。(バージョンは 0.7)
・修正箇所
Modules/Projects にある各Projectの PC(Project名).m を修正する。
writeMakefile メソッド内の [mf createPreambleForProject:self] を
createProjectAt メソッド内に移動し、
[[PCMakefileFactory sharedFactory] createPreambleForProject:self];
とする。
その後、
 make
 sudo make install
を行う。

実際のプログラム作成は、次回に報告。

Babylon.js WebGPU Cloth Simulation (3)

前々回アップした、XPBD(extended position-based dynamics) による Cloth シミュレーションの解説です。今回はマウス操作について説明しています。

実行結果

cloth の上でマウスをクリックしドラッグすると、cloth が引っ張られた状態になります。

1 mouse down
 mesh 上をマウスでクリックして、クリックされた mesh の index や位置情報を取得する。mesh 情報の取得には、Babylon.js の raycast 機能を使用する。

 Scene の onPointerDown property は、scene 上をマウスでクリックすると、
IPointerEvent, PickingInfo, PointerEventTypes パラメータを持つコールバックを
起す。

   scene.onPointerDown = function (event, pickInfo){
       //console.log(“mouseDown pickInfo", pickInfo.hit);
            
      if (pickInfo.hit == true) {
         //console.log(“mouseDown pickInfo id", pickInfo.pickedMesh.id);
         //console.log(“mouseDown pickInfo faceId", pickInfo.faceId);
         gGrabber.start(pickInfo);
         camera.detachControl(canvas);
      }
   }

event が IPointerEvent に、pickInfo が PickingInfo に相当する。PointerEventTypes は使用していない。マウスクリックが起こると、function 内の処理が実行される。

 mesh の情報は、pickInfo から得られる。マウスをクリックすると、クリック位置から画面に垂直奥方向に ray が発せられる。そこに mesh があれば、pickInfo.hit が true になる。true の場合は、
 pickInfo.pickedMesh: ray が当たった mesh data
 pickInfo.faceId: ray が当たった mesh の face(triangle)番号
 pickInfo.pickedPoint: mesh 上で ray が当たった位置
などが得られる。
ray の情報も得られる。
 pickInfo.ray.origin:クリックした mouse の位置
 pickInfo.ray.direction:ray の方向ベクトル
ray の情報は、mesh が無い場合でも得られる。

 mouse down をしたとき、以下の処理を行う。

  1. クリックした位置(下図の {\bf{p}}_1)に対応する mesh 上の位置(下図の {\bf{p}})と、mesh index を取得し、その mesh point の position に設定する。
  2. mouse drag 中に mesh が移動しないように、カメラコントロールをオフにする。
  camera.detachControl(canvas)


 mesh の vertex point の position 設定は、以下のように行なっている。
・mesh 上の位置の取得

 {\bf{p}}_1 の座標(マウスポインターの座標)は ray.origin、
 {\bf{p}} の座標(対応する mesh 上の座標)は pickInfo.pickedPoint、
から得られる。 O はウィンドウの原点である。

・mesh index の取得
 mesh の拡大図

 {\bf{p}} のある triangle の face 番号は、pickInfo.faceId から得られる。
 {\bf{p}} と triangle の座標  {\bf{x}_1} ,  {\bf{x}_2} ,  {\bf{x}_3} との距離が最小となる index を見つけ、それを grabId とする。
これを行なっている部分が、Grabber.start(pickInfo) である。

 次に、 {\bf{p}} を uniform data、grabId を grabIdBuffer data として compute shader startGrab に送り、ここで grabId の point の invMass を 0(重力の影響を受けない)、position を  {\bf{p}} に設定する。
これを行なっている部分が、this.physicsObject.startGrab(pos) である。
this.physicsObject には、Cloth が設定されている。

2 mouse move
 mouse move した時の処理は、scene.onPointerMove で行う。
この場合は、pickInfo.hit は常に false の状態である。移動後の mesh point の座標は、ray 情報から得る。

mouse move した様子

drag したマウスポインターの座標 は、pickInfo.ray.origin から得られる。
マウスポインターの移動に対応した mesh point の座標  {\bf{p}} は、以下のようにする。
 {\bf{p}} = {\bf{p}}_1 + {\bf{d}}
 {\bf{d}} = pickInfo.ray.direction x distance
distance : mouse down 時の マウスポインターと mesh との距離(this.distance = pickInfo.distance)
これを行なっている部分が、Grabber.move(pickInfo) である。

次に、移動後の mesh point の座標  {\bf{p}} を、uniform data として compute shader moveGrabbed に送り、grabId の point の position を  {\bf{p}} に設定する。
これを行なっている部分が、Cloth.moveGrabbed(pos, vel) である。(vel は使用していない。)

また、Grabber.move(pickInfo)では、mesh point の速度(this.vel)の計算も行なっている。

3 mouse up
 drag を終了し mouse up を行うと、以下の処理をする。(scene.onPointerUP)

1 compute shader で、grab された mesh point の vel、invMass を設定する。      (this.physicsObject.endGrab(this,prevPos, this.vel) の部分)
 mouse move で計算した mesh point の速度 vel(this.vel) を uniform data として
 compute shader endGrab に送り、grabId の point の velocity を vel に設定
 する。
 また、grabId の point の invMass (0 に設定されている) を元の
 invMass (grabInvMass) に戻す。
 その後、grabId を初期値(-1)に戻す。
2 this,physicsObject を null にする。
3 カメラコントロールをオン(camera.attachControl true)にする。