// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 // all structures and variables are local (aka lowercase) // since the plugin should be isolated to access only // by functions() to insure everything here is run // inside a dedicated goroutine package main import ( "fmt" "os" "reflect" "strconv" "sync" "time" "github.com/awesome-gocui/gocui" log "go.wit.com/log" "go.wit.com/toolkits/tree" ) var initOnce sync.Once // run initPlugin() only once var outf *os.File // hacks for capturing stdout // It's probably a terrible idea to call this 'me' // 2025 note: doesn't seem terrible to call this 'me' anymore. notsure. var me config // todo: move all this to a protobuf. then, redo all this mess. // it got me here, but now it's time to clean it up for good // I can't get a GO plugins that use protobuf to load yet (versioning mismatch) type config struct { baseGui *gocui.Gui // the main gocui handle treeRoot *tree.Node // the base of the binary tree. it should have id == 0 myTree *tree.TreeInfo // ? currentWindow *guiWidget // this is the current tab or window to show ok bool // if the user doesn't hit a key or move the mouse, gocui doesn't really start firstWindowOk bool // allows the init to wait for the first window from the application ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed helpLabel *gocui.View // ? showHelp bool // toggle boolean for the help menu (deprecate?) FramePadW int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side FramePadH int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side PadW int `default:"1" dense:"0"` // pad spacing PadH int `default:"1" dense:"0"` // pad spacing WindowW int `default:"8" dense:"0"` // how far down to start Window or Tab headings WindowH int `default:"-1"` // how far down to start Window or Tab headings TabW int `default:"5" dense:"0"` // how far down to start Window or Tab headings TabH int `default:"1" dense:"0"` // how far down to start Window or Tab headings WindowPadW int `default:"8" dense:"0"` // additional amount of space to put between window & tab widgets TabPadW int `default:"4" dense:"0"` // additional amount of space to put between window & tab widgets GroupPadW int `default:"2" dense:"1"` // additional amount of space to indent on a group BoxPadW int `default:"2" dense:"1"` // additional amount of space to indent on a box GridPadW int `default:"2" dense:"1"` // additional amount of space to indent on a grid RawW int `default:"1"` // the raw beginning of each window (or tab) RawH int `default:"5"` // the raw beginning of each window (or tab) FakeW int `default:"20"` // offset for the hidden widgets DropdownId int `default:"-78"` // the widget id to use padded bool // add space between things like buttons bookshelf bool // do you want things arranged in the box like a bookshelf or a stack? canvas bool // if set to true, the windows are a raw canvas menubar bool // for windows stretchy bool // expand things like buttons to the maximum size margin bool // add space around the frames of windows writeMutex sync.Mutex // writeMutex protects writes to *guiWidget (it's global right now maybe) ecount int // counts how many mouse and keyboard events have occurred supermouse bool // prints out every widget found while you move the mouse around depth int // used for listWidgets() debugging newWindowTrigger chan *guiWidget // work around hack to redraw windows a bit after NewWindow() stdout stdout // information for the STDOUT window dropdown dropdown // the dropdown menu textbox dropdown // the textbox popup window clock dropdown // the textbox popup window allwin []*guiWidget // for tracking which window is next dark bool // use a 'dark' color palette mouse mouse // mouse settings showDebug bool // todo: move this into config struct debug bool // todo: move this into config struct starttime time.Time // checks how long it takes on startup } // stuff controlling how the mouse works type mouse struct { down time.Time // when the mouse was pressed down up time.Time // when the mouse was released. used to detect click vs drag clicktime time.Duration // how long is too long for a mouse click vs drag mouseUp bool // is the mouse up? double bool // user is double clicking doubletime time.Duration // how long is too long for double click resize bool // mouse is resizing something downW int // where the mouse was pressed down downH int // where the mouse was pressed down currentDrag *guiWidget // what widget is currently being moved around } // settings for the stdout window type stdout struct { tk *guiWidget // where to show STDOUT wId int // the widget id w int // the width h int // the height outputOnTop bool // is the STDOUT window on top? outputOffscreen bool // is the STDOUT window offscreen? startOnscreen bool // start the output window onscreen? lastW int // the last 'w' location (used to move from offscreen to onscreen) lastH int // the last 'h' location (used to move from offscreen to onscreen) // mouseOffsetW int // the current 'w' offset // mouseOffsetH int // the current 'h' offset init bool // moves the window offscreen on startup outputS []string // the buffer of all the output pager int // allows the user to page through the buffer } // settings for the dropdown window type dropdown struct { tk *guiWidget // where to show STDOUT callerTK *guiWidget // which widget called the dropdown menu items []string // what is currently in the menu w int // the width h int // the height active bool // is the dropdown menu currently in use? init bool // moves the window offscreen on startup Id int `default:"-78"` // the widget id to use wId int `default:"-78"` // the widget id to use } // this is the gocui way // corner starts at in the upper left corner type rectType struct { w0, h0, w1, h1 int // left top right bottom } func (r *rectType) Width() int { return r.w1 - r.w0 } func (r *rectType) Height() int { if r.h0 == 0 && r.h1 == 0 { // edge case. only return 0 for these return 0 } if r.h1 == r.h0 { // if they are equal, it's actually height = 1 return 1 } if r.h1-r.h0 < 1 { // can't have negatives. something is wrong. return 1 for now return 1 } return r.h1 - r.h0 } // settings that are window specific type window struct { windowFrame *guiWidget // this is the frame for a window widget wasDragged bool // indicates the window was dragged. This keeps it from being rearranged hasTabs bool // does the window have tabs? currentTab bool // the visible tab selectedTab *tree.Node // for a window, this is currently selected tab active bool // means this window is the active one isBG bool // means this is the background widget. There is only one of these order int // what level the window is on // resize bool // only set the title once collapsed bool // only show the window title bar dense bool // true if the window is dense large bool // true if the window is huge pager int // allows the user to page through the window } type colorT struct { frame gocui.Attribute fg gocui.Attribute bg gocui.Attribute selFg gocui.Attribute selBg gocui.Attribute name string } type guiWidget struct { v *gocui.View // this is nil if the widget is not displayed cuiName string // what gocui uses to reference the widget (usually "TK " parent *guiWidget // mirrors the binary node tree children []*guiWidget // mirrors the binary node tree node *tree.Node // the pointer back to the tree windowFrame *guiWidget // this is the frame for a window widget internal bool // indicates the widget is internal to gocui and should be treated differently hasTabs bool // does the window have tabs? currentTab bool // the visible tab window window // holds information specific only to Window widgets value string // ? checked bool // ? labelN string // the actual text to display in the console vals []string // dropdown menu items enable bool // ? gocuiSize rectType // should mirror the real display size. todo: verify these are accurate. they are not yet full rectType // full size of children (used by widget.Window, etc) force rectType // force widget within these boundries (using this to debug window dragging) startW int // ? startH int // ? lastW int // used during mouse dragging lastH int // used during mouse dragging isFake bool // widget types like 'box' are 'false' widths map[int]int // how tall each row in the grid is heights map[int]int // how wide each column in the grid is tainted bool // ? frame bool // ? selectedTab *tree.Node // for a window, this is currently selected tab color *colorT // what color to use colorLast colorT // the last color the widget had defaultColor *colorT // the default colors // TODO: make a function for this instead isBG bool // means this is the background widget. There is only one of these } // THIS IS GO COMPILER MAGIC // this sets the `default` in the structs above on init() // this is cool code. thank the GO devs for this code and Alex Flint for explaining it to me func Set(ptr interface{}, tag string) error { if reflect.TypeOf(ptr).Kind() != reflect.Ptr { log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag) return fmt.Errorf("Not a pointer") } v := reflect.ValueOf(ptr).Elem() t := v.Type() for i := 0; i < t.NumField(); i++ { defaultVal := t.Field(i).Tag.Get(tag) name := t.Field(i).Name // log("Set() try name =", name, "defaultVal =", defaultVal) setField(v.Field(i), defaultVal, name) } return nil } func setField(field reflect.Value, defaultVal string, name string) error { if !field.CanSet() { // log("setField() Can't set value", field, defaultVal) return fmt.Errorf("Can't set value\n") } else { // log.Log(NOW, "setField() Can set value", name, defaultVal) } switch field.Kind() { case reflect.Int: val, _ := strconv.Atoi(defaultVal) field.Set(reflect.ValueOf(int(val)).Convert(field.Type())) case reflect.String: field.Set(reflect.ValueOf(defaultVal).Convert(field.Type())) case reflect.Bool: if defaultVal == "true" { field.Set(reflect.ValueOf(true)) } else { field.Set(reflect.ValueOf(false)) } } return nil }