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 run: build
./buttonAsPlugin ./buttonAsPlugin >/tmp/buttonAsPlugin.log 2>&1
build-release: build-release:
go get -v -u -x . 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() { func main() {
// go loadPlugin(plugHello, "../../toolkit/hello.so")
// this doesn't seem to work
captureSTDOUT()
// go loadPlugin("../../toolkit/gocli.so")
gui.Main(buttonWindow) gui.Main(buttonWindow)
} }
@ -17,9 +23,6 @@ func buttonWindow() {
gui.Config.Width = 640 gui.Config.Width = 640
gui.Config.Height = 480 gui.Config.Height = 480
// gui.Config.Exit = gui.StandardClose
// gui.SetDebug(true)
w = gui.NewWindow() w = gui.NewWindow()
g = w.NewGroup("buttonGroup") g = w.NewGroup("buttonGroup")
@ -27,7 +30,25 @@ func buttonWindow() {
log.Println("world") 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 { func (n *Node) NewGroup(name string) *Node {
var newT *toolkit.Toolkit var newT *toolkit.Toolkit
var gNode *Node var gNode *Node
if (GetDebug()) {
log.Println("toolkit.NewGroup() START", name) log.Println("toolkit.NewGroup() START", name)
}
if (n.toolkit == nil) { if (n.toolkit == nil) {
log.Println("toolkit.NewGroup() toolkit == nil") log.Println("toolkit.NewGroup() toolkit == nil")

View File

@ -29,10 +29,15 @@ func init() {
if (Config.Options.Debug) { if (Config.Options.Debug) {
Config.master.Dump() Config.master.Dump()
} }
// load the gocli plugin
PlugGocli = LoadPlugin("../../toolkit/gocli.so")
} }
func Main(f func()) { func Main(f func()) {
if (Config.Options.Debug) {
log.Println("Starting gui.Main() (using gtk via andlabs/ui)") log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
}
toolkit.Main(f) 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 DebugDump =", Config.Options.DebugDump)
log.Println("\t wit/gui DebugNode =", Config.Options.DebugNode) log.Println("\t wit/gui DebugNode =", Config.Options.DebugNode)
log.Println("\t wit/gui DebugTabs =", Config.Options.DebugTabs) log.Println("\t wit/gui DebugTabs =", Config.Options.DebugTabs)
// log.Println("\t wit/gui DebugTable =", Config.Options.DebugTable) log.Println("\t wit/gui DebugPlugin =", Config.Options.DebugPlugin)
// log.Println("\t wit/gui DebugWindow =", Config.Options.DebugWindow)
log.Println("\t wit/gui DebugChange =", Config.Options.DebugChange) log.Println("\t wit/gui DebugChange =", Config.Options.DebugChange)
log.Println("\t wit/gui DebugToolkit =", toolkit.DebugToolkit) log.Println("\t wit/gui DebugToolkit =", toolkit.DebugToolkit)
} }
// This struct can be used with go-arg
type GuiOptions struct { type GuiOptions struct {
// These are global debugging settings // These are global debugging settings
// TODO: move to a standard logging system // TODO: move to a standard logging system
@ -60,8 +60,7 @@ type GuiOptions struct {
DebugDump bool DebugDump bool
DebugNode bool DebugNode bool
DebugTabs bool DebugTabs bool
// DebugTable bool DebugPlugin bool
// DebugWindow bool
DebugChange bool `help:"debug mouse clicks and keyboard input"` 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 // create a new box
func (t *Toolkit) NewBox() *Toolkit { func (t *Toolkit) NewBox() *Toolkit {
if (DebugToolkit) {
log.Println("gui.Toolbox.NewBox() START create default") log.Println("gui.Toolbox.NewBox() START create default")
}
t.Dump() t.Dump()
if (t.uiGroup != nil) { if (t.uiGroup != nil) {
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Group") log.Println("\tgui.Toolbox.NewBox() is a Group")
}
var newTK Toolkit var newTK Toolkit
vbox := ui.NewVerticalBox() vbox := ui.NewVerticalBox()
@ -26,7 +30,9 @@ func (t *Toolkit) NewBox() *Toolkit {
return &newTK return &newTK
} }
if (t.uiBox != nil) { if (t.uiBox != nil) {
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Box") log.Println("\tgui.Toolbox.NewBox() is a Box")
}
var newTK Toolkit var newTK Toolkit
vbox := ui.NewVerticalBox() vbox := ui.NewVerticalBox()
@ -38,7 +44,9 @@ func (t *Toolkit) NewBox() *Toolkit {
return &newTK return &newTK
} }
if (t.uiWindow != nil) { if (t.uiWindow != nil) {
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() is a Window") log.Println("\tgui.Toolbox.NewBox() is a Window")
}
var newT Toolkit var newT Toolkit
vbox := ui.NewVerticalBox() vbox := ui.NewVerticalBox()
@ -50,7 +58,9 @@ func (t *Toolkit) NewBox() *Toolkit {
// panic("WTF") // panic("WTF")
return &newT return &newT
} }
if (DebugToolkit) {
log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box") log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box")
}
t.Dump() t.Dump()
return nil return nil
} }

View File

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

View File

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

View File

@ -21,8 +21,10 @@ var DebugToolkit bool
func setDefaultBehavior(s bool) { func setDefaultBehavior(s bool) {
defaultBehavior = s defaultBehavior = s
if (defaultBehavior) { if (defaultBehavior) {
if (DebugToolkit) {
log.Println("Setting this toolkit to use the default behavior.") 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.") log.Println("This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.")
}
stretchy = false stretchy = false
padded = true padded = true
menubar = 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 { func NewWindow(title string, x int, y int) *Toolkit {
var t Toolkit var t Toolkit
if (DebugToolkit) {
log.Println("toolkit NewWindow", title, x, y) log.Println("toolkit NewWindow", title, x, y)
}
w := ui.NewWindow(title, x, y, menubar) w := ui.NewWindow(title, x, y, menubar)
w.SetBorderless(canvas) w.SetBorderless(canvas)
w.SetMargined(margin) w.SetMargined(margin)
@ -50,11 +52,15 @@ func NewWindow(title string, x int, y int) *Toolkit {
} }
func (t *Toolkit) SetWindowTitle(title string) { func (t *Toolkit) SetWindowTitle(title string) {
if (DebugToolkit) {
log.Println("toolkit NewWindow", t.Name, "title", title) log.Println("toolkit NewWindow", t.Name, "title", title)
}
win := t.uiWindow win := t.uiWindow
if (win != nil) { if (win != nil) {
win.SetTitle(title) win.SetTitle(title)
} else { } else {
if (DebugToolkit) {
log.Println("Setting the window title", title) log.Println("Setting the window title", title)
} }
} }
}

View File

@ -11,6 +11,8 @@ import (
type greeting string type greeting string
// this is exported
var Greeter greeting
// func main() { // func main() {
func (g greeting) Greet() { func (g greeting) Greet() {
@ -19,16 +21,25 @@ func (g greeting) Greet() {
// ToolkitMain() // ToolkitMain()
} }
// this is exported func (g greeting) JcarrButton() {
var Greeter greeting 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) log.Println("addGroup()", name)
currentY = 2 currentY = 2
currentX += groupSize + 6 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) t := len(name)
v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0)
if err == nil { if err == nil {

View File

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

View File

@ -20,7 +20,7 @@ var bottomY int = 7
func newJ(g *gocui.Gui) error { func newJ(g *gocui.Gui) error {
// maxX, maxY := g.Size() // 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) v, err := g.SetView(name, topX, topY, bottomX, bottomY, 0)
if err == nil { if err == nil {
return err return err