cmd/vncviewer/main.go (284 lines of code) (raw):
package main
import (
"fmt"
"image"
"image/draw"
_ "image/jpeg"
"log"
"os"
"path"
"sync"
"time"
"github.com/google/gxui"
"github.com/google/gxui/drivers/gl"
"github.com/google/gxui/samples/flags"
"github.com/openai/go-vncdriver/gymvnc"
"github.com/openai/go-vncdriver/vncclient"
"github.com/pixiv/go-libjpeg/rgb"
)
func toImage(s *gymvnc.Screen) image.Image {
img := rgb.NewImage(image.Rectangle{
Max: image.Point{
X: int(s.Width),
Y: int(s.Height),
},
})
for i, c := range s.Data {
img.Pix[3*i] = uint8(c.R)
img.Pix[3*i+1] = uint8(c.G)
img.Pix[3*i+2] = uint8(c.B)
}
return img
}
func mapKey(in gxui.KeyboardKey) (out uint32) {
// see keysymdef.h
switch in {
case gxui.KeyUnknown:
return 0
case gxui.KeySpace:
return 0xff80
case gxui.KeyApostrophe:
return 0x0027
case gxui.KeyComma:
return 0x002c
case gxui.KeyMinus:
return 0x002d
case gxui.KeyPeriod:
return 0x002e
case gxui.KeySlash:
return 0x002f
case gxui.Key0:
return 0x0030
case gxui.Key1:
return 0x0031
case gxui.Key2:
return 0x0032
case gxui.Key3:
return 0x0033
case gxui.Key4:
return 0x0034
case gxui.Key5:
return 0x0035
case gxui.Key6:
return 0x0036
case gxui.Key7:
return 0x0037
case gxui.Key8:
return 0x0038
case gxui.Key9:
return 0x0039
case gxui.KeySemicolon:
return 0x003b
case gxui.KeyEqual:
return 0x003d
case gxui.KeyA:
return 0x0061
case gxui.KeyB:
return 0x0062
case gxui.KeyC:
return 0x0063
case gxui.KeyD:
return 0x0064
case gxui.KeyE:
return 0x0065
case gxui.KeyF:
return 0x0066
case gxui.KeyG:
return 0x0067
case gxui.KeyH:
return 0x0068
case gxui.KeyI:
return 0x0069
case gxui.KeyJ:
return 0x006a
case gxui.KeyK:
return 0x006b
case gxui.KeyL:
return 0x006c
case gxui.KeyM:
return 0x006d
case gxui.KeyN:
return 0x006e
case gxui.KeyO:
return 0x006f
case gxui.KeyP:
return 0x0070
case gxui.KeyQ:
return 0x0071
case gxui.KeyR:
return 0x0072
case gxui.KeyS:
return 0x0073
case gxui.KeyT:
return 0x0074
case gxui.KeyU:
return 0x0075
case gxui.KeyV:
return 0x0076
case gxui.KeyW:
return 0x0077
case gxui.KeyX:
return 0x0078
case gxui.KeyY:
return 0x0079
case gxui.KeyZ:
return 0x007a
case gxui.KeyLeftBracket:
return 0x005b
case gxui.KeyBackslash:
return 0x005c
case gxui.KeyRightBracket:
return 0x005d
case gxui.KeyGraveAccent:
return 0x0060
case gxui.KeyWorld1:
case gxui.KeyWorld2:
case gxui.KeyEscape:
return 0xff1b
case gxui.KeyEnter:
return 0xff8d
case gxui.KeyTab:
return 0xff89
case gxui.KeyBackspace:
return 0xff08
case gxui.KeyInsert:
case gxui.KeyDelete:
case gxui.KeyRight:
return 0xff53
case gxui.KeyLeft:
return 0xff51
case gxui.KeyDown:
return 0xff54
case gxui.KeyUp:
return 0xff52
case gxui.KeyPageUp:
case gxui.KeyPageDown:
case gxui.KeyHome:
case gxui.KeyEnd:
case gxui.KeyCapsLock:
case gxui.KeyScrollLock:
case gxui.KeyNumLock:
case gxui.KeyPrintScreen:
case gxui.KeyPause:
case gxui.KeyF1:
case gxui.KeyF2:
case gxui.KeyF3:
case gxui.KeyF4:
case gxui.KeyF5:
case gxui.KeyF6:
case gxui.KeyF7:
case gxui.KeyF8:
case gxui.KeyF9:
case gxui.KeyF10:
case gxui.KeyF11:
case gxui.KeyF12:
case gxui.KeyKp0:
case gxui.KeyKp1:
case gxui.KeyKp2:
case gxui.KeyKp3:
case gxui.KeyKp4:
case gxui.KeyKp5:
case gxui.KeyKp6:
case gxui.KeyKp7:
case gxui.KeyKp8:
case gxui.KeyKp9:
case gxui.KeyKpDecimal:
case gxui.KeyKpDivide:
case gxui.KeyKpMultiply:
case gxui.KeyKpSubtract:
case gxui.KeyKpAdd:
case gxui.KeyKpEnter:
case gxui.KeyKpEqual:
case gxui.KeyLeftShift:
case gxui.KeyLeftControl:
case gxui.KeyLeftAlt:
case gxui.KeyLeftSuper:
case gxui.KeyRightShift:
case gxui.KeyRightControl:
case gxui.KeyRightAlt:
case gxui.KeyRightSuper:
case gxui.KeyMenu:
case gxui.KeyLast:
}
return 0
}
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %v <vnc address>", path.Base(os.Args[0]))
}
fmt.Println("creating session")
s := gymvnc.NewVNCSession("", gymvnc.VNCSessionConfig{
Address: os.Args[1],
Password: "openai",
Encoding: "tight",
})
gl.StartDriver(func(driver gxui.Driver) {
theme := flags.CreateTheme(driver)
img := theme.CreateImage()
window := theme.CreateWindow(1000, 1000, "go-vncdriver vnc viewer")
window.AddChild(img)
window.OnClose(driver.Terminate)
var events struct {
sync.Mutex
slice []gymvnc.VNCEvent
}
onEvent := func(e gymvnc.VNCEvent) {
events.Lock()
defer events.Unlock()
events.slice = append(events.slice, e)
}
window.OnKeyDown(func(e gxui.KeyboardEvent) {
onEvent(gymvnc.KeyEvent{
Keysym: mapKey(e.Key),
Down: true,
})
})
window.OnKeyUp(func(e gxui.KeyboardEvent) {
onEvent(gymvnc.KeyEvent{
Keysym: mapKey(e.Key),
Down: false,
})
})
handleMouseEvent := func(e gxui.MouseEvent) {
var btnMask vncclient.ButtonMask
if e.State.IsDown(gxui.MouseButtonLeft) {
btnMask |= vncclient.ButtonLeft
}
if e.State.IsDown(gxui.MouseButtonMiddle) {
btnMask |= vncclient.ButtonMiddle
}
if e.State.IsDown(gxui.MouseButtonRight) {
btnMask |= vncclient.ButtonRight
}
onEvent(gymvnc.PointerEvent{
X: uint16(e.Point.X),
Y: uint16(e.Point.Y),
Mask: btnMask,
})
}
img.OnMouseDown(handleMouseEvent)
img.OnMouseUp(handleMouseEvent)
img.OnMouseMove(handleMouseEvent)
go func() {
for {
fmt.Println("tick")
time.Sleep(time.Second / 60)
events.Lock()
screen, updates, err := s.Step(events.slice)
events.slice = events.slice[:0]
events.Unlock()
if err != nil {
log.Fatal(err)
}
if len(updates) == 0 {
continue
}
source := toImage(screen)
rgba := image.NewRGBA(source.Bounds())
draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src)
driver.Call(func() {
img.SetTexture(driver.CreateTexture(rgba, 1))
})
}
}()
})
}