rev |
line source |
pascal@19991
|
1 text data bss dec hex filename
|
pascal@19991
|
2 3179 0 0 3179 c6b util-linux/fbvnc.o
|
pascal@19991
|
3 --- /dev/null
|
pascal@19991
|
4 +++ busybox/util-linux/fbvnc.c
|
pascal@19991
|
5 @@ -0,0 +1,552 @@
|
pascal@19991
|
6 +/* vi: set sw=4 ts=4: */
|
pascal@19991
|
7 +/*
|
pascal@19991
|
8 + * A small linux framebuffer VNC viewer
|
pascal@19991
|
9 + *
|
pascal@19991
|
10 + * pascal.bellard@ads-lu.com
|
pascal@19991
|
11 + *
|
pascal@19991
|
12 + * Based on Ali Gholami Rudi's fbvnc.c
|
pascal@19991
|
13 + * http://repo.or.cz/w/fbvnc.git
|
pascal@19991
|
14 + *
|
pascal@19991
|
15 + * Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
pascal@19991
|
16 + */
|
pascal@19991
|
17 +
|
pascal@19991
|
18 +//applet:IF_FBVNC(APPLET(fbvnc, BB_DIR_BIN, BB_SUID_DROP))
|
pascal@19991
|
19 +
|
pascal@19991
|
20 +//kbuild:lib-$(CONFIG_FBVNC) += fbvnc.o
|
pascal@19991
|
21 +
|
pascal@19991
|
22 +//config:config FBVNC
|
pascal@19991
|
23 +//config: bool "fbvnc"
|
pascal@19991
|
24 +//config: default n
|
pascal@19991
|
25 +//config: depends on PLATFORM_LINUX
|
pascal@19991
|
26 +//config: help
|
pascal@19991
|
27 +//config: A linux framebuffer VNC viewer.
|
pascal@19991
|
28 +
|
pascal@19991
|
29 +//usage:#define fbvnc_trivial_usage
|
pascal@19991
|
30 +//usage: "[VNC_SERVER] [PORT]"
|
pascal@19991
|
31 +//usage:#define fbvnc_full_usage "\n\n"
|
pascal@19991
|
32 +//usage: "A linux framebuffer VNC viewer."
|
pascal@19991
|
33 +//usage: "\nTo exit, press any mouse button and press ESC."
|
pascal@19991
|
34 +
|
pascal@19991
|
35 +#include "libbb.h"
|
pascal@19991
|
36 +#include "vnc.h"
|
pascal@19991
|
37 +#include "common_bufsiz.h"
|
pascal@19991
|
38 +
|
pascal@19991
|
39 +/* Stuff stolen from the kernel's fb.h */
|
pascal@19991
|
40 +#define FB_ACTIVATE_ALL 64
|
pascal@19991
|
41 +enum {
|
pascal@19991
|
42 + FBIOGET_VSCREENINFO = 0x4600,
|
pascal@19991
|
43 + FBIOPUT_VSCREENINFO = 0x4601,
|
pascal@19991
|
44 + FBIOGET_FSCREENINFO = 0x4602,
|
pascal@19991
|
45 + FBIOGETCMAP = 0x4604,
|
pascal@19991
|
46 + FBIOPUTCMAP = 0x4605
|
pascal@19991
|
47 +};
|
pascal@19991
|
48 +
|
pascal@19991
|
49 +struct fb_bitfield {
|
pascal@19991
|
50 + uint32_t offset; /* beginning of bitfield */
|
pascal@19991
|
51 + uint32_t length; /* length of bitfield */
|
pascal@19991
|
52 + uint32_t msb_right; /* !=0: Most significant bit is right */
|
pascal@19991
|
53 +};
|
pascal@19991
|
54 +struct fb_var_screeninfo {
|
pascal@19991
|
55 + uint32_t xres; /* visible resolution */
|
pascal@19991
|
56 + uint32_t yres;
|
pascal@19991
|
57 + uint32_t xres_virtual; /* virtual resolution */
|
pascal@19991
|
58 + uint32_t yres_virtual;
|
pascal@19991
|
59 + uint32_t xoffset; /* offset from virtual to visible */
|
pascal@19991
|
60 + uint32_t yoffset; /* resolution */
|
pascal@19991
|
61 +
|
pascal@19991
|
62 + uint32_t bits_per_pixel;
|
pascal@19991
|
63 + uint32_t grayscale; /* !=0 Graylevels instead of colors */
|
pascal@19991
|
64 +
|
pascal@19991
|
65 + struct fb_bitfield red; /* bitfield in fb mem if true color, */
|
pascal@19991
|
66 + struct fb_bitfield green; /* else only length is significant */
|
pascal@19991
|
67 + struct fb_bitfield blue;
|
pascal@19991
|
68 + struct fb_bitfield transp; /* transparency */
|
pascal@19991
|
69 +
|
pascal@19991
|
70 + uint32_t nonstd; /* !=0 Non standard pixel format */
|
pascal@19991
|
71 +
|
pascal@19991
|
72 + uint32_t activate; /* see FB_ACTIVATE_x */
|
pascal@19991
|
73 +
|
pascal@19991
|
74 + uint32_t height; /* height of picture in mm */
|
pascal@19991
|
75 + uint32_t width; /* width of picture in mm */
|
pascal@19991
|
76 +
|
pascal@19991
|
77 + uint32_t accel_flags; /* acceleration flags (hints) */
|
pascal@19991
|
78 +
|
pascal@19991
|
79 + /* Timing: All values in pixclocks, except pixclock (of course) */
|
pascal@19991
|
80 + uint32_t pixclock; /* pixel clock in ps (pico seconds) */
|
pascal@19991
|
81 + uint32_t left_margin; /* time from sync to picture */
|
pascal@19991
|
82 + uint32_t right_margin; /* time from picture to sync */
|
pascal@19991
|
83 + uint32_t upper_margin; /* time from sync to picture */
|
pascal@19991
|
84 + uint32_t lower_margin;
|
pascal@19991
|
85 + uint32_t hsync_len; /* length of horizontal sync */
|
pascal@19991
|
86 + uint32_t vsync_len; /* length of vertical sync */
|
pascal@19991
|
87 + uint32_t sync; /* see FB_SYNC_x */
|
pascal@19991
|
88 + uint32_t vmode; /* see FB_VMODE_x */
|
pascal@19991
|
89 + uint32_t reserved[6]; /* Reserved for future compatibility */
|
pascal@19991
|
90 +};
|
pascal@19991
|
91 +
|
pascal@19991
|
92 +#define DEFAULTFBDEV FB_0
|
pascal@19991
|
93 +
|
pascal@19991
|
94 +struct fb_fix_screeninfo {
|
pascal@19991
|
95 + char id[16]; /* identification string eg "TT Builtin" */
|
pascal@19991
|
96 + unsigned long smem_start; /* Start of frame buffer mem */
|
pascal@19991
|
97 + /* (physical address) */
|
pascal@19991
|
98 + uint32_t smem_len; /* Length of frame buffer mem */
|
pascal@19991
|
99 + uint32_t type; /* see FB_TYPE_* */
|
pascal@19991
|
100 + uint32_t type_aux; /* Interleave for interleaved Planes */
|
pascal@19991
|
101 + uint32_t visual; /* see FB_VISUAL_* */
|
pascal@19991
|
102 + uint16_t xpanstep; /* zero if no hardware panning */
|
pascal@19991
|
103 + uint16_t ypanstep; /* zero if no hardware panning */
|
pascal@19991
|
104 + uint16_t ywrapstep; /* zero if no hardware ywrap */
|
pascal@19991
|
105 + uint32_t line_length; /* length of a line in bytes */
|
pascal@19991
|
106 + unsigned long mmio_start; /* Start of Memory Mapped I/O */
|
pascal@19991
|
107 + /* (physical address) */
|
pascal@19991
|
108 + uint32_t mmio_len; /* Length of Memory Mapped I/O */
|
pascal@19991
|
109 + uint32_t accel; /* Indicate to driver which */
|
pascal@19991
|
110 + /* specific chip/card we have */
|
pascal@19991
|
111 + uint16_t reserved[3]; /* Reserved for future compatibility */
|
pascal@19991
|
112 +};
|
pascal@19991
|
113 +
|
pascal@19991
|
114 +struct fb_cmap {
|
pascal@19991
|
115 + uint32_t start; /* First entry */
|
pascal@19991
|
116 + uint32_t len; /* Number of entries */
|
pascal@19991
|
117 + uint16_t *red; /* Red values */
|
pascal@19991
|
118 + uint16_t *green;
|
pascal@19991
|
119 + uint16_t *blue;
|
pascal@19991
|
120 + uint16_t *transp; /* transparency, can be NULL */
|
pascal@19991
|
121 +};
|
pascal@19991
|
122 +
|
pascal@19991
|
123 +#define FB_VISUAL_TRUECOLOR 2 /* True color */
|
pascal@19991
|
124 +
|
pascal@19991
|
125 +#define COLORLEVELS (1 << 8)
|
pascal@19991
|
126 +
|
pascal@19991
|
127 +struct scroll_data {
|
pascal@19991
|
128 + int size;
|
pascal@19991
|
129 + int srv_size;
|
pascal@19991
|
130 + int offset;
|
pascal@19991
|
131 + int pos;
|
pascal@19991
|
132 +};
|
pascal@19991
|
133 +
|
pascal@19991
|
134 +struct globals {
|
pascal@19991
|
135 + struct termios term_orig;
|
pascal@19991
|
136 + struct pollfd ufds[3];
|
pascal@19991
|
137 +#define kbd_fd ufds[0].fd
|
pascal@19991
|
138 +#define vnc_fd ufds[1].fd
|
pascal@19991
|
139 +#define rat_fd ufds[2].fd
|
pascal@19991
|
140 + struct scroll_data scroll[2];
|
pascal@19991
|
141 +#define cols scroll[0].size
|
pascal@19991
|
142 +#define srv_cols scroll[0].srv_size
|
pascal@19991
|
143 +#define oc scroll[0].offset
|
pascal@19991
|
144 +#define mc scroll[0].pos
|
pascal@19991
|
145 +#define rows scroll[1].size
|
pascal@19991
|
146 +#define srv_rows scroll[1].srv_size
|
pascal@19991
|
147 +#define or scroll[1].offset
|
pascal@19991
|
148 +#define mr scroll[1].pos
|
pascal@19991
|
149 + char rat_buttons;
|
pascal@19991
|
150 + int fb_fd;
|
pascal@19991
|
151 + void *fb_ptr;
|
pascal@19991
|
152 + int bpp;
|
pascal@19991
|
153 + int nr, ng, nb;
|
pascal@19991
|
154 + struct fb_var_screeninfo vinfo;
|
pascal@19991
|
155 + struct fb_fix_screeninfo finfo;
|
pascal@19991
|
156 + unsigned short red[COLORLEVELS], green[COLORLEVELS], blue[COLORLEVELS];
|
pascal@19991
|
157 +};
|
pascal@19991
|
158 +
|
pascal@19991
|
159 +#define G (*ptr_to_globals)
|
pascal@19991
|
160 +#define INIT_G() do { \
|
pascal@19991
|
161 + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
|
pascal@19991
|
162 +} while (0)
|
pascal@19991
|
163 +
|
pascal@19991
|
164 +static int fb_len(void)
|
pascal@19991
|
165 +{
|
pascal@19991
|
166 + return G.finfo.line_length * G.vinfo.yres_virtual;
|
pascal@19991
|
167 +}
|
pascal@19991
|
168 +
|
pascal@19991
|
169 +static void fb_ioctl_cmap(int fct, struct fb_cmap *cmap)
|
pascal@19991
|
170 +{
|
pascal@19991
|
171 + if (G.finfo.visual == FB_VISUAL_TRUECOLOR)
|
pascal@19991
|
172 + return;
|
pascal@19991
|
173 + cmap->start = 0;
|
pascal@19991
|
174 + cmap->len = MAX(G.nr, MAX(G.ng, G.nb));
|
pascal@19991
|
175 + cmap->transp = NULL;
|
pascal@19991
|
176 + xioctl(G.fb_fd, fct, cmap);
|
pascal@19991
|
177 +}
|
pascal@19991
|
178 +
|
pascal@19991
|
179 +static void fb_cmap_save(int save)
|
pascal@19991
|
180 +{
|
pascal@19991
|
181 + struct fb_cmap cmap;
|
pascal@19991
|
182 +
|
pascal@19991
|
183 + cmap.red = G.red;
|
pascal@19991
|
184 + cmap.green = G.green;
|
pascal@19991
|
185 + cmap.blue = G.blue;
|
pascal@19991
|
186 + fb_ioctl_cmap(save ? FBIOGETCMAP : FBIOPUTCMAP, &cmap);
|
pascal@19991
|
187 +}
|
pascal@19991
|
188 +
|
pascal@19991
|
189 +static void fb_build_cmap(unsigned short *color, int n)
|
pascal@19991
|
190 +{
|
pascal@19991
|
191 + int i, inc = 65535 / (n - 1);
|
pascal@19991
|
192 +
|
pascal@19991
|
193 + for (i = 0; n--; i += inc)
|
pascal@19991
|
194 + *color++ = i;
|
pascal@19991
|
195 +}
|
pascal@19991
|
196 +
|
pascal@19991
|
197 +static void fb_cmap(void)
|
pascal@19991
|
198 +{
|
pascal@19991
|
199 + unsigned short red[COLORLEVELS], green[COLORLEVELS], blue[COLORLEVELS];
|
pascal@19991
|
200 + struct fb_cmap cmap;
|
pascal@19991
|
201 +
|
pascal@19991
|
202 + fb_build_cmap(cmap.red = red, G.nr);
|
pascal@19991
|
203 + fb_build_cmap(cmap.green = green, G.ng);
|
pascal@19991
|
204 + fb_build_cmap(cmap.blue = blue, G.nb);
|
pascal@19991
|
205 + fb_ioctl_cmap(FBIOPUTCMAP, &cmap);
|
pascal@19991
|
206 +}
|
pascal@19991
|
207 +
|
pascal@19991
|
208 +static void fb_init(void)
|
pascal@19991
|
209 +{
|
pascal@19991
|
210 + G.fb_fd = xopen(DEFAULTFBDEV, O_RDWR);
|
pascal@19991
|
211 + xioctl(G.fb_fd, FBIOGET_VSCREENINFO, &G.vinfo);
|
pascal@19991
|
212 + xioctl(G.fb_fd, FBIOGET_FSCREENINFO, &G.finfo);
|
pascal@19991
|
213 + close_on_exec_on(G.fb_fd);
|
pascal@19991
|
214 + G.fb_ptr = mmap(NULL, fb_len(), PROT_READ | PROT_WRITE, MAP_SHARED, G.fb_fd, 0);
|
pascal@19991
|
215 + if (G.fb_ptr == MAP_FAILED)
|
pascal@19991
|
216 + bb_perror_msg_and_die("mmap");
|
pascal@19991
|
217 + G.bpp = (G.vinfo.bits_per_pixel + 7) >> 3;
|
pascal@19991
|
218 + G.nr = 1 << G.vinfo.red.length;
|
pascal@19991
|
219 + G.nb = 1 << G.vinfo.blue.length;
|
pascal@19991
|
220 + G.ng = 1 << G.vinfo.green.length;
|
pascal@19991
|
221 + fb_cmap_save(1);
|
pascal@19991
|
222 + fb_cmap();
|
pascal@19991
|
223 +}
|
pascal@19991
|
224 +
|
pascal@19991
|
225 +static void fb_free(void)
|
pascal@19991
|
226 +{
|
pascal@19991
|
227 + fb_cmap_save(0);
|
pascal@19991
|
228 + munmap(G.fb_ptr, fb_len());
|
pascal@19991
|
229 + close(G.fb_fd);
|
pascal@19991
|
230 +}
|
pascal@19991
|
231 +
|
pascal@19991
|
232 +#define fb_rows vinfo.yres
|
pascal@19991
|
233 +#define fb_cols vinfo.xres
|
pascal@19991
|
234 +
|
pascal@19991
|
235 +static void fb_set(int r, int c, void *mem, int len)
|
pascal@19991
|
236 +{
|
pascal@19991
|
237 + memcpy(G.fb_ptr + (r + G.vinfo.yoffset) * G.finfo.line_length +
|
pascal@19991
|
238 + (c + G.vinfo.xoffset) * G.bpp, mem, len * G.bpp);
|
pascal@19991
|
239 +}
|
pascal@19991
|
240 +
|
pascal@19991
|
241 +#define line_buffer bb_common_bufsiz1
|
pascal@19991
|
242 +#define MAXPIX (COMMON_BUFSIZE/sizeof(uint32_t))
|
pascal@19991
|
243 +
|
pascal@19991
|
244 +static void skip(int len)
|
pascal@19991
|
245 +{
|
pascal@19991
|
246 + int n;
|
pascal@19991
|
247 + while (len > 0 && (n = read(G.vnc_fd, line_buffer,
|
pascal@19991
|
248 + MIN(len, COMMON_BUFSIZE))) > 0)
|
pascal@19991
|
249 + len -= n;
|
pascal@19991
|
250 +}
|
pascal@19991
|
251 +
|
pascal@19991
|
252 +static void vnc_init(void)
|
pascal@19991
|
253 +{
|
pascal@19991
|
254 + struct vnc_client_init clientinit;
|
pascal@19991
|
255 + struct vnc_server_init serverinit;
|
pascal@19991
|
256 + struct vnc_client_pixelfmt pixfmt_cmd;
|
pascal@19991
|
257 + int connstat = VNC_CONN_FAILED;
|
pascal@19991
|
258 +
|
pascal@19991
|
259 + write(G.vnc_fd, "RFB 003.003\n", 12);
|
pascal@19991
|
260 + skip(12);
|
pascal@19991
|
261 +
|
pascal@19991
|
262 + xread(G.vnc_fd, &connstat, sizeof(connstat));
|
pascal@19991
|
263 +
|
pascal@19991
|
264 + if (ntohl(connstat) != VNC_CONN_NOAUTH)
|
pascal@19991
|
265 + bb_perror_msg_and_die("vnc auth");
|
pascal@19991
|
266 +
|
pascal@19991
|
267 + clientinit.shared = 1;
|
pascal@19991
|
268 + write(G.vnc_fd, &clientinit, sizeof(clientinit));
|
pascal@19991
|
269 + read(G.vnc_fd, &serverinit, sizeof(serverinit));
|
pascal@19991
|
270 +
|
pascal@19991
|
271 + fb_init();
|
pascal@19991
|
272 + G.srv_cols = ntohs(serverinit.w);
|
pascal@19991
|
273 + G.srv_rows = ntohs(serverinit.h);
|
pascal@19991
|
274 + G.cols = MIN(G.srv_cols, G.fb_cols);
|
pascal@19991
|
275 + G.rows = MIN(G.srv_rows, G.fb_rows);
|
pascal@19991
|
276 + G.mr = G.rows / 2;
|
pascal@19991
|
277 + G.mc = G.cols / 2;
|
pascal@19991
|
278 +
|
pascal@19991
|
279 + skip(ntohl(serverinit.len));
|
pascal@19991
|
280 + pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
|
pascal@19991
|
281 + pixfmt_cmd.format.bigendian = 0;
|
pascal@19991
|
282 + pixfmt_cmd.format.truecolor = 1;
|
pascal@19991
|
283 + pixfmt_cmd.format.bpp =
|
pascal@19991
|
284 + pixfmt_cmd.format.depth = G.bpp << 3;
|
pascal@19991
|
285 + pixfmt_cmd.format.rmax = htons(G.nr - 1);
|
pascal@19991
|
286 + pixfmt_cmd.format.gmax = htons(G.ng - 1);
|
pascal@19991
|
287 + pixfmt_cmd.format.bmax = htons(G.nb - 1);
|
pascal@19991
|
288 + pixfmt_cmd.format.rshl = G.vinfo.red.offset;
|
pascal@19991
|
289 + pixfmt_cmd.format.gshl = G.vinfo.green.offset;
|
pascal@19991
|
290 + pixfmt_cmd.format.bshl = G.vinfo.blue.offset;
|
pascal@19991
|
291 + write(G.vnc_fd, &pixfmt_cmd, sizeof(pixfmt_cmd));
|
pascal@19991
|
292 +}
|
pascal@19991
|
293 +
|
pascal@19991
|
294 +static void vnc_refresh(int inc)
|
pascal@19991
|
295 +{
|
pascal@19991
|
296 + struct vnc_client_fbup fbup_req;
|
pascal@19991
|
297 + fbup_req.type = VNC_CLIENT_FBUP;
|
pascal@19991
|
298 + fbup_req.inc = inc;
|
pascal@19991
|
299 + fbup_req.x = htons(G.oc);
|
pascal@19991
|
300 + fbup_req.y = htons(G.or);
|
pascal@19991
|
301 + fbup_req.w = htons(G.oc + G.cols);
|
pascal@19991
|
302 + fbup_req.h = htons(G.or + G.rows);
|
pascal@19991
|
303 + write(G.vnc_fd, &fbup_req, sizeof(fbup_req));
|
pascal@19991
|
304 +}
|
pascal@19991
|
305 +
|
pascal@19991
|
306 +static void cleanup(void)
|
pascal@19991
|
307 +{
|
pascal@19991
|
308 +#define RESETSTR "\x1b[?25h\x1b[2J\x1b[H"
|
pascal@19991
|
309 + fb_free();
|
pascal@19991
|
310 + tcsetattr_stdin_TCSANOW(&G.term_orig);
|
pascal@19991
|
311 + write(STDOUT_FILENO, RESETSTR, sizeof(RESETSTR));
|
pascal@19991
|
312 + if (ENABLE_FEATURE_CLEAN_UP) {
|
pascal@19991
|
313 + close(G.vnc_fd);
|
pascal@19991
|
314 + close(G.rat_fd);
|
pascal@19991
|
315 + }
|
pascal@19991
|
316 +}
|
pascal@19991
|
317 +
|
pascal@19991
|
318 +static void killed(int code) NORETURN;
|
pascal@19991
|
319 +static void killed(int code)
|
pascal@19991
|
320 +{
|
pascal@19991
|
321 + cleanup();
|
pascal@19991
|
322 + if (code > EXIT_FAILURE)
|
pascal@19991
|
323 + kill_myself_with_sig(code);
|
pascal@19991
|
324 + exit(code);
|
pascal@19991
|
325 +}
|
pascal@19991
|
326 +
|
pascal@19991
|
327 +static void vnc_event(void)
|
pascal@19991
|
328 +{
|
pascal@19991
|
329 + struct vnc_rect uprect;
|
pascal@19991
|
330 + union {
|
pascal@19991
|
331 + struct vnc_server_fbup fbup;
|
pascal@19991
|
332 + struct vnc_server_cuttext cuttext;
|
pascal@19991
|
333 + struct vnc_server_colormap colormap;
|
pascal@19991
|
334 + } msg;
|
pascal@19991
|
335 + int n;
|
pascal@19991
|
336 +
|
pascal@19991
|
337 + switch (xread_char(G.vnc_fd)) {
|
pascal@19991
|
338 + case VNC_SERVER_FBUP:
|
pascal@19991
|
339 + xread(G.vnc_fd, &msg.fbup.pad, sizeof(msg.fbup) - 1);
|
pascal@19991
|
340 + n = ntohs(msg.fbup.n);
|
pascal@19991
|
341 + while (n--) {
|
pascal@19991
|
342 + int x, y, w, h, l, i;
|
pascal@19991
|
343 + xread(G.vnc_fd, &uprect, sizeof(uprect));
|
pascal@19991
|
344 + if (uprect.enc != 0)
|
pascal@19991
|
345 + killed(1);
|
pascal@19991
|
346 + i = 0;
|
pascal@19991
|
347 + x = ntohs(uprect.x) - G.oc;
|
pascal@19991
|
348 + y = ntohs(uprect.y) - G.or;
|
pascal@19991
|
349 + w = ntohs(uprect.w);
|
pascal@19991
|
350 + h = ntohs(uprect.h);
|
pascal@19991
|
351 + l = MIN(w, G.cols - x);
|
pascal@19991
|
352 + if (x < 0) {
|
pascal@19991
|
353 + l = MIN(w + x, G.cols);
|
pascal@19991
|
354 + i = -x;
|
pascal@19991
|
355 + x = 0;
|
pascal@19991
|
356 + }
|
pascal@19991
|
357 + for (; h--; y++) {
|
pascal@19991
|
358 + int a, b, c = i;
|
pascal@19991
|
359 + for (a = b = 0; w > b; b += a, c = 0) {
|
pascal@19991
|
360 + int len;
|
pascal@19991
|
361 + a = MIN(w - b, MAXPIX);
|
pascal@19991
|
362 + len = MIN(a, l - b) - c;
|
pascal@19991
|
363 + xread(G.vnc_fd, line_buffer, a * G.bpp);
|
pascal@19991
|
364 + if (y >= 0 && y < G.rows && len > 0)
|
pascal@19991
|
365 + fb_set(y, x + b,
|
pascal@19991
|
366 + line_buffer + (c * G.bpp),
|
pascal@19991
|
367 + len);
|
pascal@19991
|
368 + }
|
pascal@19991
|
369 + }
|
pascal@19991
|
370 + }
|
pascal@19991
|
371 + break;
|
pascal@19991
|
372 + case VNC_SERVER_BELL:
|
pascal@19991
|
373 + break;
|
pascal@19991
|
374 + case VNC_SERVER_CUTTEXT:
|
pascal@19991
|
375 + xread(G.vnc_fd, &msg.cuttext.pad1, sizeof(msg.cuttext) - 1);
|
pascal@19991
|
376 + skip(ntohl(msg.cuttext.len));
|
pascal@19991
|
377 + break;
|
pascal@19991
|
378 + case VNC_SERVER_COLORMAP:
|
pascal@19991
|
379 + xread(G.vnc_fd, &msg.colormap.pad, sizeof(msg.colormap) - 1);
|
pascal@19991
|
380 + skip(ntohs(msg.colormap.n) * 3 * 2);
|
pascal@19991
|
381 + break;
|
pascal@19991
|
382 + default:
|
pascal@19991
|
383 + killed(1);
|
pascal@19991
|
384 + }
|
pascal@19991
|
385 +}
|
pascal@19991
|
386 +
|
pascal@19991
|
387 +static int update_scroll(struct scroll_data *s)
|
pascal@19991
|
388 +{
|
pascal@19991
|
389 + int shift = s->size / 5;
|
pascal@19991
|
390 + int max = s->srv_size - s->size;
|
pascal@19991
|
391 + int status = 0;
|
pascal@19991
|
392 + if (s->pos < s->offset) {
|
pascal@19991
|
393 + if ((s->offset -= shift) < 0)
|
pascal@19991
|
394 + s->offset = 0;
|
pascal@19991
|
395 + }
|
pascal@19991
|
396 + else if (s->pos >= s->offset + s->size && s->offset < max) {
|
pascal@19991
|
397 + if ((s->offset += shift) > max)
|
pascal@19991
|
398 + s->offset = max;
|
pascal@19991
|
399 + }
|
pascal@19991
|
400 + else status++;
|
pascal@19991
|
401 + s->pos = MAX(s->offset, MIN(s->offset + s->size - 1, s->pos));
|
pascal@19991
|
402 + return status;
|
pascal@19991
|
403 +}
|
pascal@19991
|
404 +
|
pascal@19991
|
405 +static void rat_event(void)
|
pascal@19991
|
406 +{
|
pascal@19991
|
407 + static u8 btn2vnc[8] = {
|
pascal@19991
|
408 + 0, VNC_BUTTON1_MASK, VNC_BUTTON3_MASK,
|
pascal@19991
|
409 + VNC_BUTTON1_MASK + VNC_BUTTON3_MASK, VNC_BUTTON2_MASK,
|
pascal@19991
|
410 + VNC_BUTTON1_MASK + VNC_BUTTON2_MASK,
|
pascal@19991
|
411 + VNC_BUTTON2_MASK + VNC_BUTTON3_MASK,
|
pascal@19991
|
412 + VNC_BUTTON1_MASK + VNC_BUTTON2_MASK + VNC_BUTTON3_MASK
|
pascal@19991
|
413 + };
|
pascal@19991
|
414 + signed char ie[4];
|
pascal@19991
|
415 + struct vnc_client_ratevent me = {VNC_CLIENT_RATEVENT};
|
pascal@19991
|
416 + int refresh;
|
pascal@19991
|
417 +
|
pascal@19991
|
418 + xread(G.rat_fd, &ie, sizeof(ie));
|
pascal@19991
|
419 + G.mc += ie[1];
|
pascal@19991
|
420 + G.mr -= ie[2];
|
pascal@19991
|
421 + refresh = 2 - update_scroll(&G.scroll[0]) - update_scroll(&G.scroll[1]);
|
pascal@19991
|
422 + me.mask = btn2vnc[(int)(G.rat_buttons = ie[0] & 7)];
|
pascal@19991
|
423 + if (ie[3] > 0) /* wheel up */
|
pascal@19991
|
424 + me.mask |= VNC_BUTTON4_MASK;
|
pascal@19991
|
425 + if (ie[3] < 0) /* wheel down */
|
pascal@19991
|
426 + me.mask |= VNC_BUTTON5_MASK;
|
pascal@19991
|
427 + me.y = htons(G.mr);
|
pascal@19991
|
428 + me.x = htons(G.mc);
|
pascal@19991
|
429 + write(G.vnc_fd, &me, sizeof(me));
|
pascal@19991
|
430 + if (refresh)
|
pascal@19991
|
431 + vnc_refresh(0);
|
pascal@19991
|
432 +}
|
pascal@19991
|
433 +
|
pascal@19991
|
434 +static int press(int key, int down)
|
pascal@19991
|
435 +{
|
pascal@19991
|
436 + struct vnc_client_keyevent ke = {VNC_CLIENT_KEYEVENT};
|
pascal@19991
|
437 + ke.key = htonl(key);
|
pascal@19991
|
438 + ke.down = down;
|
pascal@19991
|
439 + return write(G.vnc_fd, &ke, sizeof(ke));
|
pascal@19991
|
440 +}
|
pascal@19991
|
441 +
|
pascal@19991
|
442 +static void kbd_event(void)
|
pascal@19991
|
443 +{
|
pascal@19991
|
444 + char key[1024];
|
pascal@19991
|
445 + int i, nr;
|
pascal@19991
|
446 +
|
pascal@19991
|
447 + if ((nr = read(0, key, sizeof(key))) <= 0 )
|
pascal@19991
|
448 + killed(1);
|
pascal@19991
|
449 + for (i = 0; i < nr; i++) {
|
pascal@19991
|
450 + int j, k;
|
pascal@19991
|
451 + int mod[4];
|
pascal@19991
|
452 + int nmod;
|
pascal@19991
|
453 +
|
pascal@19991
|
454 + k = nmod = 0;
|
pascal@19991
|
455 + switch (key[i]) {
|
pascal@19991
|
456 + case 0x08:
|
pascal@19991
|
457 + case 0x7f:
|
pascal@19991
|
458 + k = 0xff08;
|
pascal@19991
|
459 + break;
|
pascal@19991
|
460 + case 0x1b:
|
pascal@19991
|
461 + if (G.rat_buttons)
|
pascal@19991
|
462 + killed(0);
|
pascal@19991
|
463 + if (i + 2 < nr && key[i + 1] == '[') {
|
pascal@19991
|
464 + static const char arr2vnc[] = "HDACB";
|
pascal@19991
|
465 + char *p = strchr(arr2vnc, key[i + 2]);
|
pascal@19991
|
466 +
|
pascal@19991
|
467 + if (p) {
|
pascal@19991
|
468 + k = p - arr2vnc + 0xff50;
|
pascal@19991
|
469 + i += 2;
|
pascal@19991
|
470 + break;
|
pascal@19991
|
471 + }
|
pascal@19991
|
472 + }
|
pascal@19991
|
473 + if (i + 1 < nr) {
|
pascal@19991
|
474 + mod[nmod++] = 0xffe9;
|
pascal@19991
|
475 + i++;
|
pascal@19991
|
476 + }
|
pascal@19991
|
477 + case 0x09:
|
pascal@19991
|
478 + case 0x0d:
|
pascal@19991
|
479 + k = 0xff00;
|
pascal@19991
|
480 + goto getkey;
|
pascal@19991
|
481 + case 0x0c: /* Mouse button + ^L: redraw */
|
pascal@19991
|
482 + if (G.rat_buttons) {
|
pascal@19991
|
483 + vnc_refresh(0);
|
pascal@19991
|
484 + continue;
|
pascal@19991
|
485 + }
|
pascal@19991
|
486 + default:
|
pascal@19991
|
487 + getkey:
|
pascal@19991
|
488 + k += (unsigned char) key[i];
|
pascal@19991
|
489 + }
|
pascal@19991
|
490 + if ((k >= 'A' && k <= 'Z') || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
|
pascal@19991
|
491 + mod[nmod++] = 0xffe1;
|
pascal@19991
|
492 + if (k >= 1 && k <= 26) {
|
pascal@19991
|
493 + k += 'a' - 1;
|
pascal@19991
|
494 + mod[nmod++] = 0xffe3;
|
pascal@19991
|
495 + }
|
pascal@19991
|
496 + mod[nmod] = k;
|
pascal@19991
|
497 + for (j = 0; j <= nmod; j++)
|
pascal@19991
|
498 + press(mod[j], 1);
|
pascal@19991
|
499 + press(k, 0);
|
pascal@19991
|
500 + for (j = 0; j < nmod; j++)
|
pascal@19991
|
501 + press(mod[j], 0);
|
pascal@19991
|
502 + }
|
pascal@19991
|
503 +}
|
pascal@19991
|
504 +
|
pascal@19991
|
505 +static void term_setup(void)
|
pascal@19991
|
506 +{
|
pascal@19991
|
507 + struct termios termios;
|
pascal@19991
|
508 +#define INITSTR "\x1b[?25l\x1b[2J\x1b[H** fbvnc **"
|
pascal@19991
|
509 +
|
pascal@19991
|
510 + write(STDOUT_FILENO, INITSTR, sizeof(INITSTR));
|
pascal@19991
|
511 + tcgetattr (STDIN_FILENO, &termios);
|
pascal@19991
|
512 + G.term_orig = termios;
|
pascal@19991
|
513 + cfmakeraw(&termios);
|
pascal@19991
|
514 + tcsetattr_stdin_TCSANOW(&termios);
|
pascal@19991
|
515 +}
|
pascal@19991
|
516 +
|
pascal@19991
|
517 +int fbvnc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
pascal@19991
|
518 +int fbvnc_main(int argc, char **argv)
|
pascal@19991
|
519 +{
|
pascal@19991
|
520 + char *host = (char *) "127.0.0.1";
|
pascal@19991
|
521 + int port, pending = 0;
|
pascal@19991
|
522 +
|
pascal@19991
|
523 + INIT_G();
|
pascal@19991
|
524 + if (argc >= 2)
|
pascal@19991
|
525 + host = argv[1];
|
pascal@19991
|
526 + port = bb_lookup_port((argc >= 3) ? argv[2] : "vnc", "tcp", 5900);
|
pascal@19991
|
527 + G.vnc_fd = create_and_connect_stream_or_die(host, port);
|
pascal@19991
|
528 + vnc_init();
|
pascal@19991
|
529 + G.rat_fd = open("/dev/input/mice", O_RDWR);
|
pascal@19991
|
530 + write(G.rat_fd, "\xf3\xc8\xf3\x64\xf3\x50", 6); /* for using mouse wheel */
|
pascal@19991
|
531 + read(G.rat_fd, line_buffer, 1);
|
pascal@19991
|
532 + term_setup();
|
pascal@19991
|
533 + atexit(cleanup);
|
pascal@19991
|
534 + bb_signals(BB_FATAL_SIGS, killed);
|
pascal@19991
|
535 +
|
pascal@19991
|
536 + G.ufds[0].events =
|
pascal@19991
|
537 + G.ufds[1].events =
|
pascal@19991
|
538 + G.ufds[2].events = POLLIN;
|
pascal@19991
|
539 + vnc_refresh(0);
|
pascal@19991
|
540 + while (1) {
|
pascal@19991
|
541 + int status = poll(G.ufds, 3, 500);
|
pascal@19991
|
542 + if (status == -1 && errno != EINTR)
|
pascal@19991
|
543 + killed(1);
|
pascal@19991
|
544 + if (!status)
|
pascal@19991
|
545 + continue;
|
pascal@19991
|
546 + if (G.ufds[0].revents & POLLIN)
|
pascal@19991
|
547 + kbd_event();
|
pascal@19991
|
548 + if (G.ufds[1].revents & POLLIN) {
|
pascal@19991
|
549 + vnc_event();
|
pascal@19991
|
550 + pending = 0;
|
pascal@19991
|
551 + }
|
pascal@19991
|
552 + if (G.ufds[2].revents & POLLIN)
|
pascal@19991
|
553 + rat_event();
|
pascal@19991
|
554 + if (!pending++)
|
pascal@19991
|
555 + vnc_refresh(1);
|
pascal@19991
|
556 + }
|
pascal@19991
|
557 +}
|
pascal@19991
|
558 --- /dev/null
|
pascal@19991
|
559 +++ busybox/util-linux/vnc.h
|
pascal@19991
|
560 @@ -0,0 +1,124 @@
|
pascal@19991
|
561 +#define VNC_CONN_FAILED 0
|
pascal@19991
|
562 +#define VNC_CONN_NOAUTH 1
|
pascal@19991
|
563 +#define VNC_CONN_AUTH 2
|
pascal@19991
|
564 +
|
pascal@19991
|
565 +#define VNC_AUTH_OK 0
|
pascal@19991
|
566 +#define VNC_AUTH_FAILED 1
|
pascal@19991
|
567 +#define VNC_AUTH_TOOMANY 2
|
pascal@19991
|
568 +
|
pascal@19991
|
569 +#define VNC_SERVER_FBUP 0
|
pascal@19991
|
570 +#define VNC_SERVER_COLORMAP 1
|
pascal@19991
|
571 +#define VNC_SERVER_BELL 2
|
pascal@19991
|
572 +#define VNC_SERVER_CUTTEXT 3
|
pascal@19991
|
573 +
|
pascal@19991
|
574 +#define VNC_CLIENT_PIXFMT 0
|
pascal@19991
|
575 +#define VNC_CLIENT_COLORMAP 1
|
pascal@19991
|
576 +#define VNC_CLIENT_SETENC 2
|
pascal@19991
|
577 +#define VNC_CLIENT_FBUP 3
|
pascal@19991
|
578 +#define VNC_CLIENT_KEYEVENT 4
|
pascal@19991
|
579 +#define VNC_CLIENT_RATEVENT 5
|
pascal@19991
|
580 +#define VNC_CLIENT_CUTTEXT 6
|
pascal@19991
|
581 +
|
pascal@19991
|
582 +#define VNC_ENC_RAW 0
|
pascal@19991
|
583 +#define VNC_ENC_COPYRECT 1
|
pascal@19991
|
584 +#define VNC_ENC_RRE 2
|
pascal@19991
|
585 +#define VNC_ENC_CORRE 4
|
pascal@19991
|
586 +#define VNC_ENC_HEXTILE 5
|
pascal@19991
|
587 +
|
pascal@19991
|
588 +#define VNC_BUTTON1_MASK 0x01
|
pascal@19991
|
589 +#define VNC_BUTTON2_MASK 0x02
|
pascal@19991
|
590 +#define VNC_BUTTON3_MASK 0x04
|
pascal@19991
|
591 +#define VNC_BUTTON4_MASK 0x10
|
pascal@19991
|
592 +#define VNC_BUTTON5_MASK 0x08
|
pascal@19991
|
593 +
|
pascal@19991
|
594 +typedef unsigned char u8;
|
pascal@19991
|
595 +typedef unsigned short u16;
|
pascal@19991
|
596 +typedef unsigned int u32;
|
pascal@19991
|
597 +
|
pascal@19991
|
598 +struct vnc_pixelfmt {
|
pascal@19991
|
599 + u8 bpp;
|
pascal@19991
|
600 + u8 depth;
|
pascal@19991
|
601 + u8 bigendian;
|
pascal@19991
|
602 + u8 truecolor;
|
pascal@19991
|
603 + u16 rmax;
|
pascal@19991
|
604 + u16 gmax;
|
pascal@19991
|
605 + u16 bmax;
|
pascal@19991
|
606 + u8 rshl;
|
pascal@19991
|
607 + u8 gshl;
|
pascal@19991
|
608 + u8 bshl;
|
pascal@19991
|
609 +
|
pascal@19991
|
610 + u8 pad1;
|
pascal@19991
|
611 + u16 pad2;
|
pascal@19991
|
612 +};
|
pascal@19991
|
613 +
|
pascal@19991
|
614 +struct vnc_client_init {
|
pascal@19991
|
615 + u8 shared;
|
pascal@19991
|
616 +};
|
pascal@19991
|
617 +
|
pascal@19991
|
618 +struct vnc_server_init {
|
pascal@19991
|
619 + u16 w;
|
pascal@19991
|
620 + u16 h;
|
pascal@19991
|
621 + struct vnc_pixelfmt fmt;
|
pascal@19991
|
622 + u32 len;
|
pascal@19991
|
623 + /* char name[len]; */
|
pascal@19991
|
624 +};
|
pascal@19991
|
625 +
|
pascal@19991
|
626 +struct vnc_rect {
|
pascal@19991
|
627 + u16 x, y;
|
pascal@19991
|
628 + u16 w, h;
|
pascal@19991
|
629 + u32 enc;
|
pascal@19991
|
630 + /* rect bytes */
|
pascal@19991
|
631 +};
|
pascal@19991
|
632 +
|
pascal@19991
|
633 +struct vnc_server_fbup {
|
pascal@19991
|
634 + u8 type;
|
pascal@19991
|
635 + u8 pad;
|
pascal@19991
|
636 + u16 n;
|
pascal@19991
|
637 + /* struct vnc_rect rects[n]; */
|
pascal@19991
|
638 +};
|
pascal@19991
|
639 +
|
pascal@19991
|
640 +struct vnc_server_cuttext {
|
pascal@19991
|
641 + u8 type;
|
pascal@19991
|
642 + u8 pad1;
|
pascal@19991
|
643 + u16 pad2;
|
pascal@19991
|
644 + u32 len;
|
pascal@19991
|
645 + /* char text[length] */
|
pascal@19991
|
646 +};
|
pascal@19991
|
647 +
|
pascal@19991
|
648 +struct vnc_server_colormap {
|
pascal@19991
|
649 + u8 type;
|
pascal@19991
|
650 + u8 pad;
|
pascal@19991
|
651 + u16 first;
|
pascal@19991
|
652 + u16 n;
|
pascal@19991
|
653 + /* u8 colors[n * 3 * 2]; */
|
pascal@19991
|
654 +};
|
pascal@19991
|
655 +
|
pascal@19991
|
656 +struct vnc_client_pixelfmt {
|
pascal@19991
|
657 + u8 type;
|
pascal@19991
|
658 + u8 pad1;
|
pascal@19991
|
659 + u16 pad2;
|
pascal@19991
|
660 + struct vnc_pixelfmt format;
|
pascal@19991
|
661 +};
|
pascal@19991
|
662 +
|
pascal@19991
|
663 +struct vnc_client_fbup {
|
pascal@19991
|
664 + u8 type;
|
pascal@19991
|
665 + u8 inc;
|
pascal@19991
|
666 + u16 x;
|
pascal@19991
|
667 + u16 y;
|
pascal@19991
|
668 + u16 w;
|
pascal@19991
|
669 + u16 h;
|
pascal@19991
|
670 +};
|
pascal@19991
|
671 +
|
pascal@19991
|
672 +struct vnc_client_keyevent {
|
pascal@19991
|
673 + u8 type;
|
pascal@19991
|
674 + u8 down;
|
pascal@19991
|
675 + u16 pad;
|
pascal@19991
|
676 + u32 key;
|
pascal@19991
|
677 +};
|
pascal@19991
|
678 +
|
pascal@19991
|
679 +struct vnc_client_ratevent {
|
pascal@19991
|
680 + u8 type;
|
pascal@19991
|
681 + u8 mask;
|
pascal@19991
|
682 + u16 x;
|
pascal@19991
|
683 + u16 y;
|
pascal@19991
|
684 +};
|