fbvnc: support RRE encoding
[fbvnc.git] / fbvnc.c
blob3dc17a0c69ac455ba954658c2e91991df012f767
1 /*
2 * FBVNC: a small Linux framebuffer VNC viewer
4 * Copyright (C) 2009-2021 Ali Gholami Rudi
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <arpa/inet.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <netdb.h>
23 #include <netinet/in.h>
24 #include <poll.h>
25 #include <pwd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <termios.h>
30 #include <unistd.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <linux/input.h>
35 #include "draw.h"
36 #include "vnc.h"
38 #define MIN(a, b) ((a) < (b) ? (a) : (b))
39 #define MAX(a, b) ((a) > (b) ? (a) : (b))
40 #define OUT(msg) write(1, (msg), strlen(msg))
42 #define VNC_PORT "5900"
43 #define SCRSCRL 2
44 #define MAXRES (1 << 16)
46 static int cols, rows; /* framebuffer dimensions */
47 static int bpp; /* bytes per pixel */
48 static int srv_cols, srv_rows; /* server screen dimensions */
49 static int or, oc; /* visible screen offset */
50 static int mr, mc; /* mouse position */
51 static int nodraw; /* don't draw anything */
53 static char buf[MAXRES];
55 static int vnc_connect(char *addr, char *port)
57 struct addrinfo hints, *addrinfo;
58 int fd;
60 memset(&hints, 0, sizeof(hints));
61 hints.ai_family = AF_UNSPEC;
62 hints.ai_socktype = SOCK_STREAM;
63 hints.ai_flags = AI_PASSIVE;
65 if (getaddrinfo(addr, port, &hints, &addrinfo))
66 return -1;
67 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
68 addrinfo->ai_protocol);
70 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) {
71 close(fd);
72 freeaddrinfo(addrinfo);
73 return -1;
75 freeaddrinfo(addrinfo);
76 return fd;
79 static void fbmode_bits(int *rr, int *rg, int *rb)
81 int mode = FBM_COLORS(fb_mode());
82 *rr = (mode >> 8) & 0xf;
83 *rg = (mode >> 4) & 0xf;
84 *rb = (mode >> 0) & 0xf;
87 static int vnc_init(int fd)
89 char vncver[16];
90 int rr, rg, rb;
91 struct vnc_clientinit clientinit;
92 struct vnc_serverinit serverinit;
93 struct vnc_setpixelformat pixfmt_cmd;
94 struct vnc_setencoding enc_cmd;
95 u32 enc[] = {htonl(VNC_ENC_RAW), htonl(VNC_ENC_RRE)};
96 int connstat = VNC_CONN_FAILED;
98 /* handshake */
99 read(fd, vncver, 12);
100 strcpy(vncver, "RFB 003.003\n");
101 write(fd, vncver, 12);
102 read(fd, &connstat, sizeof(connstat));
103 if (ntohl(connstat) != VNC_CONN_NOAUTH)
104 return -1;
105 clientinit.shared = 1;
106 write(fd, &clientinit, sizeof(clientinit));
107 read(fd, &serverinit, sizeof(serverinit));
108 read(fd, buf, ntohl(serverinit.len));
109 srv_cols = ntohs(serverinit.w);
110 srv_rows = ntohs(serverinit.h);
112 /* set up the framebuffer */
113 if (fb_init())
114 return -1;
115 cols = MIN(srv_cols, fb_cols());
116 rows = MIN(srv_rows, fb_rows());
117 bpp = FBM_BPP(fb_mode());
118 mr = rows / 2;
119 mc = cols / 2;
121 /* send framebuffer configuration */
122 pixfmt_cmd.type = VNC_SETPIXELFORMAT;
123 pixfmt_cmd.format.bpp = bpp << 3;
124 pixfmt_cmd.format.depth = bpp << 3;
125 pixfmt_cmd.format.bigendian = 0;
126 pixfmt_cmd.format.truecolor = 1;
127 fbmode_bits(&rr, &rg, &rb);
128 pixfmt_cmd.format.rmax = htons((1 << rr) - 1);
129 pixfmt_cmd.format.gmax = htons((1 << rg) - 1);
130 pixfmt_cmd.format.bmax = htons((1 << rb) - 1);
132 /* assuming colors packed as RGB; shall handle other cases later */
133 pixfmt_cmd.format.rshl = rg + rb;
134 pixfmt_cmd.format.gshl = rb;
135 pixfmt_cmd.format.bshl = 0;
136 write(fd, &pixfmt_cmd, sizeof(pixfmt_cmd));
138 /* send pixel format */
139 enc_cmd.type = VNC_SETENCODING;
140 enc_cmd.pad = 0;
141 enc_cmd.n = htons(2);
142 write(fd, &enc_cmd, sizeof(enc_cmd));
143 write(fd, enc, ntohs(enc_cmd.n) * sizeof(enc[0]));
144 return 0;
147 static int vnc_free(void)
149 fb_free();
150 return 0;
153 static int vnc_refresh(int fd, int inc)
155 struct vnc_updaterequest fbup_req;
156 fbup_req.type = VNC_UPDATEREQUEST;
157 fbup_req.inc = inc;
158 fbup_req.x = htons(oc);
159 fbup_req.y = htons(or);
160 fbup_req.w = htons(cols);
161 fbup_req.h = htons(rows);
162 return write(fd, &fbup_req, sizeof(fbup_req)) != sizeof(fbup_req);
165 static void drawfb(char *s, int x, int y, int w, int h)
167 int sc; /* screen column offset */
168 int bc, bw; /* buffer column offset / row width */
169 int i;
170 sc = MAX(0, x - oc);
171 bc = x > oc ? 0 : oc - x;
172 bw = x + w < oc + cols ? w - bc : w - bc - (x + w - oc - cols);
173 for (i = y; i < y + h; i++)
174 if (i - or >= 0 && i - or < rows && bw > 0)
175 fb_set(i - or, sc, s + ((i - y) * w + bc) * bpp, bw);
178 static void xread(int fd, void *buf, int len)
180 int nr = 0;
181 int n;
182 while (nr < len && (n = read(fd, buf + nr, len - nr)) > 0)
183 nr += n;
184 if (nr < len) {
185 printf("partial vnc read!\n");
186 exit(1);
190 static void drawrect(char *pixel, int x, int y, int w, int h)
192 int i;
193 for (i = 0; i < w; i++)
194 memcpy(buf + i * bpp, pixel, bpp);
195 for (i = 0; i < h; i++)
196 drawfb(buf, x, y + i, w, 1);
199 static int readrect(int fd)
201 struct vnc_rect uprect;
202 int x, y, w, h;
203 int i;
204 xread(fd, &uprect, sizeof(uprect));
205 x = ntohs(uprect.x);
206 y = ntohs(uprect.y);
207 w = ntohs(uprect.w);
208 h = ntohs(uprect.h);
209 if (x >= srv_cols || x + w > srv_cols)
210 return -1;
211 if (y >= srv_rows || y + h > srv_rows)
212 return -1;
213 if (uprect.enc == htonl(VNC_ENC_RAW)) {
214 for (i = 0; i < h; i++) {
215 xread(fd, buf, w * bpp);
216 if (!nodraw)
217 drawfb(buf, x, y + i, w, 1);
220 if (uprect.enc == htonl(VNC_ENC_RRE)) {
221 char pixel[8];
222 u32 n;
223 xread(fd, &n, 4);
224 xread(fd, pixel, bpp);
225 if (!nodraw)
226 drawrect(pixel, x, y, w, h);
227 for (i = 0; i < ntohl(n); i++) {
228 u16 pos[4];
229 xread(fd, pixel, bpp);
230 xread(fd, pos, 8);
231 if (!nodraw)
232 drawrect(pixel, x + ntohs(pos[0]), y + ntohs(pos[1]),
233 ntohs(pos[2]), ntohs(pos[3]));
236 return 0;
239 static int vnc_event(int fd)
241 char msg[1 << 12];
242 struct vnc_update *fbup = (void *) msg;
243 struct vnc_servercuttext *cuttext = (void *) msg;
244 struct vnc_setcolormapentries *colormap = (void *) msg;
245 int i;
246 int n;
248 if (read(fd, msg, 1) != 1)
249 return -1;
250 switch (msg[0]) {
251 case VNC_UPDATE:
252 xread(fd, msg + 1, sizeof(*fbup) - 1);
253 n = ntohs(fbup->n);
254 for (i = 0; i < n; i++)
255 if (readrect(fd))
256 return -1;
257 break;
258 case VNC_BELL:
259 break;
260 case VNC_SERVERCUTTEXT:
261 xread(fd, msg + 1, sizeof(*cuttext) - 1);
262 xread(fd, buf, ntohl(cuttext->len));
263 break;
264 case VNC_SETCOLORMAPENTRIES:
265 xread(fd, msg + 1, sizeof(*colormap) - 1);
266 xread(fd, buf, ntohs(colormap->n) * 3 * 2);
267 break;
268 default:
269 fprintf(stderr, "unknown vnc msg: %d\n", msg[0]);
270 return -1;
272 return 0;
275 static int rat_event(int fd, int ratfd)
277 char ie[4] = {0};
278 struct vnc_pointerevent me = {VNC_POINTEREVENT};
279 int mask = 0;
280 int or_ = or, oc_ = oc;
281 if (ratfd > 0 && read(ratfd, &ie, sizeof(ie)) != 4)
282 return -1;
283 /* ignore mouse movements when nodraw */
284 if (nodraw)
285 return 0;
286 mc += ie[1];
287 mr -= ie[2];
289 if (mc < oc)
290 oc = MAX(0, oc - cols / SCRSCRL);
291 if (mc >= oc + cols && oc + cols < srv_cols)
292 oc = MIN(srv_cols - cols, oc + cols / SCRSCRL);
293 if (mr < or)
294 or = MAX(0, or - rows / SCRSCRL);
295 if (mr >= or + rows && or + rows < srv_rows)
296 or = MIN(srv_rows - rows, or + rows / SCRSCRL);
297 mc = MAX(oc, MIN(oc + cols - 1, mc));
298 mr = MAX(or, MIN(or + rows - 1, mr));
299 if (ie[0] & 0x01)
300 mask |= VNC_BUTTON1_MASK;
301 if (ie[0] & 0x04)
302 mask |= VNC_BUTTON2_MASK;
303 if (ie[0] & 0x02)
304 mask |= VNC_BUTTON3_MASK;
305 if (ie[3] > 0) /* wheel up */
306 mask |= VNC_BUTTON4_MASK;
307 if (ie[3] < 0) /* wheel down */
308 mask |= VNC_BUTTON5_MASK;
310 me.y = htons(mr);
311 me.x = htons(mc);
312 me.mask = mask;
313 write(fd, &me, sizeof(me));
314 if (or != or_ || oc != oc_)
315 if (vnc_refresh(fd, 0))
316 return -1;
317 return 0;
320 static int press(int fd, int key, int down)
322 struct vnc_keyevent ke = {VNC_KEYEVENT};
323 ke.key = htonl(key);
324 ke.down = down;
325 return write(fd, &ke, sizeof(ke));
328 static void showmsg(void)
330 OUT("\x1b[H\t\t\t*** fbvnc ***\r");
333 static int kbd_event(int fd, int kbdfd)
335 char key[1024];
336 int i, nr;
338 if ((nr = read(kbdfd, key, sizeof(key))) <= 0 )
339 return -1;
340 for (i = 0; i < nr; i++) {
341 int k = -1;
342 int mod[4];
343 int nmod = 0;
344 switch (key[i]) {
345 case 0x08:
346 case 0x7f:
347 k = 0xff08;
348 break;
349 case 0x09:
350 k = 0xff09;
351 break;
352 case 0x1b:
353 if (i + 2 < nr && key[i + 1] == '[') {
354 if (key[i + 2] == 'A')
355 k = 0xff52;
356 if (key[i + 2] == 'B')
357 k = 0xff54;
358 if (key[i + 2] == 'C')
359 k = 0xff53;
360 if (key[i + 2] == 'D')
361 k = 0xff51;
362 if (key[i + 2] == 'H')
363 k = 0xff50;
364 if (k > 0) {
365 i += 2;
366 break;
369 k = 0xff1b;
370 if (i + 1 < nr) {
371 mod[nmod++] = 0xffe9;
372 k = key[++i];
373 if (k == 0x03) /* esc-^C: quit */
374 return -1;
376 break;
377 case 0x0d:
378 k = 0xff0d;
379 break;
380 case 0x0: /* c-space: stop/start drawing */
381 if (!nodraw) {
382 nodraw = 1;
383 showmsg();
384 } else {
385 nodraw = 0;
386 if (vnc_refresh(fd, 0))
387 return -1;
389 default:
390 k = (unsigned char) key[i];
392 if ((k >= 'A' && k <= 'Z') || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
393 mod[nmod++] = 0xffe1;
394 if (k >= 1 && k <= 26) {
395 k = 'a' + k - 1;
396 mod[nmod++] = 0xffe3;
398 if (k > 0) {
399 int j;
400 for (j = 0; j < nmod; j++)
401 press(fd, mod[j], 1);
402 press(fd, k, 1);
403 press(fd, k, 0);
404 for (j = 0; j < nmod; j++)
405 press(fd, mod[j], 0);
408 return 0;
411 static void term_setup(struct termios *ti)
413 struct termios termios;
414 OUT("\033[2J"); /* clear the screen */
415 OUT("\033[?25l"); /* hide the cursor */
416 showmsg();
417 tcgetattr(0, &termios);
418 *ti = termios;
419 cfmakeraw(&termios);
420 tcsetattr(0, TCSANOW, &termios);
423 static void term_cleanup(struct termios *ti)
425 tcsetattr(0, TCSANOW, ti);
426 OUT("\r\n\033[?25h"); /* show the cursor */
429 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
431 struct pollfd ufds[3];
432 int pending = 0;
433 int err;
434 ufds[0].fd = kbd_fd;
435 ufds[0].events = POLLIN;
436 ufds[1].fd = vnc_fd;
437 ufds[1].events = POLLIN;
438 ufds[2].fd = rat_fd;
439 ufds[2].events = POLLIN;
440 rat_event(vnc_fd, -1);
441 if (vnc_refresh(vnc_fd, 0))
442 return;
443 while (1) {
444 err = poll(ufds, 3, 500);
445 if (err == -1 && errno != EINTR)
446 break;
447 if (!err)
448 continue;
449 if (ufds[0].revents & POLLIN)
450 if (kbd_event(vnc_fd, kbd_fd) == -1)
451 break;
452 if (ufds[1].revents & POLLIN) {
453 if (vnc_event(vnc_fd) == -1)
454 break;
455 pending = 0;
457 if (ufds[2].revents & POLLIN)
458 if (rat_event(vnc_fd, rat_fd) == -1)
459 break;
460 if (!pending++)
461 if (vnc_refresh(vnc_fd, 1))
462 break;
466 int main(int argc, char * argv[])
468 char *port = VNC_PORT;
469 char *host = "127.0.0.1";
470 struct termios ti;
471 int vnc_fd, rat_fd;
472 if (argc >= 2)
473 host = argv[1];
474 if (argc >= 3)
475 port = argv[2];
476 if ((vnc_fd = vnc_connect(host, port)) < 0) {
477 fprintf(stderr, "could not connect!\n");
478 return 1;
480 if (vnc_init(vnc_fd) < 0) {
481 close(vnc_fd);
482 fprintf(stderr, "vnc init failed!\n");
483 return 1;
485 term_setup(&ti);
487 /* entering intellimouse for using mouse wheel */
488 rat_fd = open("/dev/input/mice", O_RDWR);
489 write(rat_fd, "\xf3\xc8\xf3\x64\xf3\x50", 6);
490 read(rat_fd, buf, 1);
492 mainloop(vnc_fd, 0, rat_fd);
494 term_cleanup(&ti);
495 vnc_free();
496 close(vnc_fd);
497 close(rat_fd);
498 return 0;