andlabs: ran without crashing

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2023-04-23 09:47:54 -05:00
parent 1986bd286d
commit 28280403bf
9 changed files with 77 additions and 203 deletions

View File

@ -123,14 +123,7 @@ Creates a window helpful for debugging this package
`func ExampleCatcher(f func())` `func ExampleCatcher(f func())`
### func [FindPlugin](/plugin.go#L64) ### func [Indent](/debug.go#L126)
`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(b bool, a ...interface{})` `func Indent(b bool, a ...interface{})`
@ -138,15 +131,15 @@ attempts to locate the .so file
`func SetDebug(s bool)` `func SetDebug(s bool)`
### func [SetFlag](/debug.go#L44) ### func [SetFlag](/debug.go#L50)
`func SetFlag(s string, b bool)` `func SetFlag(s string, b bool)`
### func [ShowDebugValues](/debug.go#L79) ### func [ShowDebugValues](/debug.go#L85)
`func ShowDebugValues()` `func ShowDebugValues()`
### func [StandardExit](/main.go#L169) ### func [StandardExit](/main.go#L180)
`func StandardExit()` `func StandardExit()`
@ -187,7 +180,7 @@ var Config GuiConfig
The Node is a binary tree. This is how all GUI elements are stored 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 simply the name and the size of whatever GUI element exists
#### func [New](/main.go#L142) #### func [New](/main.go#L153)
`func New() *Node` `func New() *Node`

View File

@ -16,7 +16,7 @@ var buttonCounter int = 5
func main() { func main() {
// This will turn on all debugging // This will turn on all debugging
// gui.SetDebug(true) gui.SetDebug(true)
// myGui = gui.New().LoadToolkit("gocui") // myGui = gui.New().LoadToolkit("gocui")
myGui = gui.New().LoadToolkit("andlabs") myGui = gui.New().LoadToolkit("andlabs")

View File

@ -29,6 +29,12 @@ func SetDebug (s bool) {
debugGui = s debugGui = s
debugTabs = s debugTabs = s
logNow = s
logInfo = s
logWarn = s
logError = s
logVerbose = s
SetFlag("Node", s) SetFlag("Node", s)
SetFlag("Tabs", s) SetFlag("Tabs", s)
SetFlag("Dump", s) SetFlag("Dump", s)

31
main.go
View File

@ -112,22 +112,33 @@ func (n *Node) doUserEvent(a toolkit.Action) {
} }
func (n *Node) LoadToolkit(name string) *Node { func (n *Node) LoadToolkit(name string) *Node {
log(logInfo, "LoadToolkit() for name =", name) log(logInfo, "LoadToolkit() START for name =", name)
if (FindPlugin(name) == nil) { plug := initPlugin(name)
if (plug == nil) {
return n 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 return n
} }
func (n *Node) CloseToolkit(name string) bool { func (n *Node) CloseToolkit(name string) bool {
log(logInfo, "CloseToolkit() for name =", name) log(logInfo, "CloseToolkit() for name =", name)
for _, aplug := range allPlugins { for _, plug := range allPlugins {
log(debugGui, "CloseToolkit() found", aplug.name) log(debugGui, "CloseToolkit() found", plug.name)
if (aplug.name == name) { if (plug.name == name) {
log(debugNow, "CloseToolkit() sending close", name) log(debugNow, "CloseToolkit() sending close", name)
var a toolkit.Action var a toolkit.Action
a.ActionType = toolkit.CloseToolkit a.ActionType = toolkit.CloseToolkit
aplug.pluginChan <- a plug.pluginChan <- a
sleep(.5) sleep(.5)
return true return true
} }
@ -144,7 +155,7 @@ func New() *Node {
} }
func (n *Node) Default() *Node { func (n *Node) Default() *Node {
if (FindPlugin("gocui") == nil) { if (n.LoadToolkit("gocui") == nil) {
log(logError, "New() failed to load gocui") log(logError, "New() failed to load gocui")
} }
// if DISPLAY isn't set, return since gtk can't load // if DISPLAY isn't set, return since gtk can't load
@ -152,7 +163,7 @@ func (n *Node) Default() *Node {
if (os.Getenv("DISPLAY") == "") { if (os.Getenv("DISPLAY") == "") {
return n return n
} }
if (FindPlugin("andlabs") == nil) { if (n.LoadToolkit("andlabs") == nil) {
log(logError, "New() failed to load andlabs") log(logError, "New() failed to load andlabs")
} }
return n return n
@ -169,8 +180,8 @@ func (n *Node) StandardClose() {
func StandardExit() { func StandardExit() {
log("wit/gui Standard Window Exit. running os.Exit()") log("wit/gui Standard Window Exit. running os.Exit()")
log("StandardExit() attempt to exit each toolkit plugin") log("StandardExit() attempt to exit each toolkit plugin")
for i, aplug := range allPlugins { for i, plug := range allPlugins {
log("NewButton()", i, aplug) log("NewButton()", i, plug)
} }
exit(0) exit(0)
} }

141
plugin.go
View File

@ -16,17 +16,14 @@ var err error
type Symbol any type Symbol any
type aplug struct { type aplug struct {
// Ok bool
name string name string
filename string filename string
plug *plugin.Plugin plug *plugin.Plugin
sym *plugin.Symbol // sym *plugin.Symbol
// LoadOk bool
InitOk bool InitOk bool
// MainOk bool
// startup whatever might need to be setup in the plugin // startup whatever might need to be setup in the plugin
Init func() // Init func()
// This passes the go channel to the plugin // This passes the go channel to the plugin
// the plugin then passes actions back to // the plugin then passes actions back to
@ -49,33 +46,27 @@ type aplug struct {
// add button request // add button request
pluginChan chan toolkit.Action pluginChan chan toolkit.Action
PluginChannel func() 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 var allPlugins []*aplug
// loads and initializes a toolkit (andlabs/ui, gocui, etc) // loads and initializes a toolkit (andlabs/ui, gocui, etc)
// attempts to locate the .so file // attempts to locate the .so file
func FindPlugin(name string) *aplug { func initPlugin(name string) *aplug {
var newPlug *aplug log(logInfo, "initPlugin() START")
newPlug = new(aplug)
log(logInfo, "FindPlugin() START")
newPlug.InitOk = false
for _, aplug := range allPlugins { 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) { if (aplug.name == name) {
log(debugError, "FindPlugin() SKIPPING", name, "as you can't load it twice") log(debugError, "initPlugin() SKIPPING", name, "as you can't load it twice")
return aplug return nil
} }
} }
var newPlug *aplug
newPlug = new(aplug)
newPlug.InitOk = false
// locate the shared library file // locate the shared library file
filename := name + ".so" filename := name + ".so"
loadPlugin(newPlug, filename) loadPlugin(newPlug, filename)
@ -86,22 +77,6 @@ func FindPlugin(name string) *aplug {
// newPlug.Ok = true // newPlug.Ok = true
newPlug.name = name 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 // this tells the toolkit plugin how to send user events back to us
// for things like: the user clicked on the 'Check IPv6' // for things like: the user clicked on the 'Check IPv6'
newPlug.Callback = sendCallback(newPlug, "Callback") newPlug.Callback = sendCallback(newPlug, "Callback")
@ -112,8 +87,8 @@ func FindPlugin(name string) *aplug {
allPlugins = append(allPlugins, newPlug) allPlugins = append(allPlugins, newPlug)
log(debugPlugin, "FindPlugin() END", newPlug.name, filename) log(debugPlugin, "initPlugin() END", newPlug.name, filename)
newPlug.Init() // newPlug.Init()
// set the communication to the plugins // set the communication to the plugins
newPlug.pluginChan = newPlug.PluginChannel() newPlug.pluginChan = newPlug.PluginChannel()
@ -121,35 +96,9 @@ func FindPlugin(name string) *aplug {
newPlug.InitOk = true 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 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") // newPlug.PluginChannel = getPluginChannel(newPlug, "PluginChannel")
func getPluginChannel(p *aplug, funcName string) func() chan toolkit.Action { func getPluginChannel(p *aplug, funcName string) func() chan toolkit.Action {
var newfunc 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 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: This searches in the following order for the plugin .so files:
./toolkit/ ./toolkit/

View File

@ -24,9 +24,6 @@ func catchActionChannel() {
log(logNow, "catchActionChannel() START") log(logNow, "catchActionChannel() START")
for { for {
log(logNow, "catchActionChannel() for loop") log(logNow, "catchActionChannel() for loop")
uiMain.Do(func() {
go ui.Main(demoUI)
})
select { select {
case a := <-pluginChan: case a := <-pluginChan:
log(logNow, "catchActionChannel() SELECT widget id =", a.WidgetId, a.Name) 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 // 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(logNow, "Init() START")
log(debugToolkit, "Init()") log(debugToolkit, "Init()")
// Can you pass values to a plugin init() ? Otherwise, there is no way to safely print // 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) pluginChan = make(chan toolkit.Action, 1)
log(logNow, "Init() start channel reciever") log(logNow, "Init() start channel reciever")
go ui.Main(func() {
demoUI()
})
go catchActionChannel() go catchActionChannel()
/*
// go catchActionChannel()
go uiMain.Do(func() {
ui.Main(demoUI)
// go catchActionChannel()
})
*/
log(logNow, "Init() END") log(logNow, "Init() END")
} }

View File

@ -24,6 +24,7 @@ func Send(p *toolkit.Widget, c *toolkit.Widget) {
} }
*/ */
/*
func oldAction2(a *toolkit.Action) { func oldAction2(a *toolkit.Action) {
log(logNow, "Action() START") log(logNow, "Action() START")
if (a == nil) { if (a == nil) {
@ -31,22 +32,31 @@ func oldAction2(a *toolkit.Action) {
return return
} }
pluginChan <- *a pluginChan <- *a
/*
f := func() { f := func() {
rawAction(a) rawAction(a)
} }
// f() // f()
Queue(f) Queue(f)
*/
log(logNow, "Action() END") log(logNow, "Action() END")
} }
*/
func rawAction(a toolkit.Action) { func rawAction(a toolkit.Action) {
log(debugAction, "rawAction() START a.ActionType =", a.ActionType) log(debugAction, "rawAction() START a.ActionType =", a.ActionType)
log(debugAction, "rawAction() START a.S =", a.S) 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) log(logNow, "rawAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId)
switch a.WidgetType { switch a.WidgetType {
case toolkit.Flag: case toolkit.Flag:

View File

@ -1,15 +1,7 @@
package toolkit package toolkit
type WidgetType int
type ActionType int
// passes information between the toolkit library (plugin) // 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 // This is the only thing that is passed between the toolkit plugin
// //
// what names should be used? This is not part of [[Graphical Widget]] // 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 // Event is used too much: web dev, cloud, etc
// I'm using "Action". Maybe it should really be // I'm using "Action". Maybe it should really be
// "Interaction" as per wikipedia [[User interface]] // "Interaction" as per wikipedia [[User interface]]
// Could a protobuf be used here? (Can functions be passed?) //
type Widget2 struct { // TODO: convert this to a protobuf (?)
// Name string //
Type WidgetType
// This function is how you interact with the toolkit type WidgetType int // Button, Menu, Checkbox, etc.
// latest attempt. seems to work so far (2023/02/28) type ActionType int // Add, SetText, Click, Hide, Append, Delete, etc
// 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 Action struct { type Action struct {
ActionType ActionType ActionType ActionType
@ -51,20 +26,13 @@ type Action struct {
Text string // what is visable to the user Text string // what is visable to the user
Name string // a name useful for programming 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 // This is how the values are passed back and forth
// values from things like checkboxes & dropdown's // values from things like checkboxes & dropdown's
// The string is also used to set the button name
B bool B bool
I int I int
// maybe safe if there is correctly working Custom() between goroutines? S string
// (still probably not, almost certainly not. not possible. layer violation?)
S string // not safe to have 'S'
A any A any // switch to this or deprecate this? pros/cons?
// This GUI is intended for simple things // This GUI is intended for simple things
// We are not laying out PDF's here // We are not laying out PDF's here

View File

@ -23,12 +23,6 @@ func Watchdog() {
Config.rootNode.ListChildren(true) Config.rootNode.ListChildren(true)
} }
} }
if (i == 2) {
Config.rootNode.LoadToolkit("gocui")
}
// if (i == 3) {
// Config.rootNode.LoadToolkit("andlabs")
// }
i += 1 i += 1
time.Sleep(watchtime * time.Second / 10) time.Sleep(watchtime * time.Second / 10)
} }