Showing preview only (364K chars total). Download the full file or copy to clipboard to get everything.
Repository: vprix/vncproxy
Branch: main
Commit: 78d96284a539
Files: 149
Total size: 292.6 KB
Directory structure:
gitextract_0eg_p_n1/
├── .gitignore
├── LICENSE
├── README.md
├── bin/
│ └── .keep
├── build
├── canvas/
│ ├── canvas.go
│ └── rgb_image.go
├── cmd/
│ ├── player/
│ │ ├── main.go
│ │ ├── tcpServer.go
│ │ └── wsServer.go
│ ├── proxy/
│ │ ├── main.go
│ │ ├── tcpServer.go
│ │ └── wsServer.go
│ ├── recorder/
│ │ ├── main.go
│ │ └── recorder.go
│ ├── screenshot/
│ │ └── main.go
│ └── video/
│ └── main.go
├── docs/
│ ├── .keep
│ ├── .nojekyll
│ ├── README.md
│ ├── changelog.md
│ ├── index.html
│ ├── overview.md
│ ├── player/
│ │ ├── .keep
│ │ └── README.md
│ ├── proxy/
│ │ ├── .keep
│ │ └── README.md
│ ├── questions.md
│ ├── recorder/
│ │ ├── .keep
│ │ └── README.md
│ ├── rfc6143/
│ │ ├── GLOSSORY.md
│ │ ├── README.md
│ │ ├── handshake/
│ │ │ ├── README.md
│ │ │ ├── initial.md
│ │ │ ├── protocol-version.md
│ │ │ └── security-type.md
│ │ └── transfer/
│ │ ├── README.md
│ │ ├── display.md
│ │ ├── encoding/
│ │ │ ├── README.md
│ │ │ ├── copy-rect.md
│ │ │ ├── raw.md
│ │ │ ├── rise-and-run-length.md
│ │ │ ├── set-encoding.md
│ │ │ ├── tight-png.md
│ │ │ ├── tiled-run-length.md
│ │ │ └── zlib-run-length.md
│ │ ├── input/
│ │ │ ├── README.md
│ │ │ ├── clipboard.md
│ │ │ ├── keyboard.md
│ │ │ └── mouse.md
│ │ ├── pixel-format.md
│ │ └── set-color-map.md
│ ├── screenshot/
│ │ ├── .keep
│ │ └── README.md
│ ├── summary.md
│ └── video/
│ ├── .keep
│ └── README.md
├── encodings/
│ ├── default_encoding.go
│ ├── encoding.go
│ ├── encoding_copyrect.go
│ ├── encoding_corre.go
│ ├── encoding_h264.go
│ ├── encoding_hextile.go
│ ├── encoding_jpeg.go
│ ├── encoding_jrle.go
│ ├── encoding_raw.go
│ ├── encoding_rre.go
│ ├── encoding_tight.go
│ ├── encoding_tightpng.go
│ ├── encoding_trle.go
│ ├── encoding_zlib.go
│ ├── encoding_zrle.go
│ ├── pseudo_cursor.go
│ ├── pseudo_cursor_with_alpha.go
│ ├── pseudo_desktop_name.go
│ ├── pseudo_desktop_size.go
│ ├── pseudo_extended_desktop_size.go
│ ├── pseudo_fence.go
│ ├── pseudo_last_rect.go
│ ├── pseudo_led_state.go
│ ├── pseudo_pointer_pos.go
│ └── pseudo_x_cursor.go
├── go.mod
├── handler/
│ ├── ClientClientInitHandler.go
│ ├── ClientMessageHandler.go
│ ├── ClientSecurityHandler.go
│ ├── ClientServerInitHandler.go
│ ├── ClientVersionHandler.go
│ ├── ServerClientInitHandler.go
│ ├── ServerMessageHandler.go
│ ├── ServerSecurityHandler.go
│ ├── ServerServerInitHandler.go
│ └── ServerVersionHandler.go
├── internal/
│ ├── dbuffer/
│ │ ├── buffer.go
│ │ └── pool.go
│ └── syncPool/
│ └── sync_pool.go
├── messages/
│ ├── clientClientCutText.go
│ ├── clientClientFence.go
│ ├── clientEnableContinuousUpdates.go
│ ├── clientFramebufferUpdateRequest.go
│ ├── clientKeyEvent.go
│ ├── clientPointerEvent.go
│ ├── clientQEMUExtKeyEvent.go
│ ├── clientSetDesktopSize.go
│ ├── clientSetEncodings.go
│ ├── clientSetPixelFormat.go
│ ├── default_message.go
│ ├── serverBell.go
│ ├── serverEndOfContinuousUpdates.go
│ ├── serverFramebufferUpdate.go
│ ├── serverInit.go
│ ├── serverServerCutText.go
│ ├── serverServerFence.go
│ └── serverSetColorMapEntries.go
├── rfb/
│ ├── color_map.go
│ ├── desktop.go
│ ├── encoding.go
│ ├── encodingtype.go
│ ├── encodingtype_string.go
│ ├── handler.go
│ ├── keys.go
│ ├── keys_string.go
│ ├── message.go
│ ├── message_type_client_string.go
│ ├── message_type_server_string.go
│ ├── messagetype.go
│ ├── options.go
│ ├── pixel_format.go
│ ├── rectangle.go
│ ├── security.go
│ ├── securitysubtype_string.go
│ ├── securitytype_string.go
│ ├── session.go
│ ├── session_string.go
│ └── target_config.go
├── security/
│ ├── security_none.go
│ ├── security_tight.go
│ ├── security_vencryptplain.go
│ └── security_vnc.go
├── session/
│ ├── canvas.go
│ ├── client.go
│ ├── player.go
│ ├── recorder.go
│ └── server.go
└── vnc/
├── player.go
├── proxy.go
├── recorder.go
├── screenshot.go
└── video.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# VncProxy [](https://github.com/vprix/vncproxy/releases) [](http://goreportcard.com/report/vprix/vncproxy) [](https://github.com/vprix/vncproxy/issues?q=is%3Aopen+is%3Aissue) [](https://github.com/vprix/vncproxy/issues?q=is%3Aissue+is%3Aclosed) [](http://godoc.org/github.com/vprix/vncproxy) [](https://github.com/vprix/vncproxy/tree/main/examples)
## VncProxy简介
`VncProxy` 是使用`Golang`实现的`Vnc`远程桌面代理组件,完全解析`rfb`协议,支持远程桌面代理,rbs文件录屏,rbs文件回放,截图,录制视频.
* 全协议支持的vnc proxy。
* 支持Tcp代理
* 支持Websocket代理
* 屏幕录像,保存为`RBS`文件
* 重播服务器,支持vnc客户端链接,播放`RBS`文件。
* 支持实时录制视频
* 支持通过`RBS`文件录制视频。
* 支持屏幕截图
## 支持的编码格式
- [x] Raw
- [x] CopyRect
- [x] CoRRE
- [x] rre
- [x] Hextile
- [x] Tight
- [x] TightPng
- [x] ZLib
- [x] Zrle
- [x] CursorPseudo
- [x] CursorWithAlphaPseudo
- [x] DesktopNamePseudo
- [x] DesktopSizePseudo
- [x] ExtendedDesktopSizePseudo
- [x] LedStatePseudo
- [x] CursorPosPseudo
- [x] XCursorPseudo
- [ ] jpeg
- [ ] jrle
- [ ] trle
## 组件说明
### Proxy
1. 启动`server`接受`vnc viewer`的链接.
2. 启动`client`连接到指定的`vnc server`.
3. 为`vnc viewer`和`vnc server`之间建立起消息转发通道。
4. 因为`rfb`协议被完全解析,可以针对通信的消息进行转发处理,产生了后续的功能。
### Recorder
1. 启动`client`连接到指定的`vnc server`.
2. 发送帧缓冲区更新消息`FramebufferUpdateRequest`到`vnc server`。
3. 处理`vnc server`回复的界面更新消息`FramebufferUpdate`。
4. 把这一过程以`rbs`文件格式记录下来。
### Player
1. 启动`server`接受`vnc viewer`的链接.
2. 读取`rbs`文件,并按格式生成`FramebufferUpdate`消息发送给`vnc viewer`。
3. `vnc viewer`的界面就会回放动作。
### Video
1. 支持`Proxy`,`Recorder`和`rbs`文件作为输入源。
2. 把`FramebufferUpdate`消息转换为视频文件。
### Screenshot
1. 支持`Proxy`,`Recorder`和`rbs`文件作为输入源。
2. 把当前的界面视图转换为图片文件。
## 使用说明
`vncProxy`项目有多种应用场景。
可以作为单独的应用程序编译,也可以作为库被其他应用程序引用。
接下来,分别介绍各种场景下的使用方式。
### 编译
```shell
# 使用方式:
# build.sh [-s app_name] [-v version] [-g go_bin]
# app_name 需要编译的应用名称
# 选项: proxy,player,recorder,video,screenshot.
# 默认是所有应用,多个应用可以逗号分割.
# version 编译后的文件版本号,默认为当前git的commit id.
# go_bin 使用的golang程序
# 编译所有应用
$ ./build
# 编译proxy
$ ./build -s proxy -v v0.1.0
# 编译player,recorder
$ ./build -s player,recorder -v v0.1.0
```
编译后的二进制文件在`./bin/`目录
### Proxy
代码路径在`./cmd/proxy`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./proxy --help
# 查看版本信息
$ ./proxy version
```
#### 启动tcp服务
```shell
# 启动tcp server接受vnc viewer的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# tcpHost 本地监听的地址
# tcpPort 本地监听的端口
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start tcpServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--tcpHost=0.0.0.0 \
--tcpPort=8989 \
--proxyPassword=12345612 \
--debug
```
#### 启动WebSocket服务
```shell
# 启动ws server接受novnc的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# wsHost 本地监听的地址
# wsPort 本地监听的端口
# wsPath websocket连接的地址
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start wsServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--wsHost=0.0.0.0 \
--wsPort=8988 \
--wsPath=/websockify \
--proxyPassword=12345612 \
--debug
```
### Recorder
代码路径在`./cmd/recorder`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./recorder --help
# 查看版本信息
$ ./recorder version
```
#### 启动Recorder服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# debug 使用debug模式启动服务
$ ./recorder start --rbsFile=/path/to/foo.rbs
--vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--debug
```
### Player
代码路径在`./cmd/player`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./player --help
# 查看版本信息
$ ./player version
```
#### 启动Player Tcp服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# tcpHost 本地监听的tcp协议地址 默认0.0.0.0
# tcpPort 本地监听的tcp协议端口 默认8989
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start tcpServer --rbsFile=/path/to/foo.rbs
--tcpHost=0.0.0.0
--tcpPort=8989
--proxyPassword=12345612
--debug
```
#### 启动Player WS服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# wsHost 启动websocket服务的本地地址 默认 0.0.0.0
# wsPort 启动websocket服务的本地端口 默认8988
# wsPath 启动websocket服务的url path 默认'/'
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start wsServer --rbsFile=/path/to/foo.rbs
--wsHost=0.0.0.0
--wsPort=8989
--wsPath=/
--proxyPassword=12345612
--debug
```
### Screenshot
代码路径在`./cmd/screenshot`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./screenshot --help
# 查看版本信息
$ ./screenshot version
```
#### 启动Screenshot 获取vnc服务器的屏幕截图
```shell
# imageFile 要生成的截图地址,暂时只支持jpeg格式(必填)
# vncHost 要连接的vnc服务端地址(必填)
# vncPort 要连接的vnc服务端端口(必填)
# vncPassword 要连接的vnc服务端密码,不传则使用auth none
$ ./screenshot --imageFile=./screen.jpeg --vncHost=127.0.0.1 --vncPort=5900 --vncPassword=12345612
```
## 项目参考
本项目参考了以下项目完成。
* [vncproxy](https://github.com/amitbet/vncproxy)
* [vnc2video](https://github.com/amitbet/vnc2video)
* [rfbproto](https://github.com/rfbproto/rfbproto)
## 交流
我在做这个项目的过程中碰到了很多问题,查遍了互联网,缺少中文资料,大部分信息都是雷同的。
所以我萌生了开源的想法,帮助更多有需要的人。
我建立了一个可供交流的微信群,以便大家在使用的过程中碰到疑问,能有解答的地方。
当然,如果你对vnc有兴趣,也可以加我微信,多多交流。
欢迎各位贡献代码。

================================================
FILE: bin/.keep
================================================
================================================
FILE: build
================================================
#!/usr/bin/env bash
set -e
export GOPROXY=https://proxy.golang.com.cn,direct
help() {
echo "使用方式:"
echo " build.sh [-s app_name] [-v version] [-g go_bin]"
echo "参数详解:"
echo " app_name 需要编译的应用名称,选项: proxy,player,recorder,video,screenshot.默认是所有应用,多个应用可以逗号分割"
echo " version 编译后的文件版本号,默认为当前git的commit id"
echo " go_bin 使用的golang程序"
exit
}
getOutFile(){
build_name=$1
output_dir=$2
output_file="${output_dir}"/${build_name}
}
while getopts 's:v:g:h' OPT; do
case $OPT in
s) app_names="$OPTARG";;
v) build_version="$OPTARG";;
g) goBin="$OPTARG";;
h) help;;
?) help;;
esac
done
## 获取当前环境
## shellcheck disable=SC2046
cd $(dirname "$0")/ || exit 1;
# 如果go bin 不存在,则去环境变量中查找
if [ ! -x "$goBin" ]; then
goBin=$(which go)
fi
if [ ! -x "$goBin" ]; then
echo "No goBin found."
exit 2
fi
# 编译时间
build_date=$(date +"%Y-%m-%d %H:%M:%S")
# 编译时候当前git的commit id
build_git=$(git rev-parse --short HEAD)
# 编译的golang版本
go_version=$(${goBin} version)
#编译版本
if [ -z "$build_version" ]; then
build_version="$build_git"
fi
if [ -z "$app_names" ]; then
app_names="proxy,player,recorder,video,screenshot"
fi
echo "start to build project $app_names" "$build_date"
# shellcheck disable=SC2154
echo "$go_version"
pwd
root_dir="$(pwd)"
ldflags=()
# 链接时设置变量值
ldflags+=("-X" "\"main.BuildVersion=${build_version}\"")
ldflags+=("-X" "\"github.com/osgochina/dmicro/easyservice.BuildVersion=${build_version}\"")
ldflags+=("-X" "\"github.com/osgochina/dmicro/easyservice.BuildGoVersion=${go_version}\"")
ldflags+=("-X" "\"github.com/osgochina/dmicro/easyservice.BuildGitCommitId=${build_git}\"")
ldflags+=("-X" "\"github.com/osgochina/dmicro/easyservice.BuildTime=${build_date}\"")
for app_name in $(echo $app_names | sed "s/,/ /g")
do
getOutFile $app_name "$root_dir/bin"
cd "$root_dir/cmd/$app_name/"
echo "进入[$(pwd)]目录"
${goBin} build -v -ldflags "${ldflags[*]}" -o "${output_file}" || exit 1
echo "build $app_name done."
done
================================================
FILE: canvas/canvas.go
================================================
package canvas
import (
"encoding/binary"
"errors"
"fmt"
"github.com/vprix/vncproxy/rfb"
"image"
"image/color"
"image/draw"
"io"
)
const (
BlockWidth = 16
BlockHeight = 16
)
type VncCanvas struct {
draw.Image
imageBuffs [2]draw.Image
Cursor draw.Image
CursorMask [][]bool
CursorBackup draw.Image
CursorOffset *image.Point
CursorLocation *image.Point
DrawCursor bool
Changed map[string]bool
}
func NewVncCanvas(width, height int) *VncCanvas {
writeImg := NewRGBImage(image.Rect(0, 0, width, height))
canvas := &VncCanvas{
Image: writeImg,
}
return canvas
}
// Read 从链接中读取数据
func (that *VncCanvas) Read(buf []byte) (int, error) {
return 0, nil
}
// Write 写入数据到链接
func (that *VncCanvas) Write(buf []byte) (int, error) {
return 0, nil
}
// Close 关闭会话
func (that *VncCanvas) Close() error {
return nil
}
func (that *VncCanvas) SetChanged(rect *rfb.Rectangle) {
if that.Changed == nil {
that.Changed = make(map[string]bool)
}
for x := int(rect.X) / BlockWidth; x*BlockWidth < int(rect.X+rect.Width); x++ {
for y := int(rect.Y) / BlockHeight; y*BlockHeight < int(rect.Y+rect.Height); y++ {
key := fmt.Sprintf("%d,%d", x, y)
//fmt.Println("setting block: ", key)
that.Changed[key] = true
}
}
}
func (that *VncCanvas) Reset(rect *rfb.Rectangle) {
that.Changed = nil
}
func (that *VncCanvas) RemoveCursor() image.Image {
if that.Cursor == nil || that.CursorLocation == nil {
return that.Image
}
if !that.DrawCursor {
return that.Image
}
rect := that.Cursor.Bounds()
loc := that.CursorLocation
img := that.Image
for y := rect.Min.Y; y < int(rect.Max.Y); y++ {
for x := rect.Min.X; x < int(rect.Max.X); x++ {
// offset := y*int(rect.Width) + x
// if bitmask[y*int(scanLine)+x/8]&(1<<uint(7-x%8)) > 0 {
col := that.CursorBackup.At(x, y)
//mask := c.CursorMask.At(x, y).(color.RGBA)
mask := that.CursorMask[x][y]
//logger.Info("Drawing Cursor: ", x, y, col, mask)
if mask {
//logger.Info("Drawing Cursor for real: ", x, y, col)
img.Set(x+loc.X-that.CursorOffset.X, y+loc.Y-that.CursorOffset.Y, col)
}
// //logger.Tracef("CursorPseudoEncoding.Read: setting pixel: (%d,%d) %v", x+int(rect.X), y+int(rect.Y), colors[offset])
// }
}
}
return img
}
func (that *VncCanvas) PaintCursor() image.Image {
if that.Cursor == nil || that.CursorLocation == nil {
return that.Image
}
if !that.DrawCursor {
return that.Image
}
rect := that.Cursor.Bounds()
if that.CursorBackup == nil {
that.CursorBackup = image.NewRGBA(that.Cursor.Bounds())
}
loc := that.CursorLocation
img := that.Image
for y := rect.Min.Y; y < int(rect.Max.Y); y++ {
for x := rect.Min.X; x < int(rect.Max.X); x++ {
// offset := y*int(rect.Width) + x
// if bitmask[y*int(scanLine)+x/8]&(1<<uint(7-x%8)) > 0 {
col := that.Cursor.At(x, y)
//mask := c.CursorMask.At(x, y).(RGBColor)
mask := that.CursorMask[x][y]
backup := that.Image.At(x+loc.X-that.CursorOffset.X, y+loc.Y-that.CursorOffset.Y)
//c.CursorBackup.Set(x, y, backup)
//backup the previous data at this point
//logger.Info("Drawing Cursor: ", x, y, col, mask)
if mask {
that.CursorBackup.Set(x, y, backup)
//logger.Info("Drawing Cursor for real: ", x, y, col)
img.Set(x+loc.X-that.CursorOffset.X, y+loc.Y-that.CursorOffset.Y, col)
}
// //logger.Tracef("CursorPseudoEncoding.Read: setting pixel: (%d,%d) %v", x+int(rect.X), y+int(rect.Y), colors[offset])
// }
}
}
return img
}
// FillRect 为指定的矩形区域填充颜色
func (that *VncCanvas) FillRect(rect *image.Rectangle, c color.Color) {
for x := rect.Min.X; x < rect.Max.X; x++ {
for y := rect.Min.Y; y < rect.Max.Y; y++ {
that.Set(x, y, c)
}
}
}
// ReadColor Read unmarshal color from conn
func (that *VncCanvas) ReadColor(c io.Reader, pf *rfb.PixelFormat) (*color.RGBA, error) {
if pf.TrueColor == 0 {
return nil, errors.New("support for non true color formats was not implemented")
}
order := pf.Order()
var pixel uint32
switch pf.BPP {
case 8:
var px uint8
if err := binary.Read(c, order, &px); err != nil {
return nil, err
}
pixel = uint32(px)
case 16:
var px uint16
if err := binary.Read(c, order, &px); err != nil {
return nil, err
}
pixel = uint32(px)
case 32:
var px uint32
if err := binary.Read(c, order, &px); err != nil {
return nil, err
}
pixel = uint32(px)
}
rgb := color.RGBA{
R: uint8((pixel >> pf.RedShift) & uint32(pf.RedMax)),
G: uint8((pixel >> pf.GreenShift) & uint32(pf.GreenMax)),
B: uint8((pixel >> pf.BlueShift) & uint32(pf.BlueMax)),
A: 1,
}
return &rgb, nil
}
func (that *VncCanvas) DecodeRaw(reader io.Reader, pf *rfb.PixelFormat, rect *rfb.Rectangle) error {
for y := 0; y < int(rect.Height); y++ {
for x := 0; x < int(rect.Width); x++ {
col, err := that.ReadColor(reader, pf)
if err != nil {
return err
}
that.Set(int(rect.X)+x, int(rect.Y)+y, col)
}
}
return nil
}
func MakeRect(x, y, width, height int) image.Rectangle {
return image.Rectangle{Min: image.Point{X: x, Y: y}, Max: image.Point{X: x + width, Y: y + height}}
}
func MakeRectFromVncRect(rect *rfb.Rectangle) image.Rectangle {
return MakeRect(int(rect.X), int(rect.Y), int(rect.Width), int(rect.Height))
}
================================================
FILE: canvas/rgb_image.go
================================================
package canvas
import (
"image"
"image/color"
)
type RGBColor struct {
R, G, B uint8
}
func (that RGBColor) RGBA() (r, g, b, a uint32) {
return uint32(that.R), uint32(that.G), uint32(that.B), 1
}
type RGBImage struct {
// Pix holds the image's pixels, in R, G, B, A order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect image.Rectangle
}
func (that RGBImage) ColorModel() color.Model {
return nil
}
func (that RGBImage) Bounds() image.Rectangle {
return that.Rect
}
func (that RGBImage) At(x, y int) color.Color {
col := that.RGBAt(x, y)
return color.RGBA{R: col.R, G: col.G, B: col.B, A: 1}
}
func (that *RGBImage) RGBAt(x, y int) *RGBColor {
if !(image.Point{X: x, Y: y}.In(that.Rect)) {
return &RGBColor{}
}
i := that.PixOffset(x, y)
return &RGBColor{that.Pix[i+0], that.Pix[i+1], that.Pix[i+2]}
}
func (that *RGBImage) PixOffset(x, y int) int {
return (y-that.Rect.Min.Y)*that.Stride + (x-that.Rect.Min.X)*3
}
func (that RGBImage) Set(x, y int, c color.Color) {
if !(image.Point{X: x, Y: y}.In(that.Rect)) {
return
}
i := that.PixOffset(x, y)
c1 := color.RGBAModel.Convert(c).(color.RGBA)
that.Pix[i+0] = c1.R
that.Pix[i+1] = c1.G
that.Pix[i+2] = c1.B
}
func (that *RGBImage) SetRGB(x, y int, c color.RGBA) {
if !(image.Point{X: x, Y: y}.In(that.Rect)) {
return
}
i := that.PixOffset(x, y)
that.Pix[i+0] = c.R
that.Pix[i+1] = c.G
that.Pix[i+2] = c.B
}
func NewRGBImage(r image.Rectangle) *RGBImage {
w, h := r.Dx(), r.Dy()
buf := make([]uint8, 3*w*h)
return &RGBImage{buf, 3 * w, r}
}
================================================
FILE: cmd/player/main.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/osgochina/dmicro/easyservice"
"github.com/osgochina/dmicro/logger"
"golang.org/x/net/context"
"os"
)
var (
helpContent = gstr.TrimLeft(`
USAGE
./server [start|stop|quit] [tcpServer|wsServer] [OPTION]
OPTION
--rbsFile 使用的rbs文件地址 必传
--tcpHost 本地监听的tcp协议地址 默认0.0.0.0
--tcpPort 本地监听的tcp协议端口 默认8989
--proxyPassword 连接到proxy的密码 不传入密码则使用auth none
--wsHost 启动websocket服务的本地地址 默认 0.0.0.0
--wsPort 启动websocket服务的本地端口 默认8988
--wsPath 启动websocket服务的url path 默认'/'
--debug 是否开启debug 默认debug=false
-d,--daemon 使用守护进程模式启动
--pid 设置pid文件的地址,默认是/tmp/[server].pid
-h,--help 获取帮助信息
-v,--version 获取编译版本信息
EXAMPLES
/path/to/server
/path/to/server start --env=dev --debug=true --pid=/tmp/server.pid
/path/to/server start -c=config.product.toml
/path/to/server start tcpServer,wsServer --config=config.product.toml
/path/to/server start wsServer --rbsFile=/path/to/foo.rbs
--wsHost=0.0.0.0
--wsPort=8988
--proxyPassword=12345612
--debug
/path/to/server start tcpServer --rbsFile=/path/to/foo.rbs
--tcpHost=0.0.0.0
--tcpPort=8989
--proxyPassword=12345612
--debug
/path/to/server stop
/path/to/server quit
/path/to/server reload
/path/to/server version
/path/to/server help
`)
)
func main() {
easyservice.Authors = "ClownFish"
easyservice.SetHelpContent(helpContent)
easyservice.SetOptions(
map[string]bool{
"tcpHost": true, //本地监听的tcp协议地址 默认0.0.0.0
"tcpPort": true, //本地监听的tcp协议端口 默认8989
"proxyPassword": true, //连接到proxy的密码 不传入密码则使用auth none
"wsHost": true, //启动websocket服务的本地地址 默认 0.0.0.0
"wsPort": true, //启动websocket服务的本地端口 默认8988
"wsPath": true, //启动websocket服务的url path 默认'/'
"rbsFile": true, // 使用的rbs文件地址 必传
})
easyservice.Setup(func(svr *easyservice.EasyService) {
//注册服务停止时要执行法方法
svr.BeforeStop(func(service *easyservice.EasyService) bool {
fmt.Println("Vnc player server stop")
return true
})
cfg := svr.Config()
rbsFile := svr.CmdParser().GetOpt("rbsFile", "")
if len(rbsFile.String()) <= 0 || !gfile.Exists(rbsFile.String()) {
svr.Help()
os.Exit(0)
}
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("rbsFile", rbsFile.String())
logger.SetDebug(cfg.MustGet(context.TODO(), "Debug").Bool())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("tcpHost", svr.CmdParser().GetOpt("tcpHost", "0.0.0.0").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("tcpPort", svr.CmdParser().GetOpt("tcpPort", 8989).Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("proxyPassword", svr.CmdParser().GetOpt("proxyPassword", "").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsHost", svr.CmdParser().GetOpt("wsHost", "0.0.0.0").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsPort", svr.CmdParser().GetOpt("wsPort", 8988).Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsPath", svr.CmdParser().GetOpt("wsPath", "/").String())
if svr.SandboxNames().ContainsI("tcpserver") {
svr.AddSandBox(NewTcpSandBox(cfg))
return
}
if svr.SandboxNames().ContainsI("wsserver") {
svr.AddSandBox(NewWSSandBox(cfg))
return
}
svr.AddSandBox(NewTcpSandBox(cfg))
svr.AddSandBox(NewWSSandBox(cfg))
})
}
================================================
FILE: cmd/player/tcpServer.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/glog"
"github.com/osgochina/dmicro/drpc"
"github.com/osgochina/dmicro/drpc/status"
"github.com/osgochina/dmicro/easyservice"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/security"
"github.com/vprix/vncproxy/session"
"github.com/vprix/vncproxy/vnc"
"golang.org/x/net/context"
"io"
"net"
)
// TcpSandBox Tcp的服务
type TcpSandBox struct {
id int
name string
cfg *gcfg.Config
service *easyservice.EasyService
lis net.Listener
closed chan struct{}
}
// NewTcpSandBox 创建一个默认的服务沙盒
func NewTcpSandBox(cfg *gcfg.Config) *TcpSandBox {
id := easyservice.GetNextSandBoxId()
sBox := &TcpSandBox{
id: id,
name: fmt.Sprintf("tcp_%d", id),
cfg: cfg,
closed: make(chan struct{}),
}
return sBox
}
func (that *TcpSandBox) ID() int {
return that.id
}
func (that *TcpSandBox) Name() string {
return that.name
}
func (that *TcpSandBox) Setup() error {
var err error
addr := fmt.Sprintf("%s:%d", that.cfg.MustGet(context.TODO(), "tcpHost"), that.cfg.MustGet(context.TODO(), "tcpPort"))
that.lis, err = net.Listen("tcp", addr)
if err != nil {
glog.Fatalf(context.TODO(), "Error listen. %v", err)
}
fmt.Printf("Tcp proxy started! listening %s . vnc server %s:%d\n", that.lis.Addr().String(), that.cfg.MustGet(context.TODO(), "vncHost"), that.cfg.MustGet(context.TODO(), "vncPort"))
securityHandlers := []rfb.ISecurityHandler{&security.ServerAuthNone{}}
if len(that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()) > 0 {
securityHandlers = append(securityHandlers, &security.ServerAuthVNC{Password: that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()})
}
for {
conn, err := that.lis.Accept()
if err != nil {
select {
case <-that.closed:
return drpc.ErrListenClosed
default:
}
return err
}
go func(c net.Conn) {
defer func() {
//捕获错误,并且继续执行
if p := recover(); p != nil {
err = fmt.Errorf("panic:%v\n%s", p, status.PanicStackTrace())
}
}()
svrSession := session.NewServerSession(
rfb.OptSecurityHandlers(securityHandlers...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return c, nil
}),
)
play := vnc.NewPlayer(that.cfg.MustGet(context.TODO(), "rbsFile").String(), svrSession)
err = play.Start()
if err != nil {
glog.Warning(context.TODO(), err)
return
}
glog.Info(context.TODO(), "play finished")
}(conn)
}
}
func (that *TcpSandBox) Shutdown() error {
close(that.closed)
return that.lis.Close()
}
func (that *TcpSandBox) Service() *easyservice.EasyService {
return that.service
}
================================================
FILE: cmd/player/wsServer.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/glog"
"github.com/osgochina/dmicro/easyservice"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/security"
"github.com/vprix/vncproxy/session"
"github.com/vprix/vncproxy/vnc"
"golang.org/x/net/context"
"golang.org/x/net/websocket"
"io"
)
// WSSandBox Tcp的服务
type WSSandBox struct {
id int
name string
cfg *gcfg.Config
service *easyservice.EasyService
svr *ghttp.Server
}
// NewWSSandBox 创建一个默认的服务沙盒
func NewWSSandBox(cfg *gcfg.Config) *WSSandBox {
id := easyservice.GetNextSandBoxId()
sBox := &WSSandBox{
id: id,
name: fmt.Sprintf("ws_%d", id),
cfg: cfg,
}
return sBox
}
func (that *WSSandBox) ID() int {
return that.id
}
func (that *WSSandBox) Name() string {
return that.name
}
func (that *WSSandBox) Setup() error {
that.svr = g.Server()
that.svr.BindHandler(that.cfg.MustGet(context.TODO(), "wsPath", "/").String(), func(r *ghttp.Request) {
h := websocket.Handler(func(conn *websocket.Conn) {
conn.PayloadType = websocket.BinaryFrame
securityHandlers := []rfb.ISecurityHandler{&security.ServerAuthNone{}}
if len(that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()) > 0 {
securityHandlers = []rfb.ISecurityHandler{&security.ServerAuthVNC{Password: that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()}}
}
svrSession := session.NewServerSession(
rfb.OptSecurityHandlers(securityHandlers...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return conn, nil
}),
)
play := vnc.NewPlayer(that.cfg.MustGet(context.TODO(), "rfbFile").String(), svrSession)
err := play.Start()
if err != nil {
glog.Warning(context.TODO(), err)
return
}
glog.Info(context.TODO(), "play session end")
})
h.ServeHTTP(r.Response.Writer, r.Request)
})
that.svr.SetAddr(fmt.Sprintf("%s:%d", that.cfg.MustGet(context.TODO(), "wsHost").String(), that.cfg.MustGet(context.TODO(), "wsPort").Int()))
return that.svr.Start()
}
func (that *WSSandBox) Shutdown() error {
return that.svr.Shutdown()
}
func (that *WSSandBox) Service() *easyservice.EasyService {
return that.service
}
================================================
FILE: cmd/proxy/main.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/text/gstr"
"github.com/osgochina/dmicro/easyservice"
"github.com/osgochina/dmicro/logger"
"golang.org/x/net/context"
"os"
)
var (
helpContent = gstr.TrimLeft(`
USAGE
./proxy [start|stop|quit] [tcpServer|wsServer] [OPTION]
OPTION
--vncHost 要连接的vnc服务端地址 必传
--vncPort 要连接的vnc服务端端口 必传
--vncPassword 要连接的vnc服务端密码 不传则使用auth none
--tcpHost 本地监听的tcp协议地址 默认0.0.0.0
--tcpPort 本地监听的tcp协议端口 默认8989
--proxyPassword 连接到proxy的密码 不传入密码则使用auth none
--wsHost 启动websocket服务的本地地址 默认 0.0.0.0
--wsPort 启动websocket服务的本地端口 默认8988
--wsPath 启动websocket服务的url path 默认'/'
--debug 是否开启debug 默认debug=false
-d,--daemon 使用守护进程模式启动
--pid 设置pid文件的地址,默认是/tmp/[server].pid
-h,--help 获取帮助信息
-v,--version 获取编译版本信息
EXAMPLES
/path/to/proxy
/path/to/proxy start --env=dev --debug=true --pid=/tmp/server.pid
/path/to/proxy start -c=config.product.toml
/path/to/proxy start tcpServer,wsServer --config=config.product.toml
/path/to/proxy start wsServer --vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--wsHost=0.0.0.0
--wsPort=8988
--wsPath=/
--proxyPassword=12345612
--debug
/path/to/proxy start tcpServer --vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--tcpHost=0.0.0.0
--tcpPort=8989
--proxyPassword=12345612
--debug
/path/to/proxy stop
/path/to/proxy quit
/path/to/proxy reload
/path/to/proxy version
/path/to/proxy help
`)
)
func main() {
easyservice.Authors = "ClownFish"
easyservice.SetHelpContent(helpContent)
easyservice.SetOptions(
map[string]bool{
"tcpHost": true, //本地监听的tcp协议地址 默认0.0.0.0
"tcpPort": true, //本地监听的tcp协议端口 默认8989
"proxyPassword": true, //连接到proxy的密码 不传入密码则使用auth none
"wsHost": true, //启动websocket服务的本地地址 默认 0.0.0.0
"wsPort": true, //启动websocket服务的本地端口 默认8988
"wsPath": true, //启动websocket服务的url path 默认'/'
"vncHost": true, // 要连接的vnc服务端地址 必传
"vncPort": true, // 要连接的vnc服务端端口 必传
"vncPassword": true, // 要连接的vnc服务端密码 不传则使用auth none
})
easyservice.Setup(func(svr *easyservice.EasyService) {
//注册服务停止时要执行法方法
svr.BeforeStop(func(service *easyservice.EasyService) bool {
fmt.Println("Vnc proxy server stop")
return true
})
cfg := svr.Config()
vncHost := svr.CmdParser().GetOpt("vncHost", "")
if len(vncHost.String()) <= 0 {
svr.Help()
os.Exit(0)
}
vncPort := svr.CmdParser().GetOpt("vncPort", 0)
if vncPort.Int() <= 0 {
svr.Help()
os.Exit(0)
}
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncHost", vncHost.String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPort", vncPort.Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPassword", svr.CmdParser().GetOpt("vncPassword", "").String())
logger.SetDebug(cfg.MustGet(context.TODO(), "Debug").Bool())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("tcpHost", svr.CmdParser().GetOpt("tcpHost", "0.0.0.0").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("tcpPort", svr.CmdParser().GetOpt("tcpPort", 8989).Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("proxyPassword", svr.CmdParser().GetOpt("proxyPassword", "").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsHost", svr.CmdParser().GetOpt("wsHost", "0.0.0.0").String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsPort", svr.CmdParser().GetOpt("wsPort", 8988).Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("wsPath", svr.CmdParser().GetOpt("wsPath", "/").String())
if svr.SandboxNames().ContainsI("tcpserver") {
svr.AddSandBox(NewTcpSandBox(cfg))
return
}
if svr.SandboxNames().ContainsI("wsserver") {
svr.AddSandBox(NewWSSandBox(cfg))
return
}
svr.AddSandBox(NewTcpSandBox(cfg))
svr.AddSandBox(NewWSSandBox(cfg))
})
}
================================================
FILE: cmd/proxy/tcpServer.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/glog"
"github.com/osgochina/dmicro/drpc"
"github.com/osgochina/dmicro/drpc/status"
"github.com/osgochina/dmicro/easyservice"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/security"
"github.com/vprix/vncproxy/session"
"github.com/vprix/vncproxy/vnc"
"golang.org/x/net/context"
"io"
"net"
"time"
)
// TcpSandBox Tcp的服务
type TcpSandBox struct {
id int
name string
cfg *gcfg.Config
service *easyservice.EasyService
lis net.Listener
closed chan struct{}
proxyHub *gmap.StrAnyMap
}
// NewTcpSandBox 创建一个默认的服务沙盒
func NewTcpSandBox(cfg *gcfg.Config) *TcpSandBox {
id := easyservice.GetNextSandBoxId()
sBox := &TcpSandBox{
id: id,
name: fmt.Sprintf("tcp_%d", id),
cfg: cfg,
closed: make(chan struct{}),
proxyHub: gmap.NewStrAnyMap(true),
}
return sBox
}
func (that *TcpSandBox) ID() int {
return that.id
}
func (that *TcpSandBox) Name() string {
return that.name
}
func (that *TcpSandBox) Setup() error {
var err error
addr := fmt.Sprintf("%s:%d", that.cfg.MustGet(context.TODO(), "tcpHost").String(), that.cfg.MustGet(context.TODO(), "tcpPort").Int())
that.lis, err = net.Listen("tcp", addr)
if err != nil {
glog.Fatalf(context.TODO(), "Error listen. %v", err)
}
fmt.Printf("Tcp proxy started! listening %s . vnc server %s:%d\n", that.lis.Addr().String(), that.cfg.MustGet(context.TODO(), "vncHost").String(), that.cfg.MustGet(context.TODO(), "vncPort"))
securityHandlers := []rfb.ISecurityHandler{
&security.ServerAuthNone{},
}
if len(that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()) > 0 {
securityHandlers = append(securityHandlers, &security.ServerAuthVNC{Password: that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()})
}
targetCfg := rfb.TargetConfig{
Host: that.cfg.MustGet(context.TODO(), "vncHost").String(),
Port: that.cfg.MustGet(context.TODO(), "vncPort").Int(),
Password: that.cfg.MustGet(context.TODO(), "vncPassword").Bytes(),
}
for {
conn, err := that.lis.Accept()
if err != nil {
select {
case <-that.closed:
return drpc.ErrListenClosed
default:
}
return err
}
go func(c net.Conn) {
defer func() {
//捕获错误,并且继续执行
if p := recover(); p != nil {
err = fmt.Errorf("panic:%v\n%s", p, status.PanicStackTrace())
}
}()
svrSess := session.NewServerSession(
rfb.OptDesktopName([]byte("Vprix VNC Proxy")),
rfb.OptHeight(768),
rfb.OptWidth(1024),
rfb.OptSecurityHandlers(securityHandlers...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return c, nil
}),
)
timeout := 10 * time.Second
network := "tcp"
cliSess := session.NewClient(
rfb.OptSecurityHandlers([]rfb.ISecurityHandler{&security.ClientAuthVNC{Password: targetCfg.Password}}...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return net.DialTimeout(network, targetCfg.Addr(), timeout)
}),
)
p := vnc.NewVncProxy(cliSess, svrSess)
remoteKey := c.RemoteAddr().String()
that.proxyHub.Set(remoteKey, p)
err = p.Start()
if err != nil {
glog.Warning(context.TODO(), err)
return
}
glog.Info(context.TODO(), "proxy session closed")
}(conn)
}
}
func (that *TcpSandBox) Shutdown() error {
close(that.closed)
return that.lis.Close()
}
func (that *TcpSandBox) Service() *easyservice.EasyService {
return that.service
}
================================================
FILE: cmd/proxy/wsServer.go
================================================
package main
import (
"context"
"fmt"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/glog"
"github.com/osgochina/dmicro/easyservice"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/security"
"github.com/vprix/vncproxy/session"
"github.com/vprix/vncproxy/vnc"
"golang.org/x/net/websocket"
"io"
"net"
)
// WSSandBox Tcp的服务
type WSSandBox struct {
id int
name string
cfg *gcfg.Config
service *easyservice.EasyService
svr *ghttp.Server
proxyHub *gmap.StrAnyMap
}
// NewWSSandBox 创建一个默认的服务沙盒
func NewWSSandBox(cfg *gcfg.Config) *WSSandBox {
id := easyservice.GetNextSandBoxId()
sBox := &WSSandBox{
id: id,
name: fmt.Sprintf("ws_%d", id),
cfg: cfg,
proxyHub: gmap.NewStrAnyMap(true),
}
return sBox
}
func (that *WSSandBox) ID() int {
return that.id
}
func (that *WSSandBox) Name() string {
return that.name
}
func (that *WSSandBox) Setup() error {
that.svr = g.Server()
that.svr.BindHandler(that.cfg.MustGet(context.TODO(), "wsPath", "/").String(), func(r *ghttp.Request) {
h := websocket.Handler(func(conn *websocket.Conn) {
conn.PayloadType = websocket.BinaryFrame
securityHandlers := []rfb.ISecurityHandler{
&security.ServerAuthNone{},
}
if len(that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()) > 0 {
securityHandlers = append(securityHandlers, &security.ServerAuthVNC{Password: that.cfg.MustGet(context.TODO(), "proxyPassword").Bytes()})
}
targetCfg := rfb.TargetConfig{
Host: that.cfg.MustGet(context.TODO(), "vncHost").String(),
Port: that.cfg.MustGet(context.TODO(), "vncPort").Int(),
Password: that.cfg.MustGet(context.TODO(), "vncPassword").Bytes(),
}
var err error
svrSess := session.NewServerSession(
rfb.OptDesktopName([]byte("Vprix VNC Proxy")),
rfb.OptHeight(768),
rfb.OptWidth(1024),
rfb.OptSecurityHandlers(securityHandlers...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return conn, nil
}),
)
cliSess := session.NewClient(
rfb.OptSecurityHandlers([]rfb.ISecurityHandler{&security.ClientAuthVNC{Password: targetCfg.Password}}...),
rfb.OptGetConn(func(sess rfb.ISession) (io.ReadWriteCloser, error) {
return net.DialTimeout(targetCfg.GetNetwork(), targetCfg.Addr(), targetCfg.GetTimeout())
}),
)
p := vnc.NewVncProxy(cliSess, svrSess)
remoteKey := conn.RemoteAddr().String()
that.proxyHub.Set(remoteKey, p)
err = p.Start()
if err != nil {
glog.Warning(context.TODO(), err)
return
}
glog.Info(context.TODO(), "proxy session end")
})
h.ServeHTTP(r.Response.Writer, r.Request)
})
that.svr.SetAddr(fmt.Sprintf("%s:%d", that.cfg.MustGet(context.TODO(), "wsHost").String(), that.cfg.MustGet(context.TODO(), "wsPort").Int()))
return that.svr.Start()
}
func (that *WSSandBox) Shutdown() error {
return that.svr.Shutdown()
}
func (that *WSSandBox) Service() *easyservice.EasyService {
return that.service
}
================================================
FILE: cmd/recorder/main.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/text/gstr"
"github.com/osgochina/dmicro/easyservice"
"github.com/osgochina/dmicro/logger"
"golang.org/x/net/context"
"os"
)
var (
helpContent = gstr.TrimLeft(`
USAGE
./recorder [start|stop|quit] [OPTION]
OPTION
--rbsFile 使用的rbs文件地址 必传
--vncHost 要连接的vnc服务端地址 必传
--vncPort 要连接的vnc服务端端口 必传
--vncPassword 要连接的vnc服务端密码 不传则使用auth none
--debug 是否开启debug 默认debug=false
-d,--daemon 使用守护进程模式启动
--pid 设置pid文件的地址,默认是/tmp/[server].pid
-h,--help 获取帮助信息
-v,--version 获取编译版本信息
EXAMPLES
/path/to/recorder
/path/to/recorder start --env=dev --debug=true --pid=/tmp/server.pid
/path/to/recorder start -c=config.product.toml
/path/to/recorder start --config=config.product.toml
/path/to/recorder start --rbsFile=/path/to/foo.rbs
--vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--debug
/path/to/server stop
/path/to/server quit
/path/to/server reload
/path/to/server version
/path/to/server help
`)
)
func main() {
easyservice.Authors = "ClownFish"
easyservice.SetHelpContent(helpContent)
easyservice.SetOptions(
map[string]bool{
"rbsFile": true, // 使用的rbs文件地址 必传
"vncHost": true, // 要连接的vnc服务端地址 必传
"vncPort": true, // 要连接的vnc服务端端口 必传
"vncPassword": true, // 要连接的vnc服务端密码 不传则使用auth none
})
easyservice.Setup(func(svr *easyservice.EasyService) {
//注册服务停止时要执行法方法
svr.BeforeStop(func(service *easyservice.EasyService) bool {
fmt.Println("Vnc player server stop")
return true
})
cfg := svr.Config()
rbsFile := svr.CmdParser().GetOpt("rbsFile", "")
if len(rbsFile.String()) <= 0 {
svr.Help()
os.Exit(0)
}
vncHost := svr.CmdParser().GetOpt("vncHost", "")
if len(vncHost.String()) <= 0 {
svr.Help()
os.Exit(0)
}
vncPort := svr.CmdParser().GetOpt("vncPort", 0)
if vncPort.Int() <= 0 {
svr.Help()
os.Exit(0)
}
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("rbsFile", rbsFile.String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncHost", vncHost.String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPort", vncPort.Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPassword", svr.CmdParser().GetOpt("vncPassword", ""))
logger.SetDebug(cfg.MustGet(context.TODO(), "Debug").Bool())
svr.AddSandBox(NewRecorderSandBox(cfg))
})
}
================================================
FILE: cmd/recorder/recorder.go
================================================
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"github.com/osgochina/dmicro/easyservice"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/encodings"
"github.com/vprix/vncproxy/messages"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/security"
"github.com/vprix/vncproxy/session"
"github.com/vprix/vncproxy/vnc"
"golang.org/x/net/context"
"io"
"net"
"os"
"time"
)
// RecorderSandBox 记录服务
type RecorderSandBox struct {
id int
name string
cfg *gcfg.Config
service *easyservice.EasyService
recorder *vnc.Recorder
closed chan struct{}
}
// NewRecorderSandBox 创建一个默认的服务沙盒
func NewRecorderSandBox(cfg *gcfg.Config) *RecorderSandBox {
id := easyservice.GetNextSandBoxId()
sBox := &RecorderSandBox{
id: id,
name: fmt.Sprintf("tcp_%d", id),
cfg: cfg,
closed: make(chan struct{}),
}
return sBox
}
func (that *RecorderSandBox) ID() int {
return that.id
}
func (that *RecorderSandBox) Name() string {
return that.name
}
func (that *RecorderSandBox) Setup() error {
saveFilePath := that.cfg.MustGet(context.TODO(), "rbsFile").String()
targetCfg := rfb.TargetConfig{
Network: "tcp",
Host: that.cfg.MustGet(context.TODO(), "vncHost").String(),
Port: that.cfg.MustGet(context.TODO(), "vncPort").Int(),
Password: that.cfg.MustGet(context.TODO(), "vncPassword").Bytes(),
Timeout: 10 * time.Second,
}
var securityHandlers = []rfb.ISecurityHandler{
&security.ClientAuthNone{},
}
if len(targetCfg.Password) > 0 {
securityHandlers = []rfb.ISecurityHandler{
&security.ClientAuthVNC{Password: targetCfg.Password},
}
}
// 创建会话
recorderSess := session.NewRecorder(
rfb.OptEncodings(encodings.DefaultEncodings...),
rfb.OptMessages(messages.DefaultServerMessages...),
rfb.OptPixelFormat(rfb.PixelFormat32bit),
rfb.OptGetConn(func(iSession rfb.ISession) (io.ReadWriteCloser, error) {
if gfile.Exists(saveFilePath) {
saveFilePath = fmt.Sprintf("%s%s%s_%d%s",
gfile.Dir(saveFilePath),
gfile.Separator,
gfile.Name(gfile.Basename(saveFilePath)),
gtime.Now().Unix(),
gfile.Ext(gfile.Basename(saveFilePath)),
)
}
return gfile.OpenFile(saveFilePath, os.O_RDWR|os.O_CREATE, 0644)
}),
)
cliSession := session.NewClient(
rfb.OptEncodings(encodings.DefaultEncodings...),
rfb.OptMessages(messages.DefaultServerMessages...),
rfb.OptPixelFormat(rfb.PixelFormat32bit),
rfb.OptGetConn(func(iSession rfb.ISession) (io.ReadWriteCloser, error) {
return net.DialTimeout(targetCfg.Network, targetCfg.Addr(), targetCfg.Timeout)
}),
rfb.OptSecurityHandlers(securityHandlers...),
)
that.recorder = vnc.NewRecorder(recorderSess, cliSession)
err := that.recorder.Start()
if err != nil {
logger.Fatal(context.TODO(), err)
}
return err
}
func (that *RecorderSandBox) Shutdown() error {
close(that.closed)
that.recorder.Close()
return nil
}
func (that *RecorderSandBox) Service() *easyservice.EasyService {
return that.service
}
================================================
FILE: cmd/screenshot/main.go
================================================
package main
import (
"bytes"
"context"
"fmt"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/osgochina/dmicro/easyservice"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
"github.com/vprix/vncproxy/vnc"
"image/draw"
"image/jpeg"
"os"
"time"
)
var (
helpContent = gstr.TrimLeft(`
USAGE
./server [start|stop|quit] [OPTION]
OPTION
--imageFile 要生成的截图地址,暂时只支持jpeg格式 必传
--vncHost 要连接的vnc服务端地址 必传
--vncPort 要连接的vnc服务端端口 必传
--vncPassword 要连接的vnc服务端密码 不传则使用auth none
--debug 是否开启debug 默认debug=false
-h,--help 获取帮助信息
-v,--version 获取编译版本信息
EXAMPLES
/path/to/server
/path/to/server start --env=dev --debug=true
/path/to/server start -c=config.product.toml
/path/to/server start --config=config.product.toml
/path/to/server start --imageFile=/path/to/foo.jpeg
--vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--debug
/path/to/server version
/path/to/server help
`)
)
func main() {
easyservice.Authors = "ClownFish"
easyservice.SetHelpContent(helpContent)
easyservice.SetOptions(
map[string]bool{
"imageFile": true, // 要生成的截图地址,暂时只支持jpeg格式 必传
"vncHost": true, // 要连接的vnc服务端地址 必传
"vncPort": true, // 要连接的vnc服务端端口 必传
"vncPassword": true, // 要连接的vnc服务端密码 不传则使用auth none
})
easyservice.Setup(func(svr *easyservice.EasyService) {
//注册服务停止时要执行法方法
svr.BeforeStop(func(service *easyservice.EasyService) bool {
fmt.Println("Vnc player server stop")
return true
})
cfg := svr.Config()
rbsFile := svr.CmdParser().GetOpt("imageFile", "")
if len(rbsFile.String()) <= 0 {
svr.Help()
os.Exit(0)
}
vncHost := svr.CmdParser().GetOpt("vncHost", "")
if len(vncHost.String()) <= 0 {
svr.Help()
os.Exit(0)
}
vncPort := svr.CmdParser().GetOpt("vncPort", 0)
if vncPort.Int() <= 0 {
svr.Help()
os.Exit(0)
}
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("rbsFile", rbsFile.String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncHost", vncHost.String())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPort", vncPort.Int())
_ = cfg.GetAdapter().(*gcfg.AdapterFile).Set("vncPassword", svr.CmdParser().GetOpt("vncPassword", "").String())
logger.SetDebug(cfg.MustGet(context.TODO(), "Debug").Bool())
v := vnc.NewScreenshot(
rfb.TargetConfig{
Network: "tcp",
Host: vncHost.String(),
Port: vncPort.Int(),
Password: svr.CmdParser().GetOpt("vncPassword", "").Bytes(),
Timeout: 5 * time.Second,
},
)
img, err := v.GetImage()
if err != nil {
logger.Fatal(context.TODO(), err)
}
j := &bytes.Buffer{}
err = jpeg.Encode(j, img.(draw.Image), &jpeg.Options{Quality: 100})
if err != nil {
fmt.Println(err)
}
err = gfile.PutBytes(rbsFile.String(), j.Bytes())
if err != nil {
fmt.Println(err)
}
os.Exit(0)
})
}
================================================
FILE: cmd/video/main.go
================================================
package main
func main() {
// 暂未实现
//v := vnc.NewVideo(nil,
// rfb.TargetConfig{
// Network: "tcp",
// Host: "127.0.0.1",
// Port: 5901,
// Password: []byte("@abc1234"),
// Timeout: 10 * time.Second,
// },
//)
//go func() {
// err := v.Start()
// if err != nil {
// logger.Fatal(err)
// }
//}()
//for {
// err := <-v.Error()
// logger.Error(err)
//}
}
================================================
FILE: docs/.keep
================================================
================================================
FILE: docs/.nojekyll
================================================
================================================
FILE: docs/README.md
================================================
# VncProxy [](https://github.com/vprix/vncproxy/releases) [](http://goreportcard.com/report/vprix/vncproxy) [](https://github.com/vprix/vncproxy/issues?q=is%3Aopen+is%3Aissue) [](https://github.com/vprix/vncproxy/issues?q=is%3Aissue+is%3Aclosed) [](http://godoc.org/github.com/vprix/vncproxy) [](https://github.com/vprix/vncproxy/tree/main/examples)
## VncProxy简介
`VncProxy` 是使用`golang`实现的rfb协议解析库,支持rfb协议解析,在其上实现了很多好用的功能。
* 全协议支持的vnc proxy。
* 支持Tcp代理
* 支持Websocket代理
* 屏幕录像,保存为`RBS`文件
* 重播服务器,支持vnc客户端链接,播放`RBS`文件。
* 支持实时录制视频
* 支持通过`RBS`文件录制视频。
* 支持屏幕截图
## 支持的编码格式
- [x] Raw
- [x] CopyRect
- [x] CoRRE
- [x] rre
- [x] Hextile
- [x] Tight
- [x] TightPng
- [x] ZLib
- [x] Zrle
- [x] CursorPseudo
- [x] CursorWithAlphaPseudo
- [x] DesktopNamePseudo
- [x] DesktopSizePseudo
- [x] ExtendedDesktopSizePseudo
- [x] LedStatePseudo
- [x] CursorPosPseudo
- [x] XCursorPseudo
- [ ] jpeg
- [ ] jrle
- [ ] trle
## 组件说明
### Proxy
1. 启动`server`接受`vnc viewer`的链接.
2. 启动`client`连接到指定的`vnc server`.
3. 为`vnc viewer`和`vnc server`之间建立起消息转发通道。
4. 因为`rfb`协议被完全解析,可以针对通信的消息进行转发处理,产生了后续的功能。
### Recorder
1. 启动`client`连接到指定的`vnc server`.
2. 发送帧缓冲区更新消息`FramebufferUpdateRequest`到`vnc server`。
3. 处理`vnc server`回复的界面更新消息`FramebufferUpdate`。
4. 把这一过程以`rbs`文件格式记录下来。
### Player
1. 启动`server`接受`vnc viewer`的链接.
2. 读取`rbs`文件,并按格式生成`FramebufferUpdate`消息发送给`vnc viewer`。
3. `vnc viewer`的界面就会回放动作。
### Video
1. 支持`Proxy`,`Recorder`和`rbs`文件作为输入源。
2. 把`FramebufferUpdate`消息转换为视频文件。
### Screenshot
1. 支持`Proxy`,`Recorder`和`rbs`文件作为输入源。
2. 把当前的界面视图转换为图片文件。
## 使用说明
`vncProxy`项目有多种应用场景。
可以作为单独的应用程序编译,也可以作为库被其他应用程序引用。
接下来,分别介绍各种场景下的使用方式。
### 编译
```shell
# 使用方式:
# build.sh [-s app_name] [-v version] [-g go_bin]
# app_name 需要编译的应用名称
# 选项: proxy,player,recorder,video,screenshot.
# 默认是所有应用,多个应用可以逗号分割.
# version 编译后的文件版本号,默认为当前git的commit id.
# go_bin 使用的golang程序
# 编译所有应用
$ ./build
# 编译proxy
$ ./build -s proxy -v v0.1.0
# 编译player,recorder
$ ./build -s player,recorder -v v0.1.0
```
编译后的二进制文件在`./bin/`目录
### Proxy
代码路径在`./cmd/proxy`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./proxy --help
# 查看版本信息
$ ./proxy version
```
#### 启动tcp服务
```shell
# 启动tcp server接受vnc viewer的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# tcpHost 本地监听的地址
# tcpPort 本地监听的端口
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start tcpServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--tcpHost=0.0.0.0 \
--tcpPort=8989 \
--proxyPassword=12345612 \
--debug
```
#### 启动WebSocket服务
```shell
# 启动ws server接受novnc的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# wsHost 本地监听的地址
# wsPort 本地监听的端口
# wsPath websocket连接的地址
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start wsServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--wsHost=0.0.0.0 \
--wsPort=8988 \
--wsPath=/websockify \
--proxyPassword=12345612 \
--debug
```
### Recorder
代码路径在`./cmd/recorder`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./recorder --help
# 查看版本信息
$ ./recorder version
```
#### 启动Recorder服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# debug 使用debug模式启动服务
$ ./recorder start --rbsFile=/path/to/foo.rbs
--vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--debug
```
### Player
代码路径在`./cmd/player`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./player --help
# 查看版本信息
$ ./player version
```
#### 启动Player Tcp服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# tcpHost 本地监听的tcp协议地址 默认0.0.0.0
# tcpPort 本地监听的tcp协议端口 默认8989
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start tcpServer --rbsFile=/path/to/foo.rbs
--tcpHost=0.0.0.0
--tcpPort=8989
--proxyPassword=12345612
--debug
```
#### 启动Player WS服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# wsHost 启动websocket服务的本地地址 默认 0.0.0.0
# wsPort 启动websocket服务的本地端口 默认8988
# wsPath 启动websocket服务的url path 默认'/'
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start wsServer --rbsFile=/path/to/foo.rbs
--wsHost=0.0.0.0
--wsPort=8989
--wsPath=/
--proxyPassword=12345612
--debug
```
### Screenshot
代码路径在`./cmd/screenshot`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
#### 获取帮助信息
```shell
# 查看帮助信息
$ ./screenshot --help
# 查看版本信息
$ ./screenshot version
```
#### 启动Screenshot 获取vnc服务器的屏幕截图
```shell
# imageFile 要生成的截图地址,暂时只支持jpeg格式(必填)
# vncHost 要连接的vnc服务端地址(必填)
# vncPort 要连接的vnc服务端端口(必填)
# vncPassword 要连接的vnc服务端密码,不传则使用auth none
$ ./screenshot --imageFile=./screen.jpeg --vncHost=127.0.0.1 --vncPort=5900 --vncPassword=12345612
```
## 项目参考
本项目参考了以下项目完成。
* [vncproxy](https://github.com/amitbet/vncproxy)
* [vnc2video](https://github.com/amitbet/vnc2video)
* [rfbproto](https://github.com/rfbproto/rfbproto)
## 交流
我在做这个项目的过程中碰到了很多问题,查遍了互联网,缺少中文资料,大部分信息都是雷同的。
所以我萌生了开源的想法,帮助更多有需要的人。
我建立了一个可供交流的微信群,以便大家在使用的过程中碰到疑问,能有解答的地方。
当然,如果你对vnc有兴趣,也可以加我微信,多多交流。
欢迎各位贡献代码。

================================================
FILE: docs/changelog.md
================================================
================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VncProxy - RFB协议 - 使用文档</title>
<link rel="shortcut icon" href="favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="VNC Proxy 远程桌面代理,rfb协议解析">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.bootcdn.net/ajax/libs/docsify/4.12.1/themes/vue.min.css">
<style>
.app-name img{
width:50px;
height:60px
}
</style>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'VNC Proxy',
loadSidebar: 'summary.md',
alias: {
'/.*/summary.md': '/summary.md'
},
themeColor: '#3F71B5',
auto2top: true,
subMaxLevel: 2,
topMargin: "20",
repo: 'https://github.com/vprix/vncproxy',
search: "auto",
logo: '/logo.svg',
}
</script>
<!-- Docsify v4 -->
<script src="//cdn.bootcdn.net/ajax/libs/docsify/4.12.1/docsify.min.js"></script>
<!--搜索插件-->
<script src="//cdn.bootcdn.net/ajax/libs/docsify/4.12.1/plugins/search.min.js"></script>
<!--golang的语法高亮-->
<script src="//cdn.bootcdn.net/ajax/libs/prism/1.24.1/components/prism-go.min.js"></script>
<!--图片缩放 - Zoom image-->
<script src="//cdn.bootcdn.net/ajax/libs/docsify/4.12.1/plugins/zoom-image.min.js"></script>
<!--美人鱼图-->
<script src="//unpkg.com/mermaid/dist/mermaid.min.js"></script>
<script src="//unpkg.com/docsify-mermaid@latest/dist/docsify-mermaid.js"></script>
<script>mermaid.initialize({ startOnLoad: true });</script>
</body>
</html>
================================================
FILE: docs/overview.md
================================================
================================================
FILE: docs/player/.keep
================================================
================================================
FILE: docs/player/README.md
================================================
## Player
代码路径在`./cmd/player`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
### 获取帮助信息
```shell
# 查看帮助信息
$ ./player --help
# 查看版本信息
$ ./player version
```
### 启动Player Tcp服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# tcpHost 本地监听的tcp协议地址 默认0.0.0.0
# tcpPort 本地监听的tcp协议端口 默认8989
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start tcpServer --rbsFile=/path/to/foo.rbs
--tcpHost=0.0.0.0
--tcpPort=8989
--proxyPassword=12345612
--debug
```
### 启动Player WS服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# wsHost 启动websocket服务的本地地址 默认 0.0.0.0
# wsPort 启动websocket服务的本地端口 默认8988
# wsPath 启动websocket服务的url path 默认'/'
# proxyPassword 连接到proxy的密码 不传入密码则使用auth none
# debug 使用debug模式启动服务
$ ./player start wsServer --rbsFile=/path/to/foo.rbs
--wsHost=0.0.0.0
--wsPort=8989
--wsPath=/
--proxyPassword=12345612
--debug
```
================================================
FILE: docs/proxy/.keep
================================================
================================================
FILE: docs/proxy/README.md
================================================
## Proxy
代码路径在`./cmd/proxy`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
### 获取帮助信息
```shell
# 查看帮助信息
$ ./proxy --help
# 查看版本信息
$ ./proxy version
```
### 启动tcp服务
```shell
# 启动tcp server接受vnc viewer的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# tcpHost 本地监听的地址
# tcpPort 本地监听的端口
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start tcpServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--tcpHost=0.0.0.0 \
--tcpPort=8989 \
--proxyPassword=12345612 \
--debug
```
### 启动WebSocket服务
```shell
# 启动ws server接受novnc的连接
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# wsHost 本地监听的地址
# wsPort 本地监听的端口
# wsPath websocket连接的地址
# proxyPassword vnc连接的密码
# debug 使用debug模式启动服务
$ ./proxy start wsServer --vncHost=192.168.1.2 \
--vncPort=5901 \
--vncPassword=vprix \
--wsHost=0.0.0.0 \
--wsPort=8988 \
--wsPath=/websockify \
--proxyPassword=12345612 \
--debug
```
================================================
FILE: docs/questions.md
================================================
================================================
FILE: docs/recorder/.keep
================================================
================================================
FILE: docs/recorder/README.md
================================================
## Recorder
代码路径在`./cmd/recorder`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
### 获取帮助信息
```shell
# 查看帮助信息
$ ./recorder --help
# 查看版本信息
$ ./recorder version
```
### 启动Recorder服务
```shell
# rbsFile 要保存的rbs文件路径(必填)
# vncHost vnc服务器host
# vncPort vnc服务器port
# vncPassword vnc服务器密码
# debug 使用debug模式启动服务
$ ./recorder start --rbsFile=/path/to/foo.rbs
--vncHost=192.168.1.2
--vncPort=5901
--vncPassword=vprix
--debug
```
================================================
FILE: docs/rfc6143/GLOSSORY.md
================================================
# 帧缓冲
帧缓冲器也称为帧缓冲或者显存,是用来存储渲染数据的地方。帧缓冲的每一个存储单位对应一个像素,它是屏幕显示画面的直接映象,又称为位映射图(Bit Map)。

# 调色板
传统的帧缓冲器支持的色彩模式很广泛。受限于昂贵的内存,大多数早期的帧缓冲器使用的是1位、2位、4位或 8位的色深。小的色深导致不能产生完整的色彩范围。其解决方法是为帧缓冲器增加一个查找表(lookup table,LUT),把帧缓冲器中存储的每个“颜色”作为一个索引。这就是所谓的索引色(indexed color)模式。

# 游程编码
游程编码(run-length encoding,RLE)是一种比较简单的压缩算法,其基本思想是将重复且连续出现多次的字符使用(连续出现次数,某个字符)来描述。
举例来说,字符串"AAAABBBCCDEEEE",由4个A、3个B、2个C、1个D、4个E组成,游程编码将其压缩为4A3B2C1D4E,由14个字符转成10个字符,压缩比 71.4%。
游程编码的优点是将重复性高的数据压缩成小单位;若数据出现频率不高,压缩结果可能比原始数据大。例如:"ABCDE",压缩结果为"1A1B1C1D1E",由5个字符转成10个字符,压缩比 200%。

================================================
FILE: docs/rfc6143/README.md
================================================
# 远程帧缓冲协议
该文档原著为:https://github.com/vincenthcui/rfc6143
## RFB 协议
RFB (Remote Framebuffer Protocol) 远程帧缓冲协议,是一种允许用户通过网络连接控制远端计算机的七层网络协议。
在 RFB 协议中,用户通过本地鼠标、键盘输入,经由远端计算机计算后,将图形用户界面(GUI)回传本地进行输出。

### 协议特点
协议设计有以下几个特点:
- 瘦客户端。客户端职责简单清晰,无状态
- 运行在弱网络环境下
- 跨操作系统兼容性
## 协议版本
RFB 协议有三个公开版本,分别是 3.3、3.7和3.8,3.8 是稳定版本。
| 版本 | 发布时间 | 协议差异 |
|:-------------------:|:----------:|:---------:|
| Version 3.3 | 1998-01 | 服务器单向认证 |
| Version 3.7 | 2003-8-12 | 关闭连接时返回原因 |
| Version 3.8 (Final) | 2010-11-26 | - |
三个版本只在协议的握手阶段和初始化阶段存在差异,在数据流交换阶段保持一致。
### 协议的拓展
第三方 VNC 服务端和客户端拓展了 3.8 版本协议,提供更多的认证方式,优化传输效率。
- Tight
- RealVNC
- Ultra
- VMWare
## RFB 的发展历史
- 2002 年,AT&T 关闭其位于英国剑桥的 Olivetti 研究实验室。
- 2002 年,VNC 技术发明者 Tristan Richardson 合伙成立 RealVNC 公司,向商业公司提供企业级远程访问软件。
- 2003年8月12日,Richardson 公开 RFB 协议的 3.7 版本。
- 2010年11月26日,发布稳定协议版本 v3.8。
> RFB 是 IETF 公开的开源通信协议
>
> RFB® 和 VNC® 是 RealVNC 公司的注册商标。
## 公开协议版本及资料
- [RFC 6143: The Remote Framebuffer Protocol (describes Version 3.8)](https://tools.ietf.org/html/rfc6143)
- [The RFB Protocol - Community Version](https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst)
- [The RFB Protocol - Version 3.8 (2010-11-26)](https://web.archive.org/web/20160410055332/http://www.realvnc.com/docs/rfbproto.pdf)
- [The RFB Protocol - Version 3.7 (2003-08-12)](https://web.archive.org/web/20040325204925/http://www.realvnc.com/docs/rfbproto.pdf)
================================================
FILE: docs/rfc6143/handshake/README.md
================================================
# 握手
RFB 协议有四个阶段:
```mermaid
graph LR
protocol["协议握手"]
auth["认证"]
initial["初始化"]
transfer("数据交互")
subgraph 握手
protocol --> auth
auth --> initial
end
initial --> transfer
transfer --> transfer
```
- [协议握手](/rfc6143/handshake/protocol-version.md):对协议版本达成共识
- [认证](/rfc6143/handshake/security-type.md):认证客户端身份
- [初始化](/rfc6143/handshake/initial.md):交换像素格式等背景数据
- [数据交互](/rfc6143/transfer/README.md):传输交互事件,更新图像帧
================================================
FILE: docs/rfc6143/handshake/initial.md
================================================
# 初始化
收 SecurityResult 后,客户端应当发送 [ClientInit](#客户端初始化) 数据包,收到后,服务端发送 [ServerInit](#服务端初始化) 包。
```mermaid
sequenceDiagram
participant Client
participant Server
Server->>Client: SecurityResult(success)
Client->>Server: ClientInit
Server->>Client: ServerInit
```
## 客户端初始化
客户端初始化需要声明是否的共享屏幕。
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 1 | U8 | shared-flag |
+--------------+--------------+-------------+
```
- shared-flag: 是否与其他客户端共享连接。如果是 1,允许服务端保持/加入其他客户端的连接。如果是0,服务端应该主动断开与其他客户端的连接。
## 服务端初始化
收到 ClientInit 消息后,服务端发送 ServerInit 消息,声明帧缓冲区大小、像素格式以及桌面的名称。
```
+--------------+--------------+------------------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+------------------------------+
| 2 | U16 | framebuffer-width in pixels |
| 2 | U16 | framebuffer-height in pixels |
| 16 | PIXEL_FORMAT | server-pixel-format |
| 4 | U32 | name-length |
| name-length | U8 array | name-string |
+--------------+--------------+------------------------------+
```
- framebuffer-width in pixels: 屏幕宽度
- framebuffer-height in pixels: 屏幕高度
- server-pixel-format: 服务器默认像素格式
- name-length/name-string: 桌面的名字
如果客户端无法响应服务端指定的 pixel-format,可以主动发起 [SetPixelFormat](/display/pixel-format.md#SetPixelFormat) 重新设置。
================================================
FILE: docs/rfc6143/handshake/protocol-version.md
================================================
# 协议握手
协议握手对客户端和服务端通信过程使用的协议版本达成共识,有可能的情况下,对不同协议版本实现向前兼容。
## 握手过程
建立 TCP 连接后,服务器首先向客户端发送版本 X,收到 X 后,客户端向服务器发送不高于 X 的版本 Y。
RFB 有三个公开可选版本 3.3/3.7/3.8。
部分客户端或浏览器变种可能会发送其他的协议版本,统一将非标协议认定为 3.3(协议认为未公开协议版本没有实现3.7/3.8 中引入的特殊握手流程)。
```mermaid
sequenceDiagram
participant Client
participant Server
Server->>Client: ProtocolVersion
Client->>Server: ProtocolVersion
```
## 协议报文
协议消息由标识符、主次版本组成,其结构体如下:
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 3 | U8 array | protocol |
| 1 | U8 [32] | blank |
| 3 | U8 array | major version|
| 1 | U8 [42] | pot |
| 3 | U8 array | minor version|
+--------------+--------------+--------------+
```
对于 3.8 版本协议,其发送协议头部如下:
```
RFB 003.008\n (hex 52 46 42 20 30 30 33 2e 30 30 38 0a)
```
================================================
FILE: docs/rfc6143/handshake/security-type.md
================================================
# 安全握手
协商好协议版本后,客户端和服务端进行安全握手,就认证方式达成共识,并完成认证过程。
## 握手过程
认证的流程图如下:
```mermaid
sequenceDiagram
participant Client
participant Server
alt incompatible
Server->>Client: SecurityTypes(Empty)
Server->>Client: FailReason
Server-XClient: Close
end
Server->>Client: SecurityTypes
Client->>Server: SecurityType
alt None
else VNC Auth
Server->>Client: Challenge
Client->>Server: ChallengeResponse
end
Server->>Client: SecurityResult
alt failed
Server->>Client: FailReason
Server-XClient: Close
end
```
- 服务器向客户端列举支持的加密方式,客户端挑选支持的认证方式,告知服务端。
- 根据认证方式,完成认证
- 服务端返回认证结果,完成安全握手
## 协议报文
### security-types
```
+--------------------------+-------------+--------------------------+
| No. of bytes | Type | Description |
| | [Value] | |
+--------------------------+-------------+--------------------------+
| 1 | U8 | number-of-security-types |
| number-of-security-types | U8 array | security-types |
+--------------------------+-------------+--------------------------+
```
- number-of-security-types: 认证方式数量
- security-types: 认证方式标识符
协议定义的标识符有三种,剩下的由协议厂家进行拓展:
```
+--------+--------------------+
| Number | Name |
+--------+--------------------+
| 0 | Invalid |
| 1 | None |
| 2 | VNC Authentication |
+--------+--------------------+
```
### security-type
客户端以单字节报文告知选择的认证方式
```
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 1 | U8 | security-type |
+--------------+--------------+---------------+
```
- security-type: 达成共识的协议标识符
### fail-reason
当错误发送时,例如,服务端不兼容客户端版本,服务端会发送空的 SecurityTypes,发送消息说明错误原因后,关闭连接。
FailReason 报文如下:
```
+---------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------+
| 4 | U32 | reason-length |
| reason-length | U8 array | reason-string |
+---------------+--------------+---------------+
```
- reason-length: 原因长度
- reason-string: 错误原因
### VNC Auth
VNC Auth 过程,服务器首先发送 16 字节的随机字符串,作为 `challenge`。客户端用密码通过 `DES` 算法对 `challenge` 进行加密,将加密后的 16 字节结果告知服务端。
> TODO: 加密算法详解
有几点需要注意:
- 为格式化密钥,密码会被删节/补齐为 8 字符
- VNC Auth 是弱加密,不能用于不可信网络
- 为了更安全访问,可以通过 IPsec/SSH 加密链路
#### Challenge
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 16 | U8 | challenge |
+--------------+--------------+-------------+
```
#### ChallengeResponse
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 16 | U8 | response |
+--------------+--------------+-------------+
```
### SecurityResult
客户端选择认证方式后,服务端返回 SecurityResult 告知认证结果,哪怕使用 `None` 认证。
如果认证失败,服务器发送 `failed` 和 fail-reason 报文,主动关闭连接。
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 4 | U32 | status: |
| | 0 | OK |
| | 1 | failed |
+--------------+--------------+-------------+
```
================================================
FILE: docs/rfc6143/transfer/README.md
================================================
# 数据交互
数据交互阶段,客户端向服务端发送鼠标、键盘事件,或请求更新图像。
```mermaid
graph LR
A("鼠标事件")
B("键盘事件")
C("更新图像")
```
================================================
FILE: docs/rfc6143/transfer/display.md
================================================
# 显示协议
客户端可能处于弱网络环境,或只有较低性能的渲染设备。如果服务端不加限制的向客户端发送像素画面,很容易造成客户端卡死或网络堵塞。
在 RFB 协议中,当且仅当客户端主动请求显示数据时,服务端才会将 [FramebufferUpdate](#FramebufferUpdate) 发往客户端。响应 [FramebufferUpdateRequest](#FramebufferUpdateRequest) 往往需要返回多条 FramebufferUpdate。
```mermaid
sequenceDiagram
participant Client
participant Server
Client->>Server: FramebufferUpdateRequest
loop no change
Client->>Server: FramebufferUpdateRequest
end
Server->>Client: FramebufferUpdate
Server->>Client: FramebufferUpdate
Server->>Client: FramebufferUpdate
```
## FramebufferUpdateRequest
FramebufferUpdateRequest 告知服务端,客户端希望得到指定区域的内容。
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [3] | message-type |
| 1 | U8 | incremental |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
+--------------+--------------+--------------+
```
- message-type: 消息类型,固定 `3`
- incremental: 是否是增量请求。
- x-position/y-position: 区域的起始坐标
- width/height: 区域的长度和宽度
incremental 通常为非 0 值,服务器只需要发有变化的图像信息。当客户端丢失了缓存的帧缓冲信息,或者刚建立连接,需要完整的图像信息时,将 incremental 置为 0,获取全量信息。
## FramebufferUpdate
FramebufferUpdate 由一组矩形图像(rectangles of pixel)组成,客户端收到 FramebufferUpdate 消息后,将消息内的矩形填充到帧缓冲对应区域,完成图像展示。
```
+--------------+--------------+----------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+----------------------+
| 1 | U8 [0] | message-type |
| 1 | | padding |
| 2 | U16 | number-of-rectangles |
+--------------+--------------+----------------------+
```
- message-type: 消息类型,固定 0
- number-of-rectangles: 矩形的数量
### FramebufferUpdateRectangle
FramebufferUpdate 携带 `number-of-rectangles` 数量的矩形信息,每个矩形都有头部信息
```
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
| 4 | S32 | encoding-type |
+--------------+--------------+---------------+
```
- x-position/y-position: 矩形起始坐标
- width/height: 矩形宽度和高度
- encoding-type: 编码类型
================================================
FILE: docs/rfc6143/transfer/encoding/README.md
================================================
# 编码
”编码“ 是像素数据的表达方式。在发往客户端前,服务器经常将发送的图形数据进行编码、压缩,提高图像传输效率。
```
+--------+-----------------------------+
| Number | Name |
+--------+-----------------------------+
| 0 | Raw |
| 1 | CopyRect |
| 2 | RRE |
| 5 | Hextile |
| 15 | TRLE |
| 16 | ZRLE |
| -239 | Cursor pseudo-encoding |
| -223 | DesktopSize pseudo-encoding |
+--------+-----------------------------+
```
- [Raw](/rfc6143/transfer/encoding/raw.md): 原始位图编码,即不编码
- [CopyRect](/rfc6143/transfer/encoding/copy-rect.md): 从帧缓冲复制
- [RRE](/rfc6143/transfer/encoding/rise-and-run-length.md): rise-and-run-length 二维游程编码
- Hextile: RRE 的变种,图块游程编码
- [TRLE](/rfc6143/transfer/encoding/tiled-run-length.md): 图块游程编码
- [ZRLE](/rfc6143/transfer/encoding/zlib-run-length.md): Zlib Run-Length Encoding,zlib 压缩的游程编码
- Cursor pseudo-encoding: 鼠标指针伪编码
- DesktopSize pseudo-encoding: 桌面分辨率伪编码
================================================
FILE: docs/rfc6143/transfer/encoding/copy-rect.md
================================================
# 镜像编码 Copy Rect
CopyRect 指示客户端,从已有帧缓冲区域复制到新区域。这种编码常用于窗口拖动、页面滚动等场景。
报文只说明起始坐标,区域的长度和宽度由 [FramebufferUpdateRectangle](/rfc6143/transfer/display.md#FramebufferUpdateRectangle) 指定。
```
+--------------+--------------+----------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+----------------+
| 2 | U16 | src-x-position |
| 2 | U16 | src-y-position |
+--------------+--------------+----------------+
```
- src-x-position/src-y-position: 源图像的起点坐标
================================================
FILE: docs/rfc6143/transfer/encoding/raw.md
================================================
# Raw 原始编码
Raw 是最简单也是最原始的编码,直接向客户端传递位图信息,不进行编码优化。
在 Raw 格式中,像素按从左到右,从上到下的顺序排列成一维数组。
> 协议要求客户端必须支持 Raw 类型编码。
>
> 协议要求服务器必须传输 Raw 类型编码,除非客户端另有要求。
```
+----------------------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+-------------+
| width*height*bytesPerPixel | PIXEL array | pixels |
+----------------------------+--------------+-------------+
```
- pixels: 像素数组
================================================
FILE: docs/rfc6143/transfer/encoding/rise-and-run-length.md
================================================
# 上升和游程编码 Rise-and-run-length
RRE 是游程编码的二维变种。基本思想是将大的矩形拆分为子矩形,每个子矩形有单值的像素组成,所有小矩形的并集构成原始矩形区域。
编码由背景像素值 Vb 、计数 N ,以及 N 个子矩形列表组成。子矩形由元组 <v,x,y,w,h> 表示,其中 v 是像素值(v != Vb),x/y/w/h 表示子矩形相对主矩形的坐标,和大小。
绘制时,客户端先以背景像素值填充矩形,再绘制每个子矩形,叠加出原始图像。
```
+---------------+--------------+-------------------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+-------------------------+
| 4 | U32 | number-of-subrectangles |
| bytesPerPixel | PIXEL | background-pixel-value |
+---------------+--------------+-------------------------+
```
- number-of-subrectangles: 子矩形数量
- background-pixel-value: 矩形背景色
对于子矩形
```
+---------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------------+
| bytesPerPixel | PIXEL | subrect-pixel-value |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
+---------------+--------------+---------------------+
```
- subrect-pixel-value: 子矩形色值
- x-position/y-position: 与背景矩行的**相对位置**
- width/height: 子矩形宽度和高度
================================================
FILE: docs/rfc6143/transfer/encoding/set-encoding.md
================================================
# 设置编码
客户端用 SetEncoding 消息告知服务端,接受哪些像素[编码](/rfc6143/transfer/encoding/README.md)。
除了用于解析像素的编码外,客户端可以发送伪编码,向服务端请求拓展功能。如果服务端不识别此编码,可以直接忽略。客户端在未收到服务端明确的”支持“回复前,应当默认服务端不支持伪编码。
数据结构如下:
```
+-----------------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+-----------------------+--------------+---------------------+
| 1 | U8 [2] | message-type |
| 1 | | padding |
| 2 | U16 | number-of-encodings |
| 4*number-of-encodings | S32 array | encoding-types |
+-----------------------+--------------+---------------------+
```
- message-type: 消息类型,固定为 `2`
- number-of-encodings: 编码数量
- encoding-types: [编码标识符](/rfc6143/transfer/encoding/README.md)
================================================
FILE: docs/rfc6143/transfer/encoding/tight-png.md
================================================
# 严格Png编码 Tight Encoding
严格编码为像素数据提供了有效的压缩。为了减少执行的复杂性,任何Tight编码的矩形的宽度不能超过2048像素。
如果需要一个更宽的矩形,必须将其分成几个矩形,每个矩形都要单独编码。
每个Tight-encoded矩形的第一个字节是一个压缩控制字节。
```
+--------------+--------------+----------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+----------------+
| 1 | U8 | 压缩控制字节 |
+--------------+--------------+----------------+
```
压缩控制字节中最低有效位四位标识了客户端在解码矩形之前应该重置哪些zlib压缩流。
每个位都是独立的,对应于一个应该被重置的单独的zlib流。
```
+-----+----------------+
| Bit | Description |
+-----+----------------+
| 0 | Reset stream 0 |
| 1 | Reset stream 1 |
| 2 | Reset stream 2 |
| 3 | Reset stream 3 |
+-----+----------------+
```
在Tight编码中,支持三种可能的压缩方法之一。它们是`BasicCompression`, `FillCompression`和`JpegCompression`。
如果`压缩控制字节`的第7位(最高位)是0,那么压缩类型是BasicCompression。
在这种情况下,压缩控制的第7-4位(最重要的4位)应该被解释为如下:
```
+--------------+--------------+----------------+
| Bits | Binary value | Description |
+--------------+--------------+----------------+
| 5-4 | 00 | Use stream 0 |
| | 01 | Use stream 1 |
| | 10 | Use stream 2 |
| | 11 | Use stream 3 |
| 6 | 0 | --- |
| | 1 | read-filter-id |
| 7 | 0 | BasicCompression|
+--------------+--------------+----------------+
```
否则,如果`压缩控制字节`的第7位设置为1,则压缩方法是`FillCompression`或`JpegCompression`,取决于同一字节的其他位:
```
+--------------+--------------+----------------+
| Bits | Binary value | Description |
+--------------+--------------+----------------+
| 7-4 | 1000 | FillCompression|
| | 1001 | JpegCompression|
| | any other | Invalid |
+--------------+--------------+----------------+
```
> 注意:`JpegCompression` 只能在像素编码为 16 或 32 并且客户端已使用 ` JPEG Quality Level Pseudo-encoding`。
Tight 编码使用一种新型 `TPIXEL`(Tight 像素)。
这与商定的像素格式的 PIXEL 相同,除了真彩色标志非零、每像素位数为 32、深度为 24 以及构成红色、绿色和蓝色强度的所有位正好是 8 位宽。
在这种情况下,`TPIXEL` 只有 3 个字节长,其中第一个字节是红色分量,第二个字节是绿色分量,第三个字节是像素颜色值的蓝色分量。
> 压缩控制字节之后的数据取决于压缩方法。
- FillCompression
如果压缩类型为 `FillCompression`,则后面是唯一的像素值,采用 `TPIXEL` 格式。此值适用于矩形的所有像素。
- JpegCompression
如果压缩类型为 `JpegCompression`,则以下数据流如下所示:
```
+--------------+--------------+----------------+
| No. of bytes | Type | Description |
+--------------+-----------+-------------------+
| 1-3 | | jpeg数据的长度 |
| length | U8 array | jpeg-data |
+--------------+-----------+-------------------+
```
根据以下方案,长度动态压缩表示为一个、两个或三个字节:
```
+----------------------------+---------------------------+
| Value | Description |
+----------------------------+---------------------------+
| 0xxxxxxx | for values 0..127 |
| 1xxxxxxx 0yyyyyyy | for values 128..16383 |
| 1xxxxxxx 1yyyyyyy zzzzzzzz | for values 16384..4194303 |
+----------------------------+---------------------------+
```
这里每个字符表示一位,`xxxxxxx` 是值的最低 7 位(位 0-6),`yyyyyyy` 是 7-13 位,`zzzzzzzz` 是最高有效 8 位(14-21 位)。例如,十进制值 10000 应表示为两个字节:二进制 10010000 01001110,或十六进制 90 4E。
> jpeg-data 是 JFIF 流
- BasicCompression
如果压缩类型是 `BasicCompression` 并且压缩控制字节的第 6 位(read-filter-id 位)设置为 1,
则下一个(第二个)字节指定 `filter-id`,它告诉解码器使用什么过滤器类型编码器在压缩之前对像素数据进行预处理。
`filter-id` 字节可以是以下之一:
```
+--------------+-------+-----------+-------------------------+
| No. of bytes | Type | [Value] | Description |
+--------------+-------+-----------+-------------------------+
| 1 | U8 | | filter-id |
| | | 0 | CopyFilter (no filter) |
| | | 1 | PaletteFilter |
| | | 2 | GradientFilter |
+--------------+-------+-----------+-------------------------+
```
如果压缩控制字节的第 6 位设置为 0(无过滤器 ID 字节),则使用 `CopyFilter`。
- `CopyFilter`
当 `CopyFilter` 处于活动状态时,将压缩 `TPIXEL` 格式的原始像素值。有关压缩的详细信息,请参见下文。
- `PaletteFilter`
`PaletteFilter` 将真彩色像素数据转换为索引颜色和可由 2..256 种颜色组成的调色板。
如果颜色数为 2,则每个像素用 1 位编码,否则 8 位用于编码一个像素。
1位编码的执行方式是最高有效位对应于最左边的像素,并且每行像素都与字节边界对齐。
使用 `PaletteFilter` 时,调色板在像素数据之前发送。调色板以一个无符号字节开始,其值是调色板中的颜色数减 1(即 1 表示 2 种颜色,255 表示调色板中的 256 种颜色)。
然后是调色板本身,它由 `TPIXEL` 格式的像素值组成。
- `GradientFilter`
GradientFilter 使用一种简单的算法对像素数据进行预处理,该算法将每个颜色分量转换为“预测”强度和实际强度之间的差异。这种技术不会影响未压缩的数据大小,但有助于更好地压缩类似照片的图像。将强度转换为差异的伪代码如下:
```
P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
if (P[i,j] < 0) then P[i,j] := 0;
if (P[i,j] > MAX) then P[i,j] := MAX;
D[i,j] := V[i,j] - P[i,j];
```
这里 `V[i,j]` 是坐标 `(i,j)` 处像素的颜色分量的强度。对于当前矩形之外的像素,假设 `V[i,j]` 为零(这与 `P[i,0]` 和 `P[0,j]` 相关)。
MAX 是颜色分量的最大强度值。
> 注意:`GradientFilter` 只能在 bits-per-pixel 为 16 或 32 时使用。
使用上述三个过滤器之一过滤像素数据后,使用 `zlib` 库对其进行压缩。
但是,如果在应用过滤器之后但在压缩之前的数据大小小于 12,则数据按原样发送,未压缩。
可以使用四个单独的 zlib 流(0..3),解码器应该从压缩控制字节中读取实际的流 id。
如果不使用压缩,则按原样发送像素数据,否则数据流如下所示:
```
+--------------+--------------+----------------+
| No. of bytes | Type | Description |
+--------------+-----------+-------------------+
| 1-3 | | zlib数据的长度 |
| length | U8 array | zlibData |
+--------------+-----------+-------------------+
```
长度紧凑地表示为一个、两个或三个字节,就像在 JpegCompression 方法中一样(见上文)。
如果压缩控制字节中的某些位 0、1、2 和 3 设置为 1,则解码器必须在解码矩形之前重置 `zlib` 流。请注意,即使压缩类型为解码器,解码器也必须重置指示的 zlib 流是 `FillCompression` 或 `JpegCompression`。
================================================
FILE: docs/rfc6143/transfer/encoding/tiled-run-length.md
================================================
# 图块游程编码 TRLE
TRLE(Tiled Run-Length Encoding),使用图块压缩技术、调色板技术以及游程编码,对传输的桌面图像进行压缩。
在图块压缩技术中,传输图像被分为 N 个 16x16 的小图块,图块按从左到右,从上到下的顺序排列成一位数组。如果传输图像的尺寸不是 16 的整数倍,最后一列/最后一行图块的宽度/高度应为实际宽度/实际高度(小于16)。
TRLE 使用 CPIXEL(compressed pixel) 表示像素。CPIXEL 同样由 [PIXEL_FORMAT](/rfc6143/transfer/pixel-format.md) 定义。
在特殊情况下,CPIXEL 使用更紧凑的结构表示像素。
假如 PIXEL_FORMAT 中 true-color-flag 是非零值,bits-per-pixel 是 32,depth 是 24 或更小,只会用低 3 bit 或者高 3 bit 来表达红、绿、蓝。
## 子编码
图块使用头部单字节表示图块的子编码类型。
子编码类型的最高比特表示当前图块是否使用游程编码。
低 7-bit 指示调色板的大小。
```
+---------------+--------------+------------------+
| No. of bits | Type [Value] | Description |
+------------------------------+------------------+
| 1 | bit | run-length-flag |
| 2-8 | bit | palette-length |
+------------------------------+------------------+
```
TRLE 对自编码的值有单独定义
| 十六进制 | 十进制 | 含义 |
|-------------|-----------|-------------------------------|
| 0x00 | 0 | [Raw 模式](#raw-模式),不使用游程和调色板 |
| 0x01 | 1 | [纯色模式](#纯色模式),图块使用同一种颜色 |
| 0x02 - 0x10 | 2 - 16 | [打包像素调色板模式](#打包像素调色板模式) |
| 0x11 - 0x7E | 17 - 126 | 保留 |
| 0x7F | 127 | [打包像素调色板模式](#打包像素调色板模式),复用调色板 |
| 0x80 | 128 | [RLE 模式](#rle-模式) |
| 0x81 | 129 | [调色板游程编码](#调色板游程编码),复用调色板 |
| 0x82 - 0xFF | 130 - 255 | [调色板游程编码](#调色板游程编码) |
### Raw 模式
Raw 模式直接传输像素值,像素在图块中按从左到右、从上到下排列。不使用游程编码,不使用调色板。
```
+-----------------------------+----------------+--------------+
| No. of bytes | Type [Value] | Description |
+-----------------------------+----------------+--------------+
| 1 | SubEncoding[0] | sub-encoding |
| width*height*BytesPerCPixel | CPIXEL array | pixels |
+-----------------------------+----------------+--------------+
```
### 纯色模式
图块使用同一种颜色。
```
+----------------+----------------+--------------+
| No. of bytes | Type [Value] | Description |
+----------------+----------------+--------------+
| 1 | SubEncoding[1] | sub-encoding |
| bytesPerCPixel | CPIXEL | pixelValue |
+----------------+----------------+--------------+
```
### 打包像素调色板模式
打包像素调色板模式。使用调色板定义颜色,使用颜色在调色板中的偏移量表达颜色。由于调色板较小,可以将多个像素的色值打包到一个字节中存储,进一步压缩体积。
每个像素使用的 bit 数由调色板大小确定,N=log2 M。
如果像素的数量不是 2/4/8 的倍数,需要添加填充位,对齐字节。
| 调色板大小 | 像素比特数 | 打包后字节数 |
|:-----:|:-----:|-----------------------------|
| =2 | 1 | m = (width+7) // 8 *height |
| <=4 | 2 | m = (width+3) // 4 * height |
| <=16 | 4 | m = (width+1) // 2 * height |
```
+----------------------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+--------------+
| 1 | SubEncoding | sub-encoding |
| paletteSize*bytesPerCPixel | CPIXEL array | palette |
| m | U8 array | packedPixels |
+----------------------------+--------------+--------------+
```
### RLE 模式
RLE 模式,不使用调色板,使用游程编码。在计算游程时,允许跨行计算。
长度由一个或多个字节表示,到第一个不是 255(0xFF)停止。
```
+-------------------------+------------------+-----------------------+
| No. of bytes | Type [Value] | Description |
+-------------------------+------------------+-----------------------+
| 1 | SubEncoding[128] | sub-encoding |
| bytesPerCPixel | CPIXEL | pixelValue |
| div(runLength - 1, 255) | U8 array | 255 |
| 1 | U8 | (runLength-1) mod 255 |
+-------------------------+------------------+-----------------------+
```
例如:
| 游程长度 | 字节表示 |
|:----:|:--------:|
| 1 | 0x00 |
| 255 | 0xFE |
| 256 | 0xFF00 |
| 257 | 0xFF01 |
| 510 | 0xFFFE |
| 511 | 0xFFFF00 |
### 调色板游程编码
调色板游程编码。使用调色板,游程部分类似[RLE 模式](#rle-模式),将像素放入数组后,使用像素加游程的方式表示整个色块,像素用调色板偏移量表示。
```
+----------------------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+--------------+
| 1 | SubEncoding | sub-encoding |
| paletteSize*bytesPerCPixel | CPIXEL array | palette |
+----------------------------+--------------+--------------+
```
- 长度为1的游程,仅由调色板索引表示
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 | paletteIndex |
+--------------+--------------+--------------+
```
- 长度大于1的游程,由调色板索引+128和游程长度表示
```
+-------------------------+--------------+-----------------------+
| No. of bytes | Type [Value] | Description |
+-------------------------+--------------+-----------------------+
| 1 | U8 | paletteIndex + 128 |
| div(runLength - 1, 255) | U8 array | 255 |
| 1 | U8 | (runLength-1) mod 255 |
+-------------------------+--------------+-----------------------+
```
================================================
FILE: docs/rfc6143/transfer/encoding/zlib-run-length.md
================================================
# zlib 压缩游程编码
ZRLE 融合游程编码、Zlib 压缩算法、图块压缩算法对传输图像进行压缩。
ZRLE 使用 Zlib 进行编码和解码,要求流数据严格有序。
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 4 | U32 | length |
| length | U8 array | zlibData |
+--------------+--------------+-------------+
```
- length: 流(stream)长度
- zlibData: 流数据
ZlibData 解压后,是从左到右,从上到下排列的图块数据。图块大小是 64x64,其他跟 TRLE 一致。对于长度和宽度不足的图形,其最后一排和最后一列的宽/高是实际尺寸,不做补齐。
================================================
FILE: docs/rfc6143/transfer/input/README.md
================================================
# 输入协议
RFB 协议支持鼠标和键盘两种输入设备,同时支持剪贴板进行远程复制粘贴。
================================================
FILE: docs/rfc6143/transfer/input/clipboard.md
================================================
# 剪贴板
复制粘贴是双向事件,可以由客户端向服务端复制,也可以由服务端向客户端复制。
## 协议报文
复制剪贴板的报文如下:
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [3/6] | message-type |
| 3 | | padding |
| 4 | U32 | length |
| length | U8 array | text |
+--------------+--------------+--------------+
```
- message-type: 消息类型,客户端是 `0x6`,服务端是 `0x3`
- length: 文本长度
- text: 复制粘贴的文本,长度由 length 限制
协议有几点限制
- 只支持 ISO 8859-1 (Latin-1) 字符集
- 使用单独换行符 `0x0a`,不应该使用回车符 `0x0d`
## 拓展剪贴板伪协议
> RFB 3.8 协议限制,剪贴板只能传输 Latin-1 字符集。
> 2016年,Cendio Ossman 将 [Extended Clipboard Pseudo-Encoding](https://github.com/rfbproto/rfbproto/commit/08018f655acd52970680b34021159924357efb5d) 合入协议主分支,支持在剪贴板消息中传输 unicode 字符集。
> UltraVNC/TigerVNC/RealVNC 服务端都支持此拓展协议,x11vnc 尚未提供支持(2021/8/11)。
拓展剪贴板伪协议需要客户端和服务端软件同时支持。报文拓展了 `ServerCutText` 和 `ClientCutText`, 如下:
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [3/6] | message-type |
| 3 | | padding |
| 4 | S32 | length |
| 4 | U32 | text-type |
| length-4 | U8 array | text |
+--------------+--------------+--------------+
```
- length: 数据由 U32 改为 S32。首bit是标志位,0 表示传递原始 Latin-1 消息,1 表示传递拓展信息。abs(length) 是实际的消息长度。
- text-type: 消息头部,指示消息类型
- text: 消息内容
### 消息类型
消息用 4 字节的 text-type 作为头部。
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U32 | message-type |
+--------------+--------------+--------------+
```
text-type 分为指令(`action`)和格式(`formats`)两类。指令传输操作命令,格式传输剪贴板内容。
text-type 标记的含义如下:
| Bit | Name | Description |
|-|-|---|
| 0 | [text](#文本内容) | 文本内容 |
| 1| rtf | 微软富文本格式 |
| 2 | html | 微软 HTML 格式 |
| 3 | dib | Microsoft Device Independent Bitmap |
| 4 | files | 文件,暂未实现 |
| 5-15| fotmats 保留位 |
| 16-23 | 保留 |
| 24 | [caps](#能力声明) | 指示支持的 text-type 和最大长度 |
| 25 | request | 强制对端传递剪贴板内容 |
| 26 | peek | 强制对端提供支持的 text-type |
| 27 | notify | peek 回包,返回支持的 text-type |
| 28 | [provide](#粘贴板内容) | request 回包,返回粘贴板内容 |
| 29-31 | actions 保留位 | |
### 文本内容 text
纯文本,unicode 编码的无格式文本。以 `\r\n` 作为行的结尾,原始协议的换行符是 `\n`。
文本应该以 `\0` 结尾,即使在 ClientCutText/ServerCutText 中都声明了文本长度。
### 能力声明 caps
Caps 指示期望收到的文本类型,发送的结构是长度数组。数组大小跟格式数量相等(0-15),数组的每个条目,指示格式支持的最大长度。
#### 数据结构
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| formats*4 | U32 array | sizes |
+--------------+--------------+--------------+
```
例如:
```
[1024,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
```
表示只接受 1024 byte 以内的纯文本信息。
#### 行为约束
服务端收到支持拓展剪贴板协议的 SetEncodings 报文时,必须主动发送类型为 caps 的 ServerCutText 消息。
客户端收到 caps 消息时,应该发送类型为 caps 的 ClientCutText 消息作为回应。
否则,客户端默认会接受 text/rtf/html/request/notify/provide 消息,其中 text 默认长度为 20 Mib,其他为 0 字节。
当最大长度限制为 0 时,认为长度没有限制,如果内容长度大于声明的长度限制,则剪贴板变动的消息不会被发送。建议将所有的 caps 设置为 0,以便接受所有的剪贴板消息变动。
> 某些实现的默认行为与协议描述不一致,例如:
> - dib 也是默认支持的格式
> - text 的默认限制是 10Mid
> - rft/html 默认限制为 2Mib
> - dib 默认限制为 0 字节
> - 客户端忽略 caps 消息建议的格式和长度限制
在发送 caps 之前,只能发送指令,不能发送格式和内容。
### 粘贴板内容
粘贴板内容的 text-type 是 provide。在剪贴板变化,或对端发送 request 后发送。
在 text-type 后面,是 Zlib 压缩的字节流。对于每种支持的 text-type,会发送 size + data 数据对。
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 4 | U32 | size |
| size | U8 array | data |
+--------------+--------------+--------------+
```
================================================
FILE: docs/rfc6143/transfer/input/keyboard.md
================================================
# 键盘事件
在正常理解中,键盘事件的处理应该是简单明了的。参考以下协议报文
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [4] | message-type |
| 1 | U8 | down-flag |
| 2 | | padding |
| 4 | U32 | key |
+--------------+--------------+--------------+
```
- message-type: 固定为 `0x4`
- down-flag: `1` 表示键位按下,`0` 表示弹起
- pending: 对齐字节,方便解析
- key: 表示具体的键位
其中 key 的值在 X 系统中有[明确定义](https://www.x.org/releases/X11R7.6/doc/xproto/x11protocol.html#keysym_encoding)
```
+-----------------+--------------------+
| Key name | Keysym value (hex) |
+-----------------+--------------------+
| BackSpace | 0xff08 |
| Tab | 0xff09 |
| Return or Enter | 0xff0d |
| Escape | 0xff1b |
| Insert | 0xff63 |
| Delete | 0xffff |
| Home | 0xff50 |
| End | 0xff57 |
| Page Up | 0xff55 |
| Page Down | 0xff56 |
| Left | 0xff51 |
| Up | 0xff52 |
| Right | 0xff53 |
| Down | 0xff54 |
| F1 | 0xffbe |
| F2 | 0xffbf |
| F3 | 0xffc0 |
| F4 | 0xffc1 |
| ... | ... |
| F12 | 0xffc9 |
| Shift (left) | 0xffe1 |
| Shift (right) | 0xffe2 |
| Control (left) | 0xffe3 |
| Control (right) | 0xffe4 |
| Meta (left) | 0xffe7 |
| Meta (right) | 0xffe8 |
| Alt (left) | 0xffe9 |
| Alt (right) | 0xffea |
+-----------------+--------------------+
```
## 组合键
组合键指 `Ctrl + Alt + Del` 或 `Shift + 3` 等组合按键。受不同的操作系统、键盘布局影响,组合键是按键事件中容易发生歧义的一环。
RFB 基本遵循以下规则:
- 如果客户端 key 在 `keysym` 中存在,服务端应该遵循 `keysym` 的指示,尽可能的忽略客户端传递 `Shift`、`CpasLock` 等键位,在需要时,应该主动补充/忽略 `Shift` 等键位。例如,在 US 键盘布局中,`#` 需要按下 `Shift + 3`,但是在 UK 布局中不需要。这就意味着用户在输入 `#` 的时候不会输入 `Shift`。这种情况下,服务端应该主动模拟一个 `Shift` 状态,防止输入的键位是 `3`。同理,如果 key 输入的键位是 `A`,服务端统一要模拟一个 `Shift`,保证输入的是 `A` 而不是 `a`。
- 如果客户端 key 在 `keysym` 中不存在(例如 `Ctrl + A`),服务端应该遵循客户端指示,客户端应该主动在 `A` 前发送 `Ctrl` 的按键。
- 如果客户端通过 `Ctrl + Alt + Q` 来输入 `@`,客户端应该在发送 `Ctrl`/`Alt`/`@`后,主动发送`Ctrl`/`Alt`的弹起事件。
- 对于 `BackTab`,常见的有三种实现,`ISO_Left_Tab` `BackTab` 和 `Shift + Tab`。RFB 协议优先使用 `Shift + Tab`,但对于其他的键位,服务端和客户端应当尽量提供兼容。
- 优先使用 `ASCII` 而不是 `unicode`
- 对于 `Ctrl + Alt + Del` 等无法被客户端操作系统拦截的按键(系统拦截有更高优先级),客户端应该提供操作按钮
================================================
FILE: docs/rfc6143/transfer/input/mouse.md
================================================
# 鼠标事件
鼠标指针即鼠标操作事件,分移动事件和点击事件两种。在 RFB 协议中,使用 PointerEvent 表示客户端触发一次鼠标事件。
## 协议报文
鼠标事件的报文如下
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [5] | message-type |
| 1 | U8 | button-mask |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
+--------------+--------------+--------------+
```
- message-type: 无符号整形,固定 `0x5`
- button-mask: 8 位掩码,表示键位状态,`1`为按下,`0`为弹起
- x-position: 当前 X 坐标
- y-position: 当前 Y 坐标
button-mask 的 Bit0/1/2 位置分别代表鼠标的左键、中建、右键。使用滚轮的鼠标,每向上滑动一次,会发送一个 Bit3 的按下和弹起事件;每向下滑动一次,会发送一个 Bit4 的按下和弹起事件。
## USB/PS/2 鼠标协议
在 PS/2 或 USB 鼠标协议中,有类似 RFB 协议的表达方式。
```
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ │ Bit7 │ Bit6 │ Bit5 │ Bit4 │ Bit3 │ Bit2 │ Bit1 │ Bit0 │
├──────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│Byte 1│Y Over│X Over│X Sign│Y Sign│ 0x1 │ Mid │Right │ Left │
├──────┼──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┤
│Byte 2│ X Movement │
├──────┼───────────────────────────────────────────────────────┤
│Byte 3│ Y Movement │
├──────┼───────────────────────────────────────────────────────┤
│Byte 4│ Z Movement │
└──────┴───────────────────────────────────────────────────────┘
```
鼠标协议和 RFB 协议有两点不同:
- 鼠标协议的 Bit0/1/2 和 RFB 协议的 Bit0/2/1 对应
- 鼠标协议的中轮转动,需要转换为 RFB 协议的按下/弹起事件
> 根据 [wiki](https://en.wikipedia.org/wiki/RFB_protocol#Limitations) 描述,RFB 协议只支持8个鼠标键位,左中右和滑轮占用5个,只有3个键位给特殊按键(例如:多功能游戏鼠标)
================================================
FILE: docs/rfc6143/transfer/pixel-format.md
================================================
# 像素格式
[帧缓冲](/rfc6143/GLOSSORY.md#帧缓冲)由像素构成。

## PixelFormat
RFB 协议中,使用 16 字节结构体 PIXEL_FORMAT 描述像素的格式。
```
+--------------+--------------+-----------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-----------------+
| 1 | U8 | bits-per-pixel |
| 1 | U8 | depth |
| 1 | U8 | big-endian-flag |
| 1 | U8 | true-color-flag |
| 2 | U16 | red-max |
| 2 | U16 | green-max |
| 2 | U16 | blue-max |
| 1 | U8 | red-shift |
| 1 | U8 | green-shift |
| 1 | U8 | blue-shift |
| 3 | | padding |
+--------------+--------------+-----------------+
```
- bits-per-pixel: 像素的位数,位数越大,色彩越丰富。只支持[8|16|32]
- depth: 色深,像素中表示色彩的位数
- big-endian-flag: 多字节像素的字节序,非零即大端序
- true-color-flag: 1 表示真彩色,pixel 的值表示 RGB 颜色;0 表示调色板,pexel 的值表示颜色在调色板的偏移量
- -max/-shift: 获取红蓝绿三色的位移量和长度,max=2^N-1,N是颜色的位数
```
BigEndian: Blue Shift Green Shift Red Shift
│ │ │
▼ ▼ ▼
┌──────────────────┬─────────────────┬─────────────────┐
│ BLUE MAX │ GREEN MAX │ RED MAX │
└──────────────────┴─────────────────┴─────────────────┘
```
> bits-per-pixel 必须大于或等于 depth
## SetPixelFormat
客户端发送 SetPixelFormat,声明需要的的像素格式(画面质量)。此消息覆盖 [ServerInit](/rfc6143/handshake/initial.md#服务端初始化) 消息中服务端声明的初始化像素格式。
当 true-color-flag 为 0 时,服务端必须发送 SetColorMapEntries,声明使用的颜色表。客户端发送 SetPixelFormat 后,需清空本地缓存的颜色表,无论颜色表中是否有内容。
```
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [0] | message-type |
| 3 | | padding |
| 16 | PIXEL_FORMAT | pixel-format |
+--------------+--------------+--------------+
```
- message-type: 消息类型,固定为 0
- pixel-format: [PixelFormat](#pixelformat) 结构
================================================
FILE: docs/rfc6143/transfer/set-color-map.md
================================================
# 设置颜色表
当 PIXEL_FORMAT 的 true-color-flag 字段被设置为 0 时,服务端使用颜色表表示像素的颜色。
`SetColorMapEntries` 用于设置颜色表的内容。
```
+--------------+--------------+------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+------------------+
| 1 | U8 [1] | message-type |
| 1 | | padding |
| 2 | U16 | first-color |
| 2 | U16 | number-of-colors |
+--------------+--------------+------------------+
```
- message-type: 消息类型,固定是 `1`
- first-color: [未知](https://github.com/rfbproto/rfbproto/issues/42)
- number-of-colors: 颜色的数量
## 色值
颜色表的值总是 3 个 16 bits,代表红、绿、蓝三种颜色,每个颜色的范围是 0-65535。
例如,白色的色值是 65535,65535,65535。
```
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 2 | U16 | red |
| 2 | U16 | green |
| 2 | U16 | blue |
+--------------+--------------+-------------+
```
================================================
FILE: docs/screenshot/.keep
================================================
================================================
FILE: docs/screenshot/README.md
================================================
## Screenshot
代码路径在`./cmd/screenshot`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译
### 获取帮助信息
```shell
# 查看帮助信息
$ ./screenshot --help
# 查看版本信息
$ ./screenshot version
```
### 启动Screenshot 获取vnc服务器的屏幕截图
```shell
# imageFile 要生成的截图地址,暂时只支持jpeg格式(必填)
# vncHost 要连接的vnc服务端地址(必填)
# vncPort 要连接的vnc服务端端口(必填)
# vncPassword 要连接的vnc服务端密码,不传则使用auth none
$ ./screenshot --imageFile=./screen.jpeg --vncHost=127.0.0.1 --vncPort=5900 --vncPassword=12345612
```
================================================
FILE: docs/summary.md
================================================
* 快速入门
* [项目介绍](README.md)
* [快速开始](overview.md)
* [常见问题](questions.md)
* [版本更新记录](changelog.md)
* 代理(Proxy)
- [使用方式](proxy/README.md)
* 屏幕录像(Recorder)
- [使用方式](recorder/README.md)
* 屏幕录像播放(Player)
- [使用方式](player/README.md)
* 截屏(Screenshot)
- [使用方式](screenshot/README.md)
* 屏幕录像转视频(Video)
* RFB协议详解
- [远程帧缓冲协议](rfc6143/README.md)
- [术语表](rfc6143/GLOSSORY.md)
- [握手](rfc6143/handshake/README.md)
- [协议版本握手](rfc6143/handshake/protocol-version.md)
- [安全握手](rfc6143/handshake/security-type.md)
- [初始化](rfc6143/handshake/initial.md)
- [数据交互](rfc6143/transfer/README.md)
- [显示协议](rfc6143/transfer/display.md)
- [像素格式](rfc6143/transfer/pixel-format.md)
- [输入协议](rfc6143/transfer/input/README.md)
- [鼠标事件](rfc6143/transfer/input/mouse.md)
- [键盘事件](rfc6143/transfer/input/keyboard.md)
- [剪贴板](rfc6143/transfer/input/clipboard.md)
- [编码](rfc6143/transfer/encoding/README.md)
- [设置编码](rfc6143/transfer/encoding/set-encoding.md)
- [Raw 原始编码](rfc6143/transfer/encoding/raw.md)
- [CopyRect 镜像编码](rfc6143/transfer/encoding/copy-rect.md)
- [RRE 上升游程编码](rfc6143/transfer/encoding/rise-and-run-length.md)
- [TRLE 图块游程编码](rfc6143/transfer/encoding/tiled-run-length.md)
- [ZRLE Zlib游程编码](rfc6143/transfer/encoding/zlib-run-length.md)
================================================
FILE: docs/video/.keep
================================================
================================================
FILE: docs/video/README.md
================================================
================================================
FILE: encodings/default_encoding.go
================================================
package encodings
import "github.com/vprix/vncproxy/rfb"
var (
DefaultEncodings = []rfb.IEncoding{
&ZRLEEncoding{},
&TightEncoding{},
&HexTileEncoding{},
&TightPngEncoding{},
&RREEncoding{},
&ZLibEncoding{},
&CopyRectEncoding{},
&CoRREEncoding{},
&RawEncoding{},
&CursorPseudoEncoding{},
&DesktopNamePseudoEncoding{},
&DesktopSizePseudoEncoding{},
&CursorPosPseudoEncoding{},
&ExtendedDesktopSizePseudo{},
&CursorWithAlphaPseudoEncoding{},
&LedStatePseudo{},
&LastRectPseudo{},
&FencePseudo{},
&XCursorPseudoEncoding{},
}
)
================================================
FILE: encodings/encoding.go
================================================
package encodings
import (
"encoding/binary"
"errors"
"github.com/vprix/vncproxy/internal/dbuffer"
"github.com/vprix/vncproxy/rfb"
"io"
)
func ReadUint8(r io.Reader) (uint8, error) {
var myUint uint8
if err := binary.Read(r, binary.BigEndian, &myUint); err != nil {
return 0, err
}
return myUint, nil
}
func ReadUint16(r io.Reader) (uint16, error) {
var myUint uint16
if err := binary.Read(r, binary.BigEndian, &myUint); err != nil {
return 0, err
}
return myUint, nil
}
func ReadUint32(r io.Reader) (uint32, error) {
var myUint uint32
if err := binary.Read(r, binary.BigEndian, &myUint); err != nil {
return 0, err
}
return myUint, nil
}
func ReadBytes(count int, r io.Reader) ([]byte, error) {
buff := dbuffer.GetByteBuffer()
defer dbuffer.ReleaseByteBuffer(buff)
buff.ChangeLen(count)
lengthRead, err := io.ReadFull(r, buff.B)
if lengthRead != count {
return nil, errors.New("ReadBytes unable to read bytes")
}
if err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func ReadPixel(c io.Reader, pf *rfb.PixelFormat) ([]byte, error) {
px := make([]byte, int(pf.BPP/8))
if err := binary.Read(c, pf.Order(), &px); err != nil {
return nil, err
}
return px, nil
}
================================================
FILE: encodings/encoding_copyrect.go
================================================
package encodings
import (
"encoding/binary"
"github.com/vprix/vncproxy/rfb"
)
// CopyRectEncoding 该编码方式对于客户端在某些已经有了相同的象素数据的时候是非常简单和有效的。
// 这种编码方式在网络中表现为x,y 坐标。让客户端知道去拷贝那一个矩形的象素数据。
// 它可以应用于很多种情况。最明显的就是当用户在屏幕上移动某一个窗口的时候,还有在窗口内容滚动的时候。
// 在优化画的时候不是很明显,一个比较智能的服务器可能只会发送一次,因为它知道在客户端的帧缓存里已经存在了。
// 复制矩形编码并不是完全独立地发送所有的数据矩形,而是对于像素值完全相同的一组矩形,
// 只发送第一个矩形全部数据,随后的矩形则只需要发送左上角X、Y坐标。
// 实际上,复制矩形编码主要指的就是随后的这一系列X、Y坐标,而对于第一个矩形具体采用何种编码类型并没有限制,
// 仅仅需要知道第一个矩形在帧缓冲区中的位置,以便于完成复制操作。
// 因此,往往是把复制矩形编码和其它针对某一个矩形的编码类型结合使用。
type CopyRectEncoding struct {
SX, SY uint16
}
func (that *CopyRectEncoding) Type() rfb.EncodingType {
return rfb.EncCopyRect
}
func (that *CopyRectEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *CopyRectEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &CopyRectEncoding{}
if len(data) > 0 && data[0] {
obj.SX = that.SX
obj.SY = that.SY
}
return obj
}
func (that *CopyRectEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Read(session, binary.BigEndian, &that.SX); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.SY); err != nil {
return err
}
return nil
}
func (that *CopyRectEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Write(session, binary.BigEndian, that.SX); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.SY); err != nil {
return err
}
return nil
}
================================================
FILE: encodings/encoding_corre.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"github.com/vprix/vncproxy/rfb"
)
// CoRREEncoding CoRRE是RRE的变体,它把发送的最大矩形限制在255×255个像素以内,用一个字节就能表示子矩形的维度。
// 如果服务器想要发送一个超出限制的矩形,则只要把它划分成几个更小的RFB矩形即可。
// “对于通常的桌面,这样的方式具有比RRE更好的压缩度”。
// 实际上,如果进一步限制矩形的大小,就能够获得最好的压缩度。“矩形的最大值越小,决策的尺度就越好”。
// 但是,如果把矩形的最大值限制得太小,就增加了矩形的数量,而由于每个RFB矩形都会有一定的开销,结果反而会使压缩度变差。
// 所以应该选择一个比较恰当的数字。在目前的实现中,采用的最大值为48×48。
type CoRREEncoding struct {
buff *bytes.Buffer
}
func (that *CoRREEncoding) Type() rfb.EncodingType {
return rfb.EncCoRRE
}
func (that *CoRREEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *CoRREEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &CoRREEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *CoRREEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
pf := session.Options().PixelFormat
// 子矩形的数量
var numOfSubRectangles uint32
if err := binary.Read(session, binary.BigEndian, &numOfSubRectangles); err != nil {
return err
}
if err := binary.Write(that.buff, binary.BigEndian, numOfSubRectangles); err != nil {
return err
}
// (backgroundColor + (color=BPP + x=8b + y=8b + w=8b + h=8b))
size := uint32(pf.BPP/8) + (uint32((pf.BPP/8)+4) * numOfSubRectangles)
b, err := ReadBytes(int(size), session)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
func (that *CoRREEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) (err error) {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err = that.buff.WriteTo(session)
that.buff.Reset()
return err
}
================================================
FILE: encodings/encoding_h264.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"github.com/vprix/vncproxy/rfb"
)
type H264Encoding struct {
buff *bytes.Buffer
}
var _ rfb.IEncoding = new(H264Encoding)
func (that *H264Encoding) Type() rfb.EncodingType {
return rfb.EncH264
}
func (that *H264Encoding) Supported(session rfb.ISession) bool {
return true
}
func (that *H264Encoding) Clone(data ...bool) rfb.IEncoding {
obj := &ZLibEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *H264Encoding) Read(session rfb.ISession, rectangle *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
size, err := ReadUint32(session)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, size)
if err != nil {
return err
}
flags, err := ReadUint32(session)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, flags)
if err != nil {
return err
}
b, err := ReadBytes(int(size), session)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
func (that *H264Encoding) Write(session rfb.ISession, rectangle *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err := that.buff.WriteTo(session)
that.buff.Reset()
return err
}
================================================
FILE: encodings/encoding_hextile.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/vprix/vncproxy/canvas"
"github.com/vprix/vncproxy/rfb"
"image"
"image/color"
)
const (
HexTileRaw = 1 << 0 // Raw数据:不压缩,直接传送,一般此位置1时其它位都置0
HexTileBackgroundSpecified = 1 << 1 // 包含背景色数据:标志位之后需要接收背景色数据
HexTileForegroundSpecified = 1 << 2 // 包含前景色数据:背景色之后需要接收前景色数据
HexTileAnySubRects = 1 << 3 // 是否含有子块:只要该块中含有两种及两种以上颜色,则此位置1
HexTileSubRectsColoured = 1 << 4 // 子块的颜色:如果含有两种颜色,此位置0,子块颜色用前景色;若该块中含有两种以上的颜色,此位置1,子块颜色需要单独指明
)
// HexTileEncoding 是RRE编码的变种,把屏幕分成16x16象素的小块,每块用Raw或RRE方式转送.
// 通过解释HexTile算法,说明了简单而常用的屏幕传送和压缩算法,希望对屏幕监测、传送相关的工作有所启发
type HexTileEncoding struct {
buff *bytes.Buffer
}
var _ rfb.IEncoding = new(HexTileEncoding)
func (that *HexTileEncoding) Supported(_ rfb.ISession) bool {
return true
}
func (that *HexTileEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &HexTileEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *HexTileEncoding) Type() rfb.EncodingType {
return rfb.EncHexTile
}
// 1. 分割
// a. 传送的图像区域被分为若干个大小为16×16象素的块,如果整个矩形不是16的倍数,则最后1行(或列)的块宽度(或高度)变小
// b. 这些块按从左到右,从上到下的顺序排列,屏幕中变化的块被传送,不变的不被传送.
// c. 由于块大小是16x16,所以块内坐标XY可用一字节表示,WH可用一个字节表示
// 2. 块内部的编码
// a. 计算块内部的颜色数:一种、两种、多种,并记录出现频率最多的颜色为背景色,如果仅有两种颜色,则另一颜色记为前景色
// b. 判断块内颜色数是否为一种,如果是,则先修改传送标志位,然后传送整块大小(0,0,w,h)和背景色,此块传送即完成.
// c. 如果不是一种,则把块拆分成颜色不同的小矩形,方法如下:
// 1. 先把块复制到一块内存区中,以免破坏原始数据,暂称tmpBuf
// 2. 从第一个象素开始判断,该点颜色是否与背景色相同.
// 3. 如果不同,则分别向右和向下求得与该点颜色连续的色块.
// 4. 对比右色块和下色块,取出其中较大的一个,做为一个矩形色块
// 5. 在tmpBuf中把此矩形填成背景色,以避免重复判断
// 6. 继续判断下一象素点……
// d. 记录各个矩形色块的位置(x,y,w,h),如果块内含两种以上颜色,还要记录矩形色块的颜色值
// e. 一边取得矩形色块,一边判断矩形色块描述数据的总长是否大于原始数据,如果大于原始数据,则放弃取色块,标志字节Raw(HexTileRaw)位置
// 1. 以Raw方式直接传送原始数据,此块传送完成
// f. 如果块含两种以上颜色,则将标志位的子块位(HexTileSubRectsColoured)置1,否则置0
// g. 传送标志位,传送矩形色块个数,然后传送各矩形块数据,块中颜色数为2时不需要传送每个矩形块的颜色数据,只传位置即可.
// h. 注意:如果背景色或前景色与前一块(16x16块)相同,则可以不传送背景色或前景色,客户端会默认延用前一块的
func (that *HexTileEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
bytesPerPixel := int(session.Options().PixelFormat.BPP) / 8
// 从上到下
for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 {
th := 16
// 如果整个矩形不是16的倍数,则最后1列的块高度为实际高度
if rect.Y+rect.Height-ty < 16 {
th = int(rect.Y) + int(rect.Height) - int(ty)
}
// 从左到右
for tx := rect.X; tx < rect.X+rect.Width; tx += 16 {
tw := 16
// 如果整个矩形不是16的倍数,则最后1行的块宽度为实际宽度
if rect.X+rect.Width-tx < 16 {
tw = int(rect.X) + int(rect.Width) - int(tx)
}
var bt []byte
// 读取标志位
subEncoding, err := ReadUint8(session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_ = binary.Write(that.buff, session.Options().PixelFormat.Order(), subEncoding)
// 如果是原始编码
if (subEncoding & HexTileRaw) != 0 {
bt, err = ReadBytes(tw*th*bytesPerPixel, session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_, _ = that.buff.Write(bt)
continue
}
// 包含背景色数据
if (subEncoding & HexTileBackgroundSpecified) != 0 {
bt, err = ReadBytes(bytesPerPixel, session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_, _ = that.buff.Write(bt)
}
// 包含前景色数据
if (subEncoding & HexTileForegroundSpecified) != 0 {
bt, err = ReadBytes(bytesPerPixel, session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_, _ = that.buff.Write(bt)
}
// 不包含子块则跳过
if (subEncoding & HexTileAnySubRects) != 0 {
nSubRects, err := ReadUint8(session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_ = binary.Write(that.buff, session.Options().PixelFormat.Order(), nSubRects)
for i := 0; i < int(nSubRects); i++ {
if (subEncoding & HexTileSubRectsColoured) != 0 {
bt, err = ReadBytes(bytesPerPixel, session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_, _ = that.buff.Write(bt)
}
xy, err := ReadUint8(session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_ = binary.Write(that.buff, session.Options().PixelFormat.Order(), xy)
wh, err := ReadUint8(session)
if err != nil {
return gerror.Newf("HextileEncoding.Read: error in hextile reader: %v", err)
}
_ = binary.Write(that.buff, session.Options().PixelFormat.Order(), wh)
}
}
}
}
return nil
}
func (that *HexTileEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle) error {
if sess.Type() == rfb.CanvasSessionType {
return that.draw(sess.Conn().(*canvas.VncCanvas), sess.Options().PixelFormat, rect)
}
var err error
_, err = that.buff.WriteTo(sess)
that.buff.Reset()
return err
}
func (that *HexTileEncoding) draw(cv *canvas.VncCanvas, pf rfb.PixelFormat, rect *rfb.Rectangle) error {
var bgCol *color.RGBA
var fgCol *color.RGBA
var err error
var subEncoding byte
var dimensions byte
var nSubRects uint8
// 从上到下
for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 {
th := 16
// 如果整个矩形不是16的倍数,则最后1列的块高度为实际高度
if rect.Y+rect.Height-ty < 16 {
th = int(rect.Y) + int(rect.Height) - int(ty)
}
// 从左到右
for tx := rect.X; tx < rect.X+rect.Width; tx += 16 {
tw := 16
// 如果整个矩形不是16的倍数,则最后1行的块宽度为实际宽度
if rect.X+rect.Width-tx < 16 {
tw = int(rect.X) + int(rect.Width) - int(tx)
}
subEncoding, err = ReadUint8(that.buff)
if err != nil {
return fmt.Errorf("HextileEncoding.Read: error in hextile reader: %v", err)
}
// 如果是原始编码
if (subEncoding & HexTileRaw) != 0 {
err = cv.DecodeRaw(that.buff, &pf, &rfb.Rectangle{X: tx, Y: ty, Width: uint16(tw), Height: uint16(th), EncType: rfb.EncRaw})
if err != nil {
return err
}
continue
}
// 读取单个背景颜色
if (subEncoding & HexTileBackgroundSpecified) != 0 {
bgCol, err = cv.ReadColor(that.buff, &pf)
if err != nil {
return fmt.Errorf("HexTileEncoding.Read: error in hexTile bg color reader: %v", err)
}
}
// 绘制一个矩形
rBounds := image.Rectangle{
Min: image.Point{X: int(tx), Y: int(ty)},
Max: image.Point{X: int(tx) + tw, Y: int(ty) + th},
}
// 填充背景色
cv.FillRect(&rBounds, bgCol)
// 读取前景色
if (subEncoding & HexTileForegroundSpecified) != 0 {
fgCol, err = cv.ReadColor(that.buff, &pf)
if err != nil {
return fmt.Errorf("HexTileEncoding.Read: error in hexTile fg color reader: %v", err)
}
}
if (subEncoding & HexTileAnySubRects) == 0 {
continue
}
// 读取子块的个数
nSubRects, err = ReadUint8(that.buff)
if err != nil {
return err
}
// 是否指定子块的填充颜色,如果未指定,则使用前景色
colorSpecified := (subEncoding & HexTileSubRectsColoured) != 0
for i := 0; i < int(nSubRects); i++ {
var co *color.RGBA
if colorSpecified {
co, err = cv.ReadColor(that.buff, &pf)
if err != nil {
return fmt.Errorf("HexTileEncoding.Read: problem reading color from connection: %v", err)
}
} else {
co = fgCol
}
fgCol = co
dimensions, err = ReadUint8(that.buff) // bits 7-4 for x, bits 3-0 for y
if err != nil {
return fmt.Errorf("HexTileEncoding.Read: problem reading dimensions from connection: %v", err)
}
subTileX := dimensions >> 4 & 0x0f
subTileY := dimensions & 0x0f
dimensions, err = ReadUint8(that.buff) // bits 7-4 for x, bits 3-0 for y
if err != nil {
return fmt.Errorf("HexTileEncoding.Read: problem reading dimensions from connection: %v", err)
}
subTileWidth := 1 + (dimensions >> 4 & 0x0f)
subTileHeight := 1 + (dimensions & 0x0f)
subRectBounds := image.Rectangle{
Min: image.Point{X: int(tx) + int(subTileX), Y: int(ty) + int(subTileY)},
Max: image.Point{X: int(tx) + int(subTileX) + int(subTileWidth), Y: int(ty) + int(subTileY) + int(subTileHeight)},
}
cv.FillRect(&subRectBounds, fgCol)
}
}
}
return nil
}
================================================
FILE: encodings/encoding_jpeg.go
================================================
package encodings
================================================
FILE: encodings/encoding_jrle.go
================================================
package encodings
================================================
FILE: encodings/encoding_raw.go
================================================
package encodings
import (
"bytes"
"errors"
"github.com/vprix/vncproxy/canvas"
"github.com/vprix/vncproxy/rfb"
)
// RawEncoding 采用原始地像素数据,而不进行任何的加工处理。
// 在这种情况下,对于一个宽度乘以高度(即面积)为N的矩形,数据就由N个像素值组成,这些值表示按照扫描线顺序从左到右排列的每个像素。
// 很明显,这种编码方式是最简单的,也是效率最低的。
// RFB要求所有的客户都必须能够处理这种原始编码的数据,并且在客户没有特别指定需要某种编码方式的时候,RFB服务器就默认生成原始编码。
type RawEncoding struct {
buff *bytes.Buffer
}
var _ rfb.IEncoding = new(RawEncoding)
func (that *RawEncoding) Supported(rfb.ISession) bool {
return true
}
func (that *RawEncoding) Type() rfb.EncodingType {
return rfb.EncRaw
}
func (that *RawEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &RawEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *RawEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle) error {
if sess.Type() == rfb.CanvasSessionType {
cv, ok := sess.Conn().(*canvas.VncCanvas)
if !ok {
return errors.New("canvas error")
}
pf := sess.Options().PixelFormat
return cv.DecodeRaw(that.buff, &pf, rect)
}
var err error
_, err = that.buff.WriteTo(sess)
that.buff.Reset()
return err
}
// Read 读取原始色彩表示
func (that *RawEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
pf := session.Options().PixelFormat
// 表示单个像素是使用多少字节表示,分别为 8,16,32,对应的是1,2,4字节
// 知道表示像素的字节长度,则根据宽高就能算出此次传输的总长度
size := int(rect.Height) * int(rect.Width) * int(pf.BPP/8)
b, err := ReadBytes(size, session)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
================================================
FILE: encodings/encoding_rre.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"github.com/vprix/vncproxy/rfb"
)
// RREEncoding RRE表示提升和运行长度,正如它名字暗示的那样,它实质上表示二维向量的运行长度编码。
// RRE把矩形编码成可以被客户机的图形引擎翻译的格式。RRE不适合复杂的桌面,但在一些情况下比较有用。
// RRE的思想就是把像素矩形的数据分成一些子区域,和一些压缩原始区域的单元。最近最佳的分区方式一般是比较容易计算的。
// 编码是由像素值组成的,Vb(基本上是在矩形中最常用的像素值)和一个计数N,紧接着是N的子矩形列表,这些里面由数组组成,(x,y)是对应子矩形的坐标,
// 表示子矩形上-左的坐标值,(w,h) 则表示子矩形的宽高。客户端可以通过绘制使用背景像素数据值,然后再根据子矩形来绘制原始矩形。
// 二维行程编码本质上是对行程编码的一个二维模拟,而其压缩度可以保证与行程编码相同甚至更好。
// 而且更重要的是,采用RRE编码的矩形被传送到客户端以后,可以立即有效地被最简单的图形引擎所还原。
type RREEncoding struct {
buff *bytes.Buffer
}
func (that *RREEncoding) Type() rfb.EncodingType {
return rfb.EncRRE
}
func (that *RREEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *RREEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &RREEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *RREEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
pf := session.Options().PixelFormat
// 子矩形的数量
var numOfSubRectangles uint32
if err := binary.Read(session, binary.BigEndian, &numOfSubRectangles); err != nil {
return err
}
if err := binary.Write(that.buff, binary.BigEndian, numOfSubRectangles); err != nil {
return err
}
// (backgroundColor + (color=BPP + x=16b + y=16b + w=16b + h=16b))
size := uint32(pf.BPP/8) + (uint32((pf.BPP/8)+8) * numOfSubRectangles)
b, err := ReadBytes(int(size), session)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
func (that *RREEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err := that.buff.WriteTo(session)
that.buff.Reset()
return err
}
================================================
FILE: encodings/encoding_tight.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/vprix/vncproxy/rfb"
)
var TightMinToCompress int = 12
const (
tightCompressionBasic = 0
tightCompressionFill = 0x08
tightCompressionJPEG = 0x09
tightCompressionPNG = 0x0A
)
const (
TightFilterCopy = 0
TightFilterPalette = 1
TightFilterGradient = 2
)
type TightEncoding struct {
buff *bytes.Buffer
}
func (that *TightEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *TightEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &TightEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *TightEncoding) Type() rfb.EncodingType {
return rfb.EncTight
}
func calcTightBytePerPixel(pf *rfb.PixelFormat) int {
bytesPerPixel := int(pf.BPP / 8)
var bytesPerPixelTight int
if 24 == pf.Depth && 32 == pf.BPP {
bytesPerPixelTight = 3
} else {
bytesPerPixelTight = bytesPerPixel
}
return bytesPerPixelTight
}
func (that *TightEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err := that.buff.WriteTo(session)
that.buff.Reset()
return err
}
func (that *TightEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
pf := session.Options().PixelFormat
bytesPixel := calcTightBytePerPixel(&pf)
compressionControl, err := ReadUint8(session)
if err != nil {
return nil
}
_ = binary.Write(that.buff, binary.BigEndian, compressionControl)
compType := compressionControl >> 4 & 0x0F
switch compType {
case tightCompressionFill: // 全部为紧凑压缩
bt, err := ReadBytes(bytesPixel, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
//jpeg紧凑压缩
case tightCompressionJPEG:
if pf.BPP == 8 {
return errors.New("Tight encoding: JPEG is not supported in 8 bpp mode. ")
}
// 获取jpeg流的长度
size, err := that.ReadCompactLen(session)
//读取jpeg流
jpegBytes, err := ReadBytes(size, session)
if err != nil {
return err
}
_, _ = that.buff.Write(jpegBytes)
default:
// 默认使用基础的压缩方式
if compType > tightCompressionJPEG {
return errors.New("Compression control byte is incorrect! ")
}
err = that.handleTightFilters(session, rect, &pf, compressionControl)
return err
}
return nil
}
// ReadCompactLen 获取动态长度
func (that *TightEncoding) ReadCompactLen(session rfb.ISession) (int, error) {
var err error
part, err := ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size := uint32(part & 0x7F)
if (part & 0x80) == 0 {
return int(size), nil
}
part, err = ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size |= uint32(int(part)&0x7F) << 7
if (part & 0x80) == 0 {
return int(size), nil
}
part, err = ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size |= uint32(int(part)&0xFF) << 14
return int(size), err
}
// 基础压缩格式
func (that *TightEncoding) handleTightFilters(session rfb.ISession, rect *rfb.Rectangle, pf *rfb.PixelFormat, compCtl uint8) error {
var FilterIdMask uint8 = 0x40
var filterId uint8
var err error
if (compCtl & FilterIdMask) > 0 {
filterId, err = ReadUint8(session)
if err != nil {
return fmt.Errorf("error in handling tight encoding, reading filterid: %s", err.Error())
}
_ = binary.Write(that.buff, binary.BigEndian, filterId)
}
bytesPixel := calcTightBytePerPixel(pf)
lengthCurrentBPP := bytesPixel * int(rect.Width) * int(rect.Height)
switch filterId {
case TightFilterPalette:
palette, err := that.readTightPalette(session, bytesPixel)
if err != nil {
return err
}
var dataLength int
if palette == 2 {
dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8)
} else {
dataLength = int(rect.Width) * int(rect.Height)
}
err = that.ReadTightData(dataLength, session)
if err != nil {
return err
}
case TightFilterGradient:
err = that.ReadTightData(lengthCurrentBPP, session)
if err != nil {
return fmt.Errorf("handleTightFilters: error in handling tight encoding, Reading GRADIENT_FILTER: %v", err)
}
case TightFilterCopy:
err = that.ReadTightData(lengthCurrentBPP, session)
if err != nil {
return fmt.Errorf("handleTightFilters: error in handling tight encoding, Reading BASIC_FILTER: %v", err)
}
default:
return fmt.Errorf("handleTightFilters: Bad tight filter id: %d", filterId)
}
return nil
}
// 获取调色板数据
func (that *TightEncoding) readTightPalette(session rfb.ISession, bytesPixel int) (int, error) {
colorCount, err := ReadUint8(session)
if err != nil {
return 0, fmt.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette: %v", err)
}
_ = binary.Write(that.buff, binary.BigEndian, colorCount)
// 注意这个地方,必须先转换为int类型,不然如果colorCount为255,+1的情况下会溢出,变成0,造成bug
numColors := int(colorCount) + 1
paletteSize := numColors * bytesPixel
paletteColorBytes, err := ReadBytes(paletteSize, session)
if err != nil {
return numColors, err
}
_, _ = that.buff.Write(paletteColorBytes)
return numColors, nil
}
func (that *TightEncoding) ReadTightData(dataSize int, session rfb.ISession) error {
if dataSize < TightMinToCompress {
b, err := ReadBytes(dataSize, session)
if err == nil {
_, _ = that.buff.Write(b)
}
return err
}
zlibDataLen, err := that.ReadCompactLen(session)
if err != nil {
return err
}
zippedBytes, err := ReadBytes(zlibDataLen, session)
if err != nil {
return err
}
_, _ = that.buff.Write(zippedBytes)
return nil
}
================================================
FILE: encodings/encoding_tightpng.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/vprix/vncproxy/rfb"
)
type TightPngEncoding struct {
buff *bytes.Buffer
}
func (that *TightPngEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *TightPngEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &TightPngEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *TightPngEncoding) Type() rfb.EncodingType {
return rfb.EncTightPng
}
func (that *TightPngEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err := that.buff.WriteTo(session)
that.buff.Reset()
return err
}
func (that *TightPngEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
pf := session.Options().PixelFormat
bytesPixel := calcTightBytePerPixel(&pf)
compressionControl, err := ReadUint8(session)
if err != nil {
return nil
}
_ = binary.Write(that.buff, binary.BigEndian, compressionControl)
compType := compressionControl >> 4 & 0x0F
switch compType {
case tightCompressionPNG:
size, err := that.ReadCompactLen(session)
if err != nil {
return err
}
bt, err := ReadBytes(size, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
case tightCompressionFill:
bt, err := ReadBytes(bytesPixel, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
default:
return fmt.Errorf("unknown tight compression %d", compType)
}
return nil
}
// ReadCompactLen 获取动态长度
func (that *TightPngEncoding) ReadCompactLen(session rfb.ISession) (int, error) {
var err error
part, err := ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size := uint32(part & 0x7F)
if (part & 0x80) == 0 {
return int(size), nil
}
part, err = ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size |= uint32(int(part)&0x7F) << 7
if (part & 0x80) == 0 {
return int(size), nil
}
part, err = ReadUint8(session)
if err := binary.Write(that.buff, binary.BigEndian, part); err != nil {
return 0, err
}
size |= uint32(int(part)&0xFF) << 14
return int(size), err
}
================================================
FILE: encodings/encoding_trle.go
================================================
package encodings
================================================
FILE: encodings/encoding_zlib.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"errors"
"github.com/vprix/vncproxy/rfb"
)
type ZLibEncoding struct {
buff *bytes.Buffer
}
func (that *ZLibEncoding) Supported(c rfb.ISession) bool {
return true
}
func (that *ZLibEncoding) Type() rfb.EncodingType {
return rfb.EncZlib
}
func (that *ZLibEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &ZLibEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *ZLibEncoding) Read(sess rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
size, err := ReadUint32(sess)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, size)
if err != nil {
return err
}
b, err := ReadBytes(int(size), sess)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
func (that *ZLibEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
_, err := that.buff.WriteTo(sess)
that.buff.Reset()
return err
}
================================================
FILE: encodings/encoding_zrle.go
================================================
package encodings
import (
"bytes"
"compress/zlib"
"encoding/binary"
"errors"
"fmt"
"github.com/vprix/vncproxy/canvas"
"github.com/vprix/vncproxy/rfb"
"image/color"
"io"
)
const (
ZRLERawPixelData = 0
ZRLESingleColour = 1
)
// ZRLEEncoding ZRLE(Zlib Run - Length Encoding),它结合了zlib 压缩,片技术、调色板和运行长度编码。
// 在传输中,矩形以4 字节长度区域开始,紧接着是zlib 压缩的数据,一个单一的 zlib“流”对象被用在RFB协议的连接上,
// 因此ZRLE矩形必须严格的按照顺序进行编码和译码。
type ZRLEEncoding struct {
buff *bytes.Buffer
}
func (that *ZRLEEncoding) Type() rfb.EncodingType {
return rfb.EncZRLE
}
func (that *ZRLEEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *ZRLEEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &ZRLEEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *ZRLEEncoding) Read(sess rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
size, err := ReadUint32(sess)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, size)
if err != nil {
return err
}
b, err := ReadBytes(int(size), sess)
if err != nil {
return err
}
_, err = that.buff.Write(b)
if err != nil {
return err
}
return nil
}
func (that *ZRLEEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return errors.New("ByteBuffer is nil")
}
if sess.Type() == rfb.CanvasSessionType {
return that.draw(sess.Conn().(*canvas.VncCanvas), sess.Options().PixelFormat, rect)
}
_, err := that.buff.WriteTo(sess)
that.buff.Reset()
return err
}
// 绘制画布
func (that *ZRLEEncoding) draw(cv *canvas.VncCanvas, pf rfb.PixelFormat, rect *rfb.Rectangle) error {
var size uint32
err := binary.Read(that.buff, binary.BigEndian, &size)
if err != nil {
return err
}
b, err := ReadBytes(int(size), that.buff)
if err != nil {
return err
}
bytesBuff := bytes.NewBuffer(b)
unZipper, err := zlib.NewReader(bytesBuff)
if err != nil {
return err
}
for tileOffsetY := 0; tileOffsetY < int(rect.Height); tileOffsetY += 64 {
tileHeight := min(64, int(rect.Height)-tileOffsetY)
for tileOffsetX := 0; tileOffsetX < int(rect.Width); tileOffsetX += 64 {
tileWidth := min(64, int(rect.Width)-tileOffsetX)
// 获取二级编码格式
subEnc, err := ReadUint8(unZipper)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading subencoding: %v", err)
}
switch {
case subEnc == ZRLERawPixelData: // 原始编码格式
err = that.readZRLERaw(cv, unZipper, &pf, int(rect.X)+tileOffsetX, int(rect.Y)+tileOffsetY, tileWidth, tileHeight)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading Raw tile: %v", err)
}
case subEnc == ZRLESingleColour: // 获取一个颜色,填充指定区域
co, err := readCPixel(cv, unZipper, &pf)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading CPixel for bgColor tile: %v", err)
}
myRect := canvas.MakeRect(int(rect.X)+tileOffsetX, int(rect.Y)+tileOffsetY, tileWidth, tileHeight)
cv.FillRect(&myRect, co)
case subEnc >= 2 && subEnc <= 16: // 调色版编码
err = that.handlePaletteTile(cv, unZipper, tileOffsetX, tileOffsetY, tileWidth, tileHeight, subEnc, &pf, rect)
if err != nil {
return err
}
case subEnc == 128: //
err = that.handlePlainRLETile(cv, unZipper, tileOffsetX, tileOffsetY, tileWidth, tileHeight, &pf, rect)
if err != nil {
return err
}
case subEnc >= 130:
err = that.handlePaletteRLETile(cv, unZipper, tileOffsetX, tileOffsetY, tileWidth, tileHeight, subEnc, &pf, rect)
if err != nil {
return err
}
default:
return fmt.Errorf("Unknown ZRLE subencoding: %v ", subEnc)
}
}
}
return nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func (that *ZRLEEncoding) readZRLERaw(cv *canvas.VncCanvas, reader io.Reader, pf *rfb.PixelFormat, tx, ty, tw, th int) error {
for y := 0; y < th; y++ {
for x := 0; x < tw; x++ {
col, err := readCPixel(cv, reader, pf)
if err != nil {
return err
}
cv.Set(tx+x, ty+y, col)
}
}
return nil
}
// 获取像素格式
func readCPixel(cv *canvas.VncCanvas, c io.Reader, pf *rfb.PixelFormat) (*color.RGBA, error) {
if pf.TrueColor == 0 {
return nil, errors.New("support for non true color formats was not implemented")
}
isZRLEFormat := IsCPixelSpecific(pf)
var col *color.RGBA
if isZRLEFormat {
tBytes, err := ReadBytes(3, c)
if err != nil {
return nil, err
}
if pf.BigEndian != 1 {
col = &color.RGBA{
B: tBytes[0],
G: tBytes[1],
R: tBytes[2],
A: uint8(1),
}
} else {
col = &color.RGBA{
R: tBytes[0],
G: tBytes[1],
B: tBytes[2],
A: uint8(1),
}
}
return col, nil
}
col, err := cv.ReadColor(c, pf)
if err != nil {
return nil, fmt.Errorf("readCPixel: Error while reading zrle: %v", err)
}
return col, nil
}
func IsCPixelSpecific(pf *rfb.PixelFormat) bool {
significant := int(pf.RedMax<<pf.RedShift | pf.GreenMax<<pf.GreenShift | pf.BlueMax<<pf.BlueShift)
if pf.Depth <= 24 && 32 == pf.BPP && ((significant&0x00ff000000) == 0 || (significant&0x000000ff) == 0) {
return true
}
return false
}
// 调色板编码
func (that *ZRLEEncoding) handlePaletteTile(cv *canvas.VncCanvas, unZipper io.Reader, tileOffsetX, tileOffsetY, tileWidth, tileHeight int, subEnc uint8, pf *rfb.PixelFormat, rect *rfb.Rectangle) error {
paletteSize := subEnc
palette := make([]*color.RGBA, paletteSize)
var err error
// Read palette
for j := 0; j < int(paletteSize); j++ {
palette[j], err = readCPixel(cv, unZipper, pf)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading CPixel for palette tile: %v", err)
}
}
// Calculate index size
var indexBits, mask uint32
if paletteSize == 2 {
indexBits = 1
mask = 0x80
} else if paletteSize <= 4 {
indexBits = 2
mask = 0xC0
} else {
indexBits = 4
mask = 0xF0
}
for y := 0; y < tileHeight; y++ {
// Packing only occurs per-row
bitsAvailable := uint32(0)
buffer := uint32(0)
for x := 0; x < tileWidth; x++ {
// Buffer more bits if necessary
if bitsAvailable == 0 {
bits, err := ReadUint8(unZipper)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading first uint8 into buffer: %v", err)
}
buffer = uint32(bits)
bitsAvailable = 8
}
// Read next pixel
index := (buffer & mask) >> (8 - indexBits)
buffer <<= indexBits
bitsAvailable -= indexBits
// Write pixel to image
cv.Set(tileOffsetX+int(rect.X)+x, tileOffsetY+int(rect.Y)+y, palette[index])
}
}
return err
}
// 普通rle编码
func (that *ZRLEEncoding) handlePlainRLETile(cv *canvas.VncCanvas, unZipper io.Reader, tileOffsetX int, tileOffsetY int, tileWidth int, tileHeight int, pf *rfb.PixelFormat, rect *rfb.Rectangle) error {
var col *color.RGBA
var err error
runLen := 0
for y := 0; y < tileHeight; y++ {
for x := 0; x < tileWidth; x++ {
if runLen == 0 {
// Read length and color
col, err = readCPixel(cv, unZipper, pf)
if err != nil {
return fmt.Errorf("handlePlainRLETile: error while reading CPixel in plain RLE subencoding: %v", err)
}
runLen, err = readRunLength(unZipper)
if err != nil {
return fmt.Errorf("handlePlainRLETile: error while reading runlength in plain RLE subencoding: %v", err)
}
}
// Write pixel to image
cv.Set(tileOffsetX+int(rect.X)+x, tileOffsetY+int(rect.Y)+y, col)
runLen--
}
}
return err
}
func readRunLength(r io.Reader) (int, error) {
runLen := 1
addition, err := ReadUint8(r)
if err != nil {
return 0, fmt.Errorf("renderZRLE: error while reading addition to runLen in plain RLE subencoding: %v", err)
}
runLen += int(addition)
for addition == 255 {
addition, err = ReadUint8(r)
if err != nil {
return 0, fmt.Errorf("renderZRLE: error while reading addition to runLen in-loop plain RLE subencoding: %v", err)
}
runLen += int(addition)
}
return runLen, nil
}
// 调色板rle编码
func (that *ZRLEEncoding) handlePaletteRLETile(cv *canvas.VncCanvas, unZipper io.Reader, tileOffsetX, tileOffsetY, tileWidth, tileHeight int, subEnc uint8, pf *rfb.PixelFormat, rect *rfb.Rectangle) error {
// Palette RLE
paletteSize := subEnc - 128
palette := make([]*color.RGBA, paletteSize)
var err error
// Read RLE palette
for j := 0; j < int(paletteSize); j++ {
palette[j], err = readCPixel(cv, unZipper, pf)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading color in palette RLE subencoding: %v", err)
}
}
var index uint8
runLen := 0
for y := 0; y < tileHeight; y++ {
for x := 0; x < tileWidth; x++ {
if runLen == 0 {
// Read length and index
index, err = ReadUint8(unZipper)
if err != nil {
return fmt.Errorf("renderZRLE: error while reading length and index in palette RLE subencoding: %v", err)
}
runLen = 1
// Run is represented by index | 0x80
// Otherwise, single pixel
if (index & 0x80) != 0 {
index -= 128
runLen, err = readRunLength(unZipper)
if err != nil {
return fmt.Errorf("handlePlainRLETile: error while reading runlength in plain RLE subencoding: %v", err)
}
}
}
// Write pixel to image
cv.Set(tileOffsetX+int(rect.X)+x, tileOffsetY+int(rect.Y)+y, palette[index])
runLen--
}
}
return nil
}
================================================
FILE: encodings/pseudo_cursor.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"github.com/vprix/vncproxy/canvas"
"github.com/vprix/vncproxy/rfb"
"image"
"image/color"
"math"
)
// CursorPseudoEncoding 如果客户端请求指针/鼠标伪编码,那么就是说它有能力进行本地绘制鼠标。
// 这样就可以明显改善传输性能。服务器通过发送带有伪鼠标编码的伪矩形来设置鼠标的形状作为更新的一部分。
// 伪矩形的x 和y 表示鼠标的热点,宽和高表示用像素来表示鼠标的宽和高。包含宽X高像素值的数据带有位掩码。
// 位掩码是由从左到右,从上到下的扫描线组成,而每一扫描线被填充为floor((width +7) / 8)。
// 对应每一字节最重要的位表示最左边像素,对应1 位表示相应指针的像素是正确的。
type CursorPseudoEncoding struct {
buff *bytes.Buffer
}
func (that *CursorPseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
// Draw 绘制鼠标指针
func (that *CursorPseudoEncoding) draw(cv *canvas.VncCanvas, pf rfb.PixelFormat, rect *rfb.Rectangle) error {
numColors := int(rect.Height) * int(rect.Width)
colors := make([]color.Color, numColors)
var err error
for i := 0; i < numColors; i++ {
colors[i], err = cv.ReadColor(that.buff, &pf)
if err != nil {
return err
}
}
// 获取掩码信息
bitmask := make([]byte, int((rect.Width+7)/8*rect.Height))
if err = binary.Read(that.buff, binary.BigEndian, &bitmask); err != nil {
return err
}
scanLine := (rect.Width + 7) / 8
// 生成鼠标指针的形状
cursorImg := image.NewRGBA(canvas.MakeRect(0, 0, int(rect.Width), int(rect.Height)))
var cursorMask [][]bool
for i := 0; i < int(rect.Width); i++ {
cursorMask = append(cursorMask, make([]bool, rect.Height))
}
// 填充鼠标指针的颜色
for y := 0; y < int(rect.Height); y++ {
for x := 0; x < int(rect.Width); x++ {
offset := y*int(rect.Width) + x
if bitmask[y*int(scanLine)+x/8]&(1<<uint(7-x%8)) > 0 {
cursorImg.Set(x, y, colors[offset])
cursorMask[x][y] = true
}
}
}
// 设置鼠标指针
cv.CursorOffset = &image.Point{X: int(rect.X), Y: int(rect.Y)}
cv.Cursor = cursorImg
cv.CursorBackup = image.NewRGBA(cursorImg.Bounds())
cv.CursorMask = cursorMask
return nil
}
func (that *CursorPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &CursorPseudoEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *CursorPseudoEncoding) Type() rfb.EncodingType {
return rfb.EncCursorPseudo
}
func (that *CursorPseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if rect.Width*rect.Height == 0 {
return nil
}
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
var bt []byte
var err error
bytesPixel := int(session.Options().PixelFormat.BPP / 8) //calcTightBytePerPixel(pf)
bt, err = ReadBytes(int(rect.Width*rect.Height)*bytesPixel, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
mask := ((rect.Width + 7) / 8) * rect.Height
bt, err = ReadBytes(int(math.Floor(float64(mask))), session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
return nil
}
func (that *CursorPseudoEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return nil
}
if sess.Type() == rfb.CanvasSessionType {
return that.draw(sess.Conn().(*canvas.VncCanvas), sess.Options().PixelFormat, rect)
}
var err error
_, err = that.buff.WriteTo(sess)
that.buff.Reset()
return err
}
================================================
FILE: encodings/pseudo_cursor_with_alpha.go
================================================
package encodings
import (
"bytes"
"github.com/vprix/vncproxy/rfb"
)
type CursorWithAlphaPseudoEncoding struct {
buff *bytes.Buffer
}
func (that *CursorWithAlphaPseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *CursorWithAlphaPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &CursorWithAlphaPseudoEncoding{}
if len(data) > 0 && data[0] {
if that.buff != nil {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
}
return obj
}
func (that *CursorWithAlphaPseudoEncoding) Type() rfb.EncodingType {
return rfb.EncCursorWithAlphaPseudo
}
func (that *CursorWithAlphaPseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
var bt []byte
var err error
bt, err = ReadBytes(4, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt)
if rect.Width*rect.Height > 0 {
bt2, err := ReadBytes(int(rect.Width*rect.Height)*4, session)
if err != nil {
return err
}
_, _ = that.buff.Write(bt2)
}
return nil
}
func (that *CursorWithAlphaPseudoEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
return nil
}
var err error
_, err = that.buff.WriteTo(session)
that.buff.Reset()
return err
}
================================================
FILE: encodings/pseudo_desktop_name.go
================================================
package encodings
import (
"encoding/binary"
"github.com/vprix/vncproxy/rfb"
)
// DesktopNamePseudoEncoding 服务端设置桌面名字的消息
type DesktopNamePseudoEncoding struct {
Name []byte
}
func (that *DesktopNamePseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *DesktopNamePseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &DesktopNamePseudoEncoding{}
if len(data) > 0 && data[0] {
obj.Name = that.Name
}
return obj
}
func (that *DesktopNamePseudoEncoding) Type() rfb.EncodingType {
return rfb.EncDesktopNamePseudo
}
// Read 实现了编码接口
func (that *DesktopNamePseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
var length uint32
if err := binary.Read(session, binary.BigEndian, &length); err != nil {
return err
}
name := make([]byte, length)
if err := binary.Read(session, binary.BigEndian, &name); err != nil {
return err
}
that.Name = name
return nil
}
func (that *DesktopNamePseudoEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Write(session, binary.BigEndian, uint32(len(that.Name))); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.Name); err != nil {
return err
}
return session.Flush()
}
================================================
FILE: encodings/pseudo_desktop_size.go
================================================
package encodings
import (
"github.com/vprix/vncproxy/rfb"
)
// DesktopSizePseudoEncoding 如果客户端请求桌面大小伪编码,那么就是说它能处理帧缓存宽/高的改变。
// 服务器通过发送带有桌面大小伪编码的伪矩形作为上一个矩形来完成一次更新。
// 伪矩形的x 和y 被忽略,而宽和高表示帧缓存新的宽和高。没有其他的数据与伪矩形有关。
type DesktopSizePseudoEncoding struct {
}
func (that *DesktopSizePseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *DesktopSizePseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &DesktopSizePseudoEncoding{}
return obj
}
func (that *DesktopSizePseudoEncoding) Type() rfb.EncodingType { return rfb.EncDesktopSizePseudo }
// Read implements the Encoding interface.
func (that *DesktopSizePseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
return nil
}
func (that *DesktopSizePseudoEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
return nil
}
================================================
FILE: encodings/pseudo_extended_desktop_size.go
================================================
package encodings
import (
"bytes"
"encoding/binary"
"github.com/vprix/vncproxy/rfb"
)
// ExtendedDesktopSizePseudo 扩展适应客户端桌面分辨率
type ExtendedDesktopSizePseudo struct {
buff *bytes.Buffer
}
func (that *ExtendedDesktopSizePseudo) Supported(rfb.ISession) bool {
return true
}
func (that *ExtendedDesktopSizePseudo) Clone(data ...bool) rfb.IEncoding {
obj := &ExtendedDesktopSizePseudo{}
if len(data) > 0 && data[0] {
obj.buff = &bytes.Buffer{}
_, _ = obj.buff.Write(that.buff.Bytes())
}
return obj
}
func (that *ExtendedDesktopSizePseudo) Type() rfb.EncodingType {
return rfb.EncExtendedDesktopSizePseudo
}
func (that *ExtendedDesktopSizePseudo) Write(session rfb.ISession, rect *rfb.Rectangle) (err error) {
_, err = that.buff.WriteTo(session)
that.buff.Reset()
return err
}
func (that *ExtendedDesktopSizePseudo) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if that.buff == nil {
that.buff = &bytes.Buffer{}
}
//读取屏幕数量
screensNumber, err := ReadUint8(session)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, screensNumber)
if err != nil {
return err
}
// 填充
pad, err := ReadBytes(3, session)
if err != nil {
return err
}
err = binary.Write(that.buff, binary.BigEndian, pad)
b2, err := ReadBytes(int(screensNumber)*16, session)
if err != nil {
return err
}
_, _ = that.buff.Write(b2)
return nil
}
================================================
FILE: encodings/pseudo_fence.go
================================================
package encodings
import (
"github.com/vprix/vncproxy/rfb"
)
type FencePseudo struct {
}
func (that *FencePseudo) Supported(_ rfb.ISession) bool {
return true
}
func (that *FencePseudo) Clone(_ ...bool) rfb.IEncoding {
obj := &FencePseudo{}
return obj
}
func (that *FencePseudo) Type() rfb.EncodingType {
return rfb.EncFencePseudo
}
func (that *FencePseudo) Read(_ rfb.ISession, _ *rfb.Rectangle) error {
return nil
}
func (that *FencePseudo) Write(_ rfb.ISession, _ *rfb.Rectangle) error {
return nil
}
================================================
FILE: encodings/pseudo_last_rect.go
================================================
package encodings
import (
"github.com/vprix/vncproxy/rfb"
)
type LastRectPseudo struct {
}
func (that *LastRectPseudo) Supported(_ rfb.ISession) bool {
return true
}
func (that *LastRectPseudo) Clone(_ ...bool) rfb.IEncoding {
obj := &LastRectPseudo{}
return obj
}
func (that *LastRectPseudo) Type() rfb.EncodingType {
return rfb.EncLastRectPseudo
}
func (that *LastRectPseudo) Read(_ rfb.ISession, _ *rfb.Rectangle) error {
return nil
}
func (that *LastRectPseudo) Write(_ rfb.ISession, _ *rfb.Rectangle) error {
return nil
}
================================================
FILE: encodings/pseudo_led_state.go
================================================
package encodings
import (
"encoding/binary"
"github.com/vprix/vncproxy/rfb"
)
// LedStatePseudo 切换客户端本地小键盘锁定的led灯
// 0 滚动锁
// 1 数字锁定
// 2 大写锁定
type LedStatePseudo struct {
LedState uint8
}
func (that *LedStatePseudo) Supported(session rfb.ISession) bool {
return true
}
func (that *LedStatePseudo) Clone(data ...bool) rfb.IEncoding {
obj := &LedStatePseudo{}
return obj
}
func (that *LedStatePseudo) Type() rfb.EncodingType {
return rfb.EncLedStatePseudo
}
func (that *LedStatePseudo) Read(session rfb.ISession, rect *rfb.Rectangle) error {
u8, err := ReadUint8(session)
if err != nil {
return err
}
that.LedState = u8
return nil
}
func (that *LedStatePseudo) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Write(session, binary.BigEndian, that.LedState); err != nil {
return err
}
return nil
}
================================================
FILE: encodings/pseudo_pointer_pos.go
================================================
package encodings
import (
"errors"
"github.com/vprix/vncproxy/canvas"
"github.com/vprix/vncproxy/rfb"
"image"
"image/draw"
)
type CursorPosPseudoEncoding struct {
}
func (that *CursorPosPseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *CursorPosPseudoEncoding) Draw(img draw.Image, rect *rfb.Rectangle) error {
cv, ok := img.(*canvas.VncCanvas)
if !ok {
return errors.New("canvas error")
}
// 本地鼠标指针的位置
cv.CursorLocation = &image.Point{X: int(rect.X), Y: int(rect.Y)}
return nil
}
func (that *CursorPosPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &CursorPosPseudoEncoding{}
return obj
}
func (that *CursorPosPseudoEncoding) Type() rfb.EncodingType {
return rfb.EncPointerPosPseudo
}
func (that *CursorPosPseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
return nil
}
func (that *CursorPosPseudoEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
return nil
}
================================================
FILE: encodings/pseudo_x_cursor.go
================================================
package encodings
import (
"encoding/binary"
"github.com/vprix/vncproxy/rfb"
"math"
)
type XCursorPseudoEncoding struct {
PrimaryR, PrimaryG, PrimaryB uint8 // 主颜色
SecondaryR, SecondaryG, SecondaryB uint8 // 次颜色
Bitmap []byte //颜色位图
Bitmask []byte //透明度位掩码
}
func (that *XCursorPseudoEncoding) Supported(session rfb.ISession) bool {
return true
}
func (that *XCursorPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
obj := &XCursorPseudoEncoding{}
if len(data) > 0 && data[0] {
obj.PrimaryR = that.PrimaryR
obj.PrimaryG = that.PrimaryG
obj.PrimaryB = that.PrimaryB
obj.SecondaryR = that.SecondaryR
obj.SecondaryG = that.SecondaryG
obj.SecondaryB = that.SecondaryB
Bitmap := make([]byte, len(that.Bitmap))
Bitmask := make([]byte, len(that.Bitmask))
copy(Bitmap, that.Bitmap)
copy(Bitmask, that.Bitmask)
obj.Bitmap = Bitmap
obj.Bitmask = Bitmask
}
return obj
}
func (that *XCursorPseudoEncoding) Type() rfb.EncodingType {
return rfb.EncXCursorPseudo
}
func (that *XCursorPseudoEncoding) Read(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Read(session, binary.BigEndian, &that.PrimaryR); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.PrimaryG); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.PrimaryB); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.SecondaryR); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.SecondaryG); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.SecondaryB); err != nil {
return err
}
bitMapSize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height))
bitMaskSize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height))
that.Bitmap = make([]byte, bitMapSize)
that.Bitmask = make([]byte, bitMaskSize)
if err := binary.Read(session, binary.BigEndian, &that.Bitmap); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &that.Bitmask); err != nil {
return err
}
return nil
}
func (that *XCursorPseudoEncoding) Write(session rfb.ISession, rect *rfb.Rectangle) error {
if err := binary.Write(session, binary.BigEndian, that.PrimaryR); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.PrimaryG); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.PrimaryB); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.SecondaryR); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.SecondaryG); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.SecondaryB); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.Bitmap); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.Bitmask); err != nil {
return err
}
return nil
}
================================================
FILE: go.mod
================================================
module github.com/vprix/vncproxy
require (
github.com/gogf/gf/v2 v2.9.5
github.com/osgochina/dmicro v1.3.1
golang.org/x/net v0.47.0
)
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.12.0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/quic-go/quic-go v0.56.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/xtaci/kcp-go/v5 v5.6.37 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.14.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
go 1.24.7
================================================
FILE: handler/ClientClientInitHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ClientClientInitHandler vnc握手步骤第三步
// 1. 根据配置信息判断该vnc会话是否独占,
// 2. 发送是否独占标识给vnc服务端
type ClientClientInitHandler struct{}
func (that *ClientClientInitHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debug(context.TODO(), "[Proxy客户端->VNC服务端]: 执行vnc握手步骤第三步[ClientInit]")
}
cfg := session.Options()
var shared uint8
if cfg.Exclusive {
shared = 0
} else {
shared = 1
}
if err := binary.Write(session, binary.BigEndian, shared); err != nil {
return err
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端]: 执行ClientInit步骤,发送shared=%d", shared)
}
return session.Flush()
}
================================================
FILE: handler/ClientMessageHandler.go
================================================
package handler
import (
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
"golang.org/x/net/context"
)
// ClientMessageHandler vnc握手已结束,进入消息交互阶段
// 启动两个协程处理后续消息逻辑
// 1. 协程1:通过ClientMessageCh通道获取消息,并把该消息写入到vnc服务端会话中。
// 2. 协程2:从vnc服务端会话中读取消息类型及消息内容,组装该消息,发消息发送到ServerMessageCh通道中,供其他功能消费
// 3. 发送编码格式消息SetEncodings到vnc服务端
// 4. 发送帧数据请求消息FramebufferUpdateRequest到vnc服务端
type ClientMessageHandler struct{}
func (*ClientMessageHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debug(context.TODO(), "[Proxy客户端->VNC服务端]: vnc握手已结束,进入消息交互阶段[ClientMessageHandler]")
}
cfg := session.Options()
var err error
// proxy客户端支持的消息类型
serverMessages := make(map[rfb.MessageType]rfb.Message)
for _, m := range cfg.Messages {
serverMessages[m.Type()] = m
}
// 通过ClientMessageCh通道获取消息,并把该消息写入到vnc服务端会话中。
go func() {
for {
select {
case <-session.Wait():
return
case msg := <-cfg.Input:
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端] 消息类型:%s,消息内容:%s", rfb.ClientMessageType(msg.Type()), msg.String())
}
if err = msg.Write(session); err != nil {
cfg.ErrorCh <- err
_ = session.Close()
return
}
}
}
}()
// 从vnc服务端会话中读取消息类型及消息内容,组装该消息,发消息发送到ServerMessageCh通道中,供其他功能消费
go func() {
for {
select {
case <-session.Wait():
return
default:
// 从会话中读取消息类型
var messageType rfb.MessageType
if err = binary.Read(session, binary.BigEndian, &messageType); err != nil {
cfg.ErrorCh <- err
return
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC服务端->Proxy客户端] 消息类型:%s", rfb.ServerMessageType(messageType))
}
// 判断proxy客户端是否支持该消息
msg, ok := serverMessages[messageType]
if !ok {
err = fmt.Errorf("未知的消息类型: %v", messageType)
cfg.ErrorCh <- err
_ = session.Close()
return
}
// 读取消息内容
parsedMsg, err := msg.Read(session)
if err != nil {
cfg.ErrorCh <- err
_ = session.Close()
return
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC服务端->Proxy客户端] 消息类型:%s,消息内容:%s", rfb.ServerMessageType(parsedMsg.Type()), parsedMsg)
}
cfg.Output <- parsedMsg
}
}
}()
return nil
}
================================================
FILE: handler/ClientSecurityHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ClientSecurityHandler vnc握手步骤第二步
// 1. 读取vnc服务端支持的安全认证套件数量及类型
// 2. 匹配vnc服务端与proxy客户端的安全认证套件
// 3. 进入安全认证套件认证流程
// 4. 获取认证结果,如果认证失败,获取失败的原因。
type ClientSecurityHandler struct{}
func (*ClientSecurityHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端]: 执行vnc握手第二步:[Security]")
}
cfg := session.Options()
// 读取vnc服务端支持的安全认证套件数量
var numSecurityTypes uint8
if err := binary.Read(session, binary.BigEndian, &numSecurityTypes); err != nil {
return err
}
// 读取vnc服务端支持的安全认证套件类型
secTypes := make([]rfb.SecurityType, numSecurityTypes)
if err := binary.Read(session, binary.BigEndian, &secTypes); err != nil {
return err
}
// 匹配vnc服务端与proxy客户端的安全认证套件
var secType rfb.ISecurityHandler
for _, st := range cfg.SecurityHandlers {
for _, sc := range secTypes {
if st.Type() == sc {
secType = st
}
}
}
// 发送proxy客户端选中的安全认证套件
if err := binary.Write(session, binary.BigEndian, cfg.SecurityHandlers[0].Type()); err != nil {
return err
}
if err := session.Flush(); err != nil {
return err
}
// 进入安全认证套件认证流程
err := secType.Auth(session)
if err != nil {
return fmt.Errorf("安全认证失败, error:%v", err)
}
// 读取安全认证结果
var authCode uint32
if err := binary.Read(session, binary.BigEndian, &authCode); err != nil {
return err
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "安全认证中, 安全认证套件类型: %d,认证结果(0为成功): %d", rfb.ClientMessageType(secType.Type()), authCode)
}
//如果认证失败,则读取失败原因
if authCode == 1 {
var reasonLength uint32
if err = binary.Read(session, binary.BigEndian, &reasonLength); err != nil {
return err
}
reasonText := make([]byte, reasonLength)
if err = binary.Read(session, binary.BigEndian, &reasonText); err != nil {
return err
}
return fmt.Errorf("%s", reasonText)
}
session.SetSecurityHandler(secType)
return nil
}
================================================
FILE: handler/ClientServerInitHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/messages"
"github.com/vprix/vncproxy/rfb"
)
// ClientServerInitHandler vnc握手第四步
// 1. 读取vnc服务端发送的屏幕宽高,像素格式,桌面名称
type ClientServerInitHandler struct{}
func (*ClientServerInitHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端]: 执行vnc握手第四步:[ServerInit]")
}
var err error
srvInit := messages.ServerInit{}
if err = binary.Read(session, binary.BigEndian, &srvInit.FBWidth); err != nil {
return err
}
if err = binary.Read(session, binary.BigEndian, &srvInit.FBHeight); err != nil {
return err
}
if err = binary.Read(session, binary.BigEndian, &srvInit.PixelFormat); err != nil {
return err
}
if err = binary.Read(session, binary.BigEndian, &srvInit.NameLength); err != nil {
return err
}
srvInit.NameText = make([]byte, srvInit.NameLength)
if err = binary.Read(session, binary.BigEndian, &srvInit.NameText); err != nil {
return err
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端]: serverInit: %s", srvInit)
}
session.SetDesktopName(srvInit.NameText)
// 如果协议是aten1,则执行特殊的逻辑
if session.ProtocolVersion() == "aten1" {
session.SetWidth(800)
session.SetHeight(600)
// 发送像素格式消息
session.SetPixelFormat(rfb.NewPixelFormatAten())
} else {
session.SetWidth(srvInit.FBWidth)
session.SetHeight(srvInit.FBHeight)
//告诉vnc服务端,proxy客户端支持的像素格式,发送`SetPixelFormat`消息
pixelMsg := messages.SetPixelFormat{PF: rfb.PixelFormat32bit}
err = pixelMsg.Write(session)
if err != nil {
return err
}
session.SetPixelFormat(rfb.PixelFormat32bit)
}
// aten1协议需要再次读取扩展信息
if session.ProtocolVersion() == "aten1" {
ikvm := struct {
_ [8]byte
IKVMVideoEnable uint8
IKVMKMEnable uint8
IKVMKickEnable uint8
VUSBEnable uint8
}{}
if err = binary.Read(session, binary.BigEndian, &ikvm); err != nil {
return err
}
}
return nil
}
================================================
FILE: handler/ClientVersionHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ClientVersionHandler vnc握手第一步
// 1. 连接到vnc服务端后,读取其支持的rfb协议版本。
// 2. 解析版本,判断该版本proxy客户端是否支持。
// 3. 如果支持该版本,则发送支持的版本给vnc服务端
type ClientVersionHandler struct{}
func (*ClientVersionHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy客户端->VNC服务端]: 执行vnc握手第一步:[Version]")
}
var version [rfb.ProtoVersionLength]byte
if err := binary.Read(session, binary.BigEndian, &version); err != nil {
return err
}
major, minor, err := ParseProtoVersion(version[:])
if err != nil {
return err
}
pv := rfb.ProtoVersionUnknown
if major == 3 {
if minor >= 8 {
pv = rfb.ProtoVersion38
} else if minor >= 3 {
pv = rfb.ProtoVersion38
}
}
if pv == rfb.ProtoVersionUnknown {
return fmt.Errorf("rfb协议握手失败; 不支持的版本 '%v'", string(version[:]))
}
session.SetProtocolVersion(string(version[:]))
if err = binary.Write(session, binary.BigEndian, []byte(pv)); err != nil {
return err
}
return session.Flush()
}
func ParseProtoVersion(pv []byte) (uint, uint, error) {
var major, minor uint
if len(pv) < rfb.ProtoVersionLength {
return 0, 0, fmt.Errorf("协议版本的长度太短 (%v < %v)", len(pv), rfb.ProtoVersionLength)
}
l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor)
if l != 2 {
return 0, 0, fmt.Errorf("解析rfb协议失败")
}
if err != nil {
return 0, 0, err
}
return major, minor, nil
}
================================================
FILE: handler/ServerClientInitHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ServerClientInitHandler vnc握手步骤第三步
// 读取vnc客户端发送的是否支持共享屏幕标识
type ServerClientInitHandler struct{}
func (*ServerClientInitHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC客户端->Proxy服务端]: 执行vnc握手第三步:[ClientInit]")
}
// 读取分享屏幕标识符,proxy会无视该标识,因为通过proxy链接的vnc服务端都是默认支持分享的。
var shared uint8
if err := binary.Read(session, binary.BigEndian, &shared); err != nil {
return err
}
return nil
}
================================================
FILE: handler/ServerMessageHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ServerMessageHandler vnc握手已结束,进入消息交互阶段
// 启动两个协程,
// 1. 处理proxy服务端的ServerMessage,在ServerMessageCh通道的消息都转发写入到该会话中.
// 2. 从会话中读取clientMessages,并判断是否支持该消息,如果支持则转发到ClientMessageCh通道中。如果不支持则关闭该会话并报错。
type ServerMessageHandler struct{}
func (*ServerMessageHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debug(context.TODO(), "[VNC客户端->Proxy服务端]: vnc握手已结束,进入消息交互阶段[ServerMessageHandler]")
}
cfg := session.Options()
var err error
clientMessages := make(map[rfb.ClientMessageType]rfb.Message)
for _, m := range cfg.Messages {
clientMessages[rfb.ClientMessageType(m.Type())] = m
}
// 处理proxy服务端发送给vnc客户端的消息
go func() {
//defer wg.Done()
for {
select {
case <-session.Wait(): // 如果收到退出信号,则退出协程
return
case msg := <-cfg.Input:
// 收到proxy服务端消息,则转发写入到vnc客户端会话中。
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy服务端->VNC客户端] 消息类型:%s,消息内容:%s", rfb.ServerMessageType(msg.Type()), msg.String())
}
if err = msg.Write(session); err != nil {
cfg.ErrorCh <- err
_ = session.Close()
return
}
}
}
}()
// 处理vnc客户端发送给proxy服务端的消息
go func() {
for {
select {
case <-session.Wait():
return
default:
// 从vnc客户端的会话中读取消息类型
var messageType rfb.ClientMessageType
if err = binary.Read(session, binary.BigEndian, &messageType); err != nil {
cfg.ErrorCh <- fmt.Errorf("读取vnc客户端数据失败,err:%v", err)
_ = session.Close()
return
}
// 判断vnc客户端发送的消息类型proxy服务端是否支持。
msg, ok := clientMessages[messageType]
if !ok {
cfg.ErrorCh <- fmt.Errorf("不支持的消息类型: %v", messageType)
_ = session.Close()
return
}
// 从会话中读取消息内容
parsedMsg, e := msg.Read(session)
if e != nil {
cfg.ErrorCh <- fmt.Errorf("解析消息失败,err:%v", e)
_ = session.Close()
return
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC客户端->Proxy服务端] 消息类型:%s,消息内容:%s", rfb.ClientMessageType(parsedMsg.Type()), parsedMsg.String())
}
cfg.Output <- parsedMsg
}
}
}()
return nil
}
================================================
FILE: handler/ServerSecurityHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ServerSecurityHandler vnc握手步骤第二步
// 1.发送proxy服务端支持的安全认证套件数量及类型。
// 2.读取vnc客户端支持的安全认证套件类型,判断是否支持,
// 3.选择互相支持的安全认证套件进行认证,进入认证逻辑,如果认证成功则进入下一步,认证失败则报错。
type ServerSecurityHandler struct{}
func (*ServerSecurityHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC客户端->Proxy服务端]: 执行vnc握手第二步:[Security]")
}
cfg := session.Options()
var secType rfb.SecurityType
if session.ProtocolVersion() == rfb.ProtoVersion37 || session.ProtocolVersion() == rfb.ProtoVersion38 {
if err := binary.Write(session, binary.BigEndian, uint8(len(cfg.SecurityHandlers))); err != nil {
return err
}
for _, sType := range cfg.SecurityHandlers {
if err := binary.Write(session, binary.BigEndian, sType.Type()); err != nil {
return err
}
}
} else {
st := uint32(0)
for _, sType := range cfg.SecurityHandlers {
if uint32(sType.Type()) > st {
st = uint32(sType.Type())
secType = sType.Type()
}
}
if err := binary.Write(session, binary.BigEndian, st); err != nil {
return err
}
}
if err := session.Flush(); err != nil {
return err
}
if session.ProtocolVersion() == rfb.ProtoVersion38 {
if err := binary.Read(session, binary.BigEndian, &secType); err != nil {
return err
}
}
secTypes := make(map[rfb.SecurityType]rfb.ISecurityHandler)
for _, sType := range cfg.SecurityHandlers {
secTypes[sType.Type()] = sType
}
sType, ok := secTypes[secType]
if !ok {
return fmt.Errorf("security type %d not implemented", secType)
}
var authCode uint32
authErr := sType.Auth(session)
if authErr != nil {
authCode = uint32(1)
}
if err := binary.Write(session, binary.BigEndian, authCode); err != nil {
return err
}
if authErr == nil {
if err := session.Flush(); err != nil {
return err
}
session.SetSecurityHandler(sType)
return nil
}
if session.ProtocolVersion() == rfb.ProtoVersion38 {
if err := binary.Write(session, binary.BigEndian, uint32(len(authErr.Error()))); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, []byte(authErr.Error())); err != nil {
return err
}
if err := session.Flush(); err != nil {
return err
}
}
return authErr
}
================================================
FILE: handler/ServerServerInitHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ServerServerInitHandler vnc握手步骤第四步
// 1. 发送proxy服务端的参数信息,屏幕宽高,像素格式,桌面名称
type ServerServerInitHandler struct{}
func (*ServerServerInitHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy服务端->VNC客户端]: 执行vnc握手第四步:[ServerInit]")
}
if err := binary.Write(session, binary.BigEndian, session.Options().Width); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, session.Options().Height); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, session.Options().PixelFormat); err != nil {
return err
}
desktopName := session.Options().DesktopName
size := uint32(len(session.Options().DesktopName))
if size == 0 {
desktopName = []byte("vprix")
size = uint32(len(desktopName))
}
if err := binary.Write(session, binary.BigEndian, size); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, desktopName); err != nil {
return err
}
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[Proxy服务端->VNC客户端]: ServerInit[Width:%d,Height:%d,PixelFormat:%s,DesktopName:%s]",
session.Options().Width, session.Options().Height, session.Options().PixelFormat, desktopName)
}
return session.Flush()
}
================================================
FILE: handler/ServerVersionHandler.go
================================================
package handler
import (
"context"
"encoding/binary"
"fmt"
"github.com/osgochina/dmicro/logger"
"github.com/vprix/vncproxy/rfb"
)
// ServerVersionHandler vnc握手步骤第一步。
// 1. vnc客户端链接到proxy服务端后,proxy服务端发送rfb版本信息。
// 2. 发送版本信息后,接受vnc客户端返回的版本信息,进行版本匹配。
// 3. 确定版本信息是相互支持的,如果不支持,则返回错误信息,如果支持则进行下一步。
type ServerVersionHandler struct{}
func (*ServerVersionHandler) Handle(session rfb.ISession) error {
if logger.IsDebug() {
logger.Debugf(context.TODO(), "[VNC客户端->Proxy服务端]: 执行vnc握手第一步:[Version]")
}
var version [rfb.ProtoVersionLength]byte
if err := binary.Write(session, binary.BigEndian, []byte(rfb.ProtoVersion38)); err != nil {
return err
}
if err := session.Flush(); err != nil {
return err
}
if err := binary.Read(session, binary.BigEndian, &version); err != nil {
return err
}
major, minor, err := ParseProtoVersion(version[:])
if err != nil {
return err
}
pv := rfb.ProtoVersionUnknown
if major == 3 {
if minor >= 8 {
pv = rfb.ProtoVersion38
} else if minor >= 3 {
pv = rfb.ProtoVersion33
}
}
if pv == rfb.ProtoVersionUnknown {
return fmt.Errorf("rfb协议握手; 不支持的协议版本 '%v'", string(version[:]))
}
session.SetProtocolVersion(pv)
return nil
}
================================================
FILE: internal/dbuffer/buffer.go
================================================
package dbuffer
import (
"io"
)
// ByteBuffer provides byte buffer, which can be used for minimizing
// memory allocations.
//
// ByteBuffer may be used with functions appending data to the given []byte
// slice. See example code for details.
//
// Use Get for obtaining an empty byte buffer.
type ByteBuffer struct {
// B is a byte buffer to use in append-like workloads.
// See example code for details.
B []byte
}
// Len returns the size of the byte buffer.
func (b *ByteBuffer) Len() int {
return len(b.B)
}
// ReadFrom implements io.ReaderFrom.
//
// The function appends all the data read from r to b.
func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) {
p := b.B
nStart := int64(len(p))
nMax := int64(cap(p))
n := nStart
if nMax == 0 {
nMax = 64
p = make([]byte, nMax)
} else {
p = p[:nMax]
}
for {
if n == nMax {
nMax *= 2
bNew := make([]byte, nMax)
copy(bNew, p)
p = bNew
}
nn, err := r.Read(p[n:])
n += int64(nn)
if err != nil {
b.B = p[:n]
n -= nStart
if err == io.EOF {
return n, nil
}
return n, err
}
}
}
// WriteTo implements io.WriterTo.
func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) {
n, err := w.Write(b.B)
return int64(n), err
}
// Bytes returns b.B, i.e. all the bytes accumulated in the buffer.
//
// The purpose of this function is bytes.Buffer compatibility.
func (b *ByteBuffer) Bytes() []byte {
return b.B
}
// Write implements io.Writer - it appends p to ByteBuffer.B
func (b *ByteBuffer) Write(p []byte) (int, error) {
b.B = append(b.B, p...)
return len(p), nil
}
// WriteByte appends the byte c to the buffer.
//
// The purpose of this function is bytes.Buffer compatibility.
//
// The function always returns nil.
func (b *ByteBuffer) WriteByte(c byte) error {
b.B = append(b.B, c)
return nil
}
// WriteString appends s to ByteBuffer.B.
func (b *ByteBuffer) WriteString(s string) (int, error) {
b.B = append(b.B, s...)
return len(s), nil
}
// Set sets ByteBuffer.B to p.
func (b *ByteBuffer) Set(p []byte) {
b.B = append(b.B[:0], p...)
}
// SetString sets ByteBuffer.B to s.
func (b *ByteBuffer) SetString(s string) {
b.B = append(b.B[:0], s...)
}
// String returns string representation of ByteBuffer.B.
func (b *ByteBuffer) String() string {
return string(b.B)
}
// Reset makes ByteBuffer.B empty.
func (b *ByteBuffer) Reset() {
b.B = b.B[:0]
}
// ChangeLen changes the buffer length.
func (b *ByteBuffer) ChangeLen(newLen int) {
if cap(b.B) < newLen {
b.B = make([]byte, newLen)
} else {
b.B = b.B[:newLen]
}
}
================================================
FILE: internal/dbuffer/pool.go
================================================
package dbuffer
import (
"sort"
"sync"
"sync/atomic"
)
const (
minBitSize = 6 // 2**6=64 is a CPU cache line size
steps = 20
minSize = 1 << minBitSize
maxSize = 1 << (minBitSize + steps - 1)
calibrateCallsThreshold = 42000
maxPercentile = 0.95
)
// BufferPool represents byte buffer pool.
//
// Distinct pools may be used for distinct types of byte buffers.
// Properly determined byte buffer types with their own pools may help reducing
// memory waste.
type BufferPool struct {
calls [steps]uint64
calibrating uint64
defaultSize uint64
maxSize uint64
pool sync.Pool
}
var defaultBufferPool BufferPool
// GetByteBuffer returns an empty byte buffer from the pool.
//
// Got byte buffer may be returned to the pool via Put call.
// This reduces the number of memory allocations required for byte buffer
// management.
func GetByteBuffer() *ByteBuffer { return defaultBufferPool.Get() }
// Get returns new byte buffer with zero length.
//
// The byte buffer may be returned to the pool via Put after the use
// in order to minimize GC overhead.
func (p *BufferPool) Get() *ByteBuffer {
v := p.pool.Get()
if v != nil {
return v.(*ByteBuffer)
}
return &ByteBuffer{
B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)),
}
}
// ReleaseByteBuffer returns byte buffer to the pool.
//
// ByteBuffer.B mustn't be touched after returning it to the pool.
// Otherwise data races will occur.
func ReleaseByteBuffer(b *ByteBuffer) { defaultBufferPool.Put(b) }
// Put releases byte buffer obtained via Get to the pool.
//
// The buffer mustn't be accessed after returning to the pool.
func (p *BufferPool) Put(b *ByteBuffer) {
idx := index(len(b.B))
if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {
p.calibrate()
}
maxSize := int(atomic.LoadUint64(&p.maxSize))
if maxSize == 0 || cap(b.B) <= maxSize {
b.Reset()
p.pool.Put(b)
}
}
func (p *BufferPool) calibrate() {
if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) {
return
}
a := make(callSizes, 0, steps)
var callsSum uint64
for i := uint64(0); i < steps; i++ {
calls := atomic.SwapUint64(&p.calls[i], 0)
callsSum += calls
a = append(a, callSize{
calls: calls,
size: minSize << i,
})
}
sort.Sort(a)
defaultSize := a[0].size
maxSize := defaultSize
maxSum := uint64(float64(callsSum) * maxPercentile)
callsSum = 0
for i := 0; i < steps; i++ {
if callsSum > maxSum {
break
}
callsSum += a[i].calls
size := a[i].size
if size > maxSize {
maxSize = size
}
}
atomic.StoreUint64(&p.defaultSize, defaultSize)
atomic.StoreUint64(&p.maxSize, maxSize)
atomic.StoreUint64(&p.calibrating, 0)
}
type callSize struct {
calls uint64
size uint64
}
type callSizes []callSize
func (ci callSizes) Len() int {
return len(ci)
}
func (ci callSizes) Less(i, j int) bool {
return ci[i].calls > ci[j].calls
}
func (ci callSizes) Swap(i, j int) {
ci[i], ci[j] = ci[j], ci[i]
}
func index(n int) int {
n--
n >>= minBitSize
idx := 0
for n > 0 {
n >>= 1
idx++
}
if idx >= steps {
idx = steps - 1
}
return idx
}
================================================
FILE: internal/syncPool/sync_pool.go
================================================
package syncPool
import "sync"
type SyncPool struct {
pool sync.Pool
newFunc func() interface{}
initFunc func(interface{})
}
// NewSyncPool 创建对象池
// newFunc:创建对象的方法
// init: 对象被创建后,返回之前,调用该方法初始化对象
func NewSyncPool(newFunc func() interface{}, init func(interface{})) *SyncPool {
return &SyncPool{
newFunc: newFunc,
initFunc: init,
pool: sync.Pool{
New: newFunc,
},
}
}
// Get 获取对象
func (that *SyncPool) Get() interface{} {
var object = that.pool.Get()
if that.initFunc != nil {
that.initFunc(object)
}
return object
}
// Put 把对象放回对象池
func (that *SyncPool) Put(value interface{}) {
that.pool.Put(value)
}
================================================
FILE: messages/clientClientCutText.go
================================================
package messages
import (
"encoding/binary"
"fmt"
"github.com/vprix/vncproxy/rfb"
)
// ClientCutText 客户端发送剪切板内容到服务端
type ClientCutText struct {
_ [3]byte // 填充
Length uint32 // 剪切板内容长度
Text []byte // 剪切板
}
func (that *ClientCutText) Clone() rfb.Message {
c := &ClientCutText{
Length: that.Length,
Text: that.Text,
}
return c
}
func (that *ClientCutText) Supported(rfb.ISession) bool {
return true
}
// String
func (that *ClientCutText) String() string {
return fmt.Sprintf("length: %d", that.Length)
}
// Type returns MessageType
func (that *ClientCutText) Type() rfb.MessageType {
return rfb.MessageType(rfb.ClientCutText)
}
// Read 从会话中解析消息内容
func (that *ClientCutText) Read(session rfb.ISession) (rfb.Message, error) {
msg := &ClientCutText{}
// 读取填充字节
var pad [3]byte
if err := binary.Read(session, binary.BigEndian, &pad); err != nil {
return nil, err
}
// 读取消息长度
if err := binary.Read(session, binary.BigEndian, &msg.Length); err != nil {
return nil, err
}
// 读取指定长度的消息内容
msg.Text = make([]byte, msg.Length)
if err := binary.Read(session, binary.BigEndian, &msg.Text); err != nil {
return nil, err
}
return msg, nil
}
// Write 把消息按协议格式写入会话
func (that *ClientCutText) Write(session rfb.ISession) error {
// 写入消息类型
if err := binary.Write(session, binary.BigEndian, that.Type()); err != nil {
return err
}
// 写入3给字节的填充
var pad [3]byte
if err := binary.Write(session, binary.BigEndian, &pad); err != nil {
return err
}
if uint32(len(that.Text)) > that.Length {
that.Length = uint32(len(that.Text))
}
// 写入剪切板内容长度
if err := binary.Write(session, binary.BigEndian, that.Length); err != nil {
return err
}
// 写入消息内容
if err := binary.Write(session, binary.BigEndian, that.Text); err != nil {
return err
}
return session.Flush()
}
================================================
FILE: messages/clientClientFence.go
================================================
package messages
import (
"encoding/binary"
"fmt"
"github.com/vprix/vncproxy/rfb"
)
// ClientFence 支持 Fence扩展的客户端发送此扩展以请求数据流的同步。
type ClientFence struct {
flags uint32
length uint8
payload []byte
}
func (that *ClientFence) Clone() rfb.Message {
c := &ClientFence{
flags: that.flags,
length: that.length,
payload: that.payload,
}
return c
}
func (that *ClientFence) Supported(session rfb.ISession) bool {
return true
}
func (that *ClientFence) String() string {
return fmt.Sprintf("(type=%d)", that.Type())
}
func (that *ClientFence) Type() rfb.MessageType {
return rfb.MessageType(rfb.ClientFence)
}
// 读取数据
func (that *ClientFence) Read(session rfb.ISession) (rfb.Message, error) {
msg := &ClientFence{}
bytes := make([]byte, 3)
//c.Read(bytes)
if _, err := session.Read(bytes); err != nil {
return nil, err
}
if err := binary.Read(session, binary.BigEndian, &msg.flags); err != nil {
return nil, err
}
if err := binary.Read(session, binary.BigEndian, &msg.length); err != nil {
return nil, err
}
bytes = make([]byte, msg.length)
if _, err := session.Read(bytes); err != nil {
return nil, err
}
msg.payload = bytes
return msg, nil
}
func (that *ClientFence) Write(session rfb.ISession) error {
// 写入消息类型
if err := binary.Write(session, binary.BigEndian, that.Type()); err != nil {
return err
}
//写入填充
var pad [3]byte
if err := binary.Write(session, binary.BigEndian, pad); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.flags); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.length); err != nil {
return err
}
if err := binary.Write(session, binary.BigEndian, that.payload); err != nil {
return err
}
return session.Flush()
}
================================================
FILE: messages/clientEnableContinuousUpdates.go
================================================
package messages
import (
"encoding/binary"
"fmt"
"github.com/vprix/vncproxy/rfb"
)
// EnableContinuousUpdates 客户端发送连续更新消息
type EnableContinuousUpdates struct {
flag uint8
x uint16
y uint16
width uint16
height uint16
}
func (that *EnableContinuousUpdates) Clone() rfb.Message {
c := &EnableContinuousUpdates{
flag: that.flag,
x: that.x,
y: that.y,
width: that.width,
height: that.height,
}
return c
}
func (that *EnableContinuousUpdates) Supported(rfb.ISession) bool {
return true
}
func (that *EnableContinuousUpdates) String() string {
return fmt.Sprintf("(type=%d,flag=%d,x=%d,y=%d,width=%d,height=%d)", that.Type(), that.flag, that.x, that.y, that.width, that.height)
}
func (that *EnableContinuousUpdates) Type() rfb.MessageType {
return rfb.MessageType(rfb.EnableContinuousUpdates)
}
// 读取数据
func (that *EnableContinuousUpdates) Read(session rfb.ISession) (rfb.Message, error) {
msg := &EnableContinuousUpdates{}
if err := binary.Read(session, binary.BigEndian, &msg.flag); err != nil {
return nil, err
}
if err := binary.Read(session, binary.BigEndian, &msg.x); err != nil {
return nil, err
}
if err := binary.Read(session, binary.BigEndian, &msg.y); err != nil {
return nil, err
gitextract_0eg_p_n1/
├── .gitignore
├── LICENSE
├── README.md
├── bin/
│ └── .keep
├── build
├── canvas/
│ ├── canvas.go
│ └── rgb_image.go
├── cmd/
│ ├── player/
│ │ ├── main.go
│ │ ├── tcpServer.go
│ │ └── wsServer.go
│ ├── proxy/
│ │ ├── main.go
│ │ ├── tcpServer.go
│ │ └── wsServer.go
│ ├── recorder/
│ │ ├── main.go
│ │ └── recorder.go
│ ├── screenshot/
│ │ └── main.go
│ └── video/
│ └── main.go
├── docs/
│ ├── .keep
│ ├── .nojekyll
│ ├── README.md
│ ├── changelog.md
│ ├── index.html
│ ├── overview.md
│ ├── player/
│ │ ├── .keep
│ │ └── README.md
│ ├── proxy/
│ │ ├── .keep
│ │ └── README.md
│ ├── questions.md
│ ├── recorder/
│ │ ├── .keep
│ │ └── README.md
│ ├── rfc6143/
│ │ ├── GLOSSORY.md
│ │ ├── README.md
│ │ ├── handshake/
│ │ │ ├── README.md
│ │ │ ├── initial.md
│ │ │ ├── protocol-version.md
│ │ │ └── security-type.md
│ │ └── transfer/
│ │ ├── README.md
│ │ ├── display.md
│ │ ├── encoding/
│ │ │ ├── README.md
│ │ │ ├── copy-rect.md
│ │ │ ├── raw.md
│ │ │ ├── rise-and-run-length.md
│ │ │ ├── set-encoding.md
│ │ │ ├── tight-png.md
│ │ │ ├── tiled-run-length.md
│ │ │ └── zlib-run-length.md
│ │ ├── input/
│ │ │ ├── README.md
│ │ │ ├── clipboard.md
│ │ │ ├── keyboard.md
│ │ │ └── mouse.md
│ │ ├── pixel-format.md
│ │ └── set-color-map.md
│ ├── screenshot/
│ │ ├── .keep
│ │ └── README.md
│ ├── summary.md
│ └── video/
│ ├── .keep
│ └── README.md
├── encodings/
│ ├── default_encoding.go
│ ├── encoding.go
│ ├── encoding_copyrect.go
│ ├── encoding_corre.go
│ ├── encoding_h264.go
│ ├── encoding_hextile.go
│ ├── encoding_jpeg.go
│ ├── encoding_jrle.go
│ ├── encoding_raw.go
│ ├── encoding_rre.go
│ ├── encoding_tight.go
│ ├── encoding_tightpng.go
│ ├── encoding_trle.go
│ ├── encoding_zlib.go
│ ├── encoding_zrle.go
│ ├── pseudo_cursor.go
│ ├── pseudo_cursor_with_alpha.go
│ ├── pseudo_desktop_name.go
│ ├── pseudo_desktop_size.go
│ ├── pseudo_extended_desktop_size.go
│ ├── pseudo_fence.go
│ ├── pseudo_last_rect.go
│ ├── pseudo_led_state.go
│ ├── pseudo_pointer_pos.go
│ └── pseudo_x_cursor.go
├── go.mod
├── handler/
│ ├── ClientClientInitHandler.go
│ ├── ClientMessageHandler.go
│ ├── ClientSecurityHandler.go
│ ├── ClientServerInitHandler.go
│ ├── ClientVersionHandler.go
│ ├── ServerClientInitHandler.go
│ ├── ServerMessageHandler.go
│ ├── ServerSecurityHandler.go
│ ├── ServerServerInitHandler.go
│ └── ServerVersionHandler.go
├── internal/
│ ├── dbuffer/
│ │ ├── buffer.go
│ │ └── pool.go
│ └── syncPool/
│ └── sync_pool.go
├── messages/
│ ├── clientClientCutText.go
│ ├── clientClientFence.go
│ ├── clientEnableContinuousUpdates.go
│ ├── clientFramebufferUpdateRequest.go
│ ├── clientKeyEvent.go
│ ├── clientPointerEvent.go
│ ├── clientQEMUExtKeyEvent.go
│ ├── clientSetDesktopSize.go
│ ├── clientSetEncodings.go
│ ├── clientSetPixelFormat.go
│ ├── default_message.go
│ ├── serverBell.go
│ ├── serverEndOfContinuousUpdates.go
│ ├── serverFramebufferUpdate.go
│ ├── serverInit.go
│ ├── serverServerCutText.go
│ ├── serverServerFence.go
│ └── serverSetColorMapEntries.go
├── rfb/
│ ├── color_map.go
│ ├── desktop.go
│ ├── encoding.go
│ ├── encodingtype.go
│ ├── encodingtype_string.go
│ ├── handler.go
│ ├── keys.go
│ ├── keys_string.go
│ ├── message.go
│ ├── message_type_client_string.go
│ ├── message_type_server_string.go
│ ├── messagetype.go
│ ├── options.go
│ ├── pixel_format.go
│ ├── rectangle.go
│ ├── security.go
│ ├── securitysubtype_string.go
│ ├── securitytype_string.go
│ ├── session.go
│ ├── session_string.go
│ └── target_config.go
├── security/
│ ├── security_none.go
│ ├── security_tight.go
│ ├── security_vencryptplain.go
│ └── security_vnc.go
├── session/
│ ├── canvas.go
│ ├── client.go
│ ├── player.go
│ ├── recorder.go
│ └── server.go
└── vnc/
├── player.go
├── proxy.go
├── recorder.go
├── screenshot.go
└── video.go
SYMBOL INDEX (957 symbols across 96 files)
FILE: canvas/canvas.go
constant BlockWidth (line 15) | BlockWidth = 16
constant BlockHeight (line 16) | BlockHeight = 16
type VncCanvas (line 19) | type VncCanvas struct
method Read (line 40) | func (that *VncCanvas) Read(buf []byte) (int, error) {
method Write (line 45) | func (that *VncCanvas) Write(buf []byte) (int, error) {
method Close (line 50) | func (that *VncCanvas) Close() error {
method SetChanged (line 54) | func (that *VncCanvas) SetChanged(rect *rfb.Rectangle) {
method Reset (line 67) | func (that *VncCanvas) Reset(rect *rfb.Rectangle) {
method RemoveCursor (line 71) | func (that *VncCanvas) RemoveCursor() image.Image {
method PaintCursor (line 100) | func (that *VncCanvas) PaintCursor() image.Image {
method FillRect (line 140) | func (that *VncCanvas) FillRect(rect *image.Rectangle, c color.Color) {
method ReadColor (line 149) | func (that *VncCanvas) ReadColor(c io.Reader, pf *rfb.PixelFormat) (*c...
method DecodeRaw (line 186) | func (that *VncCanvas) DecodeRaw(reader io.Reader, pf *rfb.PixelFormat...
function NewVncCanvas (line 31) | func NewVncCanvas(width, height int) *VncCanvas {
function MakeRect (line 199) | func MakeRect(x, y, width, height int) image.Rectangle {
function MakeRectFromVncRect (line 203) | func MakeRectFromVncRect(rect *rfb.Rectangle) image.Rectangle {
FILE: canvas/rgb_image.go
type RGBColor (line 8) | type RGBColor struct
method RGBA (line 12) | func (that RGBColor) RGBA() (r, g, b, a uint32) {
type RGBImage (line 16) | type RGBImage struct
method ColorModel (line 26) | func (that RGBImage) ColorModel() color.Model {
method Bounds (line 30) | func (that RGBImage) Bounds() image.Rectangle {
method At (line 34) | func (that RGBImage) At(x, y int) color.Color {
method RGBAt (line 39) | func (that *RGBImage) RGBAt(x, y int) *RGBColor {
method PixOffset (line 47) | func (that *RGBImage) PixOffset(x, y int) int {
method Set (line 51) | func (that RGBImage) Set(x, y int, c color.Color) {
method SetRGB (line 62) | func (that *RGBImage) SetRGB(x, y int, c color.RGBA) {
function NewRGBImage (line 72) | func NewRGBImage(r image.Rectangle) *RGBImage {
FILE: cmd/player/main.go
function main (line 55) | func main() {
FILE: cmd/player/tcpServer.go
type TcpSandBox (line 20) | type TcpSandBox struct
method ID (line 41) | func (that *TcpSandBox) ID() int {
method Name (line 45) | func (that *TcpSandBox) Name() string {
method Setup (line 49) | func (that *TcpSandBox) Setup() error {
method Shutdown (line 96) | func (that *TcpSandBox) Shutdown() error {
method Service (line 101) | func (that *TcpSandBox) Service() *easyservice.EasyService {
function NewTcpSandBox (line 30) | func NewTcpSandBox(cfg *gcfg.Config) *TcpSandBox {
FILE: cmd/player/wsServer.go
type WSSandBox (line 20) | type WSSandBox struct
method ID (line 39) | func (that *WSSandBox) ID() int {
method Name (line 43) | func (that *WSSandBox) Name() string {
method Setup (line 47) | func (that *WSSandBox) Setup() error {
method Shutdown (line 78) | func (that *WSSandBox) Shutdown() error {
method Service (line 82) | func (that *WSSandBox) Service() *easyservice.EasyService {
function NewWSSandBox (line 29) | func NewWSSandBox(cfg *gcfg.Config) *WSSandBox {
FILE: cmd/proxy/main.go
function main (line 61) | func main() {
FILE: cmd/proxy/tcpServer.go
type TcpSandBox (line 22) | type TcpSandBox struct
method ID (line 45) | func (that *TcpSandBox) ID() int {
method Name (line 49) | func (that *TcpSandBox) Name() string {
method Setup (line 53) | func (that *TcpSandBox) Setup() error {
method Shutdown (line 120) | func (that *TcpSandBox) Shutdown() error {
method Service (line 125) | func (that *TcpSandBox) Service() *easyservice.EasyService {
function NewTcpSandBox (line 33) | func NewTcpSandBox(cfg *gcfg.Config) *TcpSandBox {
FILE: cmd/proxy/wsServer.go
type WSSandBox (line 22) | type WSSandBox struct
method ID (line 43) | func (that *WSSandBox) ID() int {
method Name (line 47) | func (that *WSSandBox) Name() string {
method Setup (line 51) | func (that *WSSandBox) Setup() error {
method Shutdown (line 100) | func (that *WSSandBox) Shutdown() error {
method Service (line 104) | func (that *WSSandBox) Service() *easyservice.EasyService {
function NewWSSandBox (line 32) | func NewWSSandBox(cfg *gcfg.Config) *WSSandBox {
FILE: cmd/recorder/main.go
function main (line 46) | func main() {
FILE: cmd/recorder/recorder.go
type RecorderSandBox (line 24) | type RecorderSandBox struct
method ID (line 45) | func (that *RecorderSandBox) ID() int {
method Name (line 49) | func (that *RecorderSandBox) Name() string {
method Setup (line 53) | func (that *RecorderSandBox) Setup() error {
method Shutdown (line 105) | func (that *RecorderSandBox) Shutdown() error {
method Service (line 111) | func (that *RecorderSandBox) Service() *easyservice.EasyService {
function NewRecorderSandBox (line 34) | func NewRecorderSandBox(cfg *gcfg.Config) *RecorderSandBox {
FILE: cmd/screenshot/main.go
function main (line 48) | func main() {
FILE: cmd/video/main.go
function main (line 3) | func main() {
FILE: encodings/encoding.go
function ReadUint8 (line 11) | func ReadUint8(r io.Reader) (uint8, error) {
function ReadUint16 (line 20) | func ReadUint16(r io.Reader) (uint16, error) {
function ReadUint32 (line 29) | func ReadUint32(r io.Reader) (uint32, error) {
function ReadBytes (line 38) | func ReadBytes(count int, r io.Reader) ([]byte, error) {
function ReadPixel (line 55) | func ReadPixel(c io.Reader, pf *rfb.PixelFormat) ([]byte, error) {
FILE: encodings/encoding_copyrect.go
type CopyRectEncoding (line 17) | type CopyRectEncoding struct
method Type (line 21) | func (that *CopyRectEncoding) Type() rfb.EncodingType {
method Supported (line 25) | func (that *CopyRectEncoding) Supported(session rfb.ISession) bool {
method Clone (line 29) | func (that *CopyRectEncoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 38) | func (that *CopyRectEncoding) Read(session rfb.ISession, rect *rfb.Rec...
method Write (line 49) | func (that *CopyRectEncoding) Write(session rfb.ISession, rect *rfb.Re...
FILE: encodings/encoding_corre.go
type CoRREEncoding (line 16) | type CoRREEncoding struct
method Type (line 20) | func (that *CoRREEncoding) Type() rfb.EncodingType {
method Supported (line 24) | func (that *CoRREEncoding) Supported(session rfb.ISession) bool {
method Clone (line 28) | func (that *CoRREEncoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 40) | func (that *CoRREEncoding) Read(session rfb.ISession, rect *rfb.Rectan...
method Write (line 68) | func (that *CoRREEncoding) Write(session rfb.ISession, rect *rfb.Recta...
FILE: encodings/encoding_h264.go
type H264Encoding (line 10) | type H264Encoding struct
method Type (line 16) | func (that *H264Encoding) Type() rfb.EncodingType {
method Supported (line 20) | func (that *H264Encoding) Supported(session rfb.ISession) bool {
method Clone (line 24) | func (that *H264Encoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 35) | func (that *H264Encoding) Read(session rfb.ISession, rectangle *rfb.Re...
method Write (line 66) | func (that *H264Encoding) Write(session rfb.ISession, rectangle *rfb.R...
FILE: encodings/encoding_hextile.go
constant HexTileRaw (line 15) | HexTileRaw = 1 << 0
constant HexTileBackgroundSpecified (line 16) | HexTileBackgroundSpecified = 1 << 1
constant HexTileForegroundSpecified (line 17) | HexTileForegroundSpecified = 1 << 2
constant HexTileAnySubRects (line 18) | HexTileAnySubRects = 1 << 3
constant HexTileSubRectsColoured (line 19) | HexTileSubRectsColoured = 1 << 4
type HexTileEncoding (line 24) | type HexTileEncoding struct
method Supported (line 30) | func (that *HexTileEncoding) Supported(_ rfb.ISession) bool {
method Clone (line 34) | func (that *HexTileEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 45) | func (that *HexTileEncoding) Type() rfb.EncodingType {
method Read (line 69) | func (that *HexTileEncoding) Read(session rfb.ISession, rect *rfb.Rect...
method Write (line 157) | func (that *HexTileEncoding) Write(sess rfb.ISession, rect *rfb.Rectan...
method draw (line 167) | func (that *HexTileEncoding) draw(cv *canvas.VncCanvas, pf rfb.PixelFo...
FILE: encodings/encoding_raw.go
type RawEncoding (line 14) | type RawEncoding struct
method Supported (line 20) | func (that *RawEncoding) Supported(rfb.ISession) bool {
method Type (line 23) | func (that *RawEncoding) Type() rfb.EncodingType {
method Clone (line 27) | func (that *RawEncoding) Clone(data ...bool) rfb.IEncoding {
method Write (line 38) | func (that *RawEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle)...
method Read (line 54) | func (that *RawEncoding) Read(session rfb.ISession, rect *rfb.Rectangl...
FILE: encodings/encoding_rre.go
type RREEncoding (line 17) | type RREEncoding struct
method Type (line 21) | func (that *RREEncoding) Type() rfb.EncodingType {
method Supported (line 24) | func (that *RREEncoding) Supported(session rfb.ISession) bool {
method Clone (line 27) | func (that *RREEncoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 38) | func (that *RREEncoding) Read(session rfb.ISession, rect *rfb.Rectangl...
method Write (line 65) | func (that *RREEncoding) Write(session rfb.ISession, rect *rfb.Rectang...
FILE: encodings/encoding_tight.go
constant tightCompressionBasic (line 14) | tightCompressionBasic = 0
constant tightCompressionFill (line 15) | tightCompressionFill = 0x08
constant tightCompressionJPEG (line 16) | tightCompressionJPEG = 0x09
constant tightCompressionPNG (line 17) | tightCompressionPNG = 0x0A
constant TightFilterCopy (line 21) | TightFilterCopy = 0
constant TightFilterPalette (line 22) | TightFilterPalette = 1
constant TightFilterGradient (line 23) | TightFilterGradient = 2
type TightEncoding (line 26) | type TightEncoding struct
method Supported (line 30) | func (that *TightEncoding) Supported(session rfb.ISession) bool {
method Clone (line 34) | func (that *TightEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 44) | func (that *TightEncoding) Type() rfb.EncodingType {
method Write (line 60) | func (that *TightEncoding) Write(session rfb.ISession, rect *rfb.Recta...
method Read (line 68) | func (that *TightEncoding) Read(session rfb.ISession, rect *rfb.Rectan...
method ReadCompactLen (line 115) | func (that *TightEncoding) ReadCompactLen(session rfb.ISession) (int, ...
method handleTightFilters (line 143) | func (that *TightEncoding) handleTightFilters(session rfb.ISession, re...
method readTightPalette (line 194) | func (that *TightEncoding) readTightPalette(session rfb.ISession, byte...
method ReadTightData (line 211) | func (that *TightEncoding) ReadTightData(dataSize int, session rfb.ISe...
function calcTightBytePerPixel (line 48) | func calcTightBytePerPixel(pf *rfb.PixelFormat) int {
FILE: encodings/encoding_tightpng.go
type TightPngEncoding (line 11) | type TightPngEncoding struct
method Supported (line 15) | func (that *TightPngEncoding) Supported(session rfb.ISession) bool {
method Clone (line 19) | func (that *TightPngEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 30) | func (that *TightPngEncoding) Type() rfb.EncodingType {
method Write (line 34) | func (that *TightPngEncoding) Write(session rfb.ISession, rect *rfb.Re...
method Read (line 42) | func (that *TightPngEncoding) Read(session rfb.ISession, rect *rfb.Rec...
method ReadCompactLen (line 81) | func (that *TightPngEncoding) ReadCompactLen(session rfb.ISession) (in...
FILE: encodings/encoding_zlib.go
type ZLibEncoding (line 10) | type ZLibEncoding struct
method Supported (line 14) | func (that *ZLibEncoding) Supported(c rfb.ISession) bool {
method Type (line 17) | func (that *ZLibEncoding) Type() rfb.EncodingType {
method Clone (line 21) | func (that *ZLibEncoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 32) | func (that *ZLibEncoding) Read(sess rfb.ISession, rect *rfb.Rectangle)...
method Write (line 55) | func (that *ZLibEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle...
FILE: encodings/encoding_zrle.go
constant ZRLERawPixelData (line 16) | ZRLERawPixelData = 0
constant ZRLESingleColour (line 17) | ZRLESingleColour = 1
type ZRLEEncoding (line 23) | type ZRLEEncoding struct
method Type (line 27) | func (that *ZRLEEncoding) Type() rfb.EncodingType {
method Supported (line 31) | func (that *ZRLEEncoding) Supported(session rfb.ISession) bool {
method Clone (line 35) | func (that *ZRLEEncoding) Clone(data ...bool) rfb.IEncoding {
method Read (line 46) | func (that *ZRLEEncoding) Read(sess rfb.ISession, rect *rfb.Rectangle)...
method Write (line 68) | func (that *ZRLEEncoding) Write(sess rfb.ISession, rect *rfb.Rectangle...
method draw (line 81) | func (that *ZRLEEncoding) draw(cv *canvas.VncCanvas, pf rfb.PixelForma...
method readZRLERaw (line 153) | func (that *ZRLEEncoding) readZRLERaw(cv *canvas.VncCanvas, reader io....
method handlePaletteTile (line 216) | func (that *ZRLEEncoding) handlePaletteTile(cv *canvas.VncCanvas, unZi...
method handlePlainRLETile (line 270) | func (that *ZRLEEncoding) handlePlainRLETile(cv *canvas.VncCanvas, unZ...
method handlePaletteRLETile (line 316) | func (that *ZRLEEncoding) handlePaletteRLETile(cv *canvas.VncCanvas, u...
function min (line 146) | func min(a, b int) int {
function readCPixel (line 168) | func readCPixel(cv *canvas.VncCanvas, c io.Reader, pf *rfb.PixelFormat) ...
function IsCPixelSpecific (line 206) | func IsCPixelSpecific(pf *rfb.PixelFormat) bool {
function readRunLength (line 296) | func readRunLength(r io.Reader) (int, error) {
FILE: encodings/pseudo_cursor.go
type CursorPseudoEncoding (line 18) | type CursorPseudoEncoding struct
method Supported (line 22) | func (that *CursorPseudoEncoding) Supported(session rfb.ISession) bool {
method draw (line 27) | func (that *CursorPseudoEncoding) draw(cv *canvas.VncCanvas, pf rfb.Pi...
method Clone (line 68) | func (that *CursorPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 79) | func (that *CursorPseudoEncoding) Type() rfb.EncodingType {
method Read (line 83) | func (that *CursorPseudoEncoding) Read(session rfb.ISession, rect *rfb...
method Write (line 108) | func (that *CursorPseudoEncoding) Write(sess rfb.ISession, rect *rfb.R...
FILE: encodings/pseudo_cursor_with_alpha.go
type CursorWithAlphaPseudoEncoding (line 8) | type CursorWithAlphaPseudoEncoding struct
method Supported (line 12) | func (that *CursorWithAlphaPseudoEncoding) Supported(session rfb.ISess...
method Clone (line 16) | func (that *CursorWithAlphaPseudoEncoding) Clone(data ...bool) rfb.IEn...
method Type (line 27) | func (that *CursorWithAlphaPseudoEncoding) Type() rfb.EncodingType {
method Read (line 31) | func (that *CursorWithAlphaPseudoEncoding) Read(session rfb.ISession, ...
method Write (line 53) | func (that *CursorWithAlphaPseudoEncoding) Write(session rfb.ISession,...
FILE: encodings/pseudo_desktop_name.go
type DesktopNamePseudoEncoding (line 9) | type DesktopNamePseudoEncoding struct
method Supported (line 13) | func (that *DesktopNamePseudoEncoding) Supported(session rfb.ISession)...
method Clone (line 17) | func (that *DesktopNamePseudoEncoding) Clone(data ...bool) rfb.IEncodi...
method Type (line 25) | func (that *DesktopNamePseudoEncoding) Type() rfb.EncodingType {
method Read (line 30) | func (that *DesktopNamePseudoEncoding) Read(session rfb.ISession, rect...
method Write (line 43) | func (that *DesktopNamePseudoEncoding) Write(session rfb.ISession, rec...
FILE: encodings/pseudo_desktop_size.go
type DesktopSizePseudoEncoding (line 10) | type DesktopSizePseudoEncoding struct
method Supported (line 13) | func (that *DesktopSizePseudoEncoding) Supported(session rfb.ISession)...
method Clone (line 17) | func (that *DesktopSizePseudoEncoding) Clone(data ...bool) rfb.IEncodi...
method Type (line 22) | func (that *DesktopSizePseudoEncoding) Type() rfb.EncodingType { retur...
method Read (line 25) | func (that *DesktopSizePseudoEncoding) Read(session rfb.ISession, rect...
method Write (line 29) | func (that *DesktopSizePseudoEncoding) Write(session rfb.ISession, rec...
FILE: encodings/pseudo_extended_desktop_size.go
type ExtendedDesktopSizePseudo (line 10) | type ExtendedDesktopSizePseudo struct
method Supported (line 14) | func (that *ExtendedDesktopSizePseudo) Supported(rfb.ISession) bool {
method Clone (line 18) | func (that *ExtendedDesktopSizePseudo) Clone(data ...bool) rfb.IEncodi...
method Type (line 27) | func (that *ExtendedDesktopSizePseudo) Type() rfb.EncodingType {
method Write (line 31) | func (that *ExtendedDesktopSizePseudo) Write(session rfb.ISession, rec...
method Read (line 37) | func (that *ExtendedDesktopSizePseudo) Read(session rfb.ISession, rect...
FILE: encodings/pseudo_fence.go
type FencePseudo (line 7) | type FencePseudo struct
method Supported (line 10) | func (that *FencePseudo) Supported(_ rfb.ISession) bool {
method Clone (line 14) | func (that *FencePseudo) Clone(_ ...bool) rfb.IEncoding {
method Type (line 19) | func (that *FencePseudo) Type() rfb.EncodingType {
method Read (line 23) | func (that *FencePseudo) Read(_ rfb.ISession, _ *rfb.Rectangle) error {
method Write (line 27) | func (that *FencePseudo) Write(_ rfb.ISession, _ *rfb.Rectangle) error {
FILE: encodings/pseudo_last_rect.go
type LastRectPseudo (line 7) | type LastRectPseudo struct
method Supported (line 10) | func (that *LastRectPseudo) Supported(_ rfb.ISession) bool {
method Clone (line 14) | func (that *LastRectPseudo) Clone(_ ...bool) rfb.IEncoding {
method Type (line 19) | func (that *LastRectPseudo) Type() rfb.EncodingType {
method Read (line 23) | func (that *LastRectPseudo) Read(_ rfb.ISession, _ *rfb.Rectangle) err...
method Write (line 27) | func (that *LastRectPseudo) Write(_ rfb.ISession, _ *rfb.Rectangle) er...
FILE: encodings/pseudo_led_state.go
type LedStatePseudo (line 12) | type LedStatePseudo struct
method Supported (line 16) | func (that *LedStatePseudo) Supported(session rfb.ISession) bool {
method Clone (line 20) | func (that *LedStatePseudo) Clone(data ...bool) rfb.IEncoding {
method Type (line 25) | func (that *LedStatePseudo) Type() rfb.EncodingType {
method Read (line 29) | func (that *LedStatePseudo) Read(session rfb.ISession, rect *rfb.Recta...
method Write (line 38) | func (that *LedStatePseudo) Write(session rfb.ISession, rect *rfb.Rect...
FILE: encodings/pseudo_pointer_pos.go
type CursorPosPseudoEncoding (line 11) | type CursorPosPseudoEncoding struct
method Supported (line 14) | func (that *CursorPosPseudoEncoding) Supported(session rfb.ISession) b...
method Draw (line 18) | func (that *CursorPosPseudoEncoding) Draw(img draw.Image, rect *rfb.Re...
method Clone (line 28) | func (that *CursorPosPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 33) | func (that *CursorPosPseudoEncoding) Type() rfb.EncodingType {
method Read (line 37) | func (that *CursorPosPseudoEncoding) Read(session rfb.ISession, rect *...
method Write (line 41) | func (that *CursorPosPseudoEncoding) Write(session rfb.ISession, rect ...
FILE: encodings/pseudo_x_cursor.go
type XCursorPseudoEncoding (line 9) | type XCursorPseudoEncoding struct
method Supported (line 16) | func (that *XCursorPseudoEncoding) Supported(session rfb.ISession) bool {
method Clone (line 19) | func (that *XCursorPseudoEncoding) Clone(data ...bool) rfb.IEncoding {
method Type (line 37) | func (that *XCursorPseudoEncoding) Type() rfb.EncodingType {
method Read (line 41) | func (that *XCursorPseudoEncoding) Read(session rfb.ISession, rect *rf...
method Write (line 76) | func (that *XCursorPseudoEncoding) Write(session rfb.ISession, rect *r...
FILE: handler/ClientClientInitHandler.go
type ClientClientInitHandler (line 13) | type ClientClientInitHandler struct
method Handle (line 15) | func (that *ClientClientInitHandler) Handle(session rfb.ISession) error {
FILE: handler/ClientMessageHandler.go
type ClientMessageHandler (line 17) | type ClientMessageHandler struct
method Handle (line 19) | func (*ClientMessageHandler) Handle(session rfb.ISession) error {
FILE: handler/ClientSecurityHandler.go
type ClientSecurityHandler (line 16) | type ClientSecurityHandler struct
method Handle (line 18) | func (*ClientSecurityHandler) Handle(session rfb.ISession) error {
FILE: handler/ClientServerInitHandler.go
type ClientServerInitHandler (line 13) | type ClientServerInitHandler struct
method Handle (line 15) | func (*ClientServerInitHandler) Handle(session rfb.ISession) error {
FILE: handler/ClientVersionHandler.go
type ClientVersionHandler (line 15) | type ClientVersionHandler struct
method Handle (line 17) | func (*ClientVersionHandler) Handle(session rfb.ISession) error {
function ParseProtoVersion (line 51) | func ParseProtoVersion(pv []byte) (uint, uint, error) {
FILE: handler/ServerClientInitHandler.go
type ServerClientInitHandler (line 12) | type ServerClientInitHandler struct
method Handle (line 14) | func (*ServerClientInitHandler) Handle(session rfb.ISession) error {
FILE: handler/ServerMessageHandler.go
type ServerMessageHandler (line 15) | type ServerMessageHandler struct
method Handle (line 17) | func (*ServerMessageHandler) Handle(session rfb.ISession) error {
FILE: handler/ServerSecurityHandler.go
type ServerSecurityHandler (line 15) | type ServerSecurityHandler struct
method Handle (line 17) | func (*ServerSecurityHandler) Handle(session rfb.ISession) error {
FILE: handler/ServerServerInitHandler.go
type ServerServerInitHandler (line 12) | type ServerServerInitHandler struct
method Handle (line 14) | func (*ServerServerInitHandler) Handle(session rfb.ISession) error {
FILE: handler/ServerVersionHandler.go
type ServerVersionHandler (line 15) | type ServerVersionHandler struct
method Handle (line 17) | func (*ServerVersionHandler) Handle(session rfb.ISession) error {
FILE: internal/dbuffer/buffer.go
type ByteBuffer (line 14) | type ByteBuffer struct
method Len (line 22) | func (b *ByteBuffer) Len() int {
method ReadFrom (line 29) | func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) {
method WriteTo (line 61) | func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) {
method Bytes (line 69) | func (b *ByteBuffer) Bytes() []byte {
method Write (line 74) | func (b *ByteBuffer) Write(p []byte) (int, error) {
method WriteByte (line 84) | func (b *ByteBuffer) WriteByte(c byte) error {
method WriteString (line 90) | func (b *ByteBuffer) WriteString(s string) (int, error) {
method Set (line 96) | func (b *ByteBuffer) Set(p []byte) {
method SetString (line 101) | func (b *ByteBuffer) SetString(s string) {
method String (line 106) | func (b *ByteBuffer) String() string {
method Reset (line 111) | func (b *ByteBuffer) Reset() {
method ChangeLen (line 116) | func (b *ByteBuffer) ChangeLen(newLen int) {
FILE: internal/dbuffer/pool.go
constant minBitSize (line 10) | minBitSize = 6
constant steps (line 11) | steps = 20
constant minSize (line 13) | minSize = 1 << minBitSize
constant maxSize (line 14) | maxSize = 1 << (minBitSize + steps - 1)
constant calibrateCallsThreshold (line 16) | calibrateCallsThreshold = 42000
constant maxPercentile (line 17) | maxPercentile = 0.95
type BufferPool (line 25) | type BufferPool struct
method Get (line 48) | func (p *BufferPool) Get() *ByteBuffer {
method Put (line 67) | func (p *BufferPool) Put(b *ByteBuffer) {
method calibrate (line 81) | func (p *BufferPool) calibrate() {
function GetByteBuffer (line 42) | func GetByteBuffer() *ByteBuffer { return defaultBufferPool.Get() }
function ReleaseByteBuffer (line 62) | func ReleaseByteBuffer(b *ByteBuffer) { defaultBufferPool.Put(b) }
type callSize (line 120) | type callSize struct
type callSizes (line 125) | type callSizes
method Len (line 127) | func (ci callSizes) Len() int {
method Less (line 131) | func (ci callSizes) Less(i, j int) bool {
method Swap (line 135) | func (ci callSizes) Swap(i, j int) {
function index (line 139) | func index(n int) int {
FILE: internal/syncPool/sync_pool.go
type SyncPool (line 5) | type SyncPool struct
method Get (line 25) | func (that *SyncPool) Get() interface{} {
method Put (line 34) | func (that *SyncPool) Put(value interface{}) {
function NewSyncPool (line 14) | func NewSyncPool(newFunc func() interface{}, init func(interface{})) *Sy...
FILE: messages/clientClientCutText.go
type ClientCutText (line 10) | type ClientCutText struct
method Clone (line 16) | func (that *ClientCutText) Clone() rfb.Message {
method Supported (line 23) | func (that *ClientCutText) Supported(rfb.ISession) bool {
method String (line 28) | func (that *ClientCutText) String() string {
method Type (line 33) | func (that *ClientCutText) Type() rfb.MessageType {
method Read (line 38) | func (that *ClientCutText) Read(session rfb.ISession) (rfb.Message, er...
method Write (line 58) | func (that *ClientCutText) Write(session rfb.ISession) error {
FILE: messages/clientClientFence.go
type ClientFence (line 10) | type ClientFence struct
method Clone (line 16) | func (that *ClientFence) Clone() rfb.Message {
method Supported (line 25) | func (that *ClientFence) Supported(session rfb.ISession) bool {
method String (line 28) | func (that *ClientFence) String() string {
method Type (line 32) | func (that *ClientFence) Type() rfb.MessageType {
method Read (line 37) | func (that *ClientFence) Read(session rfb.ISession) (rfb.Message, erro...
method Write (line 58) | func (that *ClientFence) Write(session rfb.ISession) error {
FILE: messages/clientEnableContinuousUpdates.go
type EnableContinuousUpdates (line 10) | type EnableContinuousUpdates struct
method Clone (line 18) | func (that *EnableContinuousUpdates) Clone() rfb.Message {
method Supported (line 29) | func (that *EnableContinuousUpdates) Supported(rfb.ISession) bool {
method String (line 32) | func (that *EnableContinuousUpdates) String() string {
method Type (line 36) | func (that *EnableContinuousUpdates) Type() rfb.MessageType {
method Read (line 41) | func (that *EnableContinuousUpdates) Read(session rfb.ISession) (rfb.M...
method Write (line 61) | func (that *EnableContinuousUpdates) Write(session rfb.ISession) error {
FILE: messages/clientFramebufferUpdateRequest.go
type FramebufferUpdateRequest (line 13) | type FramebufferUpdateRequest struct
method Clone (line 19) | func (that *FramebufferUpdateRequest) Clone() rfb.Message {
method Supported (line 30) | func (that *FramebufferUpdateRequest) Supported(session rfb.ISession) ...
method String (line 35) | func (that *FramebufferUpdateRequest) String() string {
method Type (line 40) | func (that *FramebufferUpdateRequest) Type() rfb.MessageType {
method Read (line 45) | func (that *FramebufferUpdateRequest) Read(session rfb.ISession) (rfb....
method Write (line 54) | func (that *FramebufferUpdateRequest) Write(session rfb.ISession) error {
FILE: messages/clientKeyEvent.go
type KeyEvent (line 10) | type KeyEvent struct
method Clone (line 16) | func (that *KeyEvent) Clone() rfb.Message {
method Supported (line 24) | func (that *KeyEvent) Supported(session rfb.ISession) bool {
method String (line 29) | func (that *KeyEvent) String() string {
method Type (line 34) | func (that *KeyEvent) Type() rfb.MessageType {
method Read (line 39) | func (that *KeyEvent) Read(session rfb.ISession) (rfb.Message, error) {
method Write (line 48) | func (that *KeyEvent) Write(session rfb.ISession) error {
FILE: messages/clientPointerEvent.go
type PointerEvent (line 10) | type PointerEvent struct
method Clone (line 15) | func (that *PointerEvent) Clone() rfb.Message {
method Supported (line 24) | func (that *PointerEvent) Supported(session rfb.ISession) bool {
method String (line 29) | func (that *PointerEvent) String() string {
method Type (line 34) | func (that *PointerEvent) Type() rfb.MessageType {
method Read (line 39) | func (that *PointerEvent) Read(session rfb.ISession) (rfb.Message, err...
method Write (line 48) | func (that *PointerEvent) Write(session rfb.ISession) error {
FILE: messages/clientQEMUExtKeyEvent.go
type QEMUExtKeyEvent (line 9) | type QEMUExtKeyEvent struct
method Clone (line 16) | func (that *QEMUExtKeyEvent) Clone() rfb.Message {
method Supported (line 26) | func (that *QEMUExtKeyEvent) Supported(session rfb.ISession) bool {
method Type (line 29) | func (that *QEMUExtKeyEvent) Type() rfb.MessageType {
method String (line 33) | func (that *QEMUExtKeyEvent) String() string {
method Read (line 37) | func (that *QEMUExtKeyEvent) Read(session rfb.ISession) (rfb.Message, ...
method Write (line 45) | func (that *QEMUExtKeyEvent) Write(session rfb.ISession) error {
FILE: messages/clientSetDesktopSize.go
type SetDesktopSize (line 11) | type SetDesktopSize struct
method Clone (line 15) | func (that *SetDesktopSize) Clone() rfb.Message {
method Supported (line 23) | func (that *SetDesktopSize) Supported(rfb.ISession) bool {
method String (line 26) | func (that *SetDesktopSize) String() string {
method Type (line 30) | func (that *SetDesktopSize) Type() rfb.MessageType {
method Read (line 35) | func (that *SetDesktopSize) Read(session rfb.ISession) (rfb.Message, e...
method Write (line 78) | func (that *SetDesktopSize) Write(session rfb.ISession) error {
method readExtendedDesktopSize (line 99) | func (that *SetDesktopSize) readExtendedDesktopSize(session rfb.ISessi...
FILE: messages/clientSetEncodings.go
type SetEncodings (line 11) | type SetEncodings struct
method Clone (line 17) | func (that *SetEncodings) Clone() rfb.Message {
method Supported (line 24) | func (that *SetEncodings) Supported(_ rfb.ISession) bool {
method String (line 29) | func (that *SetEncodings) String() string {
method Type (line 39) | func (that *SetEncodings) Type() rfb.MessageType {
method Read (line 44) | func (that *SetEncodings) Read(session rfb.ISession) (rfb.Message, err...
method Write (line 71) | func (that *SetEncodings) Write(session rfb.ISession) error {
FILE: messages/clientSetPixelFormat.go
type SetPixelFormat (line 10) | type SetPixelFormat struct
method Clone (line 15) | func (that *SetPixelFormat) Clone() rfb.Message {
method Supported (line 22) | func (that *SetPixelFormat) Supported(session rfb.ISession) bool {
method String (line 27) | func (that *SetPixelFormat) String() string {
method Type (line 32) | func (that *SetPixelFormat) Type() rfb.MessageType {
method Write (line 37) | func (that *SetPixelFormat) Write(session rfb.ISession) error {
method Read (line 56) | func (that *SetPixelFormat) Read(session rfb.ISession) (rfb.Message, e...
FILE: messages/serverBell.go
type Bell (line 10) | type Bell struct
method Clone (line 12) | func (that *Bell) Clone() rfb.Message {
method Supported (line 15) | func (that *Bell) Supported(session rfb.ISession) bool {
method String (line 20) | func (that *Bell) String() string {
method Type (line 25) | func (that *Bell) Type() rfb.MessageType {
method Read (line 30) | func (that *Bell) Read(session rfb.ISession) (rfb.Message, error) {
method Write (line 35) | func (that *Bell) Write(session rfb.ISession) error {
FILE: messages/serverEndOfContinuousUpdates.go
type EndOfContinuousUpdates (line 10) | type EndOfContinuousUpdates struct
method Clone (line 12) | func (that *EndOfContinuousUpdates) Clone() rfb.Message {
method Supported (line 15) | func (that *EndOfContinuousUpdates) Supported(session rfb.ISession) bo...
method String (line 20) | func (that *EndOfContinuousUpdates) String() string {
method Type (line 25) | func (that *EndOfContinuousUpdates) Type() rfb.MessageType {
method Read (line 30) | func (that *EndOfContinuousUpdates) Read(session rfb.ISession) (rfb.Me...
method Write (line 35) | func (that *EndOfContinuousUpdates) Write(session rfb.ISession) error {
FILE: messages/serverFramebufferUpdate.go
type FramebufferUpdate (line 12) | type FramebufferUpdate struct
method String (line 18) | func (that *FramebufferUpdate) String() string {
method Supported (line 21) | func (that *FramebufferUpdate) Supported(rfb.ISession) bool {
method Type (line 25) | func (that *FramebufferUpdate) Type() rfb.MessageType {
method Read (line 30) | func (that *FramebufferUpdate) Read(session rfb.ISession) (rfb.Message...
method Write (line 73) | func (that *FramebufferUpdate) Write(session rfb.ISession) error {
method Clone (line 96) | func (that *FramebufferUpdate) Clone() rfb.Message {
FILE: messages/serverInit.go
type ServerInit (line 9) | type ServerInit struct
method String (line 17) | func (srvInit ServerInit) String() string {
FILE: messages/serverServerCutText.go
type ServerCutText (line 10) | type ServerCutText struct
method Clone (line 16) | func (that *ServerCutText) Clone() rfb.Message {
method Supported (line 22) | func (that *ServerCutText) Supported(session rfb.ISession) bool {
method String (line 27) | func (that *ServerCutText) String() string {
method Type (line 31) | func (that *ServerCutText) Type() rfb.MessageType {
method Read (line 36) | func (that *ServerCutText) Read(session rfb.ISession) (rfb.Message, er...
method Write (line 55) | func (that *ServerCutText) Write(session rfb.ISession) error {
FILE: messages/serverServerFence.go
type ServerFence (line 10) | type ServerFence struct
method Clone (line 16) | func (that *ServerFence) Clone() rfb.Message {
method Supported (line 25) | func (that *ServerFence) Supported(session rfb.ISession) bool {
method String (line 28) | func (that *ServerFence) String() string {
method Type (line 32) | func (that *ServerFence) Type() rfb.MessageType {
method Read (line 37) | func (that *ServerFence) Read(session rfb.ISession) (rfb.Message, erro...
method Write (line 58) | func (that *ServerFence) Write(session rfb.ISession) error {
FILE: messages/serverSetColorMapEntries.go
type SetColorMapEntries (line 11) | type SetColorMapEntries struct
method Clone (line 18) | func (that *SetColorMapEntries) Clone() rfb.Message {
method Supported (line 27) | func (that *SetColorMapEntries) Supported(session rfb.ISession) bool {
method String (line 32) | func (that *SetColorMapEntries) String() string {
method Type (line 37) | func (*SetColorMapEntries) Type() rfb.MessageType {
method Read (line 41) | func (that *SetColorMapEntries) Read(session rfb.ISession) (rfb.Messag...
method Write (line 73) | func (that *SetColorMapEntries) Write(session rfb.ISession) error {
FILE: rfb/color_map.go
type ColorMap (line 6) | type ColorMap
type Color (line 9) | type Color struct
method Write (line 17) | func (that *Color) Write(session ISession) error {
method Read (line 41) | func (that *Color) Read(session ISession) error {
FILE: rfb/encoding.go
type IEncoding (line 4) | type IEncoding interface
FILE: rfb/encodingtype.go
type EncodingType (line 3) | type EncodingType
constant EncRaw (line 9) | EncRaw EncodingType = 0
constant EncCopyRect (line 10) | EncCopyRect EncodingType = 1
constant EncRRE (line 11) | EncRRE EncodingType = 2
constant EncCoRRE (line 12) | EncCoRRE EncodingType = 4
constant EncHexTile (line 13) | EncHexTile EncodingType = 5
constant EncZlib (line 14) | EncZlib EncodingType = 6
constant EncTight (line 15) | EncTight EncodingType = 7
constant EncZlibHex (line 16) | EncZlibHex EncodingType = 8
constant EncUltra1 (line 17) | EncUltra1 EncodingType = 9
constant EncUltra2 (line 18) | EncUltra2 EncodingType = 10
constant EncTRLE (line 19) | EncTRLE EncodingType = 15
constant EncZRLE (line 20) | EncZRLE EncodingType = 16
constant EncH264 (line 21) | EncH264 EncodingType = 20
constant EncJPEG (line 22) | EncJPEG EncodingType = 21
constant EncJRLE (line 23) | EncJRLE EncodingType = 22
constant EncAtenAST2100 (line 24) | EncAtenAST2100 EncodingType = 87
constant EncAtenASTJPEG (line 25) | EncAtenASTJPEG EncodingType = 88
constant EncAtenHermon (line 26) | EncAtenHermon EncodingType = 89
constant EncAtenYarkon (line 27) | EncAtenYarkon EncodingType = 90
constant EncAtenPilot3 (line 28) | EncAtenPilot3 EncodingType = 91
constant EncJPEGQualityLevelPseudo10 (line 29) | EncJPEGQualityLevelPseudo10 EncodingType = -23
constant EncJPEGQualityLevelPseudo9 (line 30) | EncJPEGQualityLevelPseudo9 EncodingType = -24
constant EncJPEGQualityLevelPseudo8 (line 31) | EncJPEGQualityLevelPseudo8 EncodingType = -25
constant EncJPEGQualityLevelPseudo7 (line 32) | EncJPEGQualityLevelPseudo7 EncodingType = -26
constant EncJPEGQualityLevelPseudo6 (line 33) | EncJPEGQualityLevelPseudo6 EncodingType = -27
constant EncJPEGQualityLevelPseudo5 (line 34) | EncJPEGQualityLevelPseudo5 EncodingType = -28
constant EncJPEGQualityLevelPseudo4 (line 35) | EncJPEGQualityLevelPseudo4 EncodingType = -29
constant EncJPEGQualityLevelPseudo3 (line 36) | EncJPEGQualityLevelPseudo3 EncodingType = -30
constant EncJPEGQualityLevelPseudo2 (line 37) | EncJPEGQualityLevelPseudo2 EncodingType = -31
constant EncJPEGQualityLevelPseudo1 (line 38) | EncJPEGQualityLevelPseudo1 EncodingType = -32
constant EncDesktopSizePseudo (line 39) | EncDesktopSizePseudo EncodingType = -223
constant EncLastRectPseudo (line 40) | EncLastRectPseudo EncodingType = -224
constant EncPointerPosPseudo (line 41) | EncPointerPosPseudo EncodingType = -232
constant EncCursorPseudo (line 42) | EncCursorPseudo EncodingType = -239
constant EncXCursorPseudo (line 43) | EncXCursorPseudo EncodingType = -240
constant EncCompressionLevel10 (line 44) | EncCompressionLevel10 EncodingType = -247
constant EncCompressionLevel9 (line 45) | EncCompressionLevel9 EncodingType = -248
constant EncCompressionLevel8 (line 46) | EncCompressionLevel8 EncodingType = -249
constant EncCompressionLevel7 (line 47) | EncCompressionLevel7 EncodingType = -250
constant EncCompressionLevel6 (line 48) | EncCompressionLevel6 EncodingType = -251
constant EncCompressionLevel5 (line 49) | EncCompressionLevel5 EncodingType = -252
constant EncCompressionLevel4 (line 50) | EncCompressionLevel4 EncodingType = -253
constant EncCompressionLevel3 (line 51) | EncCompressionLevel3 EncodingType = -254
constant EncCompressionLevel2 (line 52) | EncCompressionLevel2 EncodingType = -255
constant EncCompressionLevel1 (line 53) | EncCompressionLevel1 EncodingType = -256
constant EncQEMUPointerMotionChangePseudo (line 54) | EncQEMUPointerMotionChangePseudo EncodingType = -257
constant EncQEMUExtendedKeyEventPseudo (line 55) | EncQEMUExtendedKeyEventPseudo EncodingType = -258
constant EncTightPng (line 56) | EncTightPng EncodingType = -260
constant EncLedStatePseudo (line 57) | EncLedStatePseudo EncodingType = -261
constant EncDesktopNamePseudo (line 58) | EncDesktopNamePseudo EncodingType = -307
constant EncExtendedDesktopSizePseudo (line 59) | EncExtendedDesktopSizePseudo EncodingType = -308
constant EncXvpPseudo (line 60) | EncXvpPseudo EncodingType = -309
constant EncClientRedirect (line 61) | EncClientRedirect EncodingType = -311
constant EncFencePseudo (line 62) | EncFencePseudo EncodingType = -312
constant EncContinuousUpdatesPseudo (line 63) | EncContinuousUpdatesPseudo EncodingType = -313
constant EncCursorWithAlphaPseudo (line 64) | EncCursorWithAlphaPseudo EncodingType = -314
constant EncExtendedClipboardPseudo (line 65) | EncExtendedClipboardPseudo EncodingType = -1063131698
constant EncTightPNGBase64 (line 66) | EncTightPNGBase64 EncodingType = 21 + 0x574d5600
constant EncTightDiffComp (line 67) | EncTightDiffComp EncodingType = 22 + 0x574d5600
constant EncVMWDefineCursor (line 68) | EncVMWDefineCursor EncodingType = 100 + 0x574d5600
constant EncVMWCursorState (line 69) | EncVMWCursorState EncodingType = 101 + 0x574d5600
constant EncVMWCursorPosition (line 70) | EncVMWCursorPosition EncodingType = 102 + 0x574d5600
constant EncVMWTypematicInfo (line 71) | EncVMWTypematicInfo EncodingType = 103 + 0x574d5600
constant EncVMWLEDState (line 72) | EncVMWLEDState EncodingType = 104 + 0x574d5600
constant EncVMWServerPush2 (line 73) | EncVMWServerPush2 EncodingType = 123 + 0x574d5600
constant EncVMWServerCaps (line 74) | EncVMWServerCaps EncodingType = 122 + 0x574d5600
constant EncVMWFrameStamp (line 75) | EncVMWFrameStamp EncodingType = 124 + 0x574d5600
constant EncOffscreenCopyRect (line 76) | EncOffscreenCopyRect EncodingType = 126 + 0x574d5600
FILE: rfb/encodingtype_string.go
function _ (line 7) | func _() {
constant _EncodingType_name (line 81) | _EncodingType_name = "EncExtendedClipboardPseudoEncCursorWithAlphaPseudo...
method String (line 154) | func (i EncodingType) String() string {
FILE: rfb/handler.go
type IHandler (line 3) | type IHandler interface
constant ProtoVersionLength (line 8) | ProtoVersionLength = 12
constant ProtoVersionUnknown (line 12) | ProtoVersionUnknown = ""
constant ProtoVersion33 (line 14) | ProtoVersion33 = "RFB 003.003\n"
constant ProtoVersion38 (line 16) | ProtoVersion38 = "RFB 003.008\n"
constant ProtoVersion37 (line 18) | ProtoVersion37 = "RFB 003.007\n"
FILE: rfb/keys.go
type Key (line 5) | type Key
type Keys (line 8) | type Keys
function IntToKeys (line 25) | func IntToKeys(v int) Keys {
constant Space (line 36) | Space Key = iota + 0x0020
constant Exclaim (line 37) | Exclaim
constant QuoteDbl (line 38) | QuoteDbl
constant NumberSign (line 39) | NumberSign
constant Dollar (line 40) | Dollar
constant Percent (line 41) | Percent
constant Ampersand (line 42) | Ampersand
constant Apostrophe (line 43) | Apostrophe
constant ParenLeft (line 44) | ParenLeft
constant ParenRight (line 45) | ParenRight
constant Asterisk (line 46) | Asterisk
constant Plus (line 47) | Plus
constant Comma (line 48) | Comma
constant Minus (line 49) | Minus
constant Period (line 50) | Period
constant Slash (line 51) | Slash
constant Digit0 (line 52) | Digit0
constant Digit1 (line 53) | Digit1
constant Digit2 (line 54) | Digit2
constant Digit3 (line 55) | Digit3
constant Digit4 (line 56) | Digit4
constant Digit5 (line 57) | Digit5
constant Digit6 (line 58) | Digit6
constant Digit7 (line 59) | Digit7
constant Digit8 (line 60) | Digit8
constant Digit9 (line 61) | Digit9
constant Colon (line 62) | Colon
constant Semicolon (line 63) | Semicolon
constant Less (line 64) | Less
constant Equal (line 65) | Equal
constant Greater (line 66) | Greater
constant Question (line 67) | Question
constant At (line 68) | At
constant A (line 69) | A
constant B (line 70) | B
constant C (line 71) | C
constant D (line 72) | D
constant E (line 73) | E
constant F (line 74) | F
constant G (line 75) | G
constant H (line 76) | H
constant I (line 77) | I
constant J (line 78) | J
constant K (line 79) | K
constant L (line 80) | L
constant M (line 81) | M
constant N (line 82) | N
constant O (line 83) | O
constant P (line 84) | P
constant Q (line 85) | Q
constant R (line 86) | R
constant S (line 87) | S
constant T (line 88) | T
constant U (line 89) | U
constant V (line 90) | V
constant W (line 91) | W
constant X (line 92) | X
constant Y (line 93) | Y
constant Z (line 94) | Z
constant BracketLeft (line 95) | BracketLeft
constant Backslash (line 96) | Backslash
constant BracketRight (line 97) | BracketRight
constant AsciiCircum (line 98) | AsciiCircum
constant Underscore (line 99) | Underscore
constant Grave (line 100) | Grave
constant SmallA (line 101) | SmallA
constant SmallB (line 102) | SmallB
constant SmallC (line 103) | SmallC
constant SmallD (line 104) | SmallD
constant SmallE (line 105) | SmallE
constant SmallF (line 106) | SmallF
constant SmallG (line 107) | SmallG
constant SmallH (line 108) | SmallH
constant SmallI (line 109) | SmallI
constant SmallJ (line 110) | SmallJ
constant SmallK (line 111) | SmallK
constant SmallL (line 112) | SmallL
constant SmallM (line 113) | SmallM
constant SmallN (line 114) | SmallN
constant SmallO (line 115) | SmallO
constant SmallP (line 116) | SmallP
constant SmallQ (line 117) | SmallQ
constant SmallR (line 118) | SmallR
constant SmallS (line 119) | SmallS
constant SmallT (line 120) | SmallT
constant SmallU (line 121) | SmallU
constant SmallV (line 122) | SmallV
constant SmallW (line 123) | SmallW
constant SmallX (line 124) | SmallX
constant SmallY (line 125) | SmallY
constant SmallZ (line 126) | SmallZ
constant BraceLeft (line 127) | BraceLeft
constant Bar (line 128) | Bar
constant BraceRight (line 129) | BraceRight
constant AsciiTilde (line 130) | AsciiTilde
constant BackSpace (line 134) | BackSpace Key = iota + 0xff08
constant Tab (line 135) | Tab
constant Linefeed (line 136) | Linefeed
constant Clear (line 137) | Clear
constant _ (line 138) | _
constant Return (line 139) | Return
constant Pause (line 143) | Pause Key = iota + 0xff13
constant ScrollLock (line 144) | ScrollLock
constant SysReq (line 145) | SysReq
constant Escape (line 146) | Escape Key = 0xff1b
constant Delete (line 147) | Delete Key = 0xffff
constant Home (line 151) | Home Key = iota + 0xff50
constant Left (line 152) | Left
constant Up (line 153) | Up
constant Right (line 154) | Right
constant Down (line 155) | Down
constant PageUp (line 156) | PageUp
constant PageDown (line 157) | PageDown
constant End (line 158) | End
constant Begin (line 159) | Begin
constant Select (line 163) | Select Key = 0xff60
constant Print (line 164) | Print
constant Execute (line 165) | Execute
constant Insert (line 166) | Insert
constant Undo (line 167) | Undo
constant Redo (line 168) | Redo
constant Menu (line 169) | Menu
constant Find (line 170) | Find
constant Cancel (line 171) | Cancel
constant Help (line 172) | Help
constant Break (line 173) | Break
constant ModeSwitch (line 174) | ModeSwitch Key = 0xff7e
constant NumLock (line 175) | NumLock Key = 0xff7f
constant KeypadSpace (line 179) | KeypadSpace Key = 0xff80
constant KeypadTab (line 180) | KeypadTab Key = 0xff89
constant KeypadEnter (line 181) | KeypadEnter Key = 0xff8d
constant KeypadF1 (line 185) | KeypadF1 Key = iota + 0xff91
constant KeypadF2 (line 186) | KeypadF2
constant KeypadF3 (line 187) | KeypadF3
constant KeypadF4 (line 188) | KeypadF4
constant KeypadHome (line 189) | KeypadHome
constant KeypadLeft (line 190) | KeypadLeft
constant KeypadUp (line 191) | KeypadUp
constant KeypadRight (line 192) | KeypadRight
constant KeypadDown (line 193) | KeypadDown
constant KeypadPrior (line 194) | KeypadPrior
constant KeypadPageUp (line 195) | KeypadPageUp
constant KeypadNext (line 196) | KeypadNext
constant KeypadPageDown (line 197) | KeypadPageDown
constant KeypadEnd (line 198) | KeypadEnd
constant KeypadBegin (line 199) | KeypadBegin
constant KeypadInsert (line 200) | KeypadInsert
constant KeypadDelete (line 201) | KeypadDelete
constant KeypadMultiply (line 202) | KeypadMultiply
constant KeypadAdd (line 203) | KeypadAdd
constant KeypadSeparator (line 204) | KeypadSeparator
constant KeypadSubtract (line 205) | KeypadSubtract
constant KeypadDecimal (line 206) | KeypadDecimal
constant KeypadDivide (line 207) | KeypadDivide
constant Keypad0 (line 208) | Keypad0
constant Keypad1 (line 209) | Keypad1
constant Keypad2 (line 210) | Keypad2
constant Keypad3 (line 211) | Keypad3
constant Keypad4 (line 212) | Keypad4
constant Keypad5 (line 213) | Keypad5
constant Keypad6 (line 214) | Keypad6
constant Keypad7 (line 215) | Keypad7
constant Keypad8 (line 216) | Keypad8
constant Keypad9 (line 217) | Keypad9
constant KeypadEqual (line 218) | KeypadEqual Key = 0xffbd
constant F1 (line 222) | F1 Key = iota + 0xffbe
constant F2 (line 223) | F2
constant F3 (line 224) | F3
constant F4 (line 225) | F4
constant F5 (line 226) | F5
constant F6 (line 227) | F6
constant F7 (line 228) | F7
constant F8 (line 229) | F8
constant F9 (line 230) | F9
constant F10 (line 231) | F10
constant F11 (line 232) | F11
constant F12 (line 233) | F12
constant ShiftLeft (line 237) | ShiftLeft Key = iota + 0xffe1
constant ShiftRight (line 238) | ShiftRight
constant ControlLeft (line 239) | ControlLeft
constant ControlRight (line 240) | ControlRight
constant CapsLock (line 241) | CapsLock
constant ShiftLock (line 242) | ShiftLock
constant MetaLeft (line 243) | MetaLeft
constant MetaRight (line 244) | MetaRight
constant AltLeft (line 245) | AltLeft
constant AltRight (line 246) | AltRight
constant SuperLeft (line 247) | SuperLeft
constant SuperRight (line 248) | SuperRight
constant HyperLeft (line 249) | HyperLeft
constant HyperRight (line 250) | HyperRight
FILE: rfb/keys_string.go
constant keyName (line 5) | keyName = "SpaceExclaimQuoteDblNumberSignDollarPercentAmpersandApostroph...
method String (line 190) | func (i Key) String() string {
FILE: rfb/message.go
type Message (line 3) | type Message interface
FILE: rfb/message_type_client_string.go
function _ (line 7) | func _() {
constant _ClientMessageType_name_0 (line 22) | _ClientMessageType_name_0 = "SetPixelFormat"
constant _ClientMessageType_name_1 (line 23) | _ClientMessageType_name_1 = "SetEncodingsFramebufferUpdateRequestKeyEven...
constant _ClientMessageType_name_2 (line 24) | _ClientMessageType_name_2 = "ClientFence"
constant _ClientMessageType_name_3 (line 25) | _ClientMessageType_name_3 = "QEMUExtendedKeyEvent"
method String (line 32) | func (i ClientMessageType) String() string {
FILE: rfb/message_type_server_string.go
function _ (line 7) | func _() {
constant _ServerMessageType_name (line 17) | _ServerMessageType_name = "FramebufferUpdateSetColorMapEntriesBellServer...
method String (line 21) | func (i ServerMessageType) String() string {
FILE: rfb/messagetype.go
type MessageType (line 3) | type MessageType
type ClientMessageType (line 6) | type ClientMessageType
constant SetPixelFormat (line 11) | SetPixelFormat ClientMessageType = 0
constant SetEncodings (line 12) | SetEncodings ClientMessageType = 2
constant FramebufferUpdateRequest (line 13) | FramebufferUpdateRequest ClientMessageType = 3
constant KeyEvent (line 14) | KeyEvent ClientMessageType = 4
constant PointerEvent (line 15) | PointerEvent ClientMessageType = 5
constant ClientCutText (line 16) | ClientCutText ClientMessageType = 6
constant EnableContinuousUpdates (line 17) | EnableContinuousUpdates ClientMessageType = 150
constant ClientFence (line 18) | ClientFence ClientMessageType = 248
constant SetDesktopSize (line 19) | SetDesktopSize ClientMessageType = 251
constant QEMUExtendedKeyEvent (line 20) | QEMUExtendedKeyEvent ClientMessageType = 255
type ServerMessageType (line 24) | type ServerMessageType
constant FramebufferUpdate (line 29) | FramebufferUpdate ServerMessageType = 0
constant SetColorMapEntries (line 30) | SetColorMapEntries ServerMessageType = 1
constant Bell (line 31) | Bell ServerMessageType = 2
constant ServerCutText (line 32) | ServerCutText ServerMessageType = 3
constant EndOfContinuousUpdates (line 33) | EndOfContinuousUpdates ServerMessageType = 150
constant ServerFence (line 34) | ServerFence ServerMessageType = 248
FILE: rfb/options.go
type Option (line 5) | type Option
type GetConn (line 6) | type GetConn
type Options (line 9) | type Options struct
function OptHandlers (line 38) | func OptHandlers(opt ...IHandler) Option {
function OptSecurityHandlers (line 45) | func OptSecurityHandlers(opt ...ISecurityHandler) Option {
function OptEncodings (line 52) | func OptEncodings(opt ...IEncoding) Option {
function OptMessages (line 59) | func OptMessages(opt ...Message) Option {
function OptPixelFormat (line 66) | func OptPixelFormat(opt PixelFormat) Option {
function OptGetConn (line 73) | func OptGetConn(opt GetConn) Option {
function OptDesktopName (line 79) | func OptDesktopName(opt []byte) Option {
function OptHeight (line 85) | func OptHeight(opt int) Option {
function OptWidth (line 90) | func OptWidth(opt int) Option {
function OptDisableServerMessageType (line 97) | func OptDisableServerMessageType(opt ...ServerMessageType) Option {
function OptDisableClientMessageType (line 104) | func OptDisableClientMessageType(opt ...ClientMessageType) Option {
FILE: rfb/pixel_format.go
constant PixelFormatLen (line 8) | PixelFormatLen = 16
type PixelFormat (line 22) | type PixelFormat struct
method String (line 36) | func (that PixelFormat) String() string {
method Order (line 42) | func (that PixelFormat) Order() binary.ByteOrder {
function NewPixelFormat (line 49) | func NewPixelFormat(bpp uint8) PixelFormat {
function NewPixelFormatAten (line 88) | func NewPixelFormatAten() PixelFormat {
FILE: rfb/rectangle.go
type Rectangle (line 9) | type Rectangle struct
method String (line 21) | func (that *Rectangle) String() string {
method Read (line 26) | func (that *Rectangle) Read(sess ISession) error {
method Write (line 57) | func (that *Rectangle) Write(sess ISession) error {
method Clone (line 80) | func (that *Rectangle) Clone() *Rectangle {
function NewRectangle (line 18) | func NewRectangle() *Rectangle {
FILE: rfb/security.go
type SecurityType (line 4) | type SecurityType
constant SecTypeUnknown (line 9) | SecTypeUnknown SecurityType = SecurityType(0)
constant SecTypeNone (line 10) | SecTypeNone SecurityType = SecurityType(1)
constant SecTypeVNC (line 11) | SecTypeVNC SecurityType = SecurityType(2)
constant SecTypeTight (line 12) | SecTypeTight SecurityType = SecurityType(16)
constant SecTypeVeNCrypt (line 13) | SecTypeVeNCrypt SecurityType = SecurityType(19)
type SecuritySubType (line 17) | type SecuritySubType
constant SecSubTypeUnknown (line 23) | SecSubTypeUnknown SecuritySubType = SecuritySubType(0)
constant SecSubTypeVeNCrypt01Unknown (line 29) | SecSubTypeVeNCrypt01Unknown SecuritySubType = SecuritySubType(0)
constant SecSubTypeVeNCrypt01Plain (line 30) | SecSubTypeVeNCrypt01Plain SecuritySubType = SecuritySubType(19)
constant SecSubTypeVeNCrypt01TLSNone (line 31) | SecSubTypeVeNCrypt01TLSNone SecuritySubType = SecuritySubType(20)
constant SecSubTypeVeNCrypt01TLSVNC (line 32) | SecSubTypeVeNCrypt01TLSVNC SecuritySubType = SecuritySubType(21)
constant SecSubTypeVeNCrypt01TLSPlain (line 33) | SecSubTypeVeNCrypt01TLSPlain SecuritySubType = SecuritySubType(22)
constant SecSubTypeVeNCrypt01X509None (line 34) | SecSubTypeVeNCrypt01X509None SecuritySubType = SecuritySubType(23)
constant SecSubTypeVeNCrypt01X509VNC (line 35) | SecSubTypeVeNCrypt01X509VNC SecuritySubType = SecuritySubType(24)
constant SecSubTypeVeNCrypt01X509Plain (line 36) | SecSubTypeVeNCrypt01X509Plain SecuritySubType = SecuritySubType(25)
constant SecSubTypeVeNCrypt02Unknown (line 41) | SecSubTypeVeNCrypt02Unknown SecuritySubType = SecuritySubType(0)
constant SecSubTypeVeNCrypt02Plain (line 42) | SecSubTypeVeNCrypt02Plain SecuritySubType = SecuritySubType(256)
constant SecSubTypeVeNCrypt02TLSNone (line 43) | SecSubTypeVeNCrypt02TLSNone SecuritySubType = SecuritySubType(257)
constant SecSubTypeVeNCrypt02TLSVNC (line 44) | SecSubTypeVeNCrypt02TLSVNC SecuritySubType = SecuritySubType(258)
constant SecSubTypeVeNCrypt02TLSPlain (line 45) | SecSubTypeVeNCrypt02TLSPlain SecuritySubType = SecuritySubType(259)
constant SecSubTypeVeNCrypt02X509None (line 46) | SecSubTypeVeNCrypt02X509None SecuritySubType = SecuritySubType(260)
constant SecSubTypeVeNCrypt02X509VNC (line 47) | SecSubTypeVeNCrypt02X509VNC SecuritySubType = SecuritySubType(261)
constant SecSubTypeVeNCrypt02X509Plain (line 48) | SecSubTypeVeNCrypt02X509Plain SecuritySubType = SecuritySubType(262)
type ISecurityHandler (line 52) | type ISecurityHandler interface
FILE: rfb/securitysubtype_string.go
function _ (line 7) | func _() {
constant _SecuritySubType_name_0 (line 31) | _SecuritySubType_name_0 = "SecSubTypeUnknown"
constant _SecuritySubType_name_1 (line 32) | _SecuritySubType_name_1 = "SecSubTypeVeNCrypt01PlainSecSubTypeVeNCrypt01...
constant _SecuritySubType_name_2 (line 33) | _SecuritySubType_name_2 = "SecSubTypeVeNCrypt02PlainSecSubTypeVeNCrypt02...
method String (line 41) | func (i SecuritySubType) String() string {
FILE: rfb/securitytype_string.go
function _ (line 7) | func _() {
constant _SecurityType_name_0 (line 19) | _SecurityType_name_0 = "SecTypeUnknownSecTypeNoneSecTypeVNC"
constant _SecurityType_name_1 (line 20) | _SecurityType_name_1 = "SecTypeTight"
constant _SecurityType_name_2 (line 21) | _SecurityType_name_2 = "SecTypeVeNCrypt"
method String (line 28) | func (i SecurityType) String() string {
FILE: rfb/session.go
type ISession (line 9) | type ISession interface
type SessionType (line 33) | type SessionType
constant ClientSessionType (line 38) | ClientSessionType SessionType = 0
constant ServerSessionType (line 39) | ServerSessionType SessionType = 1
constant RecorderSessionType (line 40) | RecorderSessionType SessionType = 2
constant PlayerSessionType (line 41) | PlayerSessionType SessionType = 3
constant CanvasSessionType (line 42) | CanvasSessionType SessionType = 4
FILE: rfb/session_string.go
function _ (line 7) | func _() {
constant _SessionType_name (line 18) | _SessionType_name = "ClientSessionTypeServerSessionTypeRecorderSessionTy...
method String (line 22) | func (i SessionType) String() string {
FILE: rfb/target_config.go
type TargetConfig (line 8) | type TargetConfig struct
method Addr (line 16) | func (that TargetConfig) Addr() string {
method GetNetwork (line 20) | func (that TargetConfig) GetNetwork() string {
method GetTimeout (line 27) | func (that TargetConfig) GetTimeout() time.Duration {
FILE: security/security_none.go
type ClientAuthNone (line 6) | type ClientAuthNone struct
method Type (line 14) | func (*ClientAuthNone) Type() rfb.SecurityType {
method SubType (line 17) | func (*ClientAuthNone) SubType() rfb.SecuritySubType {
method Auth (line 21) | func (*ClientAuthNone) Auth(rfb.ISession) error {
type ServerAuthNone (line 9) | type ServerAuthNone struct
method Type (line 25) | func (*ServerAuthNone) Type() rfb.SecurityType {
method SubType (line 29) | func (*ServerAuthNone) SubType() rfb.SecuritySubType {
method Auth (line 33) | func (*ServerAuthNone) Auth(rfb.ISession) error {
FILE: security/security_vencryptplain.go
type ClientAuthVeNCrypt02Plain (line 10) | type ClientAuthVeNCrypt02Plain struct
method Type (line 15) | func (*ClientAuthVeNCrypt02Plain) Type() rfb.SecurityType {
method SubType (line 19) | func (*ClientAuthVeNCrypt02Plain) SubType() rfb.SecuritySubType {
method Auth (line 23) | func (auth *ClientAuthVeNCrypt02Plain) Auth(session rfb.ISession) error {
FILE: security/security_vnc.go
constant ChallengeLen (line 13) | ChallengeLen = 16
type ServerAuthVNC (line 16) | type ServerAuthVNC struct
method Type (line 25) | func (*ServerAuthVNC) Type() rfb.SecurityType {
method SubType (line 28) | func (*ServerAuthVNC) SubType() rfb.SecuritySubType {
method writeChallenge (line 33) | func (that *ServerAuthVNC) writeChallenge(session rfb.ISession) error {
method ReadChallenge (line 40) | func (that *ServerAuthVNC) ReadChallenge(session rfb.ISession) error {
method Auth (line 49) | func (that *ServerAuthVNC) Auth(session rfb.ISession) error {
type ClientAuthVNC (line 73) | type ClientAuthVNC struct
method Type (line 78) | func (*ClientAuthVNC) Type() rfb.SecurityType {
method SubType (line 81) | func (*ClientAuthVNC) SubType() rfb.SecuritySubType {
method Auth (line 85) | func (that *ClientAuthVNC) Auth(session rfb.ISession) error {
function AuthVNCEncode (line 106) | func AuthVNCEncode(password []byte, challenge []byte) ([]byte, error) {
FILE: session/canvas.go
type CanvasSession (line 12) | type CanvasSession struct
method Init (line 31) | func (that *CanvasSession) Init(opts ...rfb.Option) error {
method configure (line 36) | func (that *CanvasSession) configure(opts ...rfb.Option) {
method Start (line 66) | func (that *CanvasSession) Start() {
method Conn (line 72) | func (that *CanvasSession) Conn() io.ReadWriteCloser {
method Options (line 77) | func (that *CanvasSession) Options() rfb.Options {
method ProtocolVersion (line 82) | func (that *CanvasSession) ProtocolVersion() string {
method SetProtocolVersion (line 87) | func (that *CanvasSession) SetProtocolVersion(pv string) {
method Encodings (line 92) | func (that *CanvasSession) Encodings() []rfb.IEncoding {
method SetEncodings (line 97) | func (that *CanvasSession) SetEncodings(encs []rfb.EncodingType) error {
method Flush (line 109) | func (that *CanvasSession) Flush() error {
method Wait (line 114) | func (that *CanvasSession) Wait() <-chan struct{} {
method SecurityHandler (line 119) | func (that *CanvasSession) SecurityHandler() rfb.ISecurityHandler {
method SetSecurityHandler (line 124) | func (that *CanvasSession) SetSecurityHandler(_ rfb.ISecurityHandler) {
method NewEncoding (line 128) | func (that *CanvasSession) NewEncoding(typ rfb.EncodingType) rfb.IEnco...
method Read (line 138) | func (that *CanvasSession) Read(buf []byte) (int, error) {
method Write (line 143) | func (that *CanvasSession) Write(buf []byte) (int, error) {
method Close (line 148) | func (that *CanvasSession) Close() error {
method Swap (line 157) | func (that *CanvasSession) Swap() *gmap.Map {
method Type (line 162) | func (that *CanvasSession) Type() rfb.SessionType {
method SetPixelFormat (line 167) | func (that *CanvasSession) SetPixelFormat(pf rfb.PixelFormat) {
method SetColorMap (line 172) | func (that *CanvasSession) SetColorMap(cm rfb.ColorMap) {
method SetWidth (line 177) | func (that *CanvasSession) SetWidth(width uint16) {
method SetHeight (line 182) | func (that *CanvasSession) SetHeight(height uint16) {
method SetDesktopName (line 187) | func (that *CanvasSession) SetDesktopName(name []byte) {
function NewCanvasSession (line 22) | func NewCanvasSession(opts ...rfb.Option) *CanvasSession {
FILE: session/client.go
type ClientSession (line 27) | type ClientSession struct
method Init (line 57) | func (that *ClientSession) Init(opts ...rfb.Option) error {
method configure (line 62) | func (that *ClientSession) configure(opts ...rfb.Option) {
method Start (line 92) | func (that *ClientSession) Start() {
method Conn (line 118) | func (that *ClientSession) Conn() io.ReadWriteCloser {
method Options (line 123) | func (that *ClientSession) Options() rfb.Options {
method ProtocolVersion (line 128) | func (that *ClientSession) ProtocolVersion() string {
method SetProtocolVersion (line 133) | func (that *ClientSession) SetProtocolVersion(pv string) {
method Encodings (line 138) | func (that *ClientSession) Encodings() []rfb.IEncoding {
method SetEncodings (line 143) | func (that *ClientSession) SetEncodings(encs []rfb.EncodingType) error {
method Flush (line 155) | func (that *ClientSession) Flush() error {
method Wait (line 160) | func (that *ClientSession) Wait() <-chan struct{} {
method SecurityHandler (line 165) | func (that *ClientSession) SecurityHandler() rfb.ISecurityHandler {
method SetSecurityHandler (line 170) | func (that *ClientSession) SetSecurityHandler(securityHandler rfb.ISec...
method NewEncoding (line 175) | func (that *ClientSession) NewEncoding(typ rfb.EncodingType) rfb.IEnco...
method Read (line 185) | func (that *ClientSession) Read(buf []byte) (int, error) {
method Write (line 190) | func (that *ClientSession) Write(buf []byte) (int, error) {
method Close (line 195) | func (that *ClientSession) Close() error {
method Swap (line 203) | func (that *ClientSession) Swap() *gmap.Map {
method Type (line 208) | func (that *ClientSession) Type() rfb.SessionType {
method SetPixelFormat (line 213) | func (that *ClientSession) SetPixelFormat(pf rfb.PixelFormat) {
method SetColorMap (line 218) | func (that *ClientSession) SetColorMap(cm rfb.ColorMap) {
method SetWidth (line 223) | func (that *ClientSession) SetWidth(width uint16) {
method SetHeight (line 228) | func (that *ClientSession) SetHeight(height uint16) {
method SetDesktopName (line 233) | func (that *ClientSession) SetDesktopName(name []byte) {
function NewClient (line 47) | func NewClient(opts ...rfb.Option) *ClientSession {
FILE: session/player.go
type PlayerSession (line 13) | type PlayerSession struct
method Init (line 34) | func (that *PlayerSession) Init(opts ...rfb.Option) error {
method configure (line 39) | func (that *PlayerSession) configure(opts ...rfb.Option) {
method Start (line 66) | func (that *PlayerSession) Start() {
method Conn (line 136) | func (that *PlayerSession) Conn() io.ReadWriteCloser {
method Options (line 141) | func (that *PlayerSession) Options() rfb.Options {
method ProtocolVersion (line 146) | func (that *PlayerSession) ProtocolVersion() string {
method SetProtocolVersion (line 151) | func (that *PlayerSession) SetProtocolVersion(pv string) {
method Encodings (line 156) | func (that *PlayerSession) Encodings() []rfb.IEncoding {
method SetEncodings (line 161) | func (that *PlayerSession) SetEncodings(_ []rfb.EncodingType) error {
method Flush (line 166) | func (that *PlayerSession) Flush() error {
method Wait (line 171) | func (that *PlayerSession) Wait() <-chan struct{} {
method SecurityHandler (line 176) | func (that *PlayerSession) SecurityHandler() rfb.ISecurityHandler {
method SetSecurityHandler (line 181) | func (that *PlayerSession) SetSecurityHandler(_ rfb.ISecurityHandler) {
method NewEncoding (line 185) | func (that *PlayerSession) NewEncoding(typ rfb.EncodingType) rfb.IEnco...
method Read (line 195) | func (that *PlayerSession) Read(buf []byte) (int, error) {
method Write (line 200) | func (that *PlayerSession) Write(buf []byte) (int, error) {
method Close (line 205) | func (that *PlayerSession) Close() error {
method Swap (line 212) | func (that *PlayerSession) Swap() *gmap.Map {
method Type (line 215) | func (that *PlayerSession) Type() rfb.SessionType {
method SetPixelFormat (line 220) | func (that *PlayerSession) SetPixelFormat(pf rfb.PixelFormat) {
method SetColorMap (line 225) | func (that *PlayerSession) SetColorMap(cm rfb.ColorMap) {
method SetWidth (line 230) | func (that *PlayerSession) SetWidth(width uint16) {
method SetHeight (line 235) | func (that *PlayerSession) SetHeight(height uint16) {
method SetDesktopName (line 240) | func (that *PlayerSession) SetDesktopName(name []byte) {
function NewPlayerSession (line 25) | func NewPlayerSession(opts ...rfb.Option) *PlayerSession {
FILE: session/recorder.go
constant RBSVersion (line 13) | RBSVersion = "RBS 001.001\n"
type RecorderSession (line 15) | type RecorderSession struct
method Init (line 37) | func (that *RecorderSession) Init(opts ...rfb.Option) error {
method configure (line 42) | func (that *RecorderSession) configure(opts ...rfb.Option) {
method Start (line 71) | func (that *RecorderSession) Start() {
method Conn (line 130) | func (that *RecorderSession) Conn() io.ReadWriteCloser {
method Options (line 135) | func (that *RecorderSession) Options() rfb.Options {
method ProtocolVersion (line 140) | func (that *RecorderSession) ProtocolVersion() string {
method SetProtocolVersion (line 145) | func (that *RecorderSession) SetProtocolVersion(pv string) {
method Encodings (line 150) | func (that *RecorderSession) Encodings() []rfb.IEncoding {
method SetEncodings (line 155) | func (that *RecorderSession) SetEncodings(_ []rfb.EncodingType) error {
method Flush (line 159) | func (that *RecorderSession) Flush() error {
method Wait (line 164) | func (that *RecorderSession) Wait() <-chan struct{} {
method SecurityHandler (line 169) | func (that *RecorderSession) SecurityHandler() rfb.ISecurityHandler {
method SetSecurityHandler (line 174) | func (that *RecorderSession) SetSecurityHandler(_ rfb.ISecurityHandler) {
method NewEncoding (line 178) | func (that *RecorderSession) NewEncoding(typ rfb.EncodingType) rfb.IEn...
method Read (line 188) | func (that *RecorderSession) Read(_ []byte) (int, error) {
method Write (line 193) | func (that *RecorderSession) Write(buf []byte) (int, error) {
method Close (line 198) | func (that *RecorderSession) Close() error {
method Swap (line 206) | func (that *RecorderSession) Swap() *gmap.Map {
method Type (line 211) | func (that *RecorderSession) Type() rfb.SessionType {
method SetPixelFormat (line 216) | func (that *RecorderSession) SetPixelFormat(pf rfb.PixelFormat) {
method SetColorMap (line 221) | func (that *RecorderSession) SetColorMap(cm rfb.ColorMap) {
method SetWidth (line 226) | func (that *RecorderSession) SetWidth(width uint16) {
method SetHeight (line 231) | func (that *RecorderSession) SetHeight(height uint16) {
method SetDesktopName (line 236) | func (that *RecorderSession) SetDesktopName(name []byte) {
function NewRecorder (line 28) | func NewRecorder(opts ...rfb.Option) *RecorderSession {
FILE: session/server.go
type ServerSession (line 23) | type ServerSession struct
method Init (line 48) | func (that *ServerSession) Init(opts ...rfb.Option) error {
method configure (line 53) | func (that *ServerSession) configure(opts ...rfb.Option) {
method Start (line 80) | func (that *ServerSession) Start() {
method Conn (line 105) | func (that *ServerSession) Conn() io.ReadWriteCloser {
method Options (line 108) | func (that *ServerSession) Options() rfb.Options {
method ProtocolVersion (line 113) | func (that *ServerSession) ProtocolVersion() string {
method SetProtocolVersion (line 118) | func (that *ServerSession) SetProtocolVersion(pv string) {
method Encodings (line 123) | func (that *ServerSession) Encodings() []rfb.IEncoding {
method SetEncodings (line 128) | func (that *ServerSession) SetEncodings(encs []rfb.EncodingType) error {
method Flush (line 141) | func (that *ServerSession) Flush() error {
method Wait (line 146) | func (that *ServerSession) Wait() <-chan struct{} {
method SecurityHandler (line 151) | func (that *ServerSession) SecurityHandler() rfb.ISecurityHandler {
method SetSecurityHandler (line 156) | func (that *ServerSession) SetSecurityHandler(securityHandler rfb.ISec...
method NewEncoding (line 161) | func (that *ServerSession) NewEncoding(typ rfb.EncodingType) rfb.IEnco...
method Read (line 171) | func (that *ServerSession) Read(buf []byte) (int, error) {
method Write (line 176) | func (that *ServerSession) Write(buf []byte) (int, error) {
method Close (line 181) | func (that *ServerSession) Close() error {
method Swap (line 189) | func (that *ServerSession) Swap() *gmap.Map {
method Type (line 194) | func (that *ServerSession) Type() rfb.SessionType {
method SetPixelFormat (line 199) | func (that *ServerSession) SetPixelFormat(pf rfb.PixelFormat) {
method SetColorMap (line 204) | func (that *ServerSession) SetColorMap(cm rfb.ColorMap) {
method SetWidth (line 209) | func (that *ServerSession) SetWidth(width uint16) {
method SetHeight (line 214) | func (that *ServerSession) SetHeight(height uint16) {
method SetDesktopName (line 219) | func (that *ServerSession) SetDesktopName(name []byte) {
function NewServerSession (line 38) | func NewServerSession(opts ...rfb.Option) *ServerSession {
FILE: vnc/player.go
type Player (line 20) | type Player struct
method Start (line 47) | func (that *Player) Start() error {
method Handle (line 67) | func (that *Player) Handle(sess rfb.ISession) error {
method handleIO (line 79) | func (that *Player) handleIO() {
method readRbs (line 105) | func (that *Player) readRbs() {
method Close (line 129) | func (that *Player) Close() {
function NewPlayer (line 28) | func NewPlayer(filePath string, svrSession *session.ServerSession) *Play...
FILE: vnc/proxy.go
type Proxy (line 12) | type Proxy struct
method Start (line 32) | func (that *Proxy) Start() error {
method handleIO (line 53) | func (that *Proxy) handleIO() {
method Handle (line 119) | func (that *Proxy) Handle(sess rfb.ISession) (err error) {
method Close (line 139) | func (that *Proxy) Close() {
function NewVncProxy (line 20) | func NewVncProxy(remoteSession *session.ClientSession, serverSession *se...
FILE: vnc/recorder.go
type Recorder (line 14) | type Recorder struct
method Start (line 31) | func (that *Recorder) Start() error {
method Close (line 103) | func (that *Recorder) Close() {
function NewRecorder (line 21) | func NewRecorder(recorderSess *session.RecorderSession, cliSession *sess...
FILE: vnc/screenshot.go
type Screenshot (line 16) | type Screenshot struct
method GetImage (line 46) | func (that *Screenshot) GetImage() (io.ReadWriteCloser, error) {
function NewScreenshot (line 22) | func NewScreenshot(targetCfg rfb.TargetConfig) *Screenshot {
FILE: vnc/video.go
type Video (line 16) | type Video struct
method Start (line 60) | func (that *Video) Start() error {
method Close (line 128) | func (that *Video) Close() {
function NewVideo (line 23) | func NewVideo(cliCfg *rfb.Options, targetCfg rfb.TargetConfig) *Video {
Condensed preview — 149 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (370K chars).
[
{
"path": ".gitignore",
"chars": 269,
"preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 6463,
"preview": "# VncProxy [](https://git"
},
{
"path": "bin/.keep",
"chars": 0,
"preview": ""
},
{
"path": "build",
"chars": 2031,
"preview": "#!/usr/bin/env bash\nset -e\n\nexport GOPROXY=https://proxy.golang.com.cn,direct\n\n\nhelp() {\n echo \"使用方式:\"\n echo \" b"
},
{
"path": "canvas/canvas.go",
"chars": 5253,
"preview": "package canvas\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n\t\"image\"\n\t\"image/color\"\n\t\""
},
{
"path": "canvas/rgb_image.go",
"chars": 1710,
"preview": "package canvas\n\nimport (\n\t\"image\"\n\t\"image/color\"\n)\n\ntype RGBColor struct {\n\tR, G, B uint8\n}\n\nfunc (that RGBColor) RGBA()"
},
{
"path": "cmd/player/main.go",
"chars": 3655,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/os/gfile\"\n\t\"github.com/gogf/gf/v2"
},
{
"path": "cmd/player/tcpServer.go",
"chars": 2673,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/os/glog\"\n\t\"github.com/osgochina/d"
},
{
"path": "cmd/player/wsServer.go",
"chars": 2276,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/frame/g\"\n\t\"github.com/gogf/gf/v2/net/ghttp\"\n\t\"github.com/gogf/gf/v"
},
{
"path": "cmd/proxy/main.go",
"chars": 4305,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/text/gstr\"\n\t\"github.com/osgochina"
},
{
"path": "cmd/proxy/tcpServer.go",
"chars": 3532,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf"
},
{
"path": "cmd/proxy/wsServer.go",
"chars": 3103,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/gogf/gf/v2/frame/g\"\n\t\"gith"
},
{
"path": "cmd/recorder/main.go",
"chars": 2417,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/text/gstr\"\n\t\"github.com/osgochina"
},
{
"path": "cmd/recorder/recorder.go",
"chars": 3068,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/os/gfile\"\n\t\"github.com/gogf/gf/v2"
},
{
"path": "cmd/screenshot/main.go",
"chars": 3039,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/os/gcfg\"\n\t\"github.com/gogf/gf/v2/os/gfile\"\n\t\"g"
},
{
"path": "cmd/video/main.go",
"chars": 390,
"preview": "package main\n\nfunc main() {\n\t// 暂未实现\n\t//v := vnc.NewVideo(nil,\n\t//\trfb.TargetConfig{\n\t//\t\tNetwork: \"tcp\",\n\t//\t\tHost: "
},
{
"path": "docs/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/.nojekyll",
"chars": 0,
"preview": ""
},
{
"path": "docs/README.md",
"chars": 6437,
"preview": "# VncProxy [](https://git"
},
{
"path": "docs/changelog.md",
"chars": 0,
"preview": ""
},
{
"path": "docs/index.html",
"chars": 1650,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>VncProxy - RFB协议 - 使用文档</title>\n <link rel=\"s"
},
{
"path": "docs/overview.md",
"chars": 0,
"preview": ""
},
{
"path": "docs/player/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/player/README.md",
"chars": 1119,
"preview": "## Player\n\n代码路径在`./cmd/player`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译\n\n### 获取帮助信息\n```shell\n# 查看帮助信息\n$ ./player --help\n\n# 查"
},
{
"path": "docs/proxy/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/proxy/README.md",
"chars": 1416,
"preview": "## Proxy\n\n代码路径在`./cmd/proxy`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译\n\n### 获取帮助信息\n```shell\n# 查看帮助信息\n$ ./proxy --help\n\n# 查看版本"
},
{
"path": "docs/questions.md",
"chars": 0,
"preview": ""
},
{
"path": "docs/recorder/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/recorder/README.md",
"chars": 461,
"preview": "## Recorder\n\n代码路径在`./cmd/recorder`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译\n\n### 获取帮助信息\n```shell\n# 查看帮助信息\n$ ./recorder --hel"
},
{
"path": "docs/rfc6143/GLOSSORY.md",
"chars": 666,
"preview": "# 帧缓冲\n\n帧缓冲器也称为帧缓冲或者显存,是用来存储渲染数据的地方。帧缓冲的每一个存储单位对应一个像素,它是屏幕显示画面的直接映象,又称为位映射图(Bit Map)。\n\n 远程帧缓冲协议,是一种允许用户通过"
},
{
"path": "docs/rfc6143/handshake/README.md",
"chars": 460,
"preview": "# 握手\n\nRFB 协议有四个阶段:\n\n```mermaid\ngraph LR\n protocol[\"协议握手\"]\n auth[\"认证\"]\n initial[\"初始化\"]\n transfer(\"数据交互\")\n "
},
{
"path": "docs/rfc6143/handshake/initial.md",
"chars": 1538,
"preview": "# 初始化\n\n收 SecurityResult 后,客户端应当发送 [ClientInit](#客户端初始化) 数据包,收到后,服务端发送 [ServerInit](#服务端初始化) 包。\n\n```mermaid\nsequenceDiagr"
},
{
"path": "docs/rfc6143/handshake/protocol-version.md",
"chars": 955,
"preview": "\n# 协议握手\n\n协议握手对客户端和服务端通信过程使用的协议版本达成共识,有可能的情况下,对不同协议版本实现向前兼容。\n\n## 握手过程\n\n建立 TCP 连接后,服务器首先向客户端发送版本 X,收到 X 后,客户端向服务器发送不高于 X 的"
},
{
"path": "docs/rfc6143/handshake/security-type.md",
"chars": 3521,
"preview": "# 安全握手\n\n协商好协议版本后,客户端和服务端进行安全握手,就认证方式达成共识,并完成认证过程。\n\n## 握手过程\n\n认证的流程图如下:\n\n```mermaid\nsequenceDiagram\n participant Client"
},
{
"path": "docs/rfc6143/transfer/README.md",
"chars": 108,
"preview": "# 数据交互\n\n数据交互阶段,客户端向服务端发送鼠标、键盘事件,或请求更新图像。\n\n```mermaid\ngraph LR\n A(\"鼠标事件\")\n B(\"键盘事件\")\n C(\"更新图像\")\n```\n"
},
{
"path": "docs/rfc6143/transfer/display.md",
"chars": 2520,
"preview": "# 显示协议\n\n客户端可能处于弱网络环境,或只有较低性能的渲染设备。如果服务端不加限制的向客户端发送像素画面,很容易造成客户端卡死或网络堵塞。\n\n在 RFB 协议中,当且仅当客户端主动请求显示数据时,服务端才会将 [FramebufferU"
},
{
"path": "docs/rfc6143/transfer/encoding/README.md",
"chars": 1028,
"preview": "# 编码\n\n”编码“ 是像素数据的表达方式。在发往客户端前,服务器经常将发送的图形数据进行编码、压缩,提高图像传输效率。\n\n```\n+--------+-----------------------------+\n| Number | Na"
},
{
"path": "docs/rfc6143/transfer/encoding/copy-rect.md",
"chars": 526,
"preview": "# 镜像编码 Copy Rect\n\nCopyRect 指示客户端,从已有帧缓冲区域复制到新区域。这种编码常用于窗口拖动、页面滚动等场景。\n\n报文只说明起始坐标,区域的长度和宽度由 [FramebufferUpdateRectangle](/"
},
{
"path": "docs/rfc6143/transfer/encoding/raw.md",
"chars": 472,
"preview": "# Raw 原始编码\n\nRaw 是最简单也是最原始的编码,直接向客户端传递位图信息,不进行编码优化。\n在 Raw 格式中,像素按从左到右,从上到下的顺序排列成一维数组。\n\n> 协议要求客户端必须支持 Raw 类型编码。\n> \n> 协议要求服"
},
{
"path": "docs/rfc6143/transfer/encoding/rise-and-run-length.md",
"chars": 1270,
"preview": "# 上升和游程编码 Rise-and-run-length\n\nRRE 是游程编码的二维变种。基本思想是将大的矩形拆分为子矩形,每个子矩形有单值的像素组成,所有小矩形的并集构成原始矩形区域。\n\n编码由背景像素值 Vb 、计数 N ,以及 N "
},
{
"path": "docs/rfc6143/transfer/encoding/set-encoding.md",
"chars": 818,
"preview": "# 设置编码\n\n客户端用 SetEncoding 消息告知服务端,接受哪些像素[编码](/rfc6143/transfer/encoding/README.md)。\n\n除了用于解析像素的编码外,客户端可以发送伪编码,向服务端请求拓展功能。如"
},
{
"path": "docs/rfc6143/transfer/encoding/tight-png.md",
"chars": 5607,
"preview": "# 严格Png编码 Tight Encoding\n\n严格编码为像素数据提供了有效的压缩。为了减少执行的复杂性,任何Tight编码的矩形的宽度不能超过2048像素。\n如果需要一个更宽的矩形,必须将其分成几个矩形,每个矩形都要单独编码。\n\n每个"
},
{
"path": "docs/rfc6143/transfer/encoding/tiled-run-length.md",
"chars": 5142,
"preview": "# 图块游程编码 TRLE\n\nTRLE(Tiled Run-Length Encoding),使用图块压缩技术、调色板技术以及游程编码,对传输的桌面图像进行压缩。\n\n在图块压缩技术中,传输图像被分为 N 个 16x16 的小图块,图块按从"
},
{
"path": "docs/rfc6143/transfer/encoding/zlib-run-length.md",
"chars": 506,
"preview": "# zlib 压缩游程编码\n\nZRLE 融合游程编码、Zlib 压缩算法、图块压缩算法对传输图像进行压缩。\n\nZRLE 使用 Zlib 进行编码和解码,要求流数据严格有序。\n\n```\n+--------------+------------"
},
{
"path": "docs/rfc6143/transfer/input/README.md",
"chars": 45,
"preview": "# 输入协议\n\nRFB 协议支持鼠标和键盘两种输入设备,同时支持剪贴板进行远程复制粘贴。\n"
},
{
"path": "docs/rfc6143/transfer/input/clipboard.md",
"chars": 3863,
"preview": "# 剪贴板\n\n复制粘贴是双向事件,可以由客户端向服务端复制,也可以由服务端向客户端复制。\n\n## 协议报文\n\n复制剪贴板的报文如下:\n\n```\n +--------------+--------------+--------------+"
},
{
"path": "docs/rfc6143/transfer/input/keyboard.md",
"chars": 2772,
"preview": "# 键盘事件\n\n在正常理解中,键盘事件的处理应该是简单明了的。参考以下协议报文\n\n```\n +--------------+--------------+--------------+\n | No. of bytes | Type [V"
},
{
"path": "docs/rfc6143/transfer/input/mouse.md",
"chars": 1726,
"preview": "# 鼠标事件\n\n鼠标指针即鼠标操作事件,分移动事件和点击事件两种。在 RFB 协议中,使用 PointerEvent 表示客户端触发一次鼠标事件。\n\n## 协议报文\n\n鼠标事件的报文如下\n\n```\n +--------------+---"
},
{
"path": "docs/rfc6143/transfer/pixel-format.md",
"chars": 2214,
"preview": "# 像素格式\n\n[帧缓冲](/rfc6143/GLOSSORY.md#帧缓冲)由像素构成。\n\n\n\n## PixelFor"
},
{
"path": "docs/rfc6143/transfer/set-color-map.md",
"chars": 1062,
"preview": "# 设置颜色表\n\n当 PIXEL_FORMAT 的 true-color-flag 字段被设置为 0 时,服务端使用颜色表表示像素的颜色。\n`SetColorMapEntries` 用于设置颜色表的内容。\n\n```\n+-----------"
},
{
"path": "docs/screenshot/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/screenshot/README.md",
"chars": 457,
"preview": "## Screenshot\n\n代码路径在`./cmd/screenshot`,如果单独编译该组件,也可以到该目录下自行执行`go build`命令编译\n\n### 获取帮助信息\n```shell\n# 查看帮助信息\n$ ./screenshot"
},
{
"path": "docs/summary.md",
"chars": 1326,
"preview": "* 快速入门\n * [项目介绍](README.md)\n * [快速开始](overview.md)\n * [常见问题](questions.md)\n * [版本更新记录](changelog.md)\n\n* 代理(Proxy)\n "
},
{
"path": "docs/video/.keep",
"chars": 0,
"preview": ""
},
{
"path": "docs/video/README.md",
"chars": 0,
"preview": ""
},
{
"path": "encodings/default_encoding.go",
"chars": 566,
"preview": "package encodings\n\nimport \"github.com/vprix/vncproxy/rfb\"\n\nvar (\n\tDefaultEncodings = []rfb.IEncoding{\n\t\t&ZRLEEncoding{},"
},
{
"path": "encodings/encoding.go",
"chars": 1221,
"preview": "package encodings\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/internal/dbuffer\"\n\t\"github.com/vpri"
},
{
"path": "encodings/encoding_copyrect.go",
"chars": 1466,
"preview": "package encodings\n\nimport (\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// CopyRectEncoding 该编码方式对于客户端在某些已经有了"
},
{
"path": "encodings/encoding_corre.go",
"chars": 1795,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// CoRREEncoding C"
},
{
"path": "encodings/encoding_h264.go",
"chars": 1418,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype H264Encoding "
},
{
"path": "encodings/encoding_hextile.go",
"chars": 8262,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/errors/gerror\"\n\t\"github.com/vprix"
},
{
"path": "encodings/encoding_jpeg.go",
"chars": 18,
"preview": "package encodings\n"
},
{
"path": "encodings/encoding_jrle.go",
"chars": 18,
"preview": "package encodings\n"
},
{
"path": "encodings/encoding_raw.go",
"chars": 1649,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/canvas\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n/"
},
{
"path": "encodings/encoding_rre.go",
"chars": 1888,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// RREEncoding RRE"
},
{
"path": "encodings/encoding_tight.go",
"chars": 5750,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\nvar TightMi"
},
{
"path": "encodings/encoding_tightpng.go",
"chars": 2401,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype TightP"
},
{
"path": "encodings/encoding_trle.go",
"chars": 18,
"preview": "package encodings\n"
},
{
"path": "encodings/encoding_zlib.go",
"chars": 1188,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype ZLibEncoding "
},
{
"path": "encodings/encoding_zrle.go",
"chars": 9309,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/ca"
},
{
"path": "encodings/pseudo_cursor.go",
"chars": 3128,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/canvas\"\n\t\"github.com/vprix/vncproxy/"
},
{
"path": "encodings/pseudo_cursor_with_alpha.go",
"chars": 1303,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype CursorWithAlphaPseudoEncoding struct {\n\tbu"
},
{
"path": "encodings/pseudo_desktop_name.go",
"chars": 1249,
"preview": "package encodings\n\nimport (\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// DesktopNamePseudoEncoding 服务端设置桌面名"
},
{
"path": "encodings/pseudo_desktop_size.go",
"chars": 840,
"preview": "package encodings\n\nimport (\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// DesktopSizePseudoEncoding 如果客户端请求桌面大小伪编码,那么就是说它能处理帧缓存"
},
{
"path": "encodings/pseudo_extended_desktop_size.go",
"chars": 1390,
"preview": "package encodings\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ExtendedDesktopSizePseudo"
},
{
"path": "encodings/pseudo_fence.go",
"chars": 519,
"preview": "package encodings\n\nimport (\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype FencePseudo struct {\n}\n\nfunc (that *FencePseudo) Su"
},
{
"path": "encodings/pseudo_last_rect.go",
"chars": 543,
"preview": "package encodings\n\nimport (\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype LastRectPseudo struct {\n}\n\nfunc (that *LastRectPseu"
},
{
"path": "encodings/pseudo_led_state.go",
"chars": 849,
"preview": "package encodings\n\nimport (\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// LedStatePseudo 切换客户端本地小键盘锁定的led灯\n/"
},
{
"path": "encodings/pseudo_pointer_pos.go",
"chars": 969,
"preview": "package encodings\n\nimport (\n\t\"errors\"\n\t\"github.com/vprix/vncproxy/canvas\"\n\t\"github.com/vprix/vncproxy/rfb\"\n\t\"image\"\n\t\"im"
},
{
"path": "encodings/pseudo_x_cursor.go",
"chars": 3060,
"preview": "package encodings\n\nimport (\n\t\"encoding/binary\"\n\t\"github.com/vprix/vncproxy/rfb\"\n\t\"math\"\n)\n\ntype XCursorPseudoEncoding st"
},
{
"path": "go.mod",
"chars": 1861,
"preview": "module github.com/vprix/vncproxy\n\nrequire (\n\tgithub.com/gogf/gf/v2 v2.9.5\n\tgithub.com/osgochina/dmicro v1.3.1\n\tgolang.or"
},
{
"path": "handler/ClientClientInitHandler.go",
"chars": 768,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "handler/ClientMessageHandler.go",
"chars": 2264,
"preview": "package handler\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncproxy/rf"
},
{
"path": "handler/ClientSecurityHandler.go",
"chars": 1999,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/"
},
{
"path": "handler/ClientServerInitHandler.go",
"chars": 2016,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "handler/ClientVersionHandler.go",
"chars": 1504,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/"
},
{
"path": "handler/ServerClientInitHandler.go",
"chars": 583,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "handler/ServerMessageHandler.go",
"chars": 2180,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/"
},
{
"path": "handler/ServerSecurityHandler.go",
"chars": 2342,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/"
},
{
"path": "handler/ServerServerInitHandler.go",
"chars": 1373,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "handler/ServerVersionHandler.go",
"chars": 1192,
"preview": "package handler\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/"
},
{
"path": "internal/dbuffer/buffer.go",
"chars": 2558,
"preview": "package dbuffer\n\nimport (\n\t\"io\"\n)\n\n// ByteBuffer provides byte buffer, which can be used for minimizing\n// memory alloca"
},
{
"path": "internal/dbuffer/pool.go",
"chars": 3096,
"preview": "package dbuffer\n\nimport (\n\t\"sort\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nconst (\n\tminBitSize = 6 // 2**6=64 is a CPU cache line size\n"
},
{
"path": "internal/syncPool/sync_pool.go",
"chars": 637,
"preview": "package syncPool\n\nimport \"sync\"\n\ntype SyncPool struct {\n\tpool sync.Pool\n\tnewFunc func() interface{}\n\tinitFunc func("
},
{
"path": "messages/clientClientCutText.go",
"chars": 1812,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ClientCutText 客户端发送剪切板内容到服务端"
},
{
"path": "messages/clientClientFence.go",
"chars": 1774,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ClientFence 支持 Fence扩展的客户端发送"
},
{
"path": "messages/clientEnableContinuousUpdates.go",
"chars": 2134,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// EnableContinuousUpdates 客户端发"
},
{
"path": "messages/clientFramebufferUpdateRequest.go",
"chars": 1597,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// FramebufferUpdateRequest 请求帧"
},
{
"path": "messages/clientKeyEvent.go",
"chars": 1243,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// KeyEvent 键盘按键事件\ntype KeyEven"
},
{
"path": "messages/clientPointerEvent.go",
"chars": 1209,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// PointerEvent 鼠标事件\ntype Point"
},
{
"path": "messages/clientQEMUExtKeyEvent.go",
"chars": 1356,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype QEMUExtKeyEvent struct {\n\t"
},
{
"path": "messages/clientSetDesktopSize.go",
"chars": 2639,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/internal/dbuffer\"\n\t\"github.com/vprix/vn"
},
{
"path": "messages/clientSetEncodings.go",
"chars": 2253,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/text/gstr\"\n\t\"github.com/vprix/vncproxy/rfb\""
},
{
"path": "messages/clientSetPixelFormat.go",
"chars": 1284,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// SetPixelFormat 设置像素格式\ntype S"
},
{
"path": "messages/default_message.go",
"chars": 551,
"preview": "package messages\n\nimport \"github.com/vprix/vncproxy/rfb\"\n\nvar (\n\t// DefaultClientMessage 默认client支持的消息\n\tDefaultClientMes"
},
{
"path": "messages/serverBell.go",
"chars": 743,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// Bell 响铃\ntype Bell struct{}\n\n"
},
{
"path": "messages/serverEndOfContinuousUpdates.go",
"chars": 968,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// EndOfContinuousUpdates Bell "
},
{
"path": "messages/serverFramebufferUpdate.go",
"chars": 2569,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncproxy/r"
},
{
"path": "messages/serverInit.go",
"chars": 480,
"preview": "package messages\n\nimport (\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ServerInit 握手的时候服务端初始化消息\ntype ServerInit struc"
},
{
"path": "messages/serverServerCutText.go",
"chars": 1719,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ServerCutText 服务端剪切板发送到客户端\nt"
},
{
"path": "messages/serverServerFence.go",
"chars": 1772,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// ServerFence 支持 Fence扩展的服务器发送"
},
{
"path": "messages/serverSetColorMapEntries.go",
"chars": 2598,
"preview": "package messages\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\n// SetColorMapEntries 设置颜色表的内容\n"
},
{
"path": "rfb/color_map.go",
"chars": 1599,
"preview": "package rfb\n\nimport \"encoding/binary\"\n\n// ColorMap 颜色地图\ntype ColorMap [256]Color\n\n// Color 表示颜色地图中的一个颜色。\ntype Color stru"
},
{
"path": "rfb/desktop.go",
"chars": 1251,
"preview": "package rfb\n\n//\n//type Desktop struct {\n//\tdesktopName []byte // 桌面名称\n//\tfbHeight uint16 // 缓冲帧高度\n//\tfbWidt"
},
{
"path": "rfb/encoding.go",
"chars": 214,
"preview": "package rfb\n\n// IEncoding vnc像素数据编码格式的接口定义\ntype IEncoding interface {\n\tType() EncodingType\n\tSupported(ISession) bool\n\tCl"
},
{
"path": "rfb/encodingtype.go",
"chars": 4075,
"preview": "package rfb\n\ntype EncodingType int32\n\n//go:generate stringer -type=EncodingType\n\n// https://www.iana.org/assignments/rfb"
},
{
"path": "rfb/encodingtype_string.go",
"chars": 6863,
"preview": "// Code generated by \"stringer -type=EncodingType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"inva"
},
{
"path": "rfb/handler.go",
"chars": 392,
"preview": "package rfb\n\ntype IHandler interface {\n\tHandle(session ISession) error\n}\n\n// ProtoVersionLength rfb协议长度\nconst ProtoVersi"
},
{
"path": "rfb/keys.go",
"chars": 2528,
"preview": "package rfb\n\nimport \"fmt\"\n\ntype Key uint32\n\n// Keys 按键列表\ntype Keys []Key\n\nvar keymap = map[rune]Key{\n\t'-': Minus,\n\t'0': "
},
{
"path": "rfb/keys_string.go",
"chars": 6003,
"preview": "package rfb\n\nimport \"fmt\"\n\nconst keyName = \"SpaceExclaimQuoteDblNumberSignDollarPercentAmpersandApostropheParenLeftParen"
},
{
"path": "rfb/message.go",
"chars": 176,
"preview": "package rfb\n\ntype Message interface {\n\tType() MessageType\n\tString() string\n\tSupported(ISession) bool\n\tRead(ISession) (Me"
},
{
"path": "rfb/message_type_client_string.go",
"chars": 1238,
"preview": "// Code generated by \"stringer -type=ClientMessageType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An "
},
{
"path": "rfb/message_type_server_string.go",
"chars": 803,
"preview": "// Code generated by \"stringer -type=ServerMessageType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An "
},
{
"path": "rfb/messagetype.go",
"chars": 1292,
"preview": "package rfb\n\ntype MessageType uint8\n\n// ClientMessageType vnc客户端发送给vnc服务端的消息类型\ntype ClientMessageType MessageType\n\n//go:"
},
{
"path": "rfb/options.go",
"chars": 2722,
"preview": "package rfb\n\nimport \"io\"\n\ntype Option func(*Options)\ntype GetConn func(sess ISession) (io.ReadWriteCloser, error)\n\n// Op"
},
{
"path": "rfb/pixel_format.go",
"chars": 2457,
"preview": "package rfb\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\nconst PixelFormatLen = 16\n\nvar (\n\t// PixelFormat8bit 获取8bit像素格式\n\tPixe"
},
{
"path": "rfb/rectangle.go",
"chars": 1948,
"preview": "package rfb\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\n// Rectangle 表示像素数据的矩形\ntype Rectangle struct {\n\tX uint16\n\tY "
},
{
"path": "rfb/security.go",
"chars": 1950,
"preview": "package rfb\n\n// SecurityType 安全认证类型\ntype SecurityType uint8\n\n//go:generate stringer -type=SecurityType\n\nconst (\n\tSecType"
},
{
"path": "rfb/securitysubtype_string.go",
"chars": 2038,
"preview": "// Code generated by \"stringer -type=SecuritySubType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"i"
},
{
"path": "rfb/securitytype_string.go",
"chars": 929,
"preview": "// Code generated by \"stringer -type=SecurityType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"inva"
},
{
"path": "rfb/session.go",
"chars": 1073,
"preview": "package rfb\n\nimport (\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"io\"\n)\n\n// ISession vnc连接的接口\ntype ISession interface {\n\ti"
},
{
"path": "rfb/session_string.go",
"chars": 828,
"preview": "// Code generated by \"stringer -type=SessionType\"; DO NOT EDIT.\n\npackage rfb\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"inval"
},
{
"path": "rfb/target_config.go",
"chars": 585,
"preview": "package rfb\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype TargetConfig struct {\n\tNetwork string // 网络协议\n\tTimeout time.Durati"
},
{
"path": "security/security_none.go",
"chars": 724,
"preview": "package security\n\nimport \"github.com/vprix/vncproxy/rfb\"\n\n// ClientAuthNone vnc客户端认证\ntype ClientAuthNone struct{}\n\n// Se"
},
{
"path": "security/security_tight.go",
"chars": 17,
"preview": "package security\n"
},
{
"path": "security/security_vencryptplain.go",
"chars": 2569,
"preview": "package security\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/vprix/vncproxy/rfb\"\n)\n\ntype ClientAuthVeNCryp"
},
{
"path": "security/security_vnc.go",
"chars": 3101,
"preview": "package security\n\nimport (\n\t\"bytes\"\n\t\"crypto/des\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/util/grand\"\n\t\"github"
},
{
"path": "session/canvas.go",
"chars": 4309,
"preview": "package session\n\nimport (\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/vprix/vncproxy/canvas\"\n\t\"github.com/vprix"
},
{
"path": "session/client.go",
"chars": 5210,
"preview": "package session\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/osgochina/dmic"
},
{
"path": "session/player.go",
"chars": 5331,
"preview": "package session\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "session/recorder.go",
"chars": 5280,
"preview": "package session\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/vprix/vncprox"
},
{
"path": "session/server.go",
"chars": 4976,
"preview": "package session\n\nimport (\n\t\"bufio\"\n\t\"github.com/gogf/gf/v2/container/gmap\"\n\t\"github.com/vprix/vncproxy/encodings\"\n\t\"gith"
},
{
"path": "vnc/player.go",
"chars": 3331,
"preview": "package vnc\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/gogf/gf/v2/container/gtype\"\n\t\"github.com/gogf/gf"
},
{
"path": "vnc/proxy.go",
"chars": 3801,
"preview": "package vnc\n\nimport (\n\t\"github.com/gogf/gf/v2/container/gtype\"\n\t\"github.com/gogf/gf/v2/util/gconv\"\n\t\"github.com/vprix/vn"
},
{
"path": "vnc/recorder.go",
"chars": 3149,
"preview": "package vnc\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"github.com/gogf/gf/v2/container/gtype\"\n\t\"github.com/gogf/gf/v2/os/"
},
{
"path": "vnc/screenshot.go",
"chars": 2726,
"preview": "package vnc\n\nimport (\n\t\"fmt\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncproxy/messages\"\n\t\"github.com/vp"
},
{
"path": "vnc/video.go",
"chars": 3543,
"preview": "package vnc\n\nimport (\n\t\"context\"\n\t\"github.com/osgochina/dmicro/logger\"\n\t\"github.com/vprix/vncproxy/encodings\"\n\t\"github.c"
}
]
About this extraction
This page contains the full source code of the vprix/vncproxy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 149 files (292.6 KB), approximately 99.4k tokens, and a symbol index with 957 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.