From 28280403bf843ea3b435129c1db18b7934685406 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sun, 23 Apr 2023 09:47:54 -0500 Subject: [PATCH] andlabs: ran without crashing Signed-off-by: Jeff Carr --- README-goreadme.md | 17 ++--- cmds/buttonplugin/main.go | 2 +- debug.go | 6 ++ main.go | 31 ++++++--- plugin.go | 141 ++++---------------------------------- toolkit/andlabs/main.go | 15 ++-- toolkit/andlabs/plugin.go | 16 ++++- toolkit/widget.go | 46 ++----------- watchdog.go | 6 -- 9 files changed, 77 insertions(+), 203 deletions(-) diff --git a/README-goreadme.md b/README-goreadme.md index 160264f..0b9a917 100644 --- a/README-goreadme.md +++ b/README-goreadme.md @@ -123,14 +123,7 @@ Creates a window helpful for debugging this package `func ExampleCatcher(f func())` -### func [FindPlugin](/plugin.go#L64) - -`func FindPlugin(name string) *aplug` - -loads and initializes a toolkit (andlabs/ui, gocui, etc) -attempts to locate the .so file - -### func [Indent](/debug.go#L120) +### func [Indent](/debug.go#L126) `func Indent(b bool, a ...interface{})` @@ -138,15 +131,15 @@ attempts to locate the .so file `func SetDebug(s bool)` -### func [SetFlag](/debug.go#L44) +### func [SetFlag](/debug.go#L50) `func SetFlag(s string, b bool)` -### func [ShowDebugValues](/debug.go#L79) +### func [ShowDebugValues](/debug.go#L85) `func ShowDebugValues()` -### func [StandardExit](/main.go#L169) +### func [StandardExit](/main.go#L180) `func StandardExit()` @@ -187,7 +180,7 @@ var Config GuiConfig The Node is a binary tree. This is how all GUI elements are stored simply the name and the size of whatever GUI element exists -#### func [New](/main.go#L142) +#### func [New](/main.go#L153) `func New() *Node` diff --git a/cmds/buttonplugin/main.go b/cmds/buttonplugin/main.go index 7c62b21..6d556b3 100644 --- a/cmds/buttonplugin/main.go +++ b/cmds/buttonplugin/main.go @@ -16,7 +16,7 @@ var buttonCounter int = 5 func main() { // This will turn on all debugging - // gui.SetDebug(true) + gui.SetDebug(true) // myGui = gui.New().LoadToolkit("gocui") myGui = gui.New().LoadToolkit("andlabs") diff --git a/debug.go b/debug.go index 4f5340d..d25a656 100644 --- a/debug.go +++ b/debug.go @@ -29,6 +29,12 @@ func SetDebug (s bool) { debugGui = s debugTabs = s + logNow = s + logInfo = s + logWarn = s + logError = s + logVerbose = s + SetFlag("Node", s) SetFlag("Tabs", s) SetFlag("Dump", s) diff --git a/main.go b/main.go index deb53b5..9e37080 100644 --- a/main.go +++ b/main.go @@ -112,22 +112,33 @@ func (n *Node) doUserEvent(a toolkit.Action) { } func (n *Node) LoadToolkit(name string) *Node { - log(logInfo, "LoadToolkit() for name =", name) - if (FindPlugin(name) == nil) { + log(logInfo, "LoadToolkit() START for name =", name) + plug := initPlugin(name) + if (plug == nil) { return n } + + log(logInfo, "LoadToolkit() sending InitToolkit action to the plugin channel") + var a toolkit.Action + a.ActionType = toolkit.InitToolkit + plug.pluginChan <- a + sleep(.5) // temp hack until chan communication is setup + + // TODO: find a new way to do this that is locking, safe and accurate + Config.rootNode.redraw(plug) + log(logInfo, "LoadToolkit() END for name =", name) return n } func (n *Node) CloseToolkit(name string) bool { log(logInfo, "CloseToolkit() for name =", name) - for _, aplug := range allPlugins { - log(debugGui, "CloseToolkit() found", aplug.name) - if (aplug.name == name) { + for _, plug := range allPlugins { + log(debugGui, "CloseToolkit() found", plug.name) + if (plug.name == name) { log(debugNow, "CloseToolkit() sending close", name) var a toolkit.Action a.ActionType = toolkit.CloseToolkit - aplug.pluginChan <- a + plug.pluginChan <- a sleep(.5) return true } @@ -144,7 +155,7 @@ func New() *Node { } func (n *Node) Default() *Node { - if (FindPlugin("gocui") == nil) { + if (n.LoadToolkit("gocui") == nil) { log(logError, "New() failed to load gocui") } // if DISPLAY isn't set, return since gtk can't load @@ -152,7 +163,7 @@ func (n *Node) Default() *Node { if (os.Getenv("DISPLAY") == "") { return n } - if (FindPlugin("andlabs") == nil) { + if (n.LoadToolkit("andlabs") == nil) { log(logError, "New() failed to load andlabs") } return n @@ -169,8 +180,8 @@ func (n *Node) StandardClose() { func StandardExit() { log("wit/gui Standard Window Exit. running os.Exit()") log("StandardExit() attempt to exit each toolkit plugin") - for i, aplug := range allPlugins { - log("NewButton()", i, aplug) + for i, plug := range allPlugins { + log("NewButton()", i, plug) } exit(0) } diff --git a/plugin.go b/plugin.go index 7dedd22..addab32 100644 --- a/plugin.go +++ b/plugin.go @@ -16,17 +16,14 @@ var err error type Symbol any type aplug struct { - // Ok bool name string filename string plug *plugin.Plugin - sym *plugin.Symbol - // LoadOk bool + // sym *plugin.Symbol InitOk bool - // MainOk bool // startup whatever might need to be setup in the plugin - Init func() +// Init func() // This passes the go channel to the plugin // the plugin then passes actions back to @@ -49,33 +46,27 @@ type aplug struct { // add button request pluginChan chan toolkit.Action PluginChannel func() chan toolkit.Action - - // deprecate all this - // TODO: make Main() main() and never allow the user to call it - // run plugin.Main() when the plugin is loaded - // Main func(func ()) // this never returns. Each plugin must have it's own goroutine - // Quit func() } var allPlugins []*aplug // loads and initializes a toolkit (andlabs/ui, gocui, etc) // attempts to locate the .so file -func FindPlugin(name string) *aplug { - var newPlug *aplug - newPlug = new(aplug) - - log(logInfo, "FindPlugin() START") - newPlug.InitOk = false +func initPlugin(name string) *aplug { + log(logInfo, "initPlugin() START") for _, aplug := range allPlugins { - log(debugGui, "FindPlugin() already loaded toolkit plugin =", aplug.name) + log(debugGui, "initPlugin() already loaded toolkit plugin =", aplug.name) if (aplug.name == name) { - log(debugError, "FindPlugin() SKIPPING", name, "as you can't load it twice") - return aplug + log(debugError, "initPlugin() SKIPPING", name, "as you can't load it twice") + return nil } } + var newPlug *aplug + newPlug = new(aplug) + newPlug.InitOk = false + // locate the shared library file filename := name + ".so" loadPlugin(newPlug, filename) @@ -86,22 +77,6 @@ func FindPlugin(name string) *aplug { // newPlug.Ok = true newPlug.name = name - // deprecate Init(?) - newPlug.Init = loadFuncE(newPlug, "Init") - - // should make a goroutine that never exits - // newPlug.Main = loadFuncF(newPlug, "Main") - - // should send things to the goroutine above - // newPlug.Queue = loadFuncF(&newPlug, "Queue") - - // unload the plugin and restore state - // newPlug.Quit = loadFuncE(newPlug, "Quit") - - // Sends instructions like "Add", "Delete", "Disable", etc - // Sends a widget (button, checkbox, etc) and it's parent widget - // newPlug.Action = loadFuncA(newPlug, "Action") - // this tells the toolkit plugin how to send user events back to us // for things like: the user clicked on the 'Check IPv6' newPlug.Callback = sendCallback(newPlug, "Callback") @@ -112,8 +87,8 @@ func FindPlugin(name string) *aplug { allPlugins = append(allPlugins, newPlug) - log(debugPlugin, "FindPlugin() END", newPlug.name, filename) - newPlug.Init() + log(debugPlugin, "initPlugin() END", newPlug.name, filename) + // newPlug.Init() // set the communication to the plugins newPlug.pluginChan = newPlug.PluginChannel() @@ -121,35 +96,9 @@ func FindPlugin(name string) *aplug { newPlug.InitOk = true - sleep(1) // temp hack until chan communication is setup - - // TODO: find a new way to do this that is locking, safe and accurate - Config.rootNode.redraw(newPlug) - return newPlug } -// TODO: All these functions need to be done a smarter way -// but I haven't worked out the golang syntax to make it smarter -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(debugGui, "DID NOT FIND: name =", test, "err =", err) - return nil - } - - newfunc, ok = test.(func()) - if !ok { - log(debugGui, "function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) - return nil - } - return newfunc -} - // newPlug.PluginChannel = getPluginChannel(newPlug, "PluginChannel") func getPluginChannel(p *aplug, funcName string) func() chan toolkit.Action { var newfunc func() chan toolkit.Action @@ -189,70 +138,6 @@ func sendCallback(p *aplug, funcName string) func(chan toolkit.Action) { 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(debugGui, "DID NOT FIND: name =", test, "err =", err) - return nil - } - - newfunc, ok = test.(func(*toolkit.Widget, *toolkit.Widget)) - if !ok { - log(debugGui, "function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) - return nil - } - return newfunc -} -*/ - -// does this fix loadFuncE problems? -// TODO: still need to move to channels here -func loadFuncA(p *aplug, funcName string) func(*toolkit.Action) { - var newfunc func(*toolkit.Action) - var ok bool - var test plugin.Symbol - - test, err = p.plug.Lookup(funcName) - if err != nil { - log(debugGui, "DID NOT FIND: name =", test, "err =", err) - return nil - } - - newfunc, ok = test.(func(*toolkit.Action)) - if !ok { - log(debugGui, "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(debugGui, "DID NOT FIND: name =", test, "err =", err) - return nil - } - - newfunc, ok = test.(func(func ())) - if !ok { - log(debugGui, "function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) - return nil - } - return newfunc -} - /* This searches in the following order for the plugin .so files: ./toolkit/ diff --git a/toolkit/andlabs/main.go b/toolkit/andlabs/main.go index 5a99421..e9ac6b1 100644 --- a/toolkit/andlabs/main.go +++ b/toolkit/andlabs/main.go @@ -24,9 +24,6 @@ func catchActionChannel() { log(logNow, "catchActionChannel() START") for { log(logNow, "catchActionChannel() for loop") - uiMain.Do(func() { - go ui.Main(demoUI) - }) select { case a := <-pluginChan: log(logNow, "catchActionChannel() SELECT widget id =", a.WidgetId, a.Name) @@ -95,7 +92,7 @@ func queue(f func()) { } // This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc -func Init() { +func init() { log(logNow, "Init() START") log(debugToolkit, "Init()") // Can you pass values to a plugin init() ? Otherwise, there is no way to safely print @@ -106,7 +103,17 @@ func Init() { pluginChan = make(chan toolkit.Action, 1) log(logNow, "Init() start channel reciever") + go ui.Main(func() { + demoUI() + }) go catchActionChannel() + /* + // go catchActionChannel() + go uiMain.Do(func() { + ui.Main(demoUI) + // go catchActionChannel() + }) + */ log(logNow, "Init() END") } diff --git a/toolkit/andlabs/plugin.go b/toolkit/andlabs/plugin.go index f9befd5..a7205fb 100644 --- a/toolkit/andlabs/plugin.go +++ b/toolkit/andlabs/plugin.go @@ -24,6 +24,7 @@ func Send(p *toolkit.Widget, c *toolkit.Widget) { } */ +/* func oldAction2(a *toolkit.Action) { log(logNow, "Action() START") if (a == nil) { @@ -31,22 +32,31 @@ func oldAction2(a *toolkit.Action) { return } pluginChan <- *a - /* + f := func() { rawAction(a) } // f() Queue(f) - */ log(logNow, "Action() END") } - +*/ func rawAction(a toolkit.Action) { log(debugAction, "rawAction() START a.ActionType =", a.ActionType) log(debugAction, "rawAction() START a.S =", a.S) + if (a.ActionType == toolkit.InitToolkit) { + // TODO: make sure to only do this once + // go uiMain.Do(func() { + // ui.Main(demoUI) + // go catchActionChannel() + // }) + // try doing this on toolkit load in init() + return + } + log(logNow, "rawAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId) switch a.WidgetType { case toolkit.Flag: diff --git a/toolkit/widget.go b/toolkit/widget.go index 658475e..1a5a45f 100644 --- a/toolkit/widget.go +++ b/toolkit/widget.go @@ -1,15 +1,7 @@ package toolkit -type WidgetType int -type ActionType int - // passes information between the toolkit library (plugin) // -// All Toolkit interactions should be done via a channel or Queue() -// TODO: FIGURE OUT HOW TO IMPLEMENT THIS -// https://ieftimov.com/post/golang-datastructures-trees/ -// TODO: protobuf ? -// // This is the only thing that is passed between the toolkit plugin // // what names should be used? This is not part of [[Graphical Widget]] @@ -17,29 +9,12 @@ type ActionType int // Event is used too much: web dev, cloud, etc // I'm using "Action". Maybe it should really be // "Interaction" as per wikipedia [[User interface]] -// Could a protobuf be used here? (Can functions be passed?) -type Widget2 struct { - // Name string - Type WidgetType +// +// TODO: convert this to a protobuf (?) +// - // This function is how you interact with the toolkit - // latest attempt. seems to work so far (2023/02/28) - // Hopefully this will be the barrier between the goroutines - // TODO: move this interaction to channels - Custom func() - - // re-adding an id to test channels - Id int - - // This is how the values are passed back and forth - // values from things like checkboxes & dropdown's - // The string is also used to set the button name - B bool - I int - // maybe safe if there is correctly working Custom() between goroutines? - // (still probably not, almost certainly not. not possible. layer violation?) - S string // not safe to have 'S' -} +type WidgetType int // Button, Menu, Checkbox, etc. +type ActionType int // Add, SetText, Click, Hide, Append, Delete, etc type Action struct { ActionType ActionType @@ -51,20 +26,13 @@ type Action struct { Text string // what is visable to the user Name string // a name useful for programming - // this should be the widget - // if the action is New, Hide, Enable, etc - // Widget *Widget - // This is how the values are passed back and forth // values from things like checkboxes & dropdown's - // The string is also used to set the button name B bool I int - // maybe safe if there is correctly working Custom() between goroutines? - // (still probably not, almost certainly not. not possible. layer violation?) - S string // not safe to have 'S' + S string - A any + A any // switch to this or deprecate this? pros/cons? // This GUI is intended for simple things // We are not laying out PDF's here diff --git a/watchdog.go b/watchdog.go index d992bd4..690ca44 100644 --- a/watchdog.go +++ b/watchdog.go @@ -23,12 +23,6 @@ func Watchdog() { Config.rootNode.ListChildren(true) } } - if (i == 2) { - Config.rootNode.LoadToolkit("gocui") - } -// if (i == 3) { -// Config.rootNode.LoadToolkit("andlabs") -// } i += 1 time.Sleep(watchtime * time.Second / 10) }