fail if update rect cannot be read
[fbvnc.git] / fbvnc.c
blobe311d1646421b0180053691bd0c93a82fdd2a12b
1 /*
2 * fbvnc - a small linux framebuffer vnc viewer
4 * Copyright (C) 2009-2010 Ali Gholami Rudi
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License, as published by the
8 * Free Software Foundation.
9 */
10 #include <arpa/inet.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <netdb.h>
15 #include <netinet/in.h>
16 #include <poll.h>
17 #include <pwd.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <termios.h>
26 #include <linux/input.h>
27 #include "draw.h"
28 #include "vnc.h"
30 #define VNC_PORT 5900
32 #define MAXRES (1 << 21)
33 #define MIN(a, b) ((a) < (b) ? (a) : (b))
35 static int cols, rows;
36 static int mr, mc; /* mouse position */
38 static char buf[MAXRES];
40 int vnc_init(char *addr, int port)
42 struct sockaddr_in si;
43 char vncver[] = "RFB 003.003\n";
44 struct vnc_client_init clientinit;
45 struct vnc_server_init serverinit;
46 struct vnc_client_pixelfmt pixfmt_cmd;
47 struct hostent *he;
48 int servsock;
49 int connstat = VNC_CONN_FAILED;
51 si.sin_family = AF_INET;
52 si.sin_port = htons(port);
53 he = gethostbyname(addr);
54 if (he) {
55 si.sin_addr.s_addr = *((unsigned long *)(he->h_addr));
56 } else if (inet_aton(addr, &(si.sin_addr)) < 0) {
57 fprintf(stderr, "cannot resolve hostname");
58 return -1;
61 servsock = socket(PF_INET, SOCK_STREAM, 0);
62 if (servsock == -1) {
63 perror("Cannot create socket");
64 return -1;
66 if (connect(servsock, (void *) &si, sizeof(si)) < 0) {
67 perror("cannot connect");
68 close(servsock);
69 return -1;
71 write(servsock, vncver, 12);
72 read(servsock, vncver, 12);
74 read(servsock, &connstat, sizeof(connstat));
76 switch (ntohl(connstat)) {
77 case VNC_CONN_FAILED:
78 puts("remote server says: connection failed");
79 close(servsock);
80 return -1;
81 case VNC_CONN_NOAUTH:
82 break;
83 case VNC_CONN_AUTH:
84 puts("we don't support DES yet");
85 close(servsock);
86 return -1;
89 clientinit.shared = 1;
90 write(servsock, &clientinit, sizeof(clientinit));
91 read(servsock, &serverinit, sizeof(serverinit));
93 fb_init();
94 cols = MIN(ntohs(serverinit.w), fb_cols());
95 rows = MIN(ntohs(serverinit.h), fb_rows());
96 mr = rows / 2;
97 mc = cols / 2;
99 read(servsock, buf, ntohl(serverinit.len));
100 pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
101 pixfmt_cmd.format.bpp = 8;
102 pixfmt_cmd.format.depth = 8;
103 pixfmt_cmd.format.bigendian = 0;
104 pixfmt_cmd.format.truecolor = 1;
106 pixfmt_cmd.format.rmax = htons(3);
107 pixfmt_cmd.format.gmax = htons(7);
108 pixfmt_cmd.format.bmax = htons(7);
109 pixfmt_cmd.format.rshl = 0;
110 pixfmt_cmd.format.gshl = 2;
111 pixfmt_cmd.format.bshl = 5;
113 write(servsock, &pixfmt_cmd, sizeof(pixfmt_cmd));
114 return servsock;
117 int vnc_free(void)
119 fb_free();
120 return 0;
123 int vnc_refresh(int fd, int inc)
125 struct vnc_client_fbup fbup_req;
126 fbup_req.type = VNC_CLIENT_FBUP;
127 fbup_req.inc = inc;
128 fbup_req.x = htons(0);
129 fbup_req.y = htons(0);
130 fbup_req.w = htons(cols);
131 fbup_req.h = htons(rows);
132 write(fd, &fbup_req, sizeof(fbup_req));
133 return 0;
136 static void drawfb(char *s, int x, int y, int w, int h)
138 fbval_t slice[1 << 14];
139 int i, j;
140 for (i = 0; i < h; i++) {
141 for (j = 0; j < w; j++) {
142 unsigned char *p = (void *) &s[i * w + j];
143 slice[j] = fb_color(*p, *p, *p);
145 fb_set(y + i, x, slice, w);
149 int vnc_event(int fd)
151 struct vnc_rect uprect;
152 char msg[1 << 12];
153 struct vnc_server_fbup *fbup = (void *) msg;
154 struct vnc_server_cuttext *cuttext = (void *) msg;
155 struct vnc_server_colormap *colormap = (void *) msg;
156 int nr, i, j;
157 int n;
159 if ((nr = read(fd, msg, 1)) != 1)
160 return -1;
162 switch (msg[0]) {
163 case VNC_SERVER_FBUP:
164 nr = read(fd, msg + 1, sizeof(*fbup) - 1);
165 n = ntohs(fbup->n);
166 for (j = 0; j < n; j++) {
167 int x, y, w, h;
168 nr = read(fd, &uprect, sizeof(uprect));
169 if (nr != sizeof(uprect))
170 return -1;
171 x = ntohs(uprect.x);
172 y = ntohs(uprect.y);
173 w = ntohs(uprect.w);
174 h = ntohs(uprect.h);
175 if (x >= cols || x + w > cols)
176 return -1;
177 if (y >= rows || y + h > rows)
178 return -1;
179 for (i = 0; i < w * h; ) {
180 nr = read(fd, buf + i, w * h - i);
181 if (nr <= 0)
182 return -1;
183 i += nr;
185 drawfb(buf, x, y, w, h);
187 break;
188 case VNC_SERVER_BELL:
189 break;
190 case VNC_SERVER_CUTTEXT:
191 nr = read(fd, msg + 1, sizeof(*cuttext) - 1);
192 nr = read(fd, buf, cuttext->len);
193 break;
194 case VNC_SERVER_COLORMAP:
195 nr = read(fd, msg + 1, sizeof(*colormap) - 1);
196 nr = read(fd, buf, ntohs(colormap->n) * 3 * 2);
197 break;
198 default:
199 printf("unknown msg: %d\n", msg[0]);
200 return -1;
202 return 0;
205 int rat_event(int fd, int ratfd)
207 char ie[3];
208 struct vnc_client_ratevent me = {VNC_CLIENT_RATEVENT};
209 int mask = 0;
210 if (read(ratfd, &ie, sizeof(ie)) != 3)
211 return -1;
212 mc += ie[1];
213 mr -= ie[2];
214 mc = MAX(0, MIN(cols - 1, mc));
215 mr = MAX(0, MIN(rows - 1, mr));
216 if (ie[0] & 0x01)
217 mask |= VNC_BUTTON1_MASK;
218 if (ie[0] & 0x02)
219 mask |= VNC_BUTTON1_MASK;
220 if (ie[0] & 0x04)
221 mask |= VNC_BUTTON1_MASK;
222 me.y = htons(mr);
223 me.x = htons(mc);
224 me.mask = mask;
225 write(fd, &me, sizeof(me));
226 return 0;
229 static int press(int fd, int key, int down)
231 struct vnc_client_keyevent ke = {VNC_CLIENT_KEYEVENT};
232 ke.key = htonl(key);
233 ke.down = down;
234 return write(fd, &ke, sizeof(ke));
237 int kbd_event(int fd, int kbdfd)
239 char msg[1024];
240 int i, j;
241 int mod = 0;
243 if ((j = read(kbdfd, msg, sizeof(msg))) <= 0 )
244 return -1;
245 for (i = 0; i < j; i++) {
246 int k = -1;
247 switch (msg[i]) {
248 case 0x08:
249 case 0x7f:
250 k = 0xff08;
251 break;
252 case 0x09:
253 k = 0xff09;
254 break;
255 case 0x1b:
256 k = 0xff1b;
257 break;
258 case 0x0d:
259 k = 0xff0d;
260 break;
261 case 0x03:
262 return -1;
263 default:
264 k = (unsigned char) msg[i];
266 if (isupper(k) || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
267 mod = 0xffe1;
268 if (k >= 1 && k <= 26) {
269 k = 'a' + k - 1;
270 mod = 0xffe3;
272 if (k > 0) {
273 if (mod)
274 press(fd, mod, 1);
275 press(fd, k, 1);
276 press(fd, k, 0);
277 if (mod)
278 press(fd, mod, 0);
281 return 0;
284 static void term_setup(struct termios *ti)
286 struct termios termios;
287 char *hide = "\x1b[?25l";
288 char *clear = "\x1b[2J\x1b[H";
289 char *msg = "\t\t\t*** fbvnc ***\r\n";
291 write(STDIN_FILENO, hide, strlen(hide));
292 write(STDOUT_FILENO, clear, strlen(clear));
293 write(STDOUT_FILENO, msg, strlen(msg));
294 tcgetattr (0, &termios);
295 *ti = termios;
296 cfmakeraw(&termios);
297 tcsetattr(0, TCSANOW, &termios);
300 static void term_cleanup(struct termios *ti)
302 char *show = "\x1b[?25h";
303 tcsetattr(0, TCSANOW, ti);
304 write(STDIN_FILENO, show, strlen(show));
307 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
309 struct pollfd ufds[3];
310 int pending = 0;
311 int update = 1;
312 int err;
313 ufds[0].fd = kbd_fd;
314 ufds[0].events = POLLIN;
315 ufds[1].fd = vnc_fd;
316 ufds[1].events = POLLIN;
317 ufds[2].fd = rat_fd;
318 ufds[2].events = POLLIN;
319 while (1) {
320 if (update && !pending) {
321 if (vnc_refresh(vnc_fd, 1) == -1)
322 break;
323 pending = 1;
324 update = 0;
326 err = poll(ufds, 3, 500);
327 if (err == -1 && errno != EINTR)
328 break;
329 if (!err)
330 continue;
331 if (ufds[0].revents & POLLIN) {
332 if (kbd_event(vnc_fd, kbd_fd) == -1)
333 break;
334 update = 1;
336 if (ufds[1].revents & POLLIN) {
337 if (vnc_event(vnc_fd) == -1)
338 break;
339 pending = 0;
341 if (ufds[2].revents & POLLIN) {
342 if (rat_event(vnc_fd, rat_fd) == -1)
343 break;
344 update = 1;
349 int main(int argc, char * argv[])
351 int port = VNC_PORT;
352 char *host = "127.0.0.1";
353 struct termios ti;
354 int vnc_fd, rat_fd;
355 if (argc >= 2)
356 host = argv[1];
357 if (argc >= 3)
358 port = atoi(argv[2]);
359 if ((vnc_fd = vnc_init(host, port)) == -1)
360 return -1;
361 term_setup(&ti);
362 rat_fd = open("/dev/input/mice", O_RDONLY);
364 mainloop(vnc_fd, 0, rat_fd);
366 term_cleanup(&ti);
367 vnc_free();
368 close(vnc_fd);
369 close(rat_fd);
370 return 0;