From 9a28db40c4acc6e7944da31a0a2a5e6bb5c7560e Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 02:50:33 +0200 Subject: [PATCH 01/44] new struct type sixel --- ui.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui.go b/ui.go index 951c91b5..4ed3acf6 100644 --- a/ui.go +++ b/ui.go @@ -461,8 +461,15 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]in } } +type sixel struct { + x int + y int + str string +} + type ui struct { screen tcell.Screen + sixels []sixel polling bool wins []*win promptWin *win From 5b10351c0129625e5c4105f44743c2691e415786 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 02:52:20 +0200 Subject: [PATCH 02/44] add sixel detection logic in printReg --- ui.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ui.go b/ui.go index 4ed3acf6..57fd39ca 100644 --- a/ui.go +++ b/ui.go @@ -252,7 +252,7 @@ func (win *win) printRight(screen tcell.Screen, y int, st tcell.Style, s string) win.print(screen, win.w-printLength(s), y, st, s) } -func (win *win) printReg(screen tcell.Screen, reg *reg) { +func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { if reg == nil { return } @@ -270,6 +270,15 @@ func (win *win) printReg(screen tcell.Screen, reg *reg) { break } + if a := strings.Index(l, "\x1bP"); a >= 0 { + if b := strings.Index(l[a+1:len(l)], "\x1b\\"); b >= 0 { + sixel := sixel{str: l[a : b+3], x: win.x + 2, y: win.y + i} + *sixels = append(*sixels, sixel) + + l = l[:a] + l[b+3:] + } + } + st = win.print(screen, 2, i, st, l) } } @@ -891,7 +900,7 @@ func (ui *ui) draw(nav *nav) { if curr.IsDir() { preview.printDir(ui.screen, ui.dirPrev, nav.selections, nav.saves, nav.tags, ui.styles, ui.icons) } else if curr.Mode().IsRegular() { - preview.printReg(ui.screen, ui.regPrev) + preview.printReg(ui.screen, &ui.sixels, ui.regPrev) } } } From 93edee3b08e4d102bb0170df5df56ca75f643a2b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 02:53:44 +0200 Subject: [PATCH 03/44] new ShowSixels that prints sixels to the screen --- ui.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui.go b/ui.go index 57fd39ca..361c273b 100644 --- a/ui.go +++ b/ui.go @@ -930,6 +930,7 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() + ui.ShowSixels() } func findBinds(keys map[string]expr, prefix string) (binds map[string]expr, ok bool) { @@ -1306,3 +1307,10 @@ func listMatchesMenu(ui *ui, matches []string) error { ui.menuBuf = b return nil } + +func (ui *ui) ShowSixels() { + for _, sixel := range ui.sixels { + moveCursor := fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1) + os.Stdin.WriteString(moveCursor + sixel.str) + } +} From c8c5b640a5b1d8b70724ef39f3d6c378088654ff Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 02:54:33 +0200 Subject: [PATCH 04/44] placeholder function UnshowSixels --- nav.go | 2 ++ ui.go | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/nav.go b/nav.go index 37fa540d..7200fcb4 100644 --- a/nav.go +++ b/nav.go @@ -619,6 +619,8 @@ func (nav *nav) previewLoop(ui *ui) { if err := cmd.Run(); err != nil { log.Printf("cleaning preview: %s", err) } + ui.UnshowSixels() + ui.sixels = nil nav.volatilePreview = false } if len(path) != 0 { diff --git a/ui.go b/ui.go index 361c273b..d28bebcc 100644 --- a/ui.go +++ b/ui.go @@ -1314,3 +1314,11 @@ func (ui *ui) ShowSixels() { os.Stdin.WriteString(moveCursor + sixel.str) } } + +// Placeholder +func (ui *ui) UnshowSixels() { + for _, sixel := range ui.sixels { + x, y := sixel.x, sixel.y + ui.screen.SetContent(x, y, '\u2800', nil, tcell.StyleDefault) + } +} From d782c580b17847b4f06a153c5aa3bb7607cc57ea Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 02:57:26 +0200 Subject: [PATCH 05/44] run `ui.ShowSixels` after `ui.screen.Sync` --- eval.go | 1 + 1 file changed, 1 insertion(+) diff --git a/eval.go b/eval.go index 77de6891..30bace9d 100644 --- a/eval.go +++ b/eval.go @@ -1198,6 +1198,7 @@ func (e *callExpr) eval(app *app, args []string) { } app.ui.renew() app.ui.screen.Sync() + app.ui.ShowSixels() if app.nav.height != app.ui.wins[0].h { app.nav.height = app.ui.wins[0].h app.nav.regCache = make(map[string]*reg) From 3642375454667db7db6b89fe9fbe7f8baee8d92b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 20 May 2022 16:09:37 +0200 Subject: [PATCH 06/44] fix sixel placed on wrong coordinates --- ui.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui.go b/ui.go index d28bebcc..d5ababc3 100644 --- a/ui.go +++ b/ui.go @@ -271,11 +271,11 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { } if a := strings.Index(l, "\x1bP"); a >= 0 { - if b := strings.Index(l[a+1:len(l)], "\x1b\\"); b >= 0 { - sixel := sixel{str: l[a : b+3], x: win.x + 2, y: win.y + i} + if b := strings.Index(l[a+2:], "\x1b\\"); b >= 0 { + sixel := sixel{str: l[a : a+b+4], x: win.x + a + 2, y: win.y + i} *sixels = append(*sixels, sixel) - l = l[:a] + l[b+3:] + l = l[:a] + l[a+b+4:] } } From d8c1df26ac4cda65c444bd4fb57a6f85c39e3799 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sat, 21 May 2022 00:32:30 +0200 Subject: [PATCH 07/44] change sixel termination sequence to prevent other escape sequence being run inside it --- ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui.go b/ui.go index d5ababc3..7789e58d 100644 --- a/ui.go +++ b/ui.go @@ -271,7 +271,7 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { } if a := strings.Index(l, "\x1bP"); a >= 0 { - if b := strings.Index(l[a+2:], "\x1b\\"); b >= 0 { + if b := strings.Index(l[a+2:], "\x1b"); b >= 0 && l[a+b+3] == '\\' { sixel := sixel{str: l[a : a+b+4], x: win.x + a + 2, y: win.y + i} *sixels = append(*sixels, sixel) From 4f68fd35f2f3df89fd3b9ef262a008d008b1eae7 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sat, 21 May 2022 02:45:55 +0200 Subject: [PATCH 08/44] process multi-line sixel sequence --- nav.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/nav.go b/nav.go index 7200fcb4..1183254d 100644 --- a/nav.go +++ b/nav.go @@ -700,13 +700,36 @@ func (nav *nav) preview(path string, win *win) { buf := bufio.NewScanner(reader) - for i := 0; i < win.h && buf.Scan(); i++ { + var sixel strings.Builder + sixelCont := false + for i := 0; (sixelCont || i < win.h) && buf.Scan(); i++ { for _, r := range buf.Text() { if r == 0 { reg.lines = []string{"\033[7mbinary\033[0m"} return } } + if a := strings.Index(buf.Text(), "\x1bP"); a >= 0 { + if b := strings.Index(buf.Text()[a+2:], "\x1b\\"); b >= 0 { + reg.lines = append(reg.lines, buf.Text()) + continue + } + sixel.WriteString(buf.Text()[a:]) + sixelCont = true + continue + } + if sixelCont { + if b := strings.Index(buf.Text(), "\x1b\\"); b >= 0 { + sixel.WriteString(buf.Text()[:b+2]) + reg.lines = append(reg.lines, sixel.String(), buf.Text()[b+2:]) + log.Printf("found multiline: %s", reg.lines[len(reg.lines)-1]) + sixel.Reset() + sixelCont = false + continue + } + sixel.WriteString(buf.Text()) + continue + } reg.lines = append(reg.lines, buf.Text()) } From 934e58907644f36a834e2c7d3259065ccda6cd6a Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sat, 21 May 2022 02:58:20 +0200 Subject: [PATCH 09/44] fixed bug where string before sixel image is not printed --- nav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav.go b/nav.go index 1183254d..6caf5e05 100644 --- a/nav.go +++ b/nav.go @@ -714,7 +714,7 @@ func (nav *nav) preview(path string, win *win) { reg.lines = append(reg.lines, buf.Text()) continue } - sixel.WriteString(buf.Text()[a:]) + sixel.WriteString(buf.Text()) sixelCont = true continue } From e53e6324715cdff3c7f26a71e3ba4a38f3499328 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 25 May 2022 14:42:38 +0200 Subject: [PATCH 10/44] add sixelDimPx and pxToCells --- misc.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/misc.go b/misc.go index 7083cd7a..d0e84023 100644 --- a/misc.go +++ b/misc.go @@ -299,6 +299,57 @@ func mod(a, b int) int { return (a%b + b) % b } +var reNumber = regexp.MustCompile(`^[0-9]+`) + +// needs some testing +func sixelDimPx(s string) (w int, h int) { + // TODO maybe take into account pixel aspect ratio (") + // TODO handle macro parameters P1 to P3 + // `DCS P1;P2;P3;q...` + a := strings.Index(s, "q") + 1 + if a == 0 { + //syntax error + } + var wi int + for i := a; i < len(s)-2; i++ { + c := s[i] + switch { + case '?' <= c && c <= '~': + wi++ + case c == '-': + w = max(w, wi) + wi = 0 + h++ + case c == '$': + w = max(w, wi) + wi = 0 + case c == '!': + m := reNumber.FindString(s[i+1:]) + if m == "" { + // syntax error + } + if s[i+1+len(m)] < '?' || s[i+1+len(m)] > '~' { + // syntax error + } + n, _ := strconv.Atoi(m) + wi += n - 1 + default: + } + } + if s[len(s)-3] != '-' { + w = max(w, wi) + wi = 0 + h++ // add newline on last row + } + return w, h * 6 +} + +func pxToCells(x, y, wc, hc, wpx, hpx int) (int, int) { + var fw int = wpx / wc + var fh int = hpx / hc + return x/fw + 1, y/fh + 1 // TODO should probably use ceiling instead of +1 +} + // We don't need no generic code // We don't need no type control // No dark templates in compiler From eadab5e22aa971dffe4158fded094ac8f18b8da5 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 25 May 2022 14:44:43 +0200 Subject: [PATCH 11/44] add getTermPixels for unix --- os.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/os.go b/os.go index 3dd9b1f9..cc02c3f0 100644 --- a/os.go +++ b/os.go @@ -220,3 +220,11 @@ func exportFiles(f string, fs []string, pwd string) { os.Setenv("fx", envFiles) } } + +func getTermPixels(fd int) (w, h int, err error) { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { + return -1, -1, err + } + return int(ws.Xpixel), int(ws.Ypixel), nil +} From 34838131ea87cf66ff6a1c799215b8566b757db6 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:25:00 +0200 Subject: [PATCH 12/44] remove unused method UnshowSixels --- nav.go | 2 -- ui.go | 8 -------- 2 files changed, 10 deletions(-) diff --git a/nav.go b/nav.go index 6caf5e05..787db51f 100644 --- a/nav.go +++ b/nav.go @@ -619,8 +619,6 @@ func (nav *nav) previewLoop(ui *ui) { if err := cmd.Run(); err != nil { log.Printf("cleaning preview: %s", err) } - ui.UnshowSixels() - ui.sixels = nil nav.volatilePreview = false } if len(path) != 0 { diff --git a/ui.go b/ui.go index 7789e58d..f51633ad 100644 --- a/ui.go +++ b/ui.go @@ -1314,11 +1314,3 @@ func (ui *ui) ShowSixels() { os.Stdin.WriteString(moveCursor + sixel.str) } } - -// Placeholder -func (ui *ui) UnshowSixels() { - for _, sixel := range ui.sixels { - x, y := sixel.x, sixel.y - ui.screen.SetContent(x, y, '\u2800', nil, tcell.StyleDefault) - } -} From d977957370f644a3048179e812415a1e32ac181e Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:38:40 +0200 Subject: [PATCH 13/44] fix: remove unneeded ShowSixels --- eval.go | 1 - 1 file changed, 1 deletion(-) diff --git a/eval.go b/eval.go index 30bace9d..77de6891 100644 --- a/eval.go +++ b/eval.go @@ -1198,7 +1198,6 @@ func (e *callExpr) eval(app *app, args []string) { } app.ui.renew() app.ui.screen.Sync() - app.ui.ShowSixels() if app.nav.height != app.ui.wins[0].h { app.nav.height = app.ui.wins[0].h app.nav.regCache = make(map[string]*reg) From 1d93785caa2142984da93a2d6b4b73bc930ee1f8 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:48:13 +0200 Subject: [PATCH 14/44] add new fields sixel.wPx,hPx and reg.sixels --- ui.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui.go b/ui.go index f51633ad..b7b376e2 100644 --- a/ui.go +++ b/ui.go @@ -473,6 +473,8 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]in type sixel struct { x int y int + wPx int + hPx int str string } @@ -646,6 +648,7 @@ type reg struct { loadTime time.Time path string lines []string + sixels []sixel } func (ui *ui) loadFile(nav *nav, volatile bool) { From ad88770227e557c41d767c9cc790ec8d8ba557a5 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:37:01 +0200 Subject: [PATCH 15/44] modify sixel processing logic detecting sixels in preview script now fills corresponding area with braille blank `\u2800` and saves sixel to reg.sixels --- nav.go | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/nav.go b/nav.go index 787db51f..d3fab0b8 100644 --- a/nav.go +++ b/nav.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/gdamore/tcell/v2" times "gopkg.in/djherbis/times.v1" ) @@ -623,7 +624,7 @@ func (nav *nav) previewLoop(ui *ui) { } if len(path) != 0 { win := ui.wins[len(ui.wins)-1] - nav.preview(path, win) + nav.preview(path, ui.screen, win) prev = path } } @@ -644,7 +645,7 @@ func matchPattern(pattern, name, path string) bool { return matched } -func (nav *nav) preview(path string, win *win) { +func (nav *nav) preview(path string, screen tcell.Screen, win *win) { reg := ®{loadTime: time.Now(), path: path} defer func() { nav.regChan <- reg }() @@ -698,9 +699,8 @@ func (nav *nav) preview(path string, win *win) { buf := bufio.NewScanner(reader) - var sixel strings.Builder - sixelCont := false - for i := 0; (sixelCont || i < win.h) && buf.Scan(); i++ { + sixelFrom := -1 + for i := 0; (sixelFrom != -1 || len(reg.lines) < win.h) && buf.Scan(); i++ { for _, r := range buf.Text() { if r == 0 { reg.lines = []string{"\033[7mbinary\033[0m"} @@ -708,25 +708,27 @@ func (nav *nav) preview(path string, win *win) { } } if a := strings.Index(buf.Text(), "\x1bP"); a >= 0 { - if b := strings.Index(buf.Text()[a+2:], "\x1b\\"); b >= 0 { - reg.lines = append(reg.lines, buf.Text()) + if b := strings.Index(buf.Text()[a:], "\x1b\\"); b >= 0 { + addSixel(screen, reg, buf.Text()[a:a+b+2], win.x, win.y+len(reg.lines)) + reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) continue } - sixel.WriteString(buf.Text()) - sixelCont = true + reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) + sixelFrom = len(reg.lines) - 1 continue } - if sixelCont { + if sixelFrom != -1 { if b := strings.Index(buf.Text(), "\x1b\\"); b >= 0 { - sixel.WriteString(buf.Text()[:b+2]) - reg.lines = append(reg.lines, sixel.String(), buf.Text()[b+2:]) + reg.lines = append(reg.lines, buf.Text()[:b+2]) + sx := strings.Join(reg.lines[sixelFrom:], "") + reg.lines = reg.lines[:sixelFrom] + addSixel(screen, reg, sx, win.x+2, win.y+len(reg.lines)) + + reg.lines = append(reg.lines, buf.Text()[b+2:]) log.Printf("found multiline: %s", reg.lines[len(reg.lines)-1]) - sixel.Reset() - sixelCont = false + sixelFrom = -1 continue } - sixel.WriteString(buf.Text()) - continue } reg.lines = append(reg.lines, buf.Text()) } @@ -736,6 +738,19 @@ func (nav *nav) preview(path string, win *win) { } } +func addSixel(screen tcell.Screen, reg *reg, sx string, x, y int) { + Wc, Hc := screen.Size() + Wpx, Hpx, _ := getTermPixels(int(os.Stdin.Fd())) //TODO do this elsewhere + w, h := sixelDimPx(sx) + wc, hc := pxToCells(w, h, Wc, Hc, Wpx, Hpx) + + reg.sixels = append(reg.sixels, sixel{x, y, w, h, sx}) + fill := strings.Repeat("\u2800", wc) + for j := 0; j < hc; j++ { + reg.lines = append(reg.lines, fill) + } +} + func (nav *nav) loadReg(path string, volatile bool) *reg { r, ok := nav.regCache[path] if !ok || (volatile && r.volatile) { From 2cf0a365ac53e8bd2582d97a7c592fc3ec6afcf9 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:40:07 +0200 Subject: [PATCH 16/44] modify sixel processing logic printReg doesn't process sixels directly anymore --- ui.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ui.go b/ui.go index b7b376e2..56b6ae81 100644 --- a/ui.go +++ b/ui.go @@ -270,17 +270,9 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { break } - if a := strings.Index(l, "\x1bP"); a >= 0 { - if b := strings.Index(l[a+2:], "\x1b"); b >= 0 && l[a+b+3] == '\\' { - sixel := sixel{str: l[a : a+b+4], x: win.x + a + 2, y: win.y + i} - *sixels = append(*sixels, sixel) - - l = l[:a] + l[a+b+4:] - } - } - st = win.print(screen, 2, i, st, l) } + *sixels = reg.sixels } var gThisYear = time.Now().Year() From 9e9b80677bb1a5ce094055ae0846f05e4d2a232f Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 16:58:32 +0200 Subject: [PATCH 17/44] reset sixels buffer at the start of `draw` --- ui.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ui.go b/ui.go index 56b6ae81..dc100fd2 100644 --- a/ui.go +++ b/ui.go @@ -846,6 +846,7 @@ func (ui *ui) draw(nav *nav) { ui.screen.SetContent(i, j, ' ', nil, st) } } + ui.sixels = nil ui.drawPromptLine(nav) From 5487a6322fa7685480937ab3438f08d733c6602a Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 26 May 2022 17:02:19 +0200 Subject: [PATCH 18/44] rename `ui.ShowSixels` to `showSixels` --- ui.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui.go b/ui.go index dc100fd2..960922f8 100644 --- a/ui.go +++ b/ui.go @@ -926,7 +926,7 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() - ui.ShowSixels() + ui.showSixels() } func findBinds(keys map[string]expr, prefix string) (binds map[string]expr, ok bool) { @@ -1304,7 +1304,7 @@ func listMatchesMenu(ui *ui, matches []string) error { return nil } -func (ui *ui) ShowSixels() { +func (ui *ui) showSixels() { for _, sixel := range ui.sixels { moveCursor := fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1) os.Stdin.WriteString(moveCursor + sixel.str) From 3ba360bc7837db323340374604b5ad9503a1f7f0 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sat, 28 May 2022 16:02:03 +0200 Subject: [PATCH 19/44] add constants `gSixelBegin` and `gSixelTerminate` --- nav.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nav.go b/nav.go index d3fab0b8..0b41fae8 100644 --- a/nav.go +++ b/nav.go @@ -19,6 +19,11 @@ import ( times "gopkg.in/djherbis/times.v1" ) +const ( + gSixelBegin = "\033P" + gSixelTerminate = "\033\\" +) + type linkState byte const ( From fe8c1d5ed18b87947582bdb8c6c64bea50e83d0c Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 2 Jun 2022 13:37:21 +0200 Subject: [PATCH 20/44] add check to prevent arbitrary escape code being passed to stdin --- nav.go | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/nav.go b/nav.go index 0b41fae8..3a007ba9 100644 --- a/nav.go +++ b/nav.go @@ -712,32 +712,45 @@ func (nav *nav) preview(path string, screen tcell.Screen, win *win) { return } } - if a := strings.Index(buf.Text(), "\x1bP"); a >= 0 { - if b := strings.Index(buf.Text()[a:], "\x1b\\"); b >= 0 { - addSixel(screen, reg, buf.Text()[a:a+b+2], win.x, win.y+len(reg.lines)) - reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) + if a := strings.Index(buf.Text(), gSixelBegin); a >= 0 { + if b := strings.IndexByte(buf.Text()[a+2:], gEscapeCode); b >= 0 { + if buf.Text()[a+b+1] == '\\' { + addSixel(screen, reg, buf.Text()[a:a+b+2], win.x, win.y+len(reg.lines)) + reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) + continue + } else { + reg.lines = append(reg.lines, buf.Text()) + continue + } + } else { + reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) + sixelFrom = len(reg.lines) - 1 continue } - reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) - sixelFrom = len(reg.lines) - 1 - continue } if sixelFrom != -1 { - if b := strings.Index(buf.Text(), "\x1b\\"); b >= 0 { - reg.lines = append(reg.lines, buf.Text()[:b+2]) - sx := strings.Join(reg.lines[sixelFrom:], "") - reg.lines = reg.lines[:sixelFrom] - addSixel(screen, reg, sx, win.x+2, win.y+len(reg.lines)) - - reg.lines = append(reg.lines, buf.Text()[b+2:]) - log.Printf("found multiline: %s", reg.lines[len(reg.lines)-1]) - sixelFrom = -1 - continue + if b := strings.IndexByte(buf.Text(), gEscapeCode); b >= 0 { + if buf.Text()[b+1] == '\\' { + reg.lines = append(reg.lines, buf.Text()[:b+2]) + sx := strings.Join(reg.lines[sixelFrom:], "") + reg.lines = reg.lines[:sixelFrom] + addSixel(screen, reg, sx, win.x+2, win.y+len(reg.lines)) + + reg.lines = append(reg.lines, buf.Text()[b+2:]) + sixelFrom = -1 + continue + } else { + sixelFrom = -1 + } } } reg.lines = append(reg.lines, buf.Text()) } + if len(reg.lines) > win.h { + reg.lines = reg.lines[:win.h] + } + if buf.Err() != nil { log.Printf("loading file: %s", buf.Err()) } From c87c639a766a91ca66b61281390e13de42996873 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 2 Jun 2022 13:40:33 +0200 Subject: [PATCH 21/44] fix cursor out of place in command line mode --- ui.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui.go b/ui.go index 960922f8..e64515d0 100644 --- a/ui.go +++ b/ui.go @@ -926,7 +926,10 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() - ui.showSixels() + if len(ui.sixels) > 0 { + ui.showSixels() + } + } func findBinds(keys map[string]expr, prefix string) (binds map[string]expr, ok bool) { @@ -1305,8 +1308,10 @@ func listMatchesMenu(ui *ui, matches []string) error { } func (ui *ui) showSixels() { + os.Stdin.WriteString("\0337") for _, sixel := range ui.sixels { moveCursor := fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1) os.Stdin.WriteString(moveCursor + sixel.str) } + os.Stdin.WriteString("\0338") } From 16f8e5bf7f9f0d0dc3262fb41cb774971adf206d Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 14 Jun 2022 17:24:43 +0200 Subject: [PATCH 22/44] fix bug where sixel in drawn at old location after horizontal resize --- nav.go | 4 ++-- ui.go | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/nav.go b/nav.go index 3a007ba9..8a976331 100644 --- a/nav.go +++ b/nav.go @@ -715,7 +715,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, win *win) { if a := strings.Index(buf.Text(), gSixelBegin); a >= 0 { if b := strings.IndexByte(buf.Text()[a+2:], gEscapeCode); b >= 0 { if buf.Text()[a+b+1] == '\\' { - addSixel(screen, reg, buf.Text()[a:a+b+2], win.x, win.y+len(reg.lines)) + addSixel(screen, wPx, hPx, reg, buf.Text()[a:a+b+2], 2, win.y+len(reg.lines)) reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) continue } else { @@ -734,7 +734,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, win *win) { reg.lines = append(reg.lines, buf.Text()[:b+2]) sx := strings.Join(reg.lines[sixelFrom:], "") reg.lines = reg.lines[:sixelFrom] - addSixel(screen, reg, sx, win.x+2, win.y+len(reg.lines)) + addSixel(screen, wPx, hPx, reg, sx, 2, len(reg.lines)) reg.lines = append(reg.lines, buf.Text()[b+2:]) sixelFrom = -1 diff --git a/ui.go b/ui.go index e64515d0..9d400c07 100644 --- a/ui.go +++ b/ui.go @@ -272,7 +272,13 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { st = win.print(screen, 2, i, st, l) } - *sixels = reg.sixels + + for _, sx := range reg.sixels { + s := sx + s.x += win.x + s.y += win.y + *sixels = append(*sixels, s) + } } var gThisYear = time.Now().Year() From 18efee8a19081d0c350cd02c172027bc4fb08771 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 14 Jun 2022 17:28:37 +0200 Subject: [PATCH 23/44] add ui.wPx and ui.hPx --- eval.go | 6 ++++++ nav.go | 9 ++++----- os.go | 3 ++- ui.go | 9 +++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/eval.go b/eval.go index 77de6891..aae02cca 100644 --- a/eval.go +++ b/eval.go @@ -1196,6 +1196,12 @@ func (e *callExpr) eval(app *app, args []string) { if !app.nav.init { return } + var err error + app.ui.wPx, app.ui.hPx, err = getTermPixels() + if err != nil { + app.ui.wPx, app.ui.hPx = -1, -1 + log.Printf("getting terminal pixel size: %s", err) + } app.ui.renew() app.ui.screen.Sync() if app.nav.height != app.ui.wins[0].h { diff --git a/nav.go b/nav.go index 8a976331..8a94de66 100644 --- a/nav.go +++ b/nav.go @@ -629,7 +629,7 @@ func (nav *nav) previewLoop(ui *ui) { } if len(path) != 0 { win := ui.wins[len(ui.wins)-1] - nav.preview(path, ui.screen, win) + nav.preview(path, ui.screen, ui.wPx, ui.hPx, win) prev = path } } @@ -650,7 +650,7 @@ func matchPattern(pattern, name, path string) bool { return matched } -func (nav *nav) preview(path string, screen tcell.Screen, win *win) { +func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win) { reg := ®{loadTime: time.Now(), path: path} defer func() { nav.regChan <- reg }() @@ -756,11 +756,10 @@ func (nav *nav) preview(path string, screen tcell.Screen, win *win) { } } -func addSixel(screen tcell.Screen, reg *reg, sx string, x, y int) { +func addSixel(screen tcell.Screen, wPx, hPx int, reg *reg, sx string, x, y int) { Wc, Hc := screen.Size() - Wpx, Hpx, _ := getTermPixels(int(os.Stdin.Fd())) //TODO do this elsewhere w, h := sixelDimPx(sx) - wc, hc := pxToCells(w, h, Wc, Hc, Wpx, Hpx) + wc, hc := pxToCells(w, h, Wc, Hc, wPx, hPx) reg.sixels = append(reg.sixels, sixel{x, y, w, h, sx}) fill := strings.Repeat("\u2800", wc) diff --git a/os.go b/os.go index cc02c3f0..0dba33f7 100644 --- a/os.go +++ b/os.go @@ -221,7 +221,8 @@ func exportFiles(f string, fs []string, pwd string) { } } -func getTermPixels(fd int) (w, h int, err error) { +func getTermPixels() (w, h int, err error) { + fd := int(os.Stdin.Fd()) ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) if err != nil { return -1, -1, err diff --git a/ui.go b/ui.go index 9d400c07..1d040a91 100644 --- a/ui.go +++ b/ui.go @@ -479,6 +479,7 @@ type sixel struct { type ui struct { screen tcell.Screen sixels []sixel + wPx, hPx int polling bool wins []*win promptWin *win @@ -549,6 +550,7 @@ func getWins(screen tcell.Screen) []*win { } func newUI(screen tcell.Screen) *ui { + var err error wtot, htot := screen.Size() ui := &ui{ @@ -567,6 +569,13 @@ func newUI(screen tcell.Screen) *ui { menuSelected: -2, } + ui.wPx, ui.hPx, err = getTermPixels() + // TODO getting pixel size does not gurantee sixel support + if err != nil { + ui.wPx, ui.hPx = -1, -1 + log.Printf("getting terminal pixel size: %s", err) + } + go ui.pollEvents() return ui From c37274d19ae0b21d537f94b7491f877eb16e464a Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 14 Jun 2022 22:40:34 +0200 Subject: [PATCH 24/44] buffer sixel sequences before printing --- ui.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui.go b/ui.go index 1d040a91..97ab1b17 100644 --- a/ui.go +++ b/ui.go @@ -1323,10 +1323,12 @@ func listMatchesMenu(ui *ui, matches []string) error { } func (ui *ui) showSixels() { - os.Stdin.WriteString("\0337") + var buf strings.Builder + buf.WriteString("\0337") for _, sixel := range ui.sixels { - moveCursor := fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1) - os.Stdin.WriteString(moveCursor + sixel.str) + buf.WriteString(fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1)) + buf.WriteString(sixel.str) } - os.Stdin.WriteString("\0338") + buf.WriteString("\0338") + fmt.Print(buf.String()) } From 150dd79103ab140660c2f61d56bc54b32daa8267 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 17 Jun 2022 13:21:34 +0200 Subject: [PATCH 25/44] add check for valid terminal size(px) before previewing sixel --- nav.go | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/nav.go b/nav.go index 8a94de66..257e7087 100644 --- a/nav.go +++ b/nav.go @@ -712,35 +712,37 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win return } } - if a := strings.Index(buf.Text(), gSixelBegin); a >= 0 { - if b := strings.IndexByte(buf.Text()[a+2:], gEscapeCode); b >= 0 { - if buf.Text()[a+b+1] == '\\' { - addSixel(screen, wPx, hPx, reg, buf.Text()[a:a+b+2], 2, win.y+len(reg.lines)) - reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) - continue + if wPx > 0 && hPx > 0 { + if a := strings.Index(buf.Text(), gSixelBegin); a >= 0 { + if b := strings.IndexByte(buf.Text()[a+2:], gEscapeCode); b >= 0 { + if buf.Text()[a+b+1] == '\\' { + addSixel(screen, wPx, hPx, reg, buf.Text()[a:a+b+2], 2, win.y+len(reg.lines)) + reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) + continue + } else { + reg.lines = append(reg.lines, buf.Text()) + continue + } } else { - reg.lines = append(reg.lines, buf.Text()) + reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) + sixelFrom = len(reg.lines) - 1 continue } - } else { - reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) - sixelFrom = len(reg.lines) - 1 - continue } - } - if sixelFrom != -1 { - if b := strings.IndexByte(buf.Text(), gEscapeCode); b >= 0 { - if buf.Text()[b+1] == '\\' { - reg.lines = append(reg.lines, buf.Text()[:b+2]) - sx := strings.Join(reg.lines[sixelFrom:], "") - reg.lines = reg.lines[:sixelFrom] - addSixel(screen, wPx, hPx, reg, sx, 2, len(reg.lines)) - - reg.lines = append(reg.lines, buf.Text()[b+2:]) - sixelFrom = -1 - continue - } else { - sixelFrom = -1 + if sixelFrom != -1 { + if b := strings.IndexByte(buf.Text(), gEscapeCode); b >= 0 { + if buf.Text()[b+1] == '\\' { + reg.lines = append(reg.lines, buf.Text()[:b+2]) + sx := strings.Join(reg.lines[sixelFrom:], "") + reg.lines = reg.lines[:sixelFrom] + addSixel(screen, wPx, hPx, reg, sx, 2, len(reg.lines)) + + reg.lines = append(reg.lines, buf.Text()[b+2:]) + sixelFrom = -1 + continue + } else { + sixelFrom = -1 + } } } } From a6bfeaeefae98463917199cd39e8b29f03ca2691 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 17 Jun 2022 13:59:20 +0200 Subject: [PATCH 26/44] clean up --- eval.go | 12 ++++++------ ui.go | 27 ++++++++++++--------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/eval.go b/eval.go index aae02cca..b4f21d83 100644 --- a/eval.go +++ b/eval.go @@ -1196,12 +1196,12 @@ func (e *callExpr) eval(app *app, args []string) { if !app.nav.init { return } - var err error - app.ui.wPx, app.ui.hPx, err = getTermPixels() - if err != nil { - app.ui.wPx, app.ui.hPx = -1, -1 - log.Printf("getting terminal pixel size: %s", err) - } + var err error + app.ui.wPx, app.ui.hPx, err = getTermPixels() + if err != nil { + app.ui.wPx, app.ui.hPx = -1, -1 + log.Printf("getting terminal pixel size: %s", err) + } app.ui.renew() app.ui.screen.Sync() if app.nav.height != app.ui.wins[0].h { diff --git a/ui.go b/ui.go index 97ab1b17..c1bfa134 100644 --- a/ui.go +++ b/ui.go @@ -273,12 +273,12 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { st = win.print(screen, 2, i, st, l) } - for _, sx := range reg.sixels { - s := sx - s.x += win.x - s.y += win.y - *sixels = append(*sixels, s) - } + for _, sx := range reg.sixels { + s := sx + s.x += win.x + s.y += win.y + *sixels = append(*sixels, s) + } } var gThisYear = time.Now().Year() @@ -469,11 +469,8 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]in } type sixel struct { - x int - y int - wPx int - hPx int - str string + x, y, wPx, hPx int + str string } type ui struct { @@ -570,7 +567,7 @@ func newUI(screen tcell.Screen) *ui { } ui.wPx, ui.hPx, err = getTermPixels() - // TODO getting pixel size does not gurantee sixel support + // TODO getting pixel size does not gurantee sixel support if err != nil { ui.wPx, ui.hPx = -1, -1 log.Printf("getting terminal pixel size: %s", err) @@ -1323,12 +1320,12 @@ func listMatchesMenu(ui *ui, matches []string) error { } func (ui *ui) showSixels() { - var buf strings.Builder + var buf strings.Builder buf.WriteString("\0337") for _, sixel := range ui.sixels { buf.WriteString(fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1)) - buf.WriteString(sixel.str) + buf.WriteString(sixel.str) } buf.WriteString("\0338") - fmt.Print(buf.String()) + fmt.Print(buf.String()) } From 3cccb8368d5233b111bef87a0a5bc9e39312b419 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 1 Jul 2022 14:02:57 +0200 Subject: [PATCH 27/44] placeholder function getTermPixels for windows --- os_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/os_windows.go b/os_windows.go index 1852c869..380acc43 100644 --- a/os_windows.go +++ b/os_windows.go @@ -181,3 +181,7 @@ func exportFiles(f string, fs []string, pwd string) { os.Setenv("fx", envFiles) } } + +func getTermPixels() (w, h int, err error) { + return -1, -1, nil +} From f8bfef0a05f48bfaca47f78b9af0451f13e7471f Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 3 Jul 2022 01:39:50 +0200 Subject: [PATCH 28/44] function sixelDimPx now considers image size given in the optional raster attributes field --- misc.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/misc.go b/misc.go index d0e84023..eff62919 100644 --- a/misc.go +++ b/misc.go @@ -303,15 +303,55 @@ var reNumber = regexp.MustCompile(`^[0-9]+`) // needs some testing func sixelDimPx(s string) (w int, h int) { - // TODO maybe take into account pixel aspect ratio (") + // TODO maybe take into account pixel aspect ratio // TODO handle macro parameters P1 to P3 // `DCS P1;P2;P3;q...` - a := strings.Index(s, "q") + 1 - if a == 0 { - //syntax error + + // General sixel sequence: + // DCS ;;; q [" ]; ST + // DCS is "ESC P" + // We are not interested in P1~P3 + // the optional raster attributes may contain the image size in pixels + // ST is the terminating string "ESC \" + i := strings.Index(s, "q") + 1 + if i == 0 { + // syntax error + return -1, -1 + } + + // Start of (optional) Raster Attributes + // " Pan ; Pad; Ph; Pv + // pixel aspect ratio = Pan/Pad + // We are only interested in Ph and Pv (horizontal and vertical size in px) + if s[i] == '"' { + i++ + b := strings.Index(s[i:], ";") + // pan := strconv.Atoi(s[a:b]) + i += b + 1 + b = strings.Index(s[i:], ";") + // pad := strconv.Atoi(s[a:b]) + + i += b + 1 + b = strings.Index(s[i:], ";") + ph, err1 := strconv.Atoi(s[i : i+b]) + + i += b + 1 + b = strings.Index(s[i:], ";") + pv, err2 := strconv.Atoi(s[i : i+b]) + + if err1 != nil || err2 != nil { + goto main_body // keep trying + } + + // TODO + // ph and pv are more like suggestions, it's still possible to go over the + // reported size, so we might need to parse the entire main body anyway + return ph, pv } + +main_body: var wi int - for i := a; i < len(s)-2; i++ { + for ; i < len(s)-2; i++ { c := s[i] switch { case '?' <= c && c <= '~': @@ -327,9 +367,11 @@ func sixelDimPx(s string) (w int, h int) { m := reNumber.FindString(s[i+1:]) if m == "" { // syntax error + return -1, -1 } if s[i+1+len(m)] < '?' || s[i+1+len(m)] > '~' { // syntax error + return -1, -1 } n, _ := strconv.Atoi(m) wi += n - 1 @@ -338,7 +380,6 @@ func sixelDimPx(s string) (w int, h int) { } if s[len(s)-3] != '-' { w = max(w, wi) - wi = 0 h++ // add newline on last row } return w, h * 6 From 2eddca918f92b26b28b910d329541ad70a58194e Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 5 Jul 2022 01:36:17 +0200 Subject: [PATCH 29/44] change sixel image alignment to emulate behavior of a terminal - images used to always draw on a new line - images is now placed where it starts: ``` printf 'abc xyz' ``` would result in: ``` abc +------+ | img | | here | +------+ xyz ``` old behavior: ``` abc +------+ | img | | here | +------+ xyz ``` --- nav.go | 105 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/nav.go b/nav.go index 257e7087..28655732 100644 --- a/nav.go +++ b/nav.go @@ -704,8 +704,11 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win buf := bufio.NewScanner(reader) - sixelFrom := -1 - for i := 0; (sixelFrom != -1 || len(reg.lines) < win.h) && buf.Scan(); i++ { + var sixelBuffer []string + processingSixel := false + for i := 0; (processingSixel || len(reg.lines) < win.h) && buf.Scan(); i++ { + text := buf.Text() + for _, r := range buf.Text() { if r == 0 { reg.lines = []string{"\033[7mbinary\033[0m"} @@ -713,44 +716,70 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win } } if wPx > 0 && hPx > 0 { - if a := strings.Index(buf.Text(), gSixelBegin); a >= 0 { - if b := strings.IndexByte(buf.Text()[a+2:], gEscapeCode); b >= 0 { - if buf.Text()[a+b+1] == '\\' { - addSixel(screen, wPx, hPx, reg, buf.Text()[a:a+b+2], 2, win.y+len(reg.lines)) - reg.lines = append(reg.lines, buf.Text()[:a], buf.Text()[a+b+2:]) - continue - } else { - reg.lines = append(reg.lines, buf.Text()) - continue - } - } else { - reg.lines = append(reg.lines, buf.Text()[:a] /*TODO skip empty line*/, buf.Text()[a:]) - sixelFrom = len(reg.lines) - 1 - continue - } + if a := strings.Index(text, gSixelBegin); a >= 0 { + reg.lines = append(reg.lines, text[:a]) + text = text[a:] + processingSixel = true } - if sixelFrom != -1 { - if b := strings.IndexByte(buf.Text(), gEscapeCode); b >= 0 { - if buf.Text()[b+1] == '\\' { - reg.lines = append(reg.lines, buf.Text()[:b+2]) - sx := strings.Join(reg.lines[sixelFrom:], "") - reg.lines = reg.lines[:sixelFrom] - addSixel(screen, wPx, hPx, reg, sx, 2, len(reg.lines)) - - reg.lines = append(reg.lines, buf.Text()[b+2:]) - sixelFrom = -1 + + if processingSixel { + var lookFrom int + if text[:2] == gSixelBegin { + lookFrom = 2 + } + if b := strings.IndexByte(text[lookFrom:], gEscapeCode); b >= 0 { + b += lookFrom + if len(text) > b && text[b+1] == '\\' { + sixelBuffer = append(sixelBuffer, text[:b+2]) + sx := strings.Join(sixelBuffer, "") + + xoff := runeSliceWidth([]rune(reg.lines[len(reg.lines)-1])) + 2 + yoff := len(reg.lines) - 1 + Wc, Hc := screen.Size() + w, h := sixelDimPx(sx) + if w < 0 || h < 0 { + goto discard_sixel + } + wc, hc := pxToCells(w, h, Wc, Hc, wPx, hPx) + + reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) + fill := strings.Repeat("\u2800", wc) + paddedfill := strings.Repeat(" ", xoff) + fill + reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + fill + for j := 1; j < hc; j++ { + reg.lines = append(reg.lines, paddedfill) + } + + reg.lines = append(reg.lines, text[b+2:]) + processingSixel = false continue - } else { - sixelFrom = -1 + } else { // deal with unexpected control sequence + goto discard_sixel } } + sixelBuffer = append(sixelBuffer, text) + continue + + discard_sixel: + emptyLines := win.h - len(reg.lines) + reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + sixelBuffer[0] + if emptyLines > 0 { + reg.lines = append(reg.lines, sixelBuffer[1:emptyLines+1]...) + } + reg.lines = append(reg.lines, text) + processingSixel = false + continue } } - reg.lines = append(reg.lines, buf.Text()) + reg.lines = append(reg.lines, text) } - if len(reg.lines) > win.h { - reg.lines = reg.lines[:win.h] + if processingSixel && len(sixelBuffer) > 0 { + emptyLines := win.h - len(reg.lines) + reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + sixelBuffer[0] + if emptyLines > 0 { + reg.lines = append(reg.lines, sixelBuffer[1:emptyLines+1]...) + } } if buf.Err() != nil { @@ -758,18 +787,6 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win } } -func addSixel(screen tcell.Screen, wPx, hPx int, reg *reg, sx string, x, y int) { - Wc, Hc := screen.Size() - w, h := sixelDimPx(sx) - wc, hc := pxToCells(w, h, Wc, Hc, wPx, hPx) - - reg.sixels = append(reg.sixels, sixel{x, y, w, h, sx}) - fill := strings.Repeat("\u2800", wc) - for j := 0; j < hc; j++ { - reg.lines = append(reg.lines, fill) - } -} - func (nav *nav) loadReg(path string, volatile bool) *reg { r, ok := nav.regCache[path] if !ok || (volatile && r.volatile) { From 43fb37f2d74cca76c30593b92a1e7b2958403684 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 6 Jul 2022 14:32:47 +0200 Subject: [PATCH 30/44] fix bug where raster attributes are wrongly parsed in sixelDimPx --- misc.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/misc.go b/misc.go index eff62919..87af887e 100644 --- a/misc.go +++ b/misc.go @@ -304,8 +304,6 @@ var reNumber = regexp.MustCompile(`^[0-9]+`) // needs some testing func sixelDimPx(s string) (w int, h int) { // TODO maybe take into account pixel aspect ratio - // TODO handle macro parameters P1 to P3 - // `DCS P1;P2;P3;q...` // General sixel sequence: // DCS ;;; q [" ]; ST @@ -336,8 +334,9 @@ func sixelDimPx(s string) (w int, h int) { ph, err1 := strconv.Atoi(s[i : i+b]) i += b + 1 - b = strings.Index(s[i:], ";") - pv, err2 := strconv.Atoi(s[i : i+b]) + n := reNumber.FindString(s[i:]) // use stirngs.Index(s[i:], "#") instead? + pv, err2 := strconv.Atoi(n) + i += len(n) if err1 != nil || err2 != nil { goto main_body // keep trying From 3a6f80742adbce7e1956627ce355130ed363872b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 6 Jul 2022 14:41:11 +0200 Subject: [PATCH 31/44] prevent drawing sixels while menu is active --- ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui.go b/ui.go index c1bfa134..71f830ca 100644 --- a/ui.go +++ b/ui.go @@ -938,7 +938,7 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() - if len(ui.sixels) > 0 { + if ui.menuBuf == nil && len(ui.sixels) > 0 { ui.showSixels() } From 6cce131fa0caf32620becf26cfc00de88aed6545 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 8 Jul 2022 14:25:51 +0200 Subject: [PATCH 32/44] introduce sixelScreen struct and refactor screen width,height in px to use new struct --- eval.go | 4 ++-- nav.go | 12 ++++++++---- ui.go | 30 ++++++++++++++++++++---------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/eval.go b/eval.go index b4f21d83..876b1b7d 100644 --- a/eval.go +++ b/eval.go @@ -1197,9 +1197,9 @@ func (e *callExpr) eval(app *app, args []string) { return } var err error - app.ui.wPx, app.ui.hPx, err = getTermPixels() + app.ui.sxScreen.wpx, app.ui.sxScreen.hpx, err = getTermPixels() if err != nil { - app.ui.wPx, app.ui.hPx = -1, -1 + app.ui.sxScreen.wpx, app.ui.sxScreen.hpx = -1, -1 log.Printf("getting terminal pixel size: %s", err) } app.ui.renew() diff --git a/nav.go b/nav.go index 28655732..0ea4eb84 100644 --- a/nav.go +++ b/nav.go @@ -24,6 +24,10 @@ const ( gSixelTerminate = "\033\\" ) +var ( + gSixelFiller = '.' +) + type linkState byte const ( @@ -629,7 +633,7 @@ func (nav *nav) previewLoop(ui *ui) { } if len(path) != 0 { win := ui.wins[len(ui.wins)-1] - nav.preview(path, ui.screen, ui.wPx, ui.hPx, win) + nav.preview(path, ui.screen, ui.sxScreen, win) prev = path } } @@ -650,7 +654,7 @@ func matchPattern(pattern, name, path string) bool { return matched } -func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win) { +func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, win *win) { reg := ®{loadTime: time.Now(), path: path} defer func() { nav.regChan <- reg }() @@ -715,7 +719,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win return } } - if wPx > 0 && hPx > 0 { + if sxScreen.wpx > 0 && sxScreen.hpx > 0 { if a := strings.Index(text, gSixelBegin); a >= 0 { reg.lines = append(reg.lines, text[:a]) text = text[a:] @@ -740,7 +744,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, wPx, hPx int, win *win if w < 0 || h < 0 { goto discard_sixel } - wc, hc := pxToCells(w, h, Wc, Hc, wPx, hPx) + wc, hc := pxToCells(w, h, Wc, Hc, sxScreen.wpx, sxScreen.hpx) reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) fill := strings.Repeat("\u2800", wc) diff --git a/ui.go b/ui.go index 71f830ca..ece8643a 100644 --- a/ui.go +++ b/ui.go @@ -252,7 +252,7 @@ func (win *win) printRight(screen tcell.Screen, y int, st tcell.Style, s string) win.print(screen, win.w-printLength(s), y, st, s) } -func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { +func (win *win) printReg(screen tcell.Screen, sxs *sixelScreen, reg *reg) { if reg == nil { return } @@ -277,7 +277,7 @@ func (win *win) printReg(screen tcell.Screen, sixels *[]sixel, reg *reg) { s := sx s.x += win.x s.y += win.y - *sixels = append(*sixels, s) + sxs.sx = append(sxs.sx, s) } } @@ -473,10 +473,16 @@ type sixel struct { str string } +type sixelScreen struct { + wpx, hpx int + sx []sixel + lastFile string + altFill bool +} + type ui struct { screen tcell.Screen - sixels []sixel - wPx, hPx int + sxScreen sixelScreen polling bool wins []*win promptWin *win @@ -566,10 +572,10 @@ func newUI(screen tcell.Screen) *ui { menuSelected: -2, } - ui.wPx, ui.hPx, err = getTermPixels() + ui.sxScreen.wpx, ui.sxScreen.hpx, err = getTermPixels() // TODO getting pixel size does not gurantee sixel support if err != nil { - ui.wPx, ui.hPx = -1, -1 + ui.sxScreen.wpx, ui.sxScreen.hpx = -1, -1 log.Printf("getting terminal pixel size: %s", err) } @@ -858,7 +864,7 @@ func (ui *ui) draw(nav *nav) { ui.screen.SetContent(i, j, ' ', nil, st) } } - ui.sixels = nil + ui.sxScreen.clear() ui.drawPromptLine(nav) @@ -908,7 +914,7 @@ func (ui *ui) draw(nav *nav) { if curr.IsDir() { preview.printDir(ui.screen, ui.dirPrev, nav.selections, nav.saves, nav.tags, ui.styles, ui.icons) } else if curr.Mode().IsRegular() { - preview.printReg(ui.screen, &ui.sixels, ui.regPrev) + preview.printReg(ui.screen, &ui.sxScreen, ui.regPrev) } } } @@ -938,7 +944,7 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() - if ui.menuBuf == nil && len(ui.sixels) > 0 { + if ui.menuBuf == nil && len(ui.sxScreen.sx) > 0 { ui.showSixels() } @@ -1319,10 +1325,14 @@ func listMatchesMenu(ui *ui, matches []string) error { return nil } +func (sxs *sixelScreen) clear() { + sxs.sx = nil +} + func (ui *ui) showSixels() { var buf strings.Builder buf.WriteString("\0337") - for _, sixel := range ui.sixels { + for _, sixel := range ui.sxScreen.sx { buf.WriteString(fmt.Sprintf("\033[%d;%dH", sixel.y+1, sixel.x+1)) buf.WriteString(sixel.str) } From 5929846f22e3b4f521a79c9f011ac462ec11ec7e Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 12 Jul 2022 14:19:56 +0200 Subject: [PATCH 33/44] fix prevent nested sixel sequences --- nav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav.go b/nav.go index 0ea4eb84..f8b3a99a 100644 --- a/nav.go +++ b/nav.go @@ -720,7 +720,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, } } if sxScreen.wpx > 0 && sxScreen.hpx > 0 { - if a := strings.Index(text, gSixelBegin); a >= 0 { + if a := strings.Index(text, gSixelBegin); !processingSixel && a >= 0 { reg.lines = append(reg.lines, text[:a]) text = text[a:] processingSixel = true From 2e0199d10a9093f74056520cedc0304bf307157c Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 12 Jul 2022 14:21:09 +0200 Subject: [PATCH 34/44] fix bug where rejected sixels cause indexing error --- nav.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nav.go b/nav.go index f8b3a99a..93f4c350 100644 --- a/nav.go +++ b/nav.go @@ -765,7 +765,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, continue discard_sixel: - emptyLines := win.h - len(reg.lines) + emptyLines := min(win.h-len(reg.lines), len(sixelBuffer)-1) reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + sixelBuffer[0] if emptyLines > 0 { reg.lines = append(reg.lines, sixelBuffer[1:emptyLines+1]...) @@ -779,7 +779,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, } if processingSixel && len(sixelBuffer) > 0 { - emptyLines := win.h - len(reg.lines) + emptyLines := min(win.h-len(reg.lines), len(sixelBuffer)-1) reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + sixelBuffer[0] if emptyLines > 0 { reg.lines = append(reg.lines, sixelBuffer[1:emptyLines+1]...) From 22883aa561a75086084765360882bc431aa99e9d Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 12 Jul 2022 14:22:46 +0200 Subject: [PATCH 35/44] add "alternating filler" to trick tcell into redrawing when switching between different images --- nav.go | 6 +++--- ui.go | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/nav.go b/nav.go index 93f4c350..45df668a 100644 --- a/nav.go +++ b/nav.go @@ -633,7 +633,7 @@ func (nav *nav) previewLoop(ui *ui) { } if len(path) != 0 { win := ui.wins[len(ui.wins)-1] - nav.preview(path, ui.screen, ui.sxScreen, win) + nav.preview(path, ui.screen, &ui.sxScreen, win) prev = path } } @@ -654,7 +654,7 @@ func matchPattern(pattern, name, path string) bool { return matched } -func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, win *win) { +func (nav *nav) preview(path string, screen tcell.Screen, sxScreen *sixelScreen, win *win) { reg := ®{loadTime: time.Now(), path: path} defer func() { nav.regChan <- reg }() @@ -747,7 +747,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen sixelScreen, wc, hc := pxToCells(w, h, Wc, Hc, sxScreen.wpx, sxScreen.hpx) reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) - fill := strings.Repeat("\u2800", wc) + fill := sxScreen.filler(path, wc) paddedfill := strings.Repeat(" ", xoff) + fill reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + fill for j := 1; j < hc; j++ { diff --git a/ui.go b/ui.go index ece8643a..516b7a1d 100644 --- a/ui.go +++ b/ui.go @@ -1329,6 +1329,23 @@ func (sxs *sixelScreen) clear() { sxs.sx = nil } +func (sxs *sixelScreen) filler(path string, l int) (fill string) { + if path != sxs.lastFile { + sxs.altFill = !sxs.altFill + sxs.lastFile = path + } + + if sxs.altFill { + fill = "\033[1m" + defer func() { + fill += "\033[0m" + }() + } + + fill += strings.Repeat(string(gSixelFiller), l) + return +} + func (ui *ui) showSixels() { var buf strings.Builder buf.WriteString("\0337") From 412176e15f37f9a41ecc7f9771fee5a0c921d362 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 12 Jul 2022 14:37:47 +0200 Subject: [PATCH 36/44] fix filler string wrongly indented --- nav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav.go b/nav.go index 45df668a..9a691dc6 100644 --- a/nav.go +++ b/nav.go @@ -748,7 +748,7 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen *sixelScreen, reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) fill := sxScreen.filler(path, wc) - paddedfill := strings.Repeat(" ", xoff) + fill + paddedfill := strings.Repeat(" ", xoff-2) + fill reg.lines[len(reg.lines)-1] = reg.lines[len(reg.lines)-1] + fill for j := 1; j < hc; j++ { reg.lines = append(reg.lines, paddedfill) From 1150515f54a283a43c5f6adef9b5215649a0fc13 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Tue, 12 Jul 2022 14:38:01 +0200 Subject: [PATCH 37/44] add comment --- ui.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui.go b/ui.go index 516b7a1d..f6e28d75 100644 --- a/ui.go +++ b/ui.go @@ -1329,6 +1329,8 @@ func (sxs *sixelScreen) clear() { sxs.sx = nil } +// fillers are used to control when tcell redraws the region where a sixel image is drawn. +// alternating between bold("ESC [1m") and regular is to clear the image before drawing a new one. func (sxs *sixelScreen) filler(path string, l int) (fill string) { if path != sxs.lastFile { sxs.altFill = !sxs.altFill From 91efd8d5b3358dbdc441a3ab5fea21e0eea8a9cc Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 14:30:55 +0200 Subject: [PATCH 38/44] replace pxToCells() with sixelScreen.pxToCells() --- eval.go | 7 +------ nav.go | 3 +-- ui.go | 33 ++++++++++++++++++++++----------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/eval.go b/eval.go index 876b1b7d..5bcb2521 100644 --- a/eval.go +++ b/eval.go @@ -1196,12 +1196,7 @@ func (e *callExpr) eval(app *app, args []string) { if !app.nav.init { return } - var err error - app.ui.sxScreen.wpx, app.ui.sxScreen.hpx, err = getTermPixels() - if err != nil { - app.ui.sxScreen.wpx, app.ui.sxScreen.hpx = -1, -1 - log.Printf("getting terminal pixel size: %s", err) - } + app.ui.sxScreen.updateSizes(app.ui.screen.Size()) app.ui.renew() app.ui.screen.Sync() if app.nav.height != app.ui.wins[0].h { diff --git a/nav.go b/nav.go index 9a691dc6..22af4b94 100644 --- a/nav.go +++ b/nav.go @@ -739,12 +739,11 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen *sixelScreen, xoff := runeSliceWidth([]rune(reg.lines[len(reg.lines)-1])) + 2 yoff := len(reg.lines) - 1 - Wc, Hc := screen.Size() w, h := sixelDimPx(sx) if w < 0 || h < 0 { goto discard_sixel } - wc, hc := pxToCells(w, h, Wc, Hc, sxScreen.wpx, sxScreen.hpx) + wc, hc := sxScreen.pxToCells(w, h) reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) fill := sxScreen.filler(path, wc) diff --git a/ui.go b/ui.go index f6e28d75..43c33d29 100644 --- a/ui.go +++ b/ui.go @@ -474,10 +474,11 @@ type sixel struct { } type sixelScreen struct { - wpx, hpx int - sx []sixel - lastFile string - altFill bool + wpx, hpx int + fontw, fonth int + sx []sixel + lastFile string + altFill bool } type ui struct { @@ -553,7 +554,6 @@ func getWins(screen tcell.Screen) []*win { } func newUI(screen tcell.Screen) *ui { - var err error wtot, htot := screen.Size() ui := &ui{ @@ -572,12 +572,7 @@ func newUI(screen tcell.Screen) *ui { menuSelected: -2, } - ui.sxScreen.wpx, ui.sxScreen.hpx, err = getTermPixels() - // TODO getting pixel size does not gurantee sixel support - if err != nil { - ui.sxScreen.wpx, ui.sxScreen.hpx = -1, -1 - log.Printf("getting terminal pixel size: %s", err) - } + ui.sxScreen.updateSizes(screen.Size()) go ui.pollEvents() @@ -1348,6 +1343,22 @@ func (sxs *sixelScreen) filler(path string, l int) (fill string) { return } +func (sxs *sixelScreen) updateSizes(wc, hc int) { + var err error + sxs.wpx, sxs.hpx, err = getTermPixels() + if err != nil { + sxs.wpx, sxs.hpx = -1, -1 + log.Printf("getting terminal pixel size: %s", err) + } + + sxs.fontw = sxs.wpx / wc + sxs.fonth = sxs.hpx / hc +} + +func (xsx *sixelScreen) pxToCells(x, y int) (int, int) { + return x/xsx.fontw + 1, y/xsx.fonth + 1 +} + func (ui *ui) showSixels() { var buf strings.Builder buf.WriteString("\0337") From af92f4c71cf6203097f0122bb6c276df39d7ed6c Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 14:37:16 +0200 Subject: [PATCH 39/44] add trim sixel height during preview --- misc.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++---- nav.go | 2 ++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/misc.go b/misc.go index 87af887e..05c9d25b 100644 --- a/misc.go +++ b/misc.go @@ -384,10 +384,57 @@ main_body: return w, h * 6 } -func pxToCells(x, y, wc, hc, wpx, hpx int) (int, int) { - var fw int = wpx / wc - var fh int = hpx / hc - return x/fw + 1, y/fh + 1 // TODO should probably use ceiling instead of +1 +// maybe merge with sixelDimPx() +func trimSixelHeight(s string, maxh int) (string, int) { + var h int + maxh = maxh - (maxh % 6) + + i := strings.Index(s, "q") + 1 + if i == 0 { + // syntax error + return "", -1 + } + + if s[i] == '"' { + i++ + for j := 0; j < 3; j++ { + b := strings.Index(s[i:], ";") + i += b + 1 + } + b := strings.Index(s[i:], "#") + pv, err := strconv.Atoi(s[i : i+b]) + + if err == nil && pv > maxh { + mh := strconv.Itoa(maxh) + s = s[:i] + mh + s[i+b:] + i += len(mh) + } else { + i += b + } + } + + for h < maxh { + k := strings.IndexRune(s[i+1:], '-') + if k == -1 { + if s[len(s)-3] != '-' { + h += 6 + i = len(s) - 3 + } + break + } + i += k + 1 + h += 6 + } + + if i == 0 { + return s, 6 + } + + if len(s) > i+3 { + return s[:i+1] + "\x1b\\", h + } + + return s, h } // We don't need no generic code diff --git a/nav.go b/nav.go index 22af4b94..2a04f0ce 100644 --- a/nav.go +++ b/nav.go @@ -739,10 +739,12 @@ func (nav *nav) preview(path string, screen tcell.Screen, sxScreen *sixelScreen, xoff := runeSliceWidth([]rune(reg.lines[len(reg.lines)-1])) + 2 yoff := len(reg.lines) - 1 + maxh := (win.h - yoff) * sxScreen.fonth w, h := sixelDimPx(sx) if w < 0 || h < 0 { goto discard_sixel } + sx, h = trimSixelHeight(sx, maxh) wc, hc := sxScreen.pxToCells(w, h) reg.sixels = append(reg.sixels, sixel{xoff, yoff, w, h, sx}) From cef82a039b178e2c7973fb41929f9962aada4b8f Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 14:37:35 +0200 Subject: [PATCH 40/44] add tests for trimSixelHeight --- misc_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/misc_test.go b/misc_test.go index 3568fa2f..062d34bc 100644 --- a/misc_test.go +++ b/misc_test.go @@ -243,3 +243,48 @@ func TestNaturalLess(t *testing.T) { } } } + +func TestTrimSixelHeight(t *testing.T) { + tests := []struct { + si string + hi int + so string + ho int + }{ + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{\x1b\\", 12, + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{\x1b\\", 6, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N\x1b\\", 30, + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N\x1b\\", 12, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N\x1b\\", 12, + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N\x1b\\", 12, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N\x1b\\", 11, + "\x1bPq\"1;1;1;6#0;2;97;97;97#1;2;75;75;75#0B$#1{-\x1b\\", 6, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N-\x1b\\", 30, + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N-\x1b\\", 12, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N-\x1b\\", 12, + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N-\x1b\\", 12, + }, + { + "\x1bPq\"1;1;1;12#0;2;97;97;97#1;2;75;75;75#0B$#1{-#0o$#1N-\x1b\\", 11, + "\x1bPq\"1;1;1;6#0;2;97;97;97#1;2;75;75;75#0B$#1{-\x1b\\", 6, + }, + } + + for i, test := range tests { + if got1, got2 := trimSixelHeight(test.si, test.hi); got1 != test.so || got2 != test.ho { + t.Errorf("test #%d expected height %d, got %d and string %s", i, test.ho, got2, got1[1:]) + } + } + +} From 18d4d42c3856cbdc709d61b8df19b66ee4a2d0ca Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 14:56:56 +0200 Subject: [PATCH 41/44] prevent sixel redrawing during input prompts --- ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui.go b/ui.go index 43c33d29..12147226 100644 --- a/ui.go +++ b/ui.go @@ -939,7 +939,7 @@ func (ui *ui) draw(nav *nav) { } ui.screen.Show() - if ui.menuBuf == nil && len(ui.sxScreen.sx) > 0 { + if ui.menuBuf == nil && ui.cmdPrefix == "" && len(ui.sxScreen.sx) > 0 { ui.showSixels() } From 0e7f5ee1b9c0e85887f915f72ef16a66d019753d Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 15:08:52 +0200 Subject: [PATCH 42/44] change sixel filler to braille space --- nav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav.go b/nav.go index 2a04f0ce..2a6cabb9 100644 --- a/nav.go +++ b/nav.go @@ -25,7 +25,7 @@ const ( ) var ( - gSixelFiller = '.' + gSixelFiller = '\u2800' ) type linkState byte From 4925d87c4e7b71ee390bf37b90b79b99210cdd8f Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 15:14:50 +0200 Subject: [PATCH 43/44] clean up --- nav.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nav.go b/nav.go index 2a6cabb9..d84bccf3 100644 --- a/nav.go +++ b/nav.go @@ -15,7 +15,6 @@ import ( "strings" "time" - "github.com/gdamore/tcell/v2" times "gopkg.in/djherbis/times.v1" ) @@ -633,7 +632,7 @@ func (nav *nav) previewLoop(ui *ui) { } if len(path) != 0 { win := ui.wins[len(ui.wins)-1] - nav.preview(path, ui.screen, &ui.sxScreen, win) + nav.preview(path, &ui.sxScreen, win) prev = path } } @@ -654,7 +653,7 @@ func matchPattern(pattern, name, path string) bool { return matched } -func (nav *nav) preview(path string, screen tcell.Screen, sxScreen *sixelScreen, win *win) { +func (nav *nav) preview(path string, sxScreen *sixelScreen, win *win) { reg := ®{loadTime: time.Now(), path: path} defer func() { nav.regChan <- reg }() From 6dfd6561fd3da51f08a2107d587704bd27e58efc Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 13 Jul 2022 15:18:53 +0200 Subject: [PATCH 44/44] use strings.Index instead of regex for simple search --- misc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc.go b/misc.go index 05c9d25b..17cffc2f 100644 --- a/misc.go +++ b/misc.go @@ -334,9 +334,9 @@ func sixelDimPx(s string) (w int, h int) { ph, err1 := strconv.Atoi(s[i : i+b]) i += b + 1 - n := reNumber.FindString(s[i:]) // use stirngs.Index(s[i:], "#") instead? - pv, err2 := strconv.Atoi(n) - i += len(n) + b = strings.Index(s[i:], "#") + pv, err2 := strconv.Atoi(s[i : i+b]) + i += b if err1 != nil || err2 != nil { goto main_body // keep trying