// 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 .
package sdlimgui
import (
"fmt"
"image"
"image/color"
"github.com/inkyblackness/imgui-go/v4"
"github.com/jetsetilly/gopher2600/gui/fonts"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/cdf"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
"github.com/jetsetilly/gopher2600/hardware/television/signal"
"github.com/jetsetilly/gopher2600/hardware/television/specification"
)
const winCDFStreamsID = "CDF Streams"
// keeps track of whether the datastream drag and drop is active and which
// datastreams are involved (ie. source and target)
type datastreamDragAndDrop struct {
active bool
src int
tgt int
previousSrc int
}
type winCDFStreams struct {
debuggerWin
img *SdlImgui
streamPixels [cdf.NumDatastreams]*image.RGBA
streamTextures [cdf.NumDatastreams]texture
pixelsSize image.Point
detailTexture texture
colouriser datastreamDragAndDrop
colourSource [cdf.NumDatastreams]int
trackScreen bool
scanlines int32
optionsHeight float32
}
func newWinCDFStreams(img *SdlImgui) (window, error) {
win := &winCDFStreams{
img: img,
scanlines: specification.AbsoluteMaxScanlines,
trackScreen: true,
}
win.clearColours()
for i := range win.streamTextures {
win.streamTextures[i] = img.rnd.addTexture(shaderColor, false, false, nil)
win.streamPixels[i] = image.NewRGBA(image.Rect(0, 0, 8, specification.AbsoluteMaxScanlines))
}
win.detailTexture = img.rnd.addTexture(shaderColor, false, false, nil)
win.pixelsSize = win.streamPixels[0].Bounds().Size()
for y := 0; y < win.pixelsSize.Y; y++ {
for x := 0; x < win.pixelsSize.X; x++ {
for i := range win.streamPixels {
win.streamPixels[i].SetRGBA(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
}
}
win.render()
return win, nil
}
func (win *winCDFStreams) init() {
}
func (win *winCDFStreams) id() string {
return winCDFStreamsID
}
func (win *winCDFStreams) updateStreams(regs cdf.Registers, static mapper.CartStatic) {
// keep track of scanlines
frameInfo := win.img.cache.TV.GetFrameInfo()
scanlines := frameInfo.VisibleBottom - frameInfo.VisibleTop
if !win.trackScreen {
scanlines = int(win.scanlines)
} else {
win.scanlines = int32(scanlines)
}
fg := color.RGBA{100, 100, 100, 255}
bg := color.RGBA{10, 10, 10, 255}
unused := color.RGBA{10, 10, 10, 100}
spec := win.img.cache.TV.GetFrameInfo().Spec
// draw pixels
for i := range regs.Datastream {
for y := 0; y < win.pixelsSize.Y; y++ {
// pixel data
v := regs.Datastream[i].Peek(y, static)
// colour source
col := fg
if win.colouriser.active && win.colouriser.tgt == i {
s := regs.Datastream[win.colouriser.src].Peek(y, static)
col = spec.GetColor(signal.ColorSignal(s))
} else if win.colourSource[i] > -1 {
s := regs.Datastream[win.colourSource[i]].Peek(y, static)
col = spec.GetColor(signal.ColorSignal(s))
}
// plot pixels
for x := 0; x < 8; x++ {
if y <= scanlines {
if (v<= int(win.scanlines) {
yBot = int(win.scanlines)
}
// test if mouse position intersects with the active part of
// the texture
if y >= 0 && y < int(win.scanlines) {
imgui.Spacing()
imgui.Separator()
imgui.Spacing()
// list of values
imgui.BeginGroup()
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, 0.5)
for yy := yTop; yy < y; yy++ {
v := regs.Datastream[i].Peek(yy, static)
imgui.Text(fmt.Sprintf("%03d %c %02x", yy, fonts.CaretRight, v))
}
imgui.PopStyleVar()
v := regs.Datastream[i].Peek(y, static)
imgui.Text(fmt.Sprintf("%03d %c %02x", y, fonts.CaretRight, v))
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, 0.5)
for yy := y + 1; yy <= yBot; yy++ {
v := regs.Datastream[i].Peek(yy, static)
imgui.Text(fmt.Sprintf("%03d %c %02x", yy, fonts.CaretRight, v))
}
imgui.PopStyleVar()
imgui.EndGroup()
// detail texture
imgui.SameLineV(0, 20)
// small offset to help center the detail with the "list of
// values" above
p := imgui.CursorScreenPos()
p.Y += imgui.CurrentStyle().FramePadding().Y
imgui.SetCursorScreenPos(p)
imgui.BeginGroup()
// crop the pixels from the underlying stream texture
detailCrop := image.Rect(0, y-numOfAdditionalPeeks, win.pixelsSize.X, y+numOfAdditionalPeeks+1)
detailPixels := win.streamPixels[i].SubImage(detailCrop).(*image.RGBA)
sz := detailPixels.Bounds().Size()
win.detailTexture.markForCreation()
win.detailTexture.render(detailPixels)
// height of image matches the height of the "list of
// values" above
h := imgui.FontSize() + (imgui.CurrentStyle().FramePadding().Y)
imgui.Image(imgui.TextureID(win.detailTexture.getID()), imgui.Vec2{
X: float32(sz.X) * h * 1.25,
Y: float32(sz.Y) * h,
})
imgui.EndGroup()
}
if win.colourSource[i] != -1 {
imgui.Spacing()
imgui.Separator()
imgui.Spacing()
imgui.Text(fmt.Sprintf("%c from datastream %d", fonts.PaintBrush, win.colourSource[i]))
}
}, true)
imgui.EndGroup()
imgui.SameLine()
}
}
imgui.EndChild()
win.optionsHeight = imguiMeasureHeight(func() {
imgui.Spacing()
imgui.Spacing()
imguiLabel("Stream length")
if win.trackScreen {
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
}
imgui.PushItemWidth(200)
imgui.SliderInt("##streamlength", &win.scanlines, 100, specification.AbsoluteMaxScanlines)
imgui.PopItemWidth()
if win.trackScreen {
imgui.PopItemFlag()
imgui.PopStyleVar()
}
imgui.SameLineV(0, 20)
imgui.Checkbox("Track Screen Size", &win.trackScreen)
// clear colours button is sometimes disabled
imgui.SameLineV(0, 20)
enableClearColours := false
for _, v := range win.colourSource {
if v != -1 {
enableClearColours = true
break
}
}
if !enableClearColours {
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
}
if imgui.Button("Clear Colours") {
win.clearColours()
}
if !enableClearColours {
imgui.PopStyleVar()
imgui.PopItemFlag()
}
})
}
func (win *winCDFStreams) clearColours() {
for i := range win.colourSource {
win.colourSource[i] = -1
}
}