Skip to content

Commit

Permalink
Big change! Using code based on the little-known xzoom program, so no…
Browse files Browse the repository at this point in the history
… need for XFCE

The big problem with XFCE's zoom was that it follwed the mouse. So there was no way
to have the terminal representation of the desktop map to a smaller segment of the
real desktop without the terminal mouse position being able to exactly 'hover' over
the real mouse position.

The xzoom program is a window that displays a zoom of a portion of the desktop. So
double the width of the desktop and place the xzoom window on the right, but have
it watch only the half of the desktop on the left. What's more xzoom is small and
it's C code is easily incuded in the Golang code so they act as one, even sharing
state such as mouse coords, viewport position, current zoom level, etc.

WIP. Still contains old XFCE zoom code.
  • Loading branch information
tombh committed May 19, 2016
1 parent c69e7b8 commit 932a4b3
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ build/
*.out
blank.png
stdin_forward
xzoom/xzoom_debug
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ RUN apk add --no-cache xvfb xdotool@testing xfce4 ffmpeg openssh mosh chromium
# Generate host keys
RUN ssh-keygen -A

# Installing Hiptext, video to text renderer
# Installing Hiptext, video to text renderer and our own stdin_forward
RUN apk --no-cache add --virtual build-dependencies \
build-base git freetype-dev jpeg-dev ffmpeg-dev ragel
build-base git go freetype-dev jpeg-dev ffmpeg-dev ragel libx11-dev libxt-dev
# setup GOPATH and GOBIN and build stdin_forward
RUN apk --no-cache add libgflags-dev@testing glog-dev@testing
RUN mkdir -p build \
&& cd build \
Expand Down
24 changes: 17 additions & 7 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,30 @@ DESKTOP_RES="$DESKTOP_WIDTH"x"$DESKTOP_HEIGHT"
UDP_URI='udp://127.0.0.1:1234'

# Create an X desktop in memory without actually displaying it on a real screen
Xvfb :0 -screen 0 "$DESKTOP_RES"x16 > xvfb.log 2>&1 &
# Double the width to make room for the xzoom window, which is actually what
# ffmpeg will stream;
# ---------------------------------
# | | |
# | desktop | xzoom win |
# | here | here mirrors |
# | | desktop |
# | | |
# ---------------------------------
# So xzoom mirrors the desktop and ffmpeg streams the xzoom window.
Xvfb :0 -screen 0 "$(($DESKTOP_WIDTH * 2))"x"$DESKTOP_HEIGHT"x16 > xvfb.log 2>&1 &

# TODO: detect X start rather than sleep
sleep 1

/usr/bin/startxfce4 >> xvfb.log 2>&1 &
/usr/bin/thunar >> xvfb.log 2>&1 &

# Convert the X framebuffer desktop into a video stream
# Convert the X framebuffer desktop into a video stream, but only stream the
# right hand side where the xzoom window is.
ffmpeg \
-f x11grab \
-s $DESKTOP_RES \
-r 12 \
-i :0.0 \
-i :0.0+$DESKTOP_WIDTH \
-vcodec mpeg2video \
-f mpegts \
$UDP_URI \
Expand All @@ -34,10 +44,10 @@ ffmpeg \
sleep 1

# Intercept STDIN (mouse and keypresses) and forward to the X framebuffer via xdotool
(./stdin_forward <&3 &) 3<&0
(./stdin_forward <&3 > interface.log 2>&1 &) 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
# Hiptext renders images and videos into text characters displayable in a terminal.
# It 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 \
Expand Down
71 changes: 61 additions & 10 deletions stdin_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ package main
import (
"fmt"
"strings"
"time"
"os"
"os/exec"
"math"
"github.com/nsf/termbox-go"
"github.com/tombh/termbox-go"
)

// Import the xzoom C code that creates an X window that zooms
// and pans the desktop.
// It's written in C because it borrows from the original xzoom
// binary: http://git.r-36.net/xzoom/
// NB: The following comments are parsed by `go build` ...

// #cgo LDFLAGS: -lXext -lX11 -lXt
// #include "xzoom/xzoom.h"
import "C"

var logfile = "./input.log"
var current string
var curev termbox.Event
var lastMouseButton string
Expand Down Expand Up @@ -43,7 +55,7 @@ func initialise() {
}

// 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
// the terminal as much as it can. And secondly it treats 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()
Expand All @@ -52,7 +64,7 @@ func calculateHipDimensions() {
ratio := desktopWidth / desktopHeight
bestHeight := min(th, (tw / ratio))
bestWidth := min(tw, (bestHeight * ratio))
// Not sure why the +1 and -1 are needed
// Not sure why the +1 and -1 are needed, but they are.
hipWidth = roundToInt(bestWidth) + 1
hipHeight = roundToInt(bestHeight / 2) - 1
log(fmt.Sprintf("Term dimensions: W: %d, H: %d", _tw, _th))
Expand All @@ -68,7 +80,7 @@ func min(a float32, b float32) float32 {

func log(msg string) {
msg = msg + "\n"
f, err := os.OpenFile("stdin.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
f, err := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
}
Expand All @@ -83,6 +95,9 @@ func log(msg string) {
// Issue an xdotool command to simulate mouse and keyboard input
func xdotool(args ...string) {
log(strings.Join(args, " "))
if args[0] == "noop" {
return
}
if err := exec.Command("xdotool", args...).Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand Down Expand Up @@ -120,19 +135,23 @@ func mouseButtonStr(k termbox.Key) []string {
return []string{"mouseup", lastMouseButton}
case termbox.MouseWheelUp:
if ctrlPressed() {
trackZoom("in")
C.magx++
C.magy++
return []string{"noop"}
}
return []string{"click", "4"}
case termbox.MouseWheelDown:
if ctrlPressed() {
trackZoom("out")
C.magx--
C.magy--
return []string{"noop"}
}
return []string{"click", "5"}
}
return []string{""}
}

// Auxillary data, whether the mouse was moving or a mod key like CTRL
// Auxillary data. Whether the mouse was moving or a mod key like CTRL
// is being pressed at the same time.
func modStr(m termbox.Modifier) string {
var out []string
Expand All @@ -157,9 +176,7 @@ 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 ctrlPressed() {
xdotool("keydown", "alt")
} else {
if !ctrlPressed() {
xdotool("keyup", "alt")
}

Expand Down Expand Up @@ -196,6 +213,8 @@ func setCurrentDesktopCoords() {
// For every zoom level, the terminal coords will be mapped differently onto the X desktop.
// TODO: support custom desktop sizes.
func trackZoom(direction string) {
xdotool("keydown", "alt")

if direction == "in" {
if zoomLevel <= maxZoom {
zoomLevel += zoomFactor
Expand Down Expand Up @@ -274,7 +293,37 @@ func parseInput() {
}
}

// a channel to tell it to stop
var stopchan = make(chan struct{})
// a channel to signal that it's stopped
var stoppedchan = make(chan struct{})

func xzoomBackground(){
go func(){ // work in background
// close the stoppedchan when this func
// exits
defer close(stoppedchan)
// TODO: do setup work
defer func(){
// TODO: do teardown work
}()
for {
select {
default:
C.xzoom_loop()
time.Sleep(40 * time.Millisecond) // 25fps
case <-stopchan:
// stop
return
}
}
}()
}

func main() {
C.xzoom_init()
xzoomBackground()

err := termbox.Init()
if err != nil {
panic(err)
Expand All @@ -300,6 +349,8 @@ mainloop:
current = fmt.Sprintf("%q", data)
// TODO: think of a different way to exit, 'q' will be needed for actual text input.
if current == `"q"` {
close(stopchan) // tell it to stop
<-stoppedchan // wait for it to have stopped
break mainloop
}

Expand Down
22 changes: 22 additions & 0 deletions xzoom/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
This package was maintained by tony mancill <[email protected]>, for a
while, and then picked up by Junichi Uekawa <[email protected]>. Since Sun, 15 Jun 2008 maintained by Anibal Avelar <[email protected]>.

This package was put together by Joey Hess <[email protected]>, using
sources from:
ftp://sunsite.unc.edu/pub/linux/libs/X/xzoom-0.3.tgz

Copyright: 1995-2008 Itai Nahshon

License:

This program is distributed with no warranty.

Source files for this program may be distributed freely.
Modifications to this file are okay as long as:
a. This copyright notice and comment are preserved and
left at the top of the file.
b. The man page is fixed to reflect the change.
c. The author of this change adds his name and change
description to the list of changes below.
Executable files may be distributed with sources, or with
exact location where the source code can be obtained.
2 changes: 2 additions & 0 deletions xzoom/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
all:
gcc xzoom_debug.c -lXext -lX11 -lXt -Wall -o xzoom_debug
71 changes: 71 additions & 0 deletions xzoom/scale.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* scale image from SRC to DST - parameterized by type T */

/* get pixel address of point (x,y) in image t */
#define getP(t,x,y) \
(T *) (&ximage[t]->data[(ximage[t]->xoffset+(x))*sizeof(T) + \
(y)*ximage[t]->bytes_per_line])

{
int i, j, k;

/* copy scaled lines from SRC to DST */
j = height[SRC] - 1;
do {
T *p1;
T *p2;
int p2step;
T *p1_save;

/* p1 point to begining of scanline j*magy in DST */
p1 = getP(DST, 0, j * magy);
p1_save = p1;
/* p2 point to begining of scanline j in SRC */
p2 = getP(SRC, 0, j);

i = width[SRC];
do {
T c = *p2++;
k = magx; do *p1++ = c; while (--k > 0);
} while (--i > 0);

/* draw vertical grid */
if (gridy && magx >= 2)
{
p1 = p1_save - 1;
i = magx;
k = width[SRC];
do {
p1 += i;
*p1 ^= ~((T)0);
} while (--k > 0);
}

/* duplicate that line as needed */
if (magy > 1)
{
/* p1 point to begining of scanline j*magy in DST */
p1 = p1_save;
/* p2 points to begining of next line */
p2 = p1;
p2step = ximage[DST]->bytes_per_line / sizeof(T);

i = width[DST] * sizeof(T);
k = magy - 1;
do {
p2 += p2step;
memcpy(p2, p1, i);
} while (--k > 0);

/* draw horizontal grid */
if (gridx && magy >= 2)
{
k = width[DST];
do {
*p2++ ^= ~((T)0);
} while (--k > 0);
}
}
} while (--j >= 0);
}

#undef getP
Loading

0 comments on commit 932a4b3

Please sign in to comment.