93392fc149
* Update gitignore to ignore .swp * Fix terminal state data races go run -race cmd/darktile/main.go WARNING: DATA RACE Write at 0x00c000864128 by goroutine 23: github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).sgrSequenceHandler() /home/ernestrc/src/darktile/internal/app/darktile/termutil/csi.go:973 +0x12ca github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).handleCSI() /home/ernestrc/src/darktile/internal/app/darktile/termutil/csi.go:62 +0x7e8 github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).handleANSI() /home/ernestrc/src/darktile/internal/app/darktile/termutil/ansi.go:11 +0xc86 github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).process() /home/ernestrc/src/darktile/internal/app/darktile/termutil/terminal.go:206 +0x126 github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).Run·dwrap·10() /home/ernestrc/src/darktile/internal/app/darktile/termutil/terminal.go:171 +0x39 Previous read at 0x00c000864128 by goroutine 22: [failed to restore the stack] Goroutine 23 (running) created at: github.com/liamg/darktile/internal/app/darktile/termutil.(*Terminal).Run() /home/ernestrc/src/darktile/internal/app/darktile/termutil/terminal.go:171 +0x517 github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).Run.func1() /home/ernestrc/src/darktile/internal/app/darktile/gui/gui.go:80 +0xa Goroutine 22 (running) created at: github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).Run() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw/run_notsinglethread.go:37 +0x2c4 github.com/hajimehoshi/ebiten/v2.RunGame() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/run.go:158 +0x1d4 github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).Run() /home/ernestrc/src/darktile/internal/app/darktile/gui/gui.go:99 +0x2de github.com/liamg/darktile/internal/app/darktile/cmd.glob..func2() /home/ernestrc/src/darktile/internal/app/darktile/cmd/root.go:130 +0x13a9 github.com/spf13/cobra.(*Command).execute() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:852 +0xa7d github.com/spf13/cobra.(*Command).ExecuteC() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:960 +0x5da github.com/spf13/cobra.(*Command).Execute() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:897 +0x366 github.com/liamg/darktile/internal/app/darktile/cmd.Execute() /home/ernestrc/src/darktile/internal/app/darktile/cmd/root.go:153 +0x34f main.main() /home/ernestrc/src/darktile/cmd/darktile/main.go:75 +0x24 ================== Found 26 data race(s) * Fix KeyPressed data race go run -race cmd/darktile/main.go WARNING: DATA RACE Write at 0x00c00009fbc0 by goroutine 20: runtime.mapdelete_fast64() /usr/lib/go/src/runtime/map_fast64.go:272 +0x0 github.com/liamg/darktile/internal/app/darktile/gui.(*keyState).RepeatPressed() /home/ernestrc/src/darktile/internal/app/darktile/gui/key_states.go:61 +0x428 github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).handleInput() /home/ernestrc/src/darktile/internal/app/darktile/gui/input.go:109 +0x224 github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).Update() /home/ernestrc/src/darktile/internal/app/darktile/gui/update.go:34 +0x2e github.com/hajimehoshi/ebiten/v2.(*imageDumper).update() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/imagedumper_desktop.go:111 +0x85 github.com/hajimehoshi/ebiten/v2.(*imageDumperGame).Update() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/run.go:115 +0x172 github.com/hajimehoshi/ebiten/v2.(*uiContext).updateImpl() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/uicontext.go:194 +0x12c github.com/hajimehoshi/ebiten/v2.(*uiContext).update() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/uicontext.go:166 +0x88 github.com/hajimehoshi/ebiten/v2.(*uiContext).Update() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/uicontext.go:147 +0x3b github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).loop() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw/ui.go:1036 +0x401 github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).Run.func1() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw/run_notsinglethread.go:53 +0x1b1 Previous read at 0x00c00009fbc0 by goroutine 19: github.com/liamg/darktile/internal/app/darktile/gui.(*keyState).AnythingPressed() /home/ernestrc/src/darktile/internal/app/darktile/gui/key_states.go:33 +0xbc github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).watchForUpdate() /home/ernestrc/src/darktile/internal/app/darktile/gui/gui.go:105 +0x77 github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).Run·dwrap·2() /home/ernestrc/src/darktile/internal/app/darktile/gui/gui.go:97 +0x39 Goroutine 20 (running) created at: github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).Run() /home/ernestrc/src/darktile/vendor/github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw/run_notsinglethread.go:37 +0x2c4 github.com/hajimehoshi/ebiten/v2.RunGame() Goroutine 19 (running) created at: github.com/liamg/darktile/internal/app/darktile/gui.(*GUI).Run() /home/ernestrc/src/darktile/internal/app/darktile/gui/gui.go:97 +0x2ca github.com/liamg/darktile/internal/app/darktile/cmd.glob..func2() /home/ernestrc/src/darktile/internal/app/darktile/cmd/root.go:130 +0x13a9 github.com/spf13/cobra.(*Command).execute() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:852 +0xa7d github.com/spf13/cobra.(*Command).ExecuteC() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:960 +0x5da github.com/spf13/cobra.(*Command).Execute() /home/ernestrc/src/darktile/vendor/github.com/spf13/cobra/command.go:897 +0x366 github.com/liamg/darktile/internal/app/darktile/cmd.Execute() /home/ernestrc/src/darktile/internal/app/darktile/cmd/root.go:153 +0x34f main.main() /home/ernestrc/src/darktile/cmd/darktile/main.go:75 +0x24
157 lines
3.4 KiB
Go
157 lines
3.4 KiB
Go
package gui
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"math/rand"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/liamg/darktile/internal/app/darktile/font"
|
|
"github.com/liamg/darktile/internal/app/darktile/gui/popup"
|
|
"github.com/liamg/darktile/internal/app/darktile/hinters"
|
|
"github.com/liamg/darktile/internal/app/darktile/termutil"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().UnixNano())
|
|
}
|
|
|
|
type GUI struct {
|
|
mouseStateLeft MouseState
|
|
mouseStateRight MouseState
|
|
mouseStateMiddle MouseState
|
|
mouseDrag bool
|
|
size image.Point // pixels
|
|
terminal *termutil.Terminal
|
|
updateChan chan struct{}
|
|
lastClick time.Time
|
|
clickCount int
|
|
fontManager *font.Manager
|
|
mousePos termutil.Position
|
|
hinters []hinters.Hinter
|
|
activeHinter int
|
|
popupMessages []popup.Message
|
|
screenshotRequested bool
|
|
screenshotFilename string
|
|
startupFuncs []func(g *GUI)
|
|
keyState *keyState
|
|
opacity float64
|
|
enableLigatures bool
|
|
cursorImage *ebiten.Image
|
|
}
|
|
|
|
type MouseState uint8
|
|
|
|
const (
|
|
MouseStateNone MouseState = iota
|
|
MouseStatePressed
|
|
)
|
|
|
|
func New(terminal *termutil.Terminal, options ...Option) (*GUI, error) {
|
|
|
|
g := &GUI{
|
|
terminal: terminal,
|
|
size: image.Point{80, 30},
|
|
updateChan: make(chan struct{}),
|
|
fontManager: font.NewManager(),
|
|
activeHinter: -1,
|
|
keyState: newKeyState(),
|
|
enableLigatures: true,
|
|
}
|
|
|
|
for _, option := range options {
|
|
if err := option(g); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
terminal.SetWindowManipulator(NewManipulator(g))
|
|
|
|
return g, nil
|
|
}
|
|
|
|
func (g *GUI) Run() error {
|
|
|
|
go func() {
|
|
if err := g.terminal.Run(g.updateChan, uint16(g.size.X), uint16(g.size.Y)); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}()
|
|
|
|
ebiten.SetScreenTransparent(true)
|
|
ebiten.SetScreenClearedEveryFrame(true)
|
|
ebiten.SetWindowResizable(true)
|
|
ebiten.SetRunnableOnUnfocused(true)
|
|
ebiten.SetFPSMode(ebiten.FPSModeVsyncOffMinimum)
|
|
|
|
for _, f := range g.startupFuncs {
|
|
go f(g)
|
|
}
|
|
|
|
go g.watchForUpdate()
|
|
|
|
return ebiten.RunGame(g)
|
|
}
|
|
|
|
func (g *GUI) watchForUpdate() {
|
|
for range g.updateChan {
|
|
ebiten.ScheduleFrame()
|
|
if g.keyState.AnythingPressed() {
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
ebiten.ScheduleFrame()
|
|
}()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *GUI) CellSize() image.Point {
|
|
return g.fontManager.CharSize()
|
|
}
|
|
|
|
func (g *GUI) Highlight(start termutil.Position, end termutil.Position, label string, img image.Image) {
|
|
|
|
if label == "" && img == nil {
|
|
g.terminal.GetActiveBuffer().Highlight(start, end, nil)
|
|
return
|
|
}
|
|
|
|
annotation := &termutil.Annotation{
|
|
Text: label,
|
|
Image: img,
|
|
}
|
|
|
|
if label != "" {
|
|
lines := strings.Split(label, "\n")
|
|
annotation.Height = float64(len(lines))
|
|
for _, line := range lines {
|
|
if float64(len(line)) > annotation.Width {
|
|
annotation.Width = float64(len(line))
|
|
}
|
|
}
|
|
}
|
|
|
|
if img != nil {
|
|
annotation.Height += float64(img.Bounds().Dy() / g.fontManager.CharSize().Y)
|
|
if label != "" {
|
|
annotation.Height += 0.5 // half line spacing between image + text
|
|
}
|
|
imgCellWidth := img.Bounds().Dx() / g.fontManager.CharSize().X
|
|
if float64(imgCellWidth) > annotation.Width {
|
|
annotation.Width = float64(imgCellWidth)
|
|
}
|
|
}
|
|
|
|
g.terminal.GetActiveBuffer().Highlight(start, end, annotation)
|
|
}
|
|
|
|
func (g *GUI) ClearHighlight() {
|
|
g.terminal.GetActiveBuffer().ClearHighlight()
|
|
}
|