Skip to content

Commit

Permalink
Keyboard mode. For terminals with no/bad mouse support.
Browse files Browse the repository at this point in the history
Closes #5
  • Loading branch information
tombh committed Jun 25, 2016
1 parent 01a551c commit 1453c01
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 10 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ type, scroll, use arrow keys and drag things around. However there are still som
paste. The main difference from a normal desktop is that you can zoom and pan the desktop by using `CTRL + mousewheel` and
`CTRL + drag`. This is very handy as it's hard to see what's what when you're zoomed right out.

###Keyboard Mode
If your terminal doesn't support mouse input then you can switch in and out of keyboard mode with `CTRL+ALT+M`.
This will give you the following shortcuts:

`u` mouse up
`n` mouse down
`h` mouse left
`k` mouse right

`SHIFT+u` pan up
`SHIFT+n` pan down
`SHIFT+h` pan left
`SHIFT+k` pan right

`CTRL+u` zoom in
`CTRL+n` zoom out

`j` left-click
`r` right-click
`t` middle-click

###Adding new applications
Currently, only Firefox is installed on this extremely minimal Alpine Linux distro. However you can add new packages
with [apk](https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management). Example;
```
Expand Down
123 changes: 113 additions & 10 deletions interfacer/interfacer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ var panNeedsSetup bool
var panStartingX float32
var panStartingY float32

// Keyboard mode is for interacting with the desktop with the keyboard
// instead of the mouse.
var keyboardMode = false
var kbCursorX int
var kbCursorY int
var char string

var debugMode = parseENVVar("DEBUG") == 1

func initialise() {
Expand Down Expand Up @@ -78,6 +85,8 @@ func setupDimensions() {
envDesktopWidth = parseENVVar("DESKTOP_WIDTH")
envDesktopHeight = parseENVVar("DESKTOP_HEIGHT")
}
kbCursorX = int(hipWidth / 2)
kbCursorY = int(hipHeight / 2)
C.desktop_width = C.int(envDesktopWidth)
C.width[C.SRC] = C.desktop_width
C.width[C.DST] = C.desktop_width
Expand Down Expand Up @@ -110,8 +119,25 @@ func setupLogging() {
}
}

func printXY(x, y int, s string) {
// Render text. This doesn't play very nice with hiptext, it can cause
// random tearing and flickering :( I suspect there are ways to overcome
// this for rendering outside of hiptext's area. But to render over hiptext
// it will probably mean patching hiptext.
func printXY(x, y int, s string, force bool) {
for _, r := range s {

// It seems termbox keeps an internal representation of the TTY
// and won't try updating the cell unless it thinks it has changed.
// This is only relevant because we're in competition with the
// rapid screen updates from hiptext. This means that unless we
// "remove -> flush -> redraw" everything hiptext removes whatever
// we render.
if force {
// 32 is the space character
termbox.SetCell(x, y, 32, termbox.ColorWhite, termbox.ColorDefault)
termbox.Flush()
}

termbox.SetCell(x, y, r, termbox.ColorWhite, termbox.ColorDefault)
x++
}
Expand All @@ -131,7 +157,7 @@ func log(msg string) {
}

if debugMode {
printXY(0, 0, msg)
printXY(0, hipHeight - 1, msg, true)
}
}

Expand Down Expand Up @@ -190,8 +216,8 @@ func mouseMotion() bool {
}

// Convert Termbox symbols to xdotool arguments
func mouseButtonStr(k termbox.Key) []string {
switch k {
func mouseButtonStr() []string {
switch curev.Key {
case termbox.MouseLeft:
lastMouseButton = "1"
return []string{"mousedown", lastMouseButton}
Expand Down Expand Up @@ -296,7 +322,9 @@ func modStr(m termbox.Modifier) string {
}

func isPanning() bool {
return ctrlPressed() && mouseMotion() && lastMouseButton == "1"
mousePanning := ctrlPressed() && mouseMotion() && lastMouseButton == "1"
kbPanning := (char == "U" || char == "K" || char == "N" || char == "H") && keyboardMode
return mousePanning || kbPanning
}

func mouseEvent() {
Expand All @@ -317,8 +345,8 @@ func mouseEvent() {
// Pressing of CTRL indicates that the user is panning or zooming, so there is no need to send
// button presses.
// TODO: What about CTRL+leftbutton to open new tab!?
if !ctrlPressed() {
xdotool(mouseButtonStr(curev.Key)...)
if !keyboardMode && !ctrlPressed() {
xdotool(mouseButtonStr()...)
}
}
}
Expand All @@ -338,8 +366,8 @@ func pan() {
func setCurrentDesktopCoords() {
hipWidthFloat := float32(hipWidth)
hipHeightFloat := float32(hipHeight)
eventX := float32(curev.MouseX)
eventY := float32(curev.MouseY)
eventX := float32(getCursorX())
eventY := float32(getCursorY())
width := float32(C.width[C.SRC])
height := float32(C.height[C.SRC])
xOffset := float32(C.xgrab)
Expand All @@ -354,6 +382,20 @@ func setCurrentDesktopCoords() {
hipHeightFloat, hipWidthFloat, desktopXFloat, desktopYFloat, C.magnification))
}

func getCursorX() int {
if keyboardMode {
return kbCursorX
}
return curev.MouseX
}

func getCursorY() int {
if keyboardMode {
return kbCursorY
}
return curev.MouseY
}

// Convert a keyboard event into an xdotool command
// See: http://wiki.linuxquestions.org/wiki/List_of_Keysyms_Recognised_by_Xmodmap
func keyEvent() {
Expand All @@ -364,6 +406,7 @@ func keyEvent() {

if curev.Key == 0 {
key = fmt.Sprintf("%c", curev.Ch)
char = key
command = "type"
} else {
command = "key"
Expand All @@ -377,9 +420,63 @@ func keyEvent() {
return
}

if (curev.Key == termbox.KeyCtrlM) && altPressed() {
keyboardMode = !keyboardMode
}

if keyboardMode {
mouseEvent()
handleKeyboardMode(key)
renderCursor()
printXY(0, hipHeight, "KB ON ", true)
return
}

printXY(0, hipHeight, "KB OFF", true)

xdotool(command, key)
}

func handleKeyboardMode(key string) {
switch key {
case "u", "U":
kbCursorY--
if kbCursorY < 0 {
kbCursorY = 0
}
case "n", "N":
kbCursorY++
if kbCursorY > hipHeight {
kbCursorY = hipHeight
}
case "h", "H":
kbCursorX--
if kbCursorX < 0 {
kbCursorX = 0
}
case "k", "K":
kbCursorX++
if kbCursorX > hipWidth {
kbCursorX = hipWidth
}
case "ctrl+u":
zoom("in")
case "ctrl+n":
zoom("out")
case "j":
xdotool("click", "1")
case "r":
xdotool("click", "2")
case "t":
xdotool("click", "3")
}

}

func renderCursor() {
printXY(kbCursorX, kbCursorY, "+", false)
}

func getSpecialKeyPress() string {
var key string
switch curev.Key {
Expand Down Expand Up @@ -433,8 +530,12 @@ func getSpecialKeyPress() string {
key = "Left"
case termbox.KeyArrowRight:
key = "Right"
case termbox.KeyCtrlU:
key = "ctrl+u"
case termbox.KeyCtrlL:
key = "ctrl+l"
case termbox.KeyCtrlN:
key = "ctrl+n"
}
return key
}
Expand All @@ -447,7 +548,7 @@ func parseInput() {
log(
fmt.Sprintf(
"EventMouse: x: %d, y: %d, b: %s, mod: %s",
curev.MouseX, curev.MouseY, mouseButtonStr(curev.Key), modStr(curev.Mod)))
getCursorX(), getCursorY(), mouseButtonStr(), modStr(curev.Mod)))
mouseEvent()
case termbox.EventNone:
log("EventNone")
Expand Down Expand Up @@ -510,6 +611,7 @@ func mainLoop() {
}
curev = ev
if needToExit() {
log("Exit requested by user")
return
}
copy(data, data[curev.N:])
Expand All @@ -520,6 +622,7 @@ func mainLoop() {
}
parseInput()
}

}

func main() {
Expand Down

0 comments on commit 1453c01

Please sign in to comment.