Implement a early AddButton() via a golang plugin

pass a name to gocui.AddButton()
    cleaner plugin usage
    add the start to golang plugin
    plugin stuff in a single file
    added a button correctly
    andlabs/ui added a button via plugin to gocli
  	  hot diggity!
    trying to invoke a gocli plugin function from the andlabs ui
    load the plugin
    hide more debugging output
    turn off all output

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2022-11-06 19:57:20 -06:00
parent 482c3ef8eb
commit c43a684857
15 changed files with 260 additions and 41 deletions

View File

@ -1,5 +1,5 @@
run: build
./buttonAsPlugin
./buttonAsPlugin >/tmp/buttonAsPlugin.log 2>&1
build-release:
go get -v -u -x .

View File

@ -0,0 +1,64 @@
// This creates a simple hello world window
package main
import (
"log"
"fmt"
"os"
"io"
"time"
"bufio"
arg "github.com/alexflint/go-arg"
)
var args struct {
Foo string
Bar bool
User string `arg:"env:USER"`
Demo bool `help:"run a demo"`
}
var f1 *os.File
var f2 *os.File
var err error
func init() {
arg.MustParse(&args)
fmt.Println(args.Foo, args.Bar, args.User)
f1, err = os.OpenFile("/tmp/guilogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
// hmm. is there a trick here or must this be in main()
// defer f.Close()
log.SetOutput(f1)
log.Println("This is a test log entry")
}
func captureSTDOUT() {
f2, _ = os.OpenFile("/tmp/my.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
multiWriter := io.MultiWriter(os.Stderr, f2)
rd, wr, err := os.Pipe()
if err != nil {
os.Exit(1)
}
// overwrite os.Stdout
os.Stderr = wr
go func() {
scanner := bufio.NewScanner(rd)
for scanner.Scan() {
stdoutLine := scanner.Text()
multiWriter.Write([]byte(stdoutLine + "\n"))
}
}()
fmt.Println("foobar")
// hacky sleep to ensure the go routine can write before program exits
time.Sleep(time.Second)
}

View File

@ -7,6 +7,12 @@ import (
)
func main() {
// go loadPlugin(plugHello, "../../toolkit/hello.so")
// this doesn't seem to work
captureSTDOUT()
// go loadPlugin("../../toolkit/gocli.so")
gui.Main(buttonWindow)
}
@ -17,9 +23,6 @@ func buttonWindow() {
gui.Config.Width = 640
gui.Config.Height = 480
// gui.Config.Exit = gui.StandardClose
// gui.SetDebug(true)
w = gui.NewWindow()
g = w.NewGroup("buttonGroup")
@ -27,7 +30,25 @@ func buttonWindow() {
log.Println("world")
})
g.NewButton("foo", func () {
log.Println("bar")
/*
g.NewButton("LoadPlugin()", func () {
log.Println("world")
gui.LoadPlugin("../../toolkit/gocli.so")
})
*/
g.NewButton("RunGreet()", func () {
log.Println("world")
go gui.RunGreet()
})
g.NewButton("gui.LookupJcarrButton()", func () {
log.Println("gui.LookupJcarrButton()")
gui.LookupJcarrButton()
})
g.NewButton("gui.GocuiAddButton()", func () {
log.Println("gui.GocuiAddButton()")
gui.GocuiAddButton("new foobar")
})
}

View File

@ -9,7 +9,10 @@ import toolkit "git.wit.org/wit/gui/toolkit/andlabs"
func (n *Node) NewGroup(name string) *Node {
var newT *toolkit.Toolkit
var gNode *Node
log.Println("toolkit.NewGroup() START", name)
if (GetDebug()) {
log.Println("toolkit.NewGroup() START", name)
}
if (n.toolkit == nil) {
log.Println("toolkit.NewGroup() toolkit == nil")

View File

@ -29,10 +29,15 @@ func init() {
if (Config.Options.Debug) {
Config.master.Dump()
}
// load the gocli plugin
PlugGocli = LoadPlugin("../../toolkit/gocli.so")
}
func Main(f func()) {
log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
if (Config.Options.Debug) {
log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
}
toolkit.Main(f)
}

90
plugin.go Normal file
View File

@ -0,0 +1,90 @@
package gui
import (
"log"
"os"
"plugin"
"github.com/davecgh/go-spew/spew"
)
type Greeter interface {
Greet()
JcarrButton()
AddButton(string)
}
var PlugGocli *plugin.Plugin
var PlugHello *plugin.Plugin
// var gBut plugin.Symbol
var jcarrBut plugin.Symbol
var symGreeter plugin.Symbol
var greeter Greeter
var ok bool
func LoadPlugin(name string) *plugin.Plugin {
scs := spew.ConfigState{MaxDepth: 1}
// load module
// 1. open the so file to load the symbols
plug, err := plugin.Open(name)
log.Println("plug =")
log.Println(scs.Sdump(plug))
if err != nil {
log.Println(err)
os.Exit(1)
}
PlugGocli = plug
// 2. look up a symbol (an exported function or variable)
// in this case, variable Greeter
symGreeter, err = plug.Lookup("Greeter")
log.Println("symGreater", symGreeter)
log.Println(scs.Sdump(symGreeter))
if err != nil {
log.Println(err)
os.Exit(1)
}
// 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))
if !ok {
log.Println("unexpected type from module symbol")
os.Exit(1)
}
return plug
}
func RunGreet() {
log.Println("gui.RunGreet() START")
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
greeter.Greet()
}
func LookupJcarrButton() {
log.Println("lookupJcarrButton() START")
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
greeter.JcarrButton()
}
func GocuiAddButton(name string) {
log.Println("GocuiAddButton() START", name)
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
greeter.AddButton(name)
}

View File

@ -46,13 +46,13 @@ func ShowDebugValues() {
log.Println("\t wit/gui DebugDump =", Config.Options.DebugDump)
log.Println("\t wit/gui DebugNode =", Config.Options.DebugNode)
log.Println("\t wit/gui DebugTabs =", Config.Options.DebugTabs)
// log.Println("\t wit/gui DebugTable =", Config.Options.DebugTable)
// log.Println("\t wit/gui DebugWindow =", Config.Options.DebugWindow)
log.Println("\t wit/gui DebugPlugin =", Config.Options.DebugPlugin)
log.Println("\t wit/gui DebugChange =", Config.Options.DebugChange)
log.Println("\t wit/gui DebugToolkit =", toolkit.DebugToolkit)
}
// This struct can be used with go-arg
type GuiOptions struct {
// These are global debugging settings
// TODO: move to a standard logging system
@ -60,8 +60,7 @@ type GuiOptions struct {
DebugDump bool
DebugNode bool
DebugTabs bool
// DebugTable bool
// DebugWindow bool
DebugPlugin bool
DebugChange bool `help:"debug mouse clicks and keyboard input"`
}

View File

@ -12,10 +12,14 @@ func (t *Toolkit) GetBox() *ui.Box {
// create a new box
func (t *Toolkit) NewBox() *Toolkit {
log.Println("gui.Toolbox.NewBox() START create default")
if (DebugToolkit) {
log.Println("gui.Toolbox.NewBox() START create default")
}
t.Dump()
if (t.uiGroup != nil) {
log.Println("\tgui.Toolbox.NewBox() is a Group")
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Group")
}
var newTK Toolkit
vbox := ui.NewVerticalBox()
@ -26,7 +30,9 @@ func (t *Toolkit) NewBox() *Toolkit {
return &newTK
}
if (t.uiBox != nil) {
log.Println("\tgui.Toolbox.NewBox() is a Box")
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Box")
}
var newTK Toolkit
vbox := ui.NewVerticalBox()
@ -38,7 +44,9 @@ func (t *Toolkit) NewBox() *Toolkit {
return &newTK
}
if (t.uiWindow != nil) {
log.Println("\tgui.Toolbox.NewBox() is a Window")
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Window")
}
var newT Toolkit
vbox := ui.NewVerticalBox()
@ -50,7 +58,9 @@ func (t *Toolkit) NewBox() *Toolkit {
// panic("WTF")
return &newT
}
log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box")
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box")
}
t.Dump()
return nil
}

View File

@ -3,7 +3,9 @@ package toolkit
import "log"
func init() {
log.Println("gui/toolkit init() Setting defaultBehavior = true")
if (DebugToolkit) {
log.Println("gui/toolkit init() Setting defaultBehavior = true")
}
setDefaultBehavior(true)
}
@ -37,7 +39,9 @@ func (t Toolkit) commonChange(widget string) {
func (t *Toolkit) broken() bool {
if (t.uiBox == nil) {
if (t.uiWindow != nil) {
log.Println("gui.Toolkit.UiBox == nil. This is an empty window. Try to add a box")
if (DebugToolkit) {
log.Println("gui.Toolkit.UiBox == nil. This is an empty window. Try to add a box")
}
t.NewBox()
return false
}

View File

@ -9,7 +9,9 @@ import (
)
func Main(f func()) {
log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
if (DebugToolkit) {
log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
}
ui.Main(f)
}
@ -22,6 +24,8 @@ func Main(f func()) {
// For example: Queue(NewWindow())
//
func Queue(f func()) {
log.Println("Sending function to gui.Main() (using gtk via andlabs/ui)")
if (DebugToolkit) {
log.Println("Sending function to gui.Main() (using gtk via andlabs/ui)")
}
ui.QueueMain(f)
}

View File

@ -21,8 +21,10 @@ var DebugToolkit bool
func setDefaultBehavior(s bool) {
defaultBehavior = s
if (defaultBehavior) {
log.Println("Setting this toolkit to use the default behavior.")
log.Println("This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.")
if (DebugToolkit) {
log.Println("Setting this toolkit to use the default behavior.")
log.Println("This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.")
}
stretchy = false
padded = true
menubar = true

View File

@ -17,7 +17,9 @@ func (t *Toolkit) ErrorWindow(msg1 string, msg2 string) {
func NewWindow(title string, x int, y int) *Toolkit {
var t Toolkit
log.Println("toolkit NewWindow", title, x, y)
if (DebugToolkit) {
log.Println("toolkit NewWindow", title, x, y)
}
w := ui.NewWindow(title, x, y, menubar)
w.SetBorderless(canvas)
w.SetMargined(margin)
@ -50,11 +52,15 @@ func NewWindow(title string, x int, y int) *Toolkit {
}
func (t *Toolkit) SetWindowTitle(title string) {
log.Println("toolkit NewWindow", t.Name, "title", title)
if (DebugToolkit) {
log.Println("toolkit NewWindow", t.Name, "title", title)
}
win := t.uiWindow
if (win != nil) {
win.SetTitle(title)
} else {
log.Println("Setting the window title", title)
if (DebugToolkit) {
log.Println("Setting the window title", title)
}
}
}

View File

@ -11,6 +11,8 @@ import (
type greeting string
// this is exported
var Greeter greeting
// func main() {
func (g greeting) Greet() {
@ -19,16 +21,25 @@ func (g greeting) Greet() {
// ToolkitMain()
}
// this is exported
var Greeter greeting
func (g greeting) JcarrButton() {
fmt.Println("Hello GreetButton meet Universe")
addButton("Greet foo")
addButton("Greet foo 2")
}
func AddGroup(name string) {
func addGroup(name string) {
log.Println("addGroup()", name)
currentY = 2
currentX += groupSize + 6
}
func AddButton(name string) error {
func (g greeting) AddButton(name string) {
// func (g greeting) AddButton() {
log.Println("gui.gocui.AddButton()", name)
addButton(name)
}
func addButton(name string) error {
t := len(name)
v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0)
if err == nil {

View File

@ -50,18 +50,18 @@ func Init() {
log.Panicln(err)
}
AddButton("hello")
AddButton("world")
AddButton("foo")
addButton("hello")
addButton("world")
addButton("foo")
AddGroup("blank")
AddButton("bar")
AddButton("bar none")
AddButton("bar going")
addGroup("blank")
addButton("bar")
addButton("bar none")
addButton("bar going")
AddGroup("te")
AddButton("world 2")
AddButton("foo 2")
addGroup("te")
addButton("world 2")
addButton("foo 2")
if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
log.Panicln(err)

View File

@ -20,7 +20,7 @@ var bottomY int = 7
func newJ(g *gocui.Gui) error {
// maxX, maxY := g.Size()
name := fmt.Sprintf("jcarr %v test ", idxView)
name := fmt.Sprintf("jcarr %v foo ", idxView)
v, err := g.SetView(name, topX, topY, bottomX, bottomY, 0)
if err == nil {
return err