Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Demystify CGO usage #69

Open
sarumaj opened this issue Dec 22, 2024 · 0 comments
Open

Demystify CGO usage #69

sarumaj opened this issue Dec 22, 2024 · 0 comments

Comments

@sarumaj
Copy link

sarumaj commented Dec 22, 2024

I was wondering if one could replace the CGO binding on some platforms by using ebitengine/purego?

Here an example for clipboard_darwin.go (not tested though, generated with an LLM to give an idea):

package systray

import (
	"time"
	"unsafe"

	"github.com/ebitengine/purego"
	"github.com/ebitengine/purego/objc"
)

var st = &systray{}

type systray struct {
}

// Objective-C selectors
var (
	showMenuSelector          objc.Selector
	setIconSelector           objc.Selector
	setMenuItemIconSelector   objc.Selector
	registerSystraySelector   objc.Selector
	nativeLoopSelector        objc.Selector
	nativeEndSelector         objc.Selector
	nativeStartSelector       objc.Selector
	quitSelector              objc.Selector
	setInternalLoopSelector   objc.Selector
	createMenuSelector        objc.Selector
	setMenuNilSelector        objc.Selector
	resetMenuSelector         objc.Selector
	addOrUpdateMenuItemSelector objc.Selector
)

func init() {
	objc.LoadFramework("Cocoa", "/System/Library/Frameworks/Cocoa.framework")

	showMenuSelector = objc.GetSelector("showMenu")
	setIconSelector = objc.GetSelector("setIcon:withLength:template:")
	setMenuItemIconSelector = objc.GetSelector("setMenuItemIcon:withLength:id:template:")
	registerSystraySelector = objc.GetSelector("registerSystray")
	nativeLoopSelector = objc.GetSelector("nativeLoop")
	nativeEndSelector = objc.GetSelector("nativeEnd")
	nativeStartSelector = objc.GetSelector("nativeStart")
	quitSelector = objc.GetSelector("quit")
	setInternalLoopSelector = objc.GetSelector("setInternalLoop:")
	createMenuSelector = objc.GetSelector("createMenu")
	setMenuNilSelector = objc.GetSelector("setMenuNil")
	resetMenuSelector = objc.GetSelector("resetMenu")
	addOrUpdateMenuItemSelector = objc.GetSelector("addOrUpdateMenuItemWithID:parentID:title:tooltip:shortcut:disabled:checked:checkable:")
}

func (m *systray) ShowMenu() error {
	objc.Call("SystrayManager", showMenuSelector)
	return nil
}

func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
	iconPtr := unsafe.Pointer(&templateIconBytes[0])
	objc.Call("SystrayManager", setIconSelector, iconPtr, len(templateIconBytes), true)
}

func (item *MenuItem) SetIcon(iconBytes []byte) {
	iconPtr := unsafe.Pointer(&iconBytes[0])
	objc.Call("SystrayManager", setMenuItemIconSelector, iconPtr, len(iconBytes), item.id, false)
}

func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
	iconPtr := unsafe.Pointer(&templateIconBytes[0])
	objc.Call("SystrayManager", setMenuItemIconSelector, iconPtr, len(templateIconBytes), item.id, true)
}

func registerSystray() {
	objc.Call("SystrayManager", registerSystraySelector)
}

func nativeLoop() {
	objc.Call("SystrayManager", nativeLoopSelector)
}

func nativeEnd() {
	objc.Call("SystrayManager", nativeEndSelector)
}

func nativeStart() {
	objc.Call("SystrayManager", nativeStartSelector)
}

func quit() {
	objc.Call("SystrayManager", quitSelector)
}

func setInternalLoop(internal bool) {
	objc.Call("SystrayManager", setInternalLoopSelector, internal)
}

func CreateMenu() {
	objc.Call("SystrayManager", createMenuSelector)
}

func SetMenuNil() {
	objc.Call("SystrayManager", setMenuNilSelector)
}

func SetIcon(iconBytes []byte) {
	iconPtr := unsafe.Pointer(&iconBytes[0])
	objc.Call("SystrayManager", setIconSelector, iconPtr, len(iconBytes), false)
}

func SetTitle(title string) {
	titlePtr := objc.StringToNSString(title)
	objc.Call("SystrayManager", "setTitle:", titlePtr)
}

func SetTooltip(tooltip string) {
	tooltipPtr := objc.StringToNSString(tooltip)
	objc.Call("SystrayManager", "setTooltip:", tooltipPtr)
}

func addOrUpdateMenuItem(item *MenuItem) {
	var disabled, checked, isCheckable int
	if item.disabled {
		disabled = 1
	}
	if item.checked {
		checked = 1
	}
	if item.isCheckable {
		isCheckable = 1
	}
	parentID := 0
	if item.parent != nil {
		parentID = int(item.parent.id)
	}
	objc.Call("SystrayManager", addOrUpdateMenuItemSelector, item.id, parentID, item.title, item.tooltip, item.shortcutKey, disabled, checked, isCheckable)
}

func addSeparator(id uint32) {
	objc.Call("SystrayManager", "addSeparator:", id)
}

func hideMenuItem(item *MenuItem) {
	objc.Call("SystrayManager", "hideMenuItem:", item.id)
}

func showMenuItem(item *MenuItem) {
	objc.Call("SystrayManager", "showMenuItem:", item.id)
}
}

func resetMenu() {
	objc.Call("SystrayManager", resetMenuSelector)
}

func enableOnClick() {
	if !isEnableOnClick {
		isEnableOnClick = true
		objc.Call("SystrayManager", "enableOnClick")
	}
}

func systray_ready() {
	if systrayReady != nil {
		systrayReady()
	}
}

func systray_on_exit() {
	systrayExit()
}

func systray_menu_item_selected(cID int) {
	systrayMenuItemSelected(uint32(cID))
}

func systray_on_click() {
	if dClickTime == 0 {
		dClickTime = time.Now().UnixMilli()
	} else {
		nowMilli := time.Now().UnixMilli()
		if nowMilli-dClickTime < dClickTimeMinInterval {
			dClickTime = dClickTimeMinInterval
			if onDClick != nil {
				onDClick(st)
				return
			}
		} else {
			dClickTime = nowMilli
		}
	}
	if onClick != nil {
		onClick(st)
	}
}

func systray_on_rclick() {
	if onRClick != nil {
		onRClick(st)
	} else {
		objc.Call("SystrayManager", showMenuSelector)
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant