func main() { flag.Parse() go func() { http.ListenAndServe("localhost:6060", nil) }() if err := glfw.Init(); err != nil { log.Fatalln("failed to initialize glfw:", err) } defer glfw.Terminate() glfw.WindowHint(glfw.Resizable, glfw.True) glfw.WindowHint(glfw.Visible, glfw.False) // do not steal focus glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMinor, 1) window, err := glfw.CreateWindow(800, 600, "Spector", nil, nil) if err != nil { panic(err) } window.MakeContextCurrent() window.Restore() window.SetPos(32, 64) if err := gl.Init(); err != nil { panic(err) } state := NewState() for !window.ShouldClose() { if window.GetKey(glfw.KeyEscape) == glfw.Press { return } if window.GetKey(glfw.KeyR) == glfw.Press { state.Stop() state = NewState() } start := time.Now() state.Update(1.0 / 60.0) updateTime := time.Since(start) start = time.Now() state.Render(window) renderTime := time.Since(start) text := fmt.Sprintf("update: %.2fms render: %.2fms", float32(updateTime)/float32(time.Millisecond), float32(renderTime)/float32(time.Millisecond)) w, h := window.GetSize() state.Backend.SetFontColor(ui.ColorHex(0xFF0000FF)) size := state.Backend.Measure(text) state.Backend.Text(text, ui.Block(float32(w)-size.X, float32(h)-size.Y, size.X, size.Y)) window.SwapBuffers() glfw.PollEvents() } }
func (state *State) Render(window *glfw.Window) { if !state.Dirty { return } state.Dirty = false state.Reset(window) state.Time = time.Now() state.UpdateInput(window) hue := float32(state.Time.UnixNano()/1e6%360) / 360.0 Highlight = ui.ColorHSLA(hue, 0.7, 0.7, 1.0) w, h := window.GetSize() root := &ui.Context{ Backend: state.Backend, Input: state.Input, Area: ui.Block(0, 0, float32(w), float32(h)), } if root.Input.Mouse.Drag != nil { if !root.Input.Mouse.Drag(root) { root.Input.Mouse.Drag = nil } } state.Backend.SetBack(ui.ColorHex(0xEEEEEEFF)) state.Backend.SetFore(ui.ColorHex(0xCCCCCCFF)) state.Backend.SetFontColor(ui.ColorHex(0x000000FF)) view := NewView(state, root, &state.Timeline) view.Render() }
func (view *View) Spans(proc *timeline.Proc, layer *timeline.Layer, depth, height float32) { mingap := view.TimePerPx * 2 view.Context.SetBack(ui.ColorFloat(0.5, depth, depth, 1)) for i := 0; i < len(layer.Spans); i++ { span := layer.Spans[i] if span.Stop < view.Start { continue } if span.Start > view.Stop { break } join := span for ; i < len(layer.Spans); i++ { last := layer.Spans[i] if last.Start-join.Stop > mingap { i-- break } join.Stop = last.Stop } x0 := view.TimeToPx(join.Start) x1 := view.TimeToPx(min(join.Stop, proc.Time)) block := ui.Block(x0, view.Y, x1-x0, height) view.Context.Backend.Fill(block) if view.State.Input.Mouse.PointsAt(block) { view.Context.Backend.SetFore(Highlight) view.Context.Backend.Stroke(block) } } view.Y += height }
func (view *View) H(font *Font, format string, args ...interface{}) { view.Context.Backend.SetBack(ui.ColorHex(font.Background)) view.Context.Backend.SetFontColor(ui.ColorHex(font.Foreground)) bounds := ui.Block(0, view.Y, view.Size.X, font.Height) view.Context.Backend.Fill(bounds) text := fmt.Sprintf(format, args...) view.Context.Backend.Text(text, bounds) view.Y += font.Height }
func (view *View) Block(id trace.ID, start, stop trace.Time, height float32) { x0, x1 := view.TimeToPx(start), view.TimeToPx(stop) view.Pad(2) block := ui.Block(x0, view.Y, x1-x0, height) view.Context.SetBack(IDColor(id)) view.Context.Backend.Fill(block) if view.State.Input.Mouse.PointsAt(block) { view.Context.Backend.SetFore(Highlight) view.Context.Backend.Stroke(block) } }
func (state *State) Render(window *glfw.Window) { state.Reset(window) state.UpdateInput(window) w, h := window.GetSize() root := &ui.Context{ Backend: state.Backend, Input: state.Input, Area: ui.Block(0, 0, float32(w), float32(h)), } if root.Input.Mouse.Drag != nil { if !root.Input.Mouse.Drag(root) { root.Input.Mouse.Drag = nil } } state.Backend.SetBack(ui.ColorHex(0xEEEEEEFF)) state.Backend.SetFore(ui.ColorHex(0xCCCCCCFF)) state.Backend.SetFontColor(ui.ColorHex(0x000000FF)) mm := &MainMenu{} ui.Buttons{ {"☺", mm.File}, {"Load", mm.Load}, {"Save", mm.Save}, {"Quit", mm.Quit}, }.DoDynamic(ui.LayoutToRight(50, root.Top(20).Panel())) boxbounds := ui.Bounds{ state.Box, state.Box.Offset(ui.Point{30, 30})} runtime.ReadMemStats(&state.MemStats) root.Right(state.SidePanelSize).Reflect("Mem", &state.MemStats) ui.SplitterX(root.Right(5), func(delta float32) { state.SidePanelSize -= delta }) box := root.Child(boxbounds) box.Backend.SetBack(ui.ColorHex(0xFF88EEFF)) box.Backend.SetFore(ui.ColorHex(0xFF88EEFF)) box.Backend.Fill(box.Area) box.Backend.Stroke(box.Area) ui.DoDragXY(box, func(dx, dy float32) { state.Box.X += dx state.Box.Y += dy }) }