Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite sixel code to reduce flickering #1744

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 27 additions & 33 deletions sixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,49 @@ const (
// - rarely used: the filler is used to trick tcell into redrawing, if the
// filler character appears in the user's preview, that cell might not
// be cleaned up properly
// - ideally renders as empty space: the filler alternates between bold
// and regular, using a non-space would look weird to the user.
// - ideally renders as empty space
gSixelFiller = '\u2000'
)

type sixelScreen struct {
xprev, yprev int
sixel *string
altFill bool
lastFile string // TODO maybe use hash of sixels instead to flip altFill
win *win
sixel *string
lastFile string // TODO maybe use hash of sixels instead
}

func (sxs *sixelScreen) fillerStyle(filePath string) tcell.Style {
if sxs.lastFile != filePath {
sxs.altFill = !sxs.altFill
}

if sxs.altFill {
return tcell.StyleDefault.Bold(true)
}
return tcell.StyleDefault
}

func (sxs *sixelScreen) showSixels() {
func (sxs *sixelScreen) clearSixels(screen tcell.Screen) {
if sxs.sixel == nil {
return
}

// XXX: workaround for bug where quitting lf might leave the terminal in bold
fmt.Fprint(os.Stderr, "\033[0m")
filler := strings.Repeat(string(gSixelFiller), sxs.win.w)
for y := 0; y < sxs.win.h; y++ {
sxs.win.print(screen, 0, y, tcell.StyleDefault, filler)
}

sxs.sixel = nil
}

fmt.Fprint(os.Stderr, "\0337") // Save cursor position
fmt.Fprintf(os.Stderr, "\033[%d;%dH", sxs.yprev, sxs.xprev) // Move cursor to position
fmt.Fprint(os.Stderr, *sxs.sixel) //
fmt.Fprint(os.Stderr, "\0338") // Restore cursor position
func (sxs *sixelScreen) setSixels(win *win, sixel *string) {
sxs.win = win
sxs.sixel = sixel
}

func (sxs *sixelScreen) printSixel(win *win, screen tcell.Screen, reg *reg) {
if reg.sixel == nil {
func (sxs *sixelScreen) showSixels(screen tcell.Screen, path string) {
if sxs.sixel == nil {
return
}

// HACK: fillers are used to control when tcell redraws the region where a sixel image is drawn.
// alternating between bold and regular is to clear the image before drawing a new one.
st := sxs.fillerStyle(reg.path)
for y := 0; y < win.h; y++ {
st = win.print(screen, 0, y, st, strings.Repeat(string(gSixelFiller), win.w))
if path != sxs.lastFile {
tmp := sxs.sixel
sxs.clearSixels(screen)
sxs.sixel = tmp
}

sxs.xprev, sxs.yprev = win.x+1, win.y+1
sxs.sixel = reg.sixel
fmt.Fprint(os.Stderr, "\0337") // Save cursor position
fmt.Fprintf(os.Stderr, "\033[%d;%dH", sxs.win.y+1, sxs.win.x+1) // Move cursor to position
fmt.Fprint(os.Stderr, *sxs.sixel) //
fmt.Fprint(os.Stderr, "\0338") // Restore cursor position

sxs.lastFile = path
}
27 changes: 14 additions & 13 deletions ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,19 @@ func (win *win) printReg(screen tcell.Screen, reg *reg, previewLoading bool, sxs
return
}

if reg.sixel != nil {
sxs.setSixels(win, reg.sixel)
} else {
sxs.clearSixels(screen)
}

for i, l := range reg.lines {
if i > win.h-1 {
break
}

st = win.print(screen, 2, i, st, l)
}

sxs.printSixel(win, screen, reg)
}

var gThisYear = time.Now().Year()
Expand Down Expand Up @@ -1026,14 +1030,7 @@ func (ui *ui) draw(nav *nav) {
st := tcell.StyleDefault
context := dirContext{selections: nav.selections, saves: nav.saves, tags: nav.tags}

// XXX: manual clean without flush to avoid flicker on Windows
wtot, htot := ui.screen.Size()
for i := 0; i < wtot; i++ {
for j := 0; j < htot; j++ {
ui.screen.SetContent(i, j, ' ', nil, st)
}
}
ui.sxScreen.sixel = nil
ui.screen.Clear()

ui.drawPromptLine(nav)

Expand Down Expand Up @@ -1077,7 +1074,12 @@ func (ui *ui) draw(nav *nav) {
if err == nil {
preview := ui.wins[len(ui.wins)-1]

if ui.menuBuf != nil {
ui.sxScreen.clearSixels(ui.screen)
}

if curr.IsDir() {
ui.sxScreen.clearSixels(ui.screen)
preview.printDir(ui, ui.dirPrev, &context,
&dirStyle{colors: ui.styles, icons: ui.icons, role: Preview},
nav.previewLoading)
Expand Down Expand Up @@ -1112,9 +1114,8 @@ func (ui *ui) draw(nav *nav) {
}

ui.screen.Show()
if ui.menuBuf == nil && ui.cmdPrefix == "" && ui.sxScreen.sixel != nil {
ui.sxScreen.lastFile = ui.regPrev.path
ui.sxScreen.showSixels()
if ui.menuBuf == nil && ui.sxScreen.sixel != nil {
ui.sxScreen.showSixels(ui.screen, ui.regPrev.path)
}
}

Expand Down