new-gui/plugin.go

242 lines
5.9 KiB
Go

package gui
// This is based off of the excellent example and documentation here:
// https://github.com/vladimirvivien/go-plugin-example
// There truly are great people in this world.
// It's a pleasure to be here with all of you
import (
"log"
"os"
"plugin"
"git.wit.org/wit/gui/toolkit"
)
var err error
type Symbol any
type aplug struct {
// Ok bool
name string
filename string
plug *plugin.Plugin
sym *plugin.Symbol
LoadOk bool
InitOk bool
MainOk bool
Init func()
Main func(func ())
Queue func(func ())
Quit func()
NewWindow func(*toolkit.Widget)
NewButton func(*toolkit.Widget, *toolkit.Widget)
NewGroup func(*toolkit.Widget, *toolkit.Widget)
NewCheckbox func(*toolkit.Widget, *toolkit.Widget)
NewTab func(*toolkit.Widget, *toolkit.Widget)
NewLabel func(*toolkit.Widget, *toolkit.Widget)
NewTextbox func(*toolkit.Widget, *toolkit.Widget)
NewSlider func(*toolkit.Widget, *toolkit.Widget)
NewSpinner func(*toolkit.Widget, *toolkit.Widget)
NewDropdown func(*toolkit.Widget, *toolkit.Widget)
AddDropdownName func(*toolkit.Widget, string)
}
var allPlugins []*aplug
// loads and initializes a toolkit (andlabs/ui, gocui, etc)
func LoadToolkit(name string) bool {
var newPlug aplug
log.Println("gui.LoadToolkit() START")
newPlug.LoadOk = false
for _, aplug := range allPlugins {
log.Println("gui.LoadToolkit() already loaded toolkit plugin =", aplug.name)
if (aplug.name == name) {
log.Println("gui.LoadToolkit() SKIPPING")
return true
}
}
// locate the shared library file
filename := name + ".so"
loadPlugin(&newPlug, filename)
if (newPlug.plug == nil) {
return false
}
// newPlug.Ok = true
newPlug.name = name
// map all the functions
newPlug.Init = loadFuncE(&newPlug, "Init")
newPlug.Quit = loadFuncE(&newPlug, "Quit")
// this should be laodFuncE()
newPlug.Main = loadFuncF(&newPlug, "Main")
newPlug.Queue = loadFuncF(&newPlug, "Queue")
newPlug.NewWindow = loadFunc1(&newPlug, "NewWindow")
newPlug.NewButton = loadFunc2(&newPlug, "NewButton")
newPlug.NewGroup = loadFunc2(&newPlug, "NewGroup")
newPlug.NewCheckbox = loadFunc2(&newPlug, "NewCheckbox")
newPlug.NewTab = loadFunc2(&newPlug, "NewTab")
newPlug.NewLabel = loadFunc2(&newPlug, "NewLabel")
newPlug.NewTextbox = loadFunc2(&newPlug, "NewTextbox")
newPlug.NewSlider = loadFunc2(&newPlug, "NewSlider")
newPlug.NewSpinner = loadFunc2(&newPlug, "NewSpinner")
newPlug.NewDropdown = loadFunc2(&newPlug, "NewDropdown")
newPlug.AddDropdownName = loadFuncS(&newPlug, "AddDropdownName")
allPlugins = append(allPlugins, &newPlug)
log.Println("gui.LoadToolkit() END", newPlug.name, filename)
newPlug.LoadOk = true
return true
}
func loadFuncE(p *aplug, funcName string) func() {
var newfunc func()
var ok bool
var test plugin.Symbol
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
newfunc, ok = test.(func())
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
func loadFunc1(p *aplug, funcName string) func(*toolkit.Widget) {
var newfunc func(*toolkit.Widget)
var ok bool
var test plugin.Symbol
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
newfunc, ok = test.(func(*toolkit.Widget))
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
func loadFuncS(p *aplug, funcName string) func(*toolkit.Widget, string) {
var newfunc func(*toolkit.Widget, string)
var ok bool
var test plugin.Symbol
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
newfunc, ok = test.(func(*toolkit.Widget, string))
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
func loadFunc2(p *aplug, funcName string) func(*toolkit.Widget, *toolkit.Widget) {
var newfunc func(*toolkit.Widget, *toolkit.Widget)
var ok bool
var test plugin.Symbol
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
newfunc, ok = test.(func(*toolkit.Widget, *toolkit.Widget))
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
// This is probably dangerous and should never be done
// executing arbitrary functions will cause them to run inside the goroutine that
// the GUI toolkit itself is running in. TODO: move to channels here
func loadFuncF(p *aplug, funcName string) func(func ()) {
var newfunc func(func ())
var ok bool
var test plugin.Symbol
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
newfunc, ok = test.(func(func ()))
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
func loadPlugin(p *aplug, name string) {
var filename string
// attempt to write out the file from the internal resource
internalName := "toolkit/" + name
soFile, err := res.ReadFile(internalName)
if (err != nil) {
log.Println(err)
} else {
err = os.WriteFile("/tmp/wit/" + name, soFile, 0644)
if (err != nil) {
log.Println(err)
}
}
filename = "/tmp/wit/" + name
p.plug = loadfile(filename)
if (p.plug != nil) {
p.filename = filename
return
}
filename = "/usr/share/wit/gui/" + name
p.plug = loadfile(filename)
if (p.plug != nil) {
p.filename = filename
return
}
return
}
// load module
// 1. open the shared object file to load the symbols
func loadfile(filename string) *plugin.Plugin {
plug, err := plugin.Open(filename)
log.Println("plug =", plug)
if err != nil {
log.Println(err)
return nil
}
return plug
}