summaryrefslogtreecommitdiff
path: root/plugin.go
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2022-11-13 08:53:03 -0600
committerJeff Carr <[email protected]>2022-11-13 08:53:03 -0600
commit207cf7ea16f1da8fa9f893504d77a2856298cc22 (patch)
tree54d513b83ce797be75268f7d8867e0b01ab8f23e /plugin.go
parented382bec55be25039e4dcf020d1512139855c9bb (diff)
Massive refactor to use go plugins. This is neat.
update README.md set xterm title. make os.Exit() default on window close add a toolkit.Widget to the node structure remove 'Greeter' symbol mapping scheme removed the testing greeter code plugins: attempt to load plugins in a sensible order andlabs/ui: working andlabs/ui plugin (andlabs2) buttons work in andlabs plugin TODO: re-implement non-plugin version for Windows mswindows doesn't support go plugins yet gocui: put the gocui console so file in the binary does a full init of gocui plugin Button() and Group() working very well with gogui cleanly exit gocui technically you can load two toolkits at the same time kinda both working at the same time. esoteric two working plugins at the same time give up working on two gui's at the same time this is fun, but _not interesting wow. this actually works. NewButton() from both toolkits examples: all the examples run again remove early helloplugin example buttonplugin example cmd code buttonplugin runs and ldd is minimum Signed-off-by: Jeff Carr <[email protected]>
Diffstat (limited to 'plugin.go')
-rw-r--r--plugin.go265
1 files changed, 195 insertions, 70 deletions
diff --git a/plugin.go b/plugin.go
index 58e6dbe..e13f751 100644
--- a/plugin.go
+++ b/plugin.go
@@ -8,109 +8,234 @@ package gui
import (
"log"
"os"
-
"plugin"
- "github.com/davecgh/go-spew/spew"
+
+ "git.wit.org/wit/gui/toolkit"
)
-// TODO: could a protobuf work here?
-type Greeter interface {
- Greet()
- JcarrButton()
- AddButton(string)
+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 PlugGocli *plugin.Plugin
-var PlugGocliOk bool
-var PlugHello *plugin.Plugin
+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")
-// var gBut plugin.Symbol
-var jcarrBut plugin.Symbol
-var symGreeter plugin.Symbol
-var greeter Greeter
-var ok bool
+ allPlugins = append(allPlugins, &newPlug)
-var typeToolkit plugin.Symbol
-var typeToolkitCast Greeter
+ log.Println("gui.LoadToolkit() END", newPlug.name, filename)
+ newPlug.LoadOk = true
+ return true
+}
-func LoadPlugin(name string) *plugin.Plugin {
- scs := spew.ConfigState{MaxDepth: 1}
+func loadFuncE(p *aplug, funcName string) func() {
+ var newfunc func()
+ var ok bool
+ var test plugin.Symbol
- // load module
- // 1. open the so file to load the symbols
- plug, err := plugin.Open(name)
- log.Println("plug =")
- log.Println(scs.Sdump(plug))
+ test, err = p.plug.Lookup(funcName)
if err != nil {
- log.Println(err)
+ log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
- // 2. look up a symbol (an exported function or variable)
- // in this case, variable Greeter
- typeToolkit, err = plug.Lookup("Toolkit")
- log.Println("plugin.Toolkit", typeToolkit)
- log.Println(scs.Sdump(typeToolkit))
- if err != nil {
- log.Println(err)
- os.Exit(1)
+ 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
- symGreeter, err = plug.Lookup("Greeter")
- log.Println("symGreater", symGreeter)
- log.Println(scs.Sdump(symGreeter))
+ test, err = p.plug.Lookup(funcName)
if err != nil {
- log.Println(err)
- os.Exit(1)
+ log.Println("DID NOT FIND: name =", test, "err =", err)
+ return nil
}
- // 3. Assert that loaded symbol is of a desired type
- // in this case interface type Greeter (defined above)
- // var greeter Greeter
- greeter, ok = symGreeter.(Greeter)
- log.Println("greeter", symGreeter)
- log.Println(scs.Sdump(greeter))
+ newfunc, ok = test.(func(*toolkit.Widget))
if !ok {
- log.Println("unexpected type from module symbol")
- os.Exit(1)
+ log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
+ return nil
}
+ return newfunc
+}
- /*
- typeToolkitCast, ok = typeToolkit.(Greeter)
+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("unexpected cast of Toolkit to Greeter")
- os.Exit(1)
+ log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
+ return nil
}
- */
- return plug
+ return newfunc
}
-func RunGreet() {
- log.Println("gui.RunGreet() START")
- if (greeter == nil) {
- log.Println("wit/gui gocui plugin didn't load")
- return
+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
}
- PlugGocliOk = true
- greeter.Greet()
+ return newfunc
}
-func LookupJcarrButton() {
- log.Println("lookupJcarrButton() START")
+// 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
- if (greeter == nil) {
- log.Println("wit/gui gocui plugin didn't load")
- return
+ test, err = p.plug.Lookup(funcName)
+ if err != nil {
+ log.Println("DID NOT FIND: name =", test, "err =", err)
+ return nil
}
- greeter.JcarrButton()
+
+ 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 GocuiAddButton(name string) {
- log.Println("GocuiAddButton() START", name)
+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
+ }
- if (greeter == nil) {
- log.Println("wit/gui gocui plugin didn't load")
+ filename = "/usr/share/wit/gui/" + name
+ p.plug = loadfile(filename)
+ if (p.plug != nil) {
+ p.filename = filename
return
}
- greeter.AddButton(name)
+ 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
}