kopia lustrzana https://github.com/browsh-org/browsh
Hiptext dimensions. Better zooming. Tests.
Conforming to hiptext dimensions. Bounding the zoom window so as not to overlap. Some basic tests.pull/16/head
rodzic
073db28792
commit
c69e7b85e8
10
run.sh
10
run.sh
|
@ -34,15 +34,15 @@ ffmpeg \
|
|||
sleep 1
|
||||
|
||||
# Intercept STDIN (mouse and keypresses) and forward to the X framebuffer via xdotool
|
||||
(./stdin_forward <&3) 3<&0
|
||||
(./stdin_forward <&3 &) 3<&0
|
||||
|
||||
# Hiptext renders images and videos into text characters displayable in a terminal
|
||||
# Hiptext complains unless you specify the exact path to the font, seems like a bug to me
|
||||
# TODO: support variable width, ideally dynamic sizing
|
||||
# hiptext \
|
||||
# -font /usr/share/fonts/ttf-dejavu/DejaVuSansMono.ttf \
|
||||
# $UDP_URI \
|
||||
# 2> hiptext.log
|
||||
hiptext \
|
||||
-font /usr/share/fonts/ttf-dejavu/DejaVuSansMono.ttf \
|
||||
$UDP_URI \
|
||||
2> hiptext.log
|
||||
|
||||
# Kill all the subprocesses created in this script if the script itself exits
|
||||
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
|
||||
|
|
161
stdin_forward.go
161
stdin_forward.go
|
@ -2,26 +2,68 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nsf/termbox-go"
|
||||
"strings"
|
||||
"os"
|
||||
"os/exec"
|
||||
"math"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
var current string
|
||||
var curev termbox.Event
|
||||
var lastMouseButton string
|
||||
var desktopX int
|
||||
var desktopY int
|
||||
var termWidth, termHeight = termbox.Size()
|
||||
var desktopWidth float32 = 1600
|
||||
var desktopHeight float32 = 1200
|
||||
var desktopXFloat float32
|
||||
var desktopYFloat float32
|
||||
var roundedDesktopX int
|
||||
var roundedDesktopY int
|
||||
// Dimensions of hiptext output
|
||||
var hipWidth int
|
||||
var hipHeight int
|
||||
|
||||
// For keeping track of the zoom
|
||||
var zoomLevel float32 = 1
|
||||
var viewport = map[string] float32 {
|
||||
"xSize": 1600,
|
||||
"ySize": 1200,
|
||||
"xOffset": 0,
|
||||
"yOffset": 0,
|
||||
// TODO: look at the XFCE code to accurately determine the factor. It may
|
||||
// even be linear.
|
||||
var zoomFactor float32 = 0.03
|
||||
var maxZoom float32 = 1000000
|
||||
var zoomLevel float32
|
||||
var viewport map[string] float32
|
||||
|
||||
func initialise() {
|
||||
log("Starting...")
|
||||
calculateHipDimensions()
|
||||
zoomLevel = 1
|
||||
viewport = map[string] float32 {
|
||||
"xSize": desktopWidth,
|
||||
"ySize": desktopHeight,
|
||||
"xOffset": 0,
|
||||
"yOffset": 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Hiptext needs to render the aspect ratio faithfully. So firstly it tries to fill
|
||||
// the terminal as much as it can. And secondly it treat a row as representing twice
|
||||
// as much as a column - thus why there are some multiplications/divisions by 2.
|
||||
func calculateHipDimensions() {
|
||||
_tw, _th := termbox.Size()
|
||||
tw := float32(_tw)
|
||||
th := float32(_th * 2)
|
||||
ratio := desktopWidth / desktopHeight
|
||||
bestHeight := min(th, (tw / ratio))
|
||||
bestWidth := min(tw, (bestHeight * ratio))
|
||||
// Not sure why the +1 and -1 are needed
|
||||
hipWidth = roundToInt(bestWidth) + 1
|
||||
hipHeight = roundToInt(bestHeight / 2) - 1
|
||||
log(fmt.Sprintf("Term dimensions: W: %d, H: %d", _tw, _th))
|
||||
log(fmt.Sprintf("Hiptext dimensions: W: %d, H: %d", hipWidth, hipHeight))
|
||||
}
|
||||
|
||||
func min(a float32, b float32) float32 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func log(msg string) {
|
||||
|
@ -47,6 +89,21 @@ func xdotool(args ...string) {
|
|||
}
|
||||
}
|
||||
|
||||
func roundToInt(value32 float32) int {
|
||||
var rounded float64
|
||||
value := float64(value32)
|
||||
if value < 0 {
|
||||
rounded = math.Ceil(value - 0.5)
|
||||
}
|
||||
rounded = math.Floor(value + 0.5)
|
||||
return int(rounded)
|
||||
}
|
||||
|
||||
// Whether the current input event includes a depressed CTRL key.
|
||||
func ctrlPressed() bool {
|
||||
return curev.Mod&termbox.ModCtrl != 0
|
||||
}
|
||||
|
||||
// Convert Termbox symbols to xdotool arguments
|
||||
func mouseButtonStr(k termbox.Key) []string {
|
||||
switch k {
|
||||
|
@ -62,13 +119,13 @@ func mouseButtonStr(k termbox.Key) []string {
|
|||
case termbox.MouseRelease:
|
||||
return []string{"mouseup", lastMouseButton}
|
||||
case termbox.MouseWheelUp:
|
||||
if curev.Mod&termbox.ModCtrl != 0 {
|
||||
trackZoom("out")
|
||||
if ctrlPressed() {
|
||||
trackZoom("in")
|
||||
}
|
||||
return []string{"click", "4"}
|
||||
case termbox.MouseWheelDown:
|
||||
if curev.Mod&termbox.ModCtrl != 0 {
|
||||
trackZoom("in")
|
||||
if ctrlPressed() {
|
||||
trackZoom("out")
|
||||
}
|
||||
return []string{"click", "5"}
|
||||
}
|
||||
|
@ -100,18 +157,17 @@ func mouseEvent() {
|
|||
curev.MouseX, curev.MouseY, mouseButtonStr(curev.Key), modStr(curev.Mod)))
|
||||
|
||||
// CTRL allows the user to drag the mouse to pan and zoom the desktop.
|
||||
if curev.Mod&termbox.ModCtrl != 0 {
|
||||
if ctrlPressed() {
|
||||
xdotool("keydown", "alt")
|
||||
} else {
|
||||
xdotool("keyup", "alt")
|
||||
}
|
||||
|
||||
|
||||
// Always move the mouse first. This is because we're not constantly updating the mouse position,
|
||||
// *unless* a drag event is happening. This saves bandwidth and also mouse movement isn't supported
|
||||
// *unless* a drag event is happening. This saves bandwidth. Also, mouse movement isn't supported
|
||||
// on all terminals.
|
||||
setCurrentDesktopCoords()
|
||||
xdotool("mousemove", fmt.Sprintf("%d", desktopX), fmt.Sprintf("%d", desktopY))
|
||||
xdotool("mousemove", fmt.Sprintf("%d", roundedDesktopX), fmt.Sprintf("%d", roundedDesktopY))
|
||||
|
||||
// Send a button press to X. Note that the "Motion" modifier is sent when the user is doing
|
||||
// a drag event and thus mouse reporting will be constantly streamed.
|
||||
|
@ -122,35 +178,74 @@ func mouseEvent() {
|
|||
|
||||
// Convert terminal coords into desktop coords
|
||||
func setCurrentDesktopCoords() {
|
||||
termWidthFloat := float32(termWidth)
|
||||
termHeightFloat := float32(termHeight)
|
||||
hipWidthFloat := float32(hipWidth)
|
||||
hipHeightFloat := float32(hipHeight)
|
||||
eventX := float32(curev.MouseX)
|
||||
eventY := float32(curev.MouseY)
|
||||
x := (eventX * (viewport["xSize"] / termWidthFloat)) + viewport["xOffset"]
|
||||
y := (eventY * (viewport["ySize"] / termHeightFloat)) + viewport["yOffset"]
|
||||
desktopX = int(x)
|
||||
desktopY = int(y)
|
||||
desktopXFloat = (eventX * (viewport["xSize"] / hipWidthFloat)) + viewport["xOffset"]
|
||||
desktopYFloat = (eventY * (viewport["ySize"] / hipHeightFloat)) + viewport["yOffset"]
|
||||
log(
|
||||
fmt.Sprintf(
|
||||
"setCurrentDesktopCoords: tw: %d, th: %d, dx: %d, dy: %d",
|
||||
hipHeightFloat, hipWidthFloat, desktopXFloat, desktopYFloat))
|
||||
roundedDesktopX = roundToInt(desktopXFloat)
|
||||
roundedDesktopY = roundToInt(desktopYFloat)
|
||||
}
|
||||
|
||||
// XFCE doesn't provide the current zoom, so we need to keep track of it.
|
||||
// For every zoom level the terminal coords will be mapped differently onto the X desktop.
|
||||
// XFCE doesn't provide the current zoom, so *we* need to keep track of it.
|
||||
// For every zoom level, the terminal coords will be mapped differently onto the X desktop.
|
||||
// TODO: support custom desktop sizes.
|
||||
func trackZoom(direction string) {
|
||||
if direction == "in" {
|
||||
zoomLevel += 0.2
|
||||
if zoomLevel <= maxZoom {
|
||||
zoomLevel += zoomFactor
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
zoomLevel -= 0.2
|
||||
if zoomLevel >= 1 {
|
||||
zoomLevel -= zoomFactor
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Use the existing viewport to get the current coords
|
||||
setCurrentDesktopCoords()
|
||||
desktopXFloat := float32(desktopX)
|
||||
desktopYFloat := float32(desktopY)
|
||||
|
||||
// The actual zoom
|
||||
viewport["xSize"] = desktopWidth / zoomLevel
|
||||
viewport["ySize"] = desktopHeight / zoomLevel
|
||||
viewport["xOffset"] = desktopXFloat - (viewport["xSize"] / 2)
|
||||
viewport["yOffset"] = desktopYFloat - (viewport["ySize"] / 2)
|
||||
viewport["xSize"] = 1600 / zoomLevel
|
||||
viewport["ySize"] = 1200 / zoomLevel
|
||||
|
||||
keepViewportInDesktop()
|
||||
|
||||
log(fmt.Sprintf("zoom: %s", zoomLevel))
|
||||
log(fmt.Sprintf("viewport: %s", viewport))
|
||||
}
|
||||
|
||||
// When zooming near the edges of the desktop it is possible that the viewport's edges overlap
|
||||
// the desktop's edges. So just limit the possible movement of the viewport.
|
||||
func keepViewportInDesktop() {
|
||||
xLeft := viewport["xOffset"]
|
||||
xRight := viewport["xOffset"] + viewport["xSize"]
|
||||
yTop := viewport["yOffset"]
|
||||
yBottom := viewport["yOffset"] + viewport["ySize"]
|
||||
|
||||
if xLeft < 0 {
|
||||
viewport["xOffset"] = 0
|
||||
}
|
||||
if xRight > desktopWidth {
|
||||
viewport["xOffset"] = desktopWidth - viewport["xSize"]
|
||||
}
|
||||
if yTop < 0 {
|
||||
viewport["yOffset"] = 0
|
||||
}
|
||||
if yBottom > desktopHeight {
|
||||
viewport["yOffset"] = desktopHeight - viewport["ySize"]
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a keyboard event into an xdotool command
|
||||
func keyEvent() {
|
||||
// I've no idea why this gets picked up by the terminal, or what it refers to. But whatever, we don't
|
||||
|
@ -186,6 +281,7 @@ func main() {
|
|||
}
|
||||
defer termbox.Close()
|
||||
termbox.SetInputMode(termbox.InputMouse)
|
||||
initialise()
|
||||
parseInput()
|
||||
|
||||
data := make([]byte, 0, 64)
|
||||
|
@ -202,6 +298,7 @@ mainloop:
|
|||
case termbox.EventRaw:
|
||||
data = data[:beg+ev.N]
|
||||
current = fmt.Sprintf("%q", data)
|
||||
// TODO: think of a different way to exit, 'q' will be needed for actual text input.
|
||||
if current == `"q"` {
|
||||
break mainloop
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
"github.com/nsf/termbox-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func setup() {
|
||||
|
@ -10,19 +11,125 @@ func setup() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
initialise()
|
||||
hipWidth = 90
|
||||
hipHeight = 30
|
||||
setCurrentDesktopCoords()
|
||||
}
|
||||
|
||||
func teardown() {
|
||||
termbox.Close()
|
||||
}
|
||||
|
||||
func TestPoint(t *testing.T) {
|
||||
func TestPointTL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 30
|
||||
curev.MouseY = 10
|
||||
setup()
|
||||
curev.MouseX = 11
|
||||
curev.MouseY = 11
|
||||
termWidth = 90
|
||||
termWidth = 30
|
||||
setCurrentDesktopCoords()
|
||||
t.Error(desktopX, desktopY)
|
||||
|
||||
assert.Equal(533, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(400, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestPointMiddle(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 45
|
||||
curev.MouseY = 15
|
||||
setup()
|
||||
|
||||
assert.Equal(800, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(600, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestPointBR(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 60
|
||||
curev.MouseY = 20
|
||||
setup()
|
||||
|
||||
assert.Equal(1067, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(800, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
|
||||
func TestZoomIn(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 30
|
||||
curev.MouseY = 10
|
||||
setup()
|
||||
|
||||
trackZoom("in")
|
||||
setCurrentDesktopCoords()
|
||||
|
||||
assert.Equal(444, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(333, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestZoomInMiddle(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 45
|
||||
curev.MouseY = 15
|
||||
setup()
|
||||
|
||||
trackZoom("in")
|
||||
setCurrentDesktopCoords()
|
||||
|
||||
assert.Equal(800, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(600, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestZoomInOut(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 45
|
||||
curev.MouseY = 15
|
||||
setup()
|
||||
|
||||
trackZoom("in")
|
||||
trackZoom("out")
|
||||
setCurrentDesktopCoords()
|
||||
|
||||
assert.Equal(800, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(600, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestZoomInEdgeTL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = 0
|
||||
curev.MouseY = 0
|
||||
setup()
|
||||
|
||||
trackZoom("in")
|
||||
setCurrentDesktopCoords()
|
||||
|
||||
assert.Equal(0, roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(0, roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
||||
func TestZoomInEdgeBR(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
curev.MouseX = hipWidth
|
||||
curev.MouseY = hipHeight
|
||||
setup()
|
||||
|
||||
trackZoom("in")
|
||||
setCurrentDesktopCoords()
|
||||
|
||||
assert.Equal(int(desktopWidth), roundedDesktopX, "Mapped X coord")
|
||||
assert.Equal(int(desktopHeight), roundedDesktopY, "Mapped Y coord")
|
||||
|
||||
teardown()
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue