mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
284 lines
8.3 KiB
Go
284 lines
8.3 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package sdlimgui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/inkyblackness/imgui-go/v4"
|
|
"github.com/jetsetilly/gopher2600/coprocessor/developer/dwarf"
|
|
"github.com/jetsetilly/gopher2600/coprocessor/developer/yield"
|
|
"github.com/jetsetilly/gopher2600/gui/fonts"
|
|
)
|
|
|
|
const winCoProcLocalsID = "Coprocessor Local Variables"
|
|
const winCoProcLocalsMenu = "Locals"
|
|
|
|
type winCoProcLocals struct {
|
|
debuggerWin
|
|
img *SdlImgui
|
|
|
|
optionsHeight float32
|
|
showLocatableOnly bool
|
|
filter filter
|
|
|
|
openNodes map[string]bool
|
|
}
|
|
|
|
func newWinCoProcLocals(img *SdlImgui) (window, error) {
|
|
win := &winCoProcLocals{
|
|
img: img,
|
|
filter: newFilter(img, filterFlagsVariableNamesC),
|
|
openNodes: make(map[string]bool),
|
|
}
|
|
return win, nil
|
|
}
|
|
|
|
func (win *winCoProcLocals) init() {
|
|
}
|
|
|
|
func (win *winCoProcLocals) id() string {
|
|
return winCoProcLocalsID
|
|
}
|
|
|
|
func (win *winCoProcLocals) debuggerDraw() bool {
|
|
if !win.debuggerOpen {
|
|
return false
|
|
}
|
|
|
|
// do not open window if there is no coprocessor available
|
|
coproc := win.img.cache.VCS.Mem.Cart.GetCoProc()
|
|
if coproc == nil {
|
|
return false
|
|
}
|
|
|
|
imgui.SetNextWindowPosV(imgui.Vec2{X: 982, Y: 77}, imgui.ConditionFirstUseEver, imgui.Vec2{X: 0, Y: 0})
|
|
imgui.SetNextWindowSizeV(imgui.Vec2{X: 520, Y: 390}, imgui.ConditionFirstUseEver)
|
|
win.img.setReasonableWindowConstraints()
|
|
|
|
title := fmt.Sprintf("%s %s", coproc.ProcessorID(), winCoProcLocalsID)
|
|
if imgui.BeginV(win.debuggerID(title), &win.debuggerOpen, imgui.WindowFlagsNone) {
|
|
win.draw()
|
|
}
|
|
|
|
win.debuggerGeom.update()
|
|
imgui.End()
|
|
|
|
return true
|
|
}
|
|
|
|
func (win *winCoProcLocals) draw() {
|
|
// take copy of yield state before borrowing the source
|
|
var yieldState yield.State
|
|
win.img.dbg.CoProcDev.BorrowYieldState(func(yld yield.State) {
|
|
yieldState = yld
|
|
})
|
|
|
|
// borrow source only so that we can check if whether to draw the window fully
|
|
win.img.dbg.CoProcDev.BorrowSource(func(src *dwarf.Source) {
|
|
if src == nil || len(src.Filenames) == 0 {
|
|
imgui.AlignTextToFramePadding()
|
|
imgui.Text("No source files available")
|
|
return
|
|
}
|
|
|
|
// the local variables list we use depends on whether the emulation is
|
|
// running and whether a strobe is active. we use the LocalVariableView()
|
|
// function for this
|
|
viewedAddr, viewedLocals := yieldState.LocalVariableView(win.img.dbg.State())
|
|
|
|
// exit draw early leaving a message to indicate that there are no local
|
|
// variables available to display
|
|
if len(viewedLocals) == 0 {
|
|
imgui.AlignTextToFramePadding()
|
|
imgui.Text("No local variables currently visible")
|
|
return
|
|
}
|
|
|
|
// strobe information at the top of the window
|
|
ln := src.SourceLineByAddr(viewedAddr)
|
|
imgui.AlignTextToFramePadding()
|
|
imgui.Text(fmt.Sprintf("%s line %d", ln.File.ShortFilename, ln.LineNumber))
|
|
if imgui.IsItemClicked() && imgui.IsMouseDoubleClicked(0) {
|
|
srcWin := win.img.wm.debuggerWindows[winCoProcSourceID].(*winCoProcSource)
|
|
srcWin.gotoSourceLine(ln)
|
|
}
|
|
imgui.SameLineV(0, 15)
|
|
if yieldState.Strobe {
|
|
if imgui.Button("Remove Strobe") {
|
|
win.img.dbg.CoProcDev.EnableStrobe(false, viewedAddr)
|
|
}
|
|
|
|
// the addr and the line from that address are for the current yield
|
|
// point. for the tooltip however, we want to use the strobe addr
|
|
//
|
|
// if strobe line is different to yield line then show the tooltip
|
|
sln := src.SourceLineByAddr(yieldState.StrobeAddr)
|
|
if sln != ln {
|
|
imguiTooltipSimple(fmt.Sprintf("Strobe is currently set to:\n%s line %d", sln.File.ShortFilename, sln.LineNumber), true)
|
|
}
|
|
} else {
|
|
if imgui.Button("Set Strobe") {
|
|
win.img.dbg.CoProcDev.EnableStrobe(true, viewedAddr)
|
|
}
|
|
}
|
|
|
|
imgui.Spacing()
|
|
imgui.Separator()
|
|
imgui.Spacing()
|
|
|
|
const numColumns = 3
|
|
|
|
flgs := imgui.TableFlagsScrollY
|
|
flgs |= imgui.TableFlagsSizingStretchProp
|
|
flgs |= imgui.TableFlagsResizable
|
|
flgs |= imgui.TableFlagsHideable
|
|
|
|
if !imgui.BeginTableV("##localsTable", numColumns, flgs, imgui.Vec2{Y: imguiRemainingWinHeight() - win.optionsHeight}, 0.0) {
|
|
return
|
|
}
|
|
|
|
// setup columns. the labelling column 2 depends on whether the coprocessor
|
|
// development instance has source available to it
|
|
width := imgui.ContentRegionAvail().X
|
|
imgui.TableSetupColumnV("Name", imgui.TableColumnFlagsNoSort, width*0.40, 1)
|
|
imgui.TableSetupColumnV("Type", imgui.TableColumnFlagsNoSort, width*0.30, 2)
|
|
imgui.TableSetupColumnV("Value", imgui.TableColumnFlagsNoSort, width*0.30, 3)
|
|
|
|
imgui.TableSetupScrollFreeze(0, 1)
|
|
imgui.TableHeadersRow()
|
|
|
|
for i, varb := range viewedLocals {
|
|
if !win.filter.isFiltered(varb.Name) {
|
|
win.drawVariableLocal(varb, fmt.Sprint(i))
|
|
}
|
|
}
|
|
|
|
imgui.EndTable()
|
|
|
|
win.optionsHeight = imguiMeasureHeight(func() {
|
|
imgui.Spacing()
|
|
imgui.Separator()
|
|
imgui.Spacing()
|
|
imgui.Checkbox("Hide unlocatable variables", &win.showLocatableOnly)
|
|
win.img.imguiTooltipSimple(`A unlocatable variable is a variable has been
|
|
removed by the compiler's optimisation process`)
|
|
win.filter.draw("##localsFilter")
|
|
})
|
|
})
|
|
}
|
|
|
|
func (win *winCoProcLocals) drawVariableLocal(local *dwarf.SourceVariableLocal, nodeID string) {
|
|
win.drawVariable(local.SourceVariable, 0, nodeID)
|
|
}
|
|
|
|
func (win *winCoProcLocals) drawVariable(varb *dwarf.SourceVariable, indentLevel int, nodeID string) {
|
|
// update variable
|
|
win.img.dbg.PushFunction(func() {
|
|
win.img.dbg.CoProcDev.BorrowSource(func(src *dwarf.Source) {
|
|
varb.Update()
|
|
})
|
|
})
|
|
|
|
const IndentDepth = 2
|
|
|
|
// name of variable as presented. added bug icon as appropriate
|
|
name := fmt.Sprintf("%s%s", strings.Repeat(" ", IndentDepth*indentLevel), varb.Name)
|
|
if !varb.IsValid() {
|
|
if win.showLocatableOnly {
|
|
return
|
|
}
|
|
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.CoProcVariablesNotVisible)
|
|
defer imgui.PopStyleColor()
|
|
}
|
|
|
|
imgui.TableNextRow()
|
|
imgui.TableNextColumn()
|
|
imgui.PushStyleColor(imgui.StyleColorHeaderHovered, win.img.cols.CoProcSourceHoverLine)
|
|
imgui.PushStyleColor(imgui.StyleColorHeaderActive, win.img.cols.CoProcSourceHoverLine)
|
|
imgui.SelectableV(name, false, imgui.SelectableFlagsSpanAllColumns, imgui.Vec2{X: 0, Y: 0})
|
|
imgui.PopStyleColorV(2)
|
|
|
|
if !varb.IsValid() {
|
|
imgui.TableNextColumn()
|
|
imgui.Text(varb.Type.Name)
|
|
imgui.TableNextColumn()
|
|
imgui.Text("not locatable")
|
|
return
|
|
}
|
|
|
|
if varb.NumChildren() > 0 {
|
|
// we could show a tooltip for variables with children but this needs
|
|
// work. for instance, how do we illustrate a composite type or an
|
|
// array?
|
|
win.img.imguiTooltip(func() {
|
|
drawVariableTooltipShort(varb, win.img.cols)
|
|
}, true)
|
|
|
|
if imgui.IsItemClicked() {
|
|
win.openNodes[nodeID] = !win.openNodes[nodeID]
|
|
}
|
|
|
|
imgui.TableNextColumn()
|
|
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.CoProcVariablesType)
|
|
imgui.Text(varb.Type.Name)
|
|
imgui.PopStyleColor()
|
|
|
|
// value column shows tree open/close icon unless there was an error
|
|
// during variable resolution
|
|
imgui.TableNextColumn()
|
|
if varb.Error != nil {
|
|
imgui.Text(string(fonts.CoProcBug))
|
|
} else {
|
|
if win.openNodes[nodeID] {
|
|
imgui.Text(string(fonts.TreeOpen))
|
|
} else {
|
|
imgui.Text(string(fonts.TreeClosed))
|
|
}
|
|
|
|
if win.openNodes[nodeID] {
|
|
for i := 0; i < varb.NumChildren(); i++ {
|
|
win.drawVariable(varb.Child(i), indentLevel+1, fmt.Sprint(nodeID, i))
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
win.img.imguiTooltip(func() {
|
|
if varb.Error != nil {
|
|
drawVariableTooltipShort(varb, win.img.cols)
|
|
} else {
|
|
drawVariableTooltip(varb, varb.Value(), win.img.cols)
|
|
}
|
|
}, true)
|
|
|
|
imgui.TableNextColumn()
|
|
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.CoProcVariablesType)
|
|
imgui.Text(varb.Type.Name)
|
|
imgui.PopStyleColor()
|
|
|
|
imgui.TableNextColumn()
|
|
if varb.Error != nil {
|
|
imgui.Text(string(fonts.CoProcBug))
|
|
} else if varb.Type.Conversion != nil {
|
|
c, _ := varb.Type.Conversion(varb.Value())
|
|
imgui.Text(c)
|
|
} else {
|
|
imgui.Text(fmt.Sprintf(varb.Type.Hex(), varb.Value()))
|
|
}
|
|
}
|
|
}
|