let c-space stop/start drawing on the framebuffer
[fbvnc.git] / fbvnc.c
blob7267168b5c4faded3c2b656e58821143f9076048
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 */
44 static int nodraw; /* don't draw anything */
46 static char buf[MAXRES];
48 static int vnc_connect(char *addr, char *port)
50 struct addrinfo hints, *addrinfo;
51 int fd;
53 memset(&hints, 0, sizeof(hints));
54 hints.ai_family = AF_UNSPEC;
55 hints.ai_socktype = SOCK_STREAM;
56 hints.ai_flags = AI_PASSIVE;
58 if (getaddrinfo(addr, port, &hints, &addrinfo))
59 return -1;
60 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
61 addrinfo->ai_protocol);
63 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) {
64 close(fd);
65 freeaddrinfo(addrinfo);
66 return -1;
68 freeaddrinfo(addrinfo);
69 return fd;
72 static int vnc_init(int fd)
74 char vncver[] = "RFB 003.003\n";
75 struct vnc_client_init clientinit;
76 struct vnc_server_init serverinit;
77 struct vnc_client_pixelfmt pixfmt_cmd;
78 int connstat = VNC_CONN_FAILED;
80 write(fd, vncver, 12);
81 read(fd, vncver, 12);
83 read(fd, &connstat, sizeof(connstat));
85 if (ntohl(connstat) != VNC_CONN_NOAUTH)
86 return -1;
88 clientinit.shared = 1;
89 write(fd, &clientinit, sizeof(clientinit));
90 read(fd, &serverinit, sizeof(serverinit));
92 if (fb_init())
93 return -1;
94 if (FBM_BPP(fb_mode()) != sizeof(fbval_t)) {
95 fprintf(stderr, "fbvnc: fbval_t doesn't match fb depth\n");
96 exit(1);
98 cols = MIN(ntohs(serverinit.w), fb_cols());
99 rows = MIN(ntohs(serverinit.h), fb_rows());
100 mr = rows / 2;
101 mc = cols / 2;
103 read(fd, buf, ntohl(serverinit.len));
104 pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
105 pixfmt_cmd.format.bpp = 8;
106 pixfmt_cmd.format.depth = 8;
107 pixfmt_cmd.format.bigendian = 0;
108 pixfmt_cmd.format.truecolor = 1;
110 pixfmt_cmd.format.rmax = htons(3);
111 pixfmt_cmd.format.gmax = htons(7);
112 pixfmt_cmd.format.bmax = htons(7);
113 pixfmt_cmd.format.rshl = 0;
114 pixfmt_cmd.format.gshl = 2;
115 pixfmt_cmd.format.bshl = 5;
117 write(fd, &pixfmt_cmd, sizeof(pixfmt_cmd));
118 return fd;
121 static int vnc_free(void)
123 fb_free();
124 return 0;
127 static int vnc_refresh(int fd, int inc)
129 struct vnc_client_fbup fbup_req;
130 fbup_req.type = VNC_CLIENT_FBUP;
131 fbup_req.inc = inc;
132 fbup_req.x = htons(0);
133 fbup_req.y = htons(0);
134 fbup_req.w = htons(cols);
135 fbup_req.h = htons(rows);
136 write(fd, &fbup_req, sizeof(fbup_req));
137 return 0;
140 static void drawfb(char *s, int x, int y, int w, int h)
142 fbval_t slice[1 << 14];
143 int i, j;
144 for (i = 0; i < h; i++) {
145 for (j = 0; j < w; j++) {
146 int c = *(unsigned char *) &s[i * w + j];
147 int r = (c & 0x3) << 6;
148 int g = ((c >> 2) & 0x7) << 5;
149 int b = ((c >> 5) & 0x7) << 5;
150 slice[j] = FB_VAL(r, g, b);
152 fb_set(y + i, x, slice, w);
156 static void xread(int fd, void *buf, int len)
158 int nr = 0;
159 int n;
160 while (nr < len && (n = read(fd, buf + nr, len - nr)) > 0)
161 nr += n;
162 if (nr < len) {
163 printf("partial vnc read!\n");
164 exit(1);
168 static int vnc_event(int fd)
170 struct vnc_rect uprect;
171 char msg[1 << 12];
172 struct vnc_server_fbup *fbup = (void *) msg;
173 struct vnc_server_cuttext *cuttext = (void *) msg;
174 struct vnc_server_colormap *colormap = (void *) msg;
175 int j;
176 int n;
178 if (read(fd, msg, 1) != 1)
179 return -1;
180 switch (msg[0]) {
181 case VNC_SERVER_FBUP:
182 xread(fd, msg + 1, sizeof(*fbup) - 1);
183 n = ntohs(fbup->n);
184 for (j = 0; j < n; j++) {
185 int x, y, w, h;
186 xread(fd, &uprect, sizeof(uprect));
187 x = ntohs(uprect.x);
188 y = ntohs(uprect.y);
189 w = ntohs(uprect.w);
190 h = ntohs(uprect.h);
191 if (x >= cols || x + w > cols)
192 return -1;
193 if (y >= rows || y + h > rows)
194 return -1;
195 xread(fd, buf, w * h);
196 if (!nodraw)
197 drawfb(buf, x, y, w, h);
199 break;
200 case VNC_SERVER_BELL:
201 break;
202 case VNC_SERVER_CUTTEXT:
203 xread(fd, msg + 1, sizeof(*cuttext) - 1);
204 xread(fd, buf, ntohl(cuttext->len));
205 break;
206 case VNC_SERVER_COLORMAP:
207 xread(fd, msg + 1, sizeof(*colormap) - 1);
208 xread(fd, buf, ntohs(colormap->n) * 3 * 2);
209 break;
210 default:
211 fprintf(stderr, "unknown vnc msg: %d\n", msg[0]);
212 return -1;
214 return 0;
217 static int rat_event(int fd, int ratfd)
219 char ie[3];
220 struct vnc_client_ratevent me = {VNC_CLIENT_RATEVENT};
221 int mask = 0;
222 if (read(ratfd, &ie, sizeof(ie)) != 3)
223 return -1;
224 mc += ie[1];
225 mr -= ie[2];
226 mc = MAX(0, MIN(cols - 1, mc));
227 mr = MAX(0, MIN(rows - 1, mr));
228 if (ie[0] & 0x01)
229 mask |= VNC_BUTTON1_MASK;
230 if (ie[0] & 0x04)
231 mask |= VNC_BUTTON2_MASK;
232 if (ie[0] & 0x02)
233 mask |= VNC_BUTTON3_MASK;
234 me.y = htons(mr);
235 me.x = htons(mc);
236 me.mask = mask;
237 write(fd, &me, sizeof(me));
238 return 0;
241 static int press(int fd, int key, int down)
243 struct vnc_client_keyevent ke = {VNC_CLIENT_KEYEVENT};
244 ke.key = htonl(key);
245 ke.down = down;
246 return write(fd, &ke, sizeof(ke));
249 static void showmsg(void)
251 char *msg = "\x1b[H\t\t\t*** fbvnc ***\r";
252 write(STDOUT_FILENO, msg, strlen(msg));
255 static int kbd_event(int fd, int kbdfd)
257 char key[1024];
258 int i, nr;
260 if ((nr = read(kbdfd, key, sizeof(key))) <= 0 )
261 return -1;
262 for (i = 0; i < nr; i++) {
263 int k = -1;
264 int mod[4];
265 int nmod = 0;
266 switch (key[i]) {
267 case 0x08:
268 case 0x7f:
269 k = 0xff08;
270 break;
271 case 0x09:
272 k = 0xff09;
273 break;
274 case 0x1b:
275 if (i + 2 < nr && key[i + 1] == '[') {
276 if (key[i + 2] == 'A')
277 k = 0xff52;
278 if (key[i + 2] == 'B')
279 k = 0xff54;
280 if (key[i + 2] == 'C')
281 k = 0xff53;
282 if (key[i + 2] == 'D')
283 k = 0xff51;
284 if (key[i + 2] == 'H')
285 k = 0xff50;
286 if (k > 0) {
287 i += 2;
288 break;
291 k = 0xff1b;
292 if (i + 1 < nr) {
293 mod[nmod++] = 0xffe9;
294 k = key[++i];
295 if (k == 0x03) /* esc-^C: quit */
296 return -1;
298 break;
299 case 0x0d:
300 k = 0xff0d;
301 break;
302 case 0x0: /* c-space: stop/start drawing */
303 if (!nodraw) {
304 nodraw = 1;
305 showmsg();
306 } else {
307 nodraw = 0;
308 if (vnc_refresh(fd, 0))
309 return -1;
311 default:
312 k = (unsigned char) key[i];
314 if (k >= 'A' && k <= 'Z' || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
315 mod[nmod++] = 0xffe1;
316 if (k >= 1 && k <= 26) {
317 k = 'a' + k - 1;
318 mod[nmod++] = 0xffe3;
320 if (k > 0) {
321 int j;
322 for (j = 0; j < nmod; j++)
323 press(fd, mod[j], 1);
324 press(fd, k, 1);
325 press(fd, k, 0);
326 for (j = 0; j < nmod; j++)
327 press(fd, mod[j], 0);
330 return 0;
333 static void term_setup(struct termios *ti)
335 struct termios termios;
336 char *hide = "\x1b[?25l";
337 char *clear = "\x1b[2J";
339 write(STDIN_FILENO, hide, strlen(hide));
340 write(STDOUT_FILENO, clear, strlen(clear));
341 showmsg();
342 tcgetattr(0, &termios);
343 *ti = termios;
344 cfmakeraw(&termios);
345 tcsetattr(0, TCSANOW, &termios);
348 static void term_cleanup(struct termios *ti)
350 char *show = "\x1b[?25h";
351 tcsetattr(0, TCSANOW, ti);
352 write(STDIN_FILENO, show, strlen(show));
355 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
357 struct pollfd ufds[3];
358 int pending = 0;
359 int err;
360 ufds[0].fd = kbd_fd;
361 ufds[0].events = POLLIN;
362 ufds[1].fd = vnc_fd;
363 ufds[1].events = POLLIN;
364 ufds[2].fd = rat_fd;
365 ufds[2].events = POLLIN;
366 if (vnc_refresh(vnc_fd, 0))
367 return;
368 while (1) {
369 err = poll(ufds, 3, 500);
370 if (err == -1 && errno != EINTR)
371 break;
372 if (!err)
373 continue;
374 if (ufds[0].revents & POLLIN)
375 if (kbd_event(vnc_fd, kbd_fd) == -1)
376 break;
377 if (ufds[1].revents & POLLIN) {
378 if (vnc_event(vnc_fd) == -1)
379 break;
380 pending = 0;
382 if (ufds[2].revents & POLLIN)
383 if (rat_event(vnc_fd, rat_fd) == -1)
384 break;
385 if (!pending++)
386 if (vnc_refresh(vnc_fd, 1))
387 break;
391 int main(int argc, char * argv[])
393 char *port = VNC_PORT;
394 char *host = "127.0.0.1";
395 struct termios ti;
396 int vnc_fd, rat_fd;
397 if (argc >= 2)
398 host = argv[1];
399 if (argc >= 3)
400 port = argv[2];
401 if ((vnc_fd = vnc_connect(host, port)) == -1) {
402 fprintf(stderr, "could not connect!\n");
403 return 1;
405 if (vnc_init(vnc_fd) == -1) {
406 fprintf(stderr, "vnc init failed!\n");
407 return 1;
409 term_setup(&ti);
410 rat_fd = open("/dev/input/mice", O_RDONLY);
412 mainloop(vnc_fd, 0, rat_fd);
414 term_cleanup(&ti);
415 vnc_free();
416 close(vnc_fd);
417 close(rat_fd);
418 return 0;