add FB_VAL() as a placeholder for optimized versions of fb_val()
[fbvnc.git] / fbvnc.c
bloba08675625e6ce9e53b8efa63aa1ac89ac300fef0
1 /*
2 * fbvnc - a small linux framebuffer vnc viewer
4 * Copyright (C) 2009-2011 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
7 */
8 #include <arpa/inet.h>
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <netdb.h>
13 #include <netinet/in.h>
14 #include <poll.h>
15 #include <pwd.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <termios.h>
20 #include <unistd.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <linux/input.h>
25 #include "draw.h"
26 #include "vnc.h"
28 /* framebuffer depth */
29 typedef unsigned int fbval_t;
31 /* optimized version of fb_val() */
32 #define FB_VAL(r, g, b) fb_val((r), (g), (b))
34 #define MIN(a, b) ((a) < (b) ? (a) : (b))
35 #define MAX(a, b) ((a) > (b) ? (a) : (b))
37 #define VNC_PORT "5900"
39 #define MAXRES (1 << 21)
40 #define MIN(a, b) ((a) < (b) ? (a) : (b))
42 static int cols, rows;
43 static int mr, mc; /* mouse position */
45 static char buf[MAXRES];
47 static int vnc_connect(char *addr, char *port)
49 struct addrinfo hints, *addrinfo;
50 int fd;
52 memset(&hints, 0, sizeof(hints));
53 hints.ai_family = AF_UNSPEC;
54 hints.ai_socktype = SOCK_STREAM;
55 hints.ai_flags = AI_PASSIVE;
57 if (getaddrinfo(addr, port, &hints, &addrinfo))
58 return -1;
59 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
60 addrinfo->ai_protocol);
62 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) {
63 close(fd);
64 freeaddrinfo(addrinfo);
65 return -1;
67 freeaddrinfo(addrinfo);
68 return fd;
71 static int vnc_init(int fd)
73 char vncver[] = "RFB 003.003\n";
74 struct vnc_client_init clientinit;
75 struct vnc_server_init serverinit;
76 struct vnc_client_pixelfmt pixfmt_cmd;
77 int connstat = VNC_CONN_FAILED;
79 write(fd, vncver, 12);
80 read(fd, vncver, 12);
82 read(fd, &connstat, sizeof(connstat));
84 if (ntohl(connstat) != VNC_CONN_NOAUTH)
85 return -1;
87 clientinit.shared = 1;
88 write(fd, &clientinit, sizeof(clientinit));
89 read(fd, &serverinit, sizeof(serverinit));
91 if (fb_init())
92 return -1;
93 if (FBM_BPP(fb_mode()) != sizeof(fbval_t)) {
94 fprintf(stderr, "fbvnc: fbval_t doesn't match fb depth\n");
95 exit(1);
97 cols = MIN(ntohs(serverinit.w), fb_cols());
98 rows = MIN(ntohs(serverinit.h), fb_rows());
99 mr = rows / 2;
100 mc = cols / 2;
102 read(fd, buf, ntohl(serverinit.len));
103 pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
104 pixfmt_cmd.format.bpp = 8;
105 pixfmt_cmd.format.depth = 8;
106 pixfmt_cmd.format.bigendian = 0;
107 pixfmt_cmd.format.truecolor = 1;
109 pixfmt_cmd.format.rmax = htons(3);
110 pixfmt_cmd.format.gmax = htons(7);
111 pixfmt_cmd.format.bmax = htons(7);
112 pixfmt_cmd.format.rshl = 0;
113 pixfmt_cmd.format.gshl = 2;
114 pixfmt_cmd.format.bshl = 5;
116 write(fd, &pixfmt_cmd, sizeof(pixfmt_cmd));
117 return fd;
120 static int vnc_free(void)
122 fb_free();
123 return 0;
126 static int vnc_refresh(int fd, int inc)
128 struct vnc_client_fbup fbup_req;
129 fbup_req.type = VNC_CLIENT_FBUP;
130 fbup_req.inc = inc;
131 fbup_req.x = htons(0);
132 fbup_req.y = htons(0);
133 fbup_req.w = htons(cols);
134 fbup_req.h = htons(rows);
135 write(fd, &fbup_req, sizeof(fbup_req));
136 return 0;
139 static void drawfb(char *s, int x, int y, int w, int h)
141 fbval_t slice[1 << 14];
142 int i, j;
143 for (i = 0; i < h; i++) {
144 for (j = 0; j < w; j++) {
145 int c = *(unsigned char *) &s[i * w + j];
146 int r = (c & 0x3) << 6;
147 int g = ((c >> 2) & 0x7) << 5;
148 int b = ((c >> 5) & 0x7) << 5;
149 slice[j] = FB_VAL(r, g, b);
151 fb_set(y + i, x, slice, w);
155 static void xread(int fd, void *buf, int len)
157 int nr = 0;
158 int n;
159 while (nr < len && (n = read(fd, buf + nr, len - nr)) > 0)
160 nr += n;
161 if (nr < len) {
162 printf("partial vnc read!\n");
163 exit(1);
167 static int vnc_event(int fd)
169 struct vnc_rect uprect;
170 char msg[1 << 12];
171 struct vnc_server_fbup *fbup = (void *) msg;
172 struct vnc_server_cuttext *cuttext = (void *) msg;
173 struct vnc_server_colormap *colormap = (void *) msg;
174 int j;
175 int n;
177 if (read(fd, msg, 1) != 1)
178 return -1;
179 switch (msg[0]) {
180 case VNC_SERVER_FBUP:
181 xread(fd, msg + 1, sizeof(*fbup) - 1);
182 n = ntohs(fbup->n);
183 for (j = 0; j < n; j++) {
184 int x, y, w, h;
185 xread(fd, &uprect, sizeof(uprect));
186 x = ntohs(uprect.x);
187 y = ntohs(uprect.y);
188 w = ntohs(uprect.w);
189 h = ntohs(uprect.h);
190 if (x >= cols || x + w > cols)
191 return -1;
192 if (y >= rows || y + h > rows)
193 return -1;
194 xread(fd, buf, w * h);
195 drawfb(buf, x, y, w, h);
197 break;
198 case VNC_SERVER_BELL:
199 break;
200 case VNC_SERVER_CUTTEXT:
201 xread(fd, msg + 1, sizeof(*cuttext) - 1);
202 xread(fd, buf, ntohl(cuttext->len));
203 break;
204 case VNC_SERVER_COLORMAP:
205 xread(fd, msg + 1, sizeof(*colormap) - 1);
206 xread(fd, buf, ntohs(colormap->n) * 3 * 2);
207 break;
208 default:
209 fprintf(stderr, "unknown vnc msg: %d\n", msg[0]);
210 return -1;
212 return 0;
215 static int rat_event(int fd, int ratfd)
217 char ie[3];
218 struct vnc_client_ratevent me = {VNC_CLIENT_RATEVENT};
219 int mask = 0;
220 if (read(ratfd, &ie, sizeof(ie)) != 3)
221 return -1;
222 mc += ie[1];
223 mr -= ie[2];
224 mc = MAX(0, MIN(cols - 1, mc));
225 mr = MAX(0, MIN(rows - 1, mr));
226 if (ie[0] & 0x01)
227 mask |= VNC_BUTTON1_MASK;
228 if (ie[0] & 0x04)
229 mask |= VNC_BUTTON2_MASK;
230 if (ie[0] & 0x02)
231 mask |= VNC_BUTTON3_MASK;
232 me.y = htons(mr);
233 me.x = htons(mc);
234 me.mask = mask;
235 write(fd, &me, sizeof(me));
236 return 0;
239 static int press(int fd, int key, int down)
241 struct vnc_client_keyevent ke = {VNC_CLIENT_KEYEVENT};
242 ke.key = htonl(key);
243 ke.down = down;
244 return write(fd, &ke, sizeof(ke));
247 static int kbd_event(int fd, int kbdfd)
249 char key[1024];
250 int i, nr;
252 if ((nr = read(kbdfd, key, sizeof(key))) <= 0 )
253 return -1;
254 for (i = 0; i < nr; i++) {
255 int k = -1;
256 int mod[4];
257 int nmod = 0;
258 switch (key[i]) {
259 case 0x08:
260 case 0x7f:
261 k = 0xff08;
262 break;
263 case 0x09:
264 k = 0xff09;
265 break;
266 case 0x1b:
267 if (i + 2 < nr && key[i + 1] == '[') {
268 if (key[i + 2] == 'A')
269 k = 0xff52;
270 if (key[i + 2] == 'B')
271 k = 0xff54;
272 if (key[i + 2] == 'C')
273 k = 0xff53;
274 if (key[i + 2] == 'D')
275 k = 0xff51;
276 if (key[i + 2] == 'H')
277 k = 0xff50;
278 if (k > 0) {
279 i += 2;
280 break;
283 k = 0xff1b;
284 if (i + 1 < nr) {
285 mod[nmod++] = 0xffe9;
286 k = key[++i];
287 if (k == 0x03) /* esc-^C: quit */
288 return -1;
290 break;
291 case 0x0d:
292 k = 0xff0d;
293 break;
294 case 0x0c: /* ^L: redraw */
295 if (vnc_refresh(fd, 0))
296 return -1;
297 default:
298 k = (unsigned char) key[i];
300 if (k >= 'A' && k <= 'Z' || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
301 mod[nmod++] = 0xffe1;
302 if (k >= 1 && k <= 26) {
303 k = 'a' + k - 1;
304 mod[nmod++] = 0xffe3;
306 if (k > 0) {
307 int j;
308 for (j = 0; j < nmod; j++)
309 press(fd, mod[j], 1);
310 press(fd, k, 1);
311 press(fd, k, 0);
312 for (j = 0; j < nmod; j++)
313 press(fd, mod[j], 0);
316 return 0;
319 static void term_setup(struct termios *ti)
321 struct termios termios;
322 char *hide = "\x1b[?25l";
323 char *clear = "\x1b[2J\x1b[H";
324 char *msg = "\t\t\t*** fbvnc ***\r\n";
326 write(STDIN_FILENO, hide, strlen(hide));
327 write(STDOUT_FILENO, clear, strlen(clear));
328 write(STDOUT_FILENO, msg, strlen(msg));
329 tcgetattr (0, &termios);
330 *ti = termios;
331 cfmakeraw(&termios);
332 tcsetattr(0, TCSANOW, &termios);
335 static void term_cleanup(struct termios *ti)
337 char *show = "\x1b[?25h";
338 tcsetattr(0, TCSANOW, ti);
339 write(STDIN_FILENO, show, strlen(show));
342 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
344 struct pollfd ufds[3];
345 int pending = 0;
346 int err;
347 ufds[0].fd = kbd_fd;
348 ufds[0].events = POLLIN;
349 ufds[1].fd = vnc_fd;
350 ufds[1].events = POLLIN;
351 ufds[2].fd = rat_fd;
352 ufds[2].events = POLLIN;
353 if (vnc_refresh(vnc_fd, 0))
354 return;
355 while (1) {
356 err = poll(ufds, 3, 500);
357 if (err == -1 && errno != EINTR)
358 break;
359 if (!err)
360 continue;
361 if (ufds[0].revents & POLLIN)
362 if (kbd_event(vnc_fd, kbd_fd) == -1)
363 break;
364 if (ufds[1].revents & POLLIN) {
365 if (vnc_event(vnc_fd) == -1)
366 break;
367 pending = 0;
369 if (ufds[2].revents & POLLIN)
370 if (rat_event(vnc_fd, rat_fd) == -1)
371 break;
372 if (!pending++)
373 if (vnc_refresh(vnc_fd, 1))
374 break;
378 int main(int argc, char * argv[])
380 char *port = VNC_PORT;
381 char *host = "127.0.0.1";
382 struct termios ti;
383 int vnc_fd, rat_fd;
384 if (argc >= 2)
385 host = argv[1];
386 if (argc >= 3)
387 port = argv[2];
388 if ((vnc_fd = vnc_connect(host, port)) == -1) {
389 fprintf(stderr, "could not connect!\n");
390 return 1;
392 if (vnc_init(vnc_fd) == -1) {
393 fprintf(stderr, "vnc init failed!\n");
394 return 1;
396 term_setup(&ti);
397 rat_fd = open("/dev/input/mice", O_RDONLY);
399 mainloop(vnc_fd, 0, rat_fd);
401 term_cleanup(&ti);
402 vnc_free();
403 close(vnc_fd);
404 close(rat_fd);
405 return 0;