add shift modifier only if k is ascii
[fbvnc.git] / fbvnc.c
blob4a474650e5ee95fb071a57ee53341bba73b64ae3
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 <termios.h>
22 #include <unistd.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/types.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 static int vnc_connect(char *addr, char *port)
42 struct addrinfo hints, *addrinfo;
43 int fd;
45 memset(&hints, 0, sizeof(hints));
46 hints.ai_family = AF_UNSPEC;
47 hints.ai_socktype = SOCK_STREAM;
48 hints.ai_flags = AI_PASSIVE;
50 if (getaddrinfo(addr, port, &hints, &addrinfo))
51 return -1;
52 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
53 addrinfo->ai_protocol);
55 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) {
56 close(fd);
57 freeaddrinfo(addrinfo);
58 return -1;
60 freeaddrinfo(addrinfo);
61 return fd;
64 static int vnc_init(int fd)
66 char vncver[] = "RFB 003.003\n";
67 struct vnc_client_init clientinit;
68 struct vnc_server_init serverinit;
69 struct vnc_client_pixelfmt pixfmt_cmd;
70 int connstat = VNC_CONN_FAILED;
72 write(fd, vncver, 12);
73 read(fd, vncver, 12);
75 read(fd, &connstat, sizeof(connstat));
77 if (ntohl(connstat) != VNC_CONN_NOAUTH)
78 return -1;
80 clientinit.shared = 1;
81 write(fd, &clientinit, sizeof(clientinit));
82 read(fd, &serverinit, sizeof(serverinit));
84 fb_init();
85 cols = MIN(ntohs(serverinit.w), fb_cols());
86 rows = MIN(ntohs(serverinit.h), fb_rows());
87 mr = rows / 2;
88 mc = cols / 2;
90 read(fd, buf, ntohl(serverinit.len));
91 pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
92 pixfmt_cmd.format.bpp = 8;
93 pixfmt_cmd.format.depth = 8;
94 pixfmt_cmd.format.bigendian = 0;
95 pixfmt_cmd.format.truecolor = 1;
97 pixfmt_cmd.format.rmax = htons(3);
98 pixfmt_cmd.format.gmax = htons(7);
99 pixfmt_cmd.format.bmax = htons(7);
100 pixfmt_cmd.format.rshl = 0;
101 pixfmt_cmd.format.gshl = 2;
102 pixfmt_cmd.format.bshl = 5;
104 write(fd, &pixfmt_cmd, sizeof(pixfmt_cmd));
105 return fd;
108 static int vnc_free(void)
110 fb_free();
111 return 0;
114 static int vnc_refresh(int fd, int inc)
116 struct vnc_client_fbup fbup_req;
117 fbup_req.type = VNC_CLIENT_FBUP;
118 fbup_req.inc = inc;
119 fbup_req.x = htons(0);
120 fbup_req.y = htons(0);
121 fbup_req.w = htons(cols);
122 fbup_req.h = htons(rows);
123 write(fd, &fbup_req, sizeof(fbup_req));
124 return 0;
127 static void drawfb(char *s, int x, int y, int w, int h)
129 fbval_t slice[1 << 14];
130 int i, j;
131 for (i = 0; i < h; i++) {
132 for (j = 0; j < w; j++) {
133 int c = *(unsigned char *) &s[i * w + j];
134 int r = (c & 0x3) << 6;
135 int g = ((c >> 2) & 0x7) << 5;
136 int b = ((c >> 5) & 0x7) << 5;
137 slice[j] = fb_color(r, g, b);
139 fb_set(y + i, x, slice, w);
143 static void xread(int fd, void *buf, int len)
145 int nr = 0;
146 int n;
147 while (nr < len && (n = read(fd, buf + nr, len - nr)) > 0)
148 nr += n;
149 if (nr < len) {
150 printf("partial vnc read!\n");
151 exit(1);
155 static int vnc_event(int fd)
157 struct vnc_rect uprect;
158 char msg[1 << 12];
159 struct vnc_server_fbup *fbup = (void *) msg;
160 struct vnc_server_cuttext *cuttext = (void *) msg;
161 struct vnc_server_colormap *colormap = (void *) msg;
162 int j;
163 int n;
165 if (read(fd, msg, 1) != 1)
166 return -1;
167 switch (msg[0]) {
168 case VNC_SERVER_FBUP:
169 xread(fd, msg + 1, sizeof(*fbup) - 1);
170 n = ntohs(fbup->n);
171 for (j = 0; j < n; j++) {
172 int x, y, w, h;
173 xread(fd, &uprect, sizeof(uprect));
174 x = ntohs(uprect.x);
175 y = ntohs(uprect.y);
176 w = ntohs(uprect.w);
177 h = ntohs(uprect.h);
178 if (x >= cols || x + w > cols)
179 return -1;
180 if (y >= rows || y + h > rows)
181 return -1;
182 xread(fd, buf, w * h);
183 drawfb(buf, x, y, w, h);
185 break;
186 case VNC_SERVER_BELL:
187 break;
188 case VNC_SERVER_CUTTEXT:
189 xread(fd, msg + 1, sizeof(*cuttext) - 1);
190 xread(fd, buf, ntohl(cuttext->len));
191 break;
192 case VNC_SERVER_COLORMAP:
193 xread(fd, msg + 1, sizeof(*colormap) - 1);
194 xread(fd, buf, ntohs(colormap->n) * 3 * 2);
195 break;
196 default:
197 fprintf(stderr, "unknown vnc msg: %d\n", msg[0]);
198 return -1;
200 return 0;
203 static int rat_event(int fd, int ratfd)
205 char ie[3];
206 struct vnc_client_ratevent me = {VNC_CLIENT_RATEVENT};
207 int mask = 0;
208 if (read(ratfd, &ie, sizeof(ie)) != 3)
209 return -1;
210 mc += ie[1];
211 mr -= ie[2];
212 mc = MAX(0, MIN(cols - 1, mc));
213 mr = MAX(0, MIN(rows - 1, mr));
214 if (ie[0] & 0x01)
215 mask |= VNC_BUTTON1_MASK;
216 if (ie[0] & 0x04)
217 mask |= VNC_BUTTON2_MASK;
218 if (ie[0] & 0x02)
219 mask |= VNC_BUTTON3_MASK;
220 me.y = htons(mr);
221 me.x = htons(mc);
222 me.mask = mask;
223 write(fd, &me, sizeof(me));
224 return 0;
227 static int press(int fd, int key, int down)
229 struct vnc_client_keyevent ke = {VNC_CLIENT_KEYEVENT};
230 ke.key = htonl(key);
231 ke.down = down;
232 return write(fd, &ke, sizeof(ke));
235 static int kbd_event(int fd, int kbdfd)
237 char key[1024];
238 int i, nr;
240 if ((nr = read(kbdfd, key, sizeof(key))) <= 0 )
241 return -1;
242 for (i = 0; i < nr; i++) {
243 int k = -1;
244 int mod[4];
245 int nmod = 0;
246 switch (key[i]) {
247 case 0x08:
248 case 0x7f:
249 k = 0xff08;
250 break;
251 case 0x09:
252 k = 0xff09;
253 break;
254 case 0x1b:
255 if (i + 2 < nr && key[i + 1] == '[') {
256 if (key[i + 2] == 'A')
257 k = 0xff52;
258 if (key[i + 2] == 'B')
259 k = 0xff54;
260 if (key[i + 2] == 'C')
261 k = 0xff53;
262 if (key[i + 2] == 'D')
263 k = 0xff51;
264 if (key[i + 2] == 'H')
265 k = 0xff50;
266 if (k > 0) {
267 i += 2;
268 break;
271 k = 0xff1b;
272 if (i + 1 < nr) {
273 mod[nmod++] = 0xffe9;
274 k = key[++i];
275 if (k == 0x03) /* esc-^C: quit */
276 return -1;
278 break;
279 case 0x0d:
280 k = 0xff0d;
281 break;
282 case 0x0c: /* ^L: redraw */
283 if (vnc_refresh(fd, 0))
284 return -1;
285 default:
286 k = (unsigned char) key[i];
288 if (k >= 'A' && k <= 'Z' || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
289 mod[nmod++] = 0xffe1;
290 if (k >= 1 && k <= 26) {
291 k = 'a' + k - 1;
292 mod[nmod++] = 0xffe3;
294 if (k > 0) {
295 int j;
296 for (j = 0; j < nmod; j++)
297 press(fd, mod[j], 1);
298 press(fd, k, 1);
299 press(fd, k, 0);
300 for (j = 0; j < nmod; j++)
301 press(fd, mod[j], 0);
304 return 0;
307 static void term_setup(struct termios *ti)
309 struct termios termios;
310 char *hide = "\x1b[?25l";
311 char *clear = "\x1b[2J\x1b[H";
312 char *msg = "\t\t\t*** fbvnc ***\r\n";
314 write(STDIN_FILENO, hide, strlen(hide));
315 write(STDOUT_FILENO, clear, strlen(clear));
316 write(STDOUT_FILENO, msg, strlen(msg));
317 tcgetattr (0, &termios);
318 *ti = termios;
319 cfmakeraw(&termios);
320 tcsetattr(0, TCSANOW, &termios);
323 static void term_cleanup(struct termios *ti)
325 char *show = "\x1b[?25h";
326 tcsetattr(0, TCSANOW, ti);
327 write(STDIN_FILENO, show, strlen(show));
330 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
332 struct pollfd ufds[3];
333 int pending = 0;
334 int err;
335 ufds[0].fd = kbd_fd;
336 ufds[0].events = POLLIN;
337 ufds[1].fd = vnc_fd;
338 ufds[1].events = POLLIN;
339 ufds[2].fd = rat_fd;
340 ufds[2].events = POLLIN;
341 if (vnc_refresh(vnc_fd, 0))
342 return;
343 while (1) {
344 err = poll(ufds, 3, 500);
345 if (err == -1 && errno != EINTR)
346 break;
347 if (!err)
348 continue;
349 if (ufds[0].revents & POLLIN)
350 if (kbd_event(vnc_fd, kbd_fd) == -1)
351 break;
352 if (ufds[1].revents & POLLIN) {
353 if (vnc_event(vnc_fd) == -1)
354 break;
355 pending = 0;
357 if (ufds[2].revents & POLLIN)
358 if (rat_event(vnc_fd, rat_fd) == -1)
359 break;
360 if (!pending++)
361 if (vnc_refresh(vnc_fd, 1))
362 break;
366 int main(int argc, char * argv[])
368 char *port = VNC_PORT;
369 char *host = "127.0.0.1";
370 struct termios ti;
371 int vnc_fd, rat_fd;
372 if (argc >= 2)
373 host = argv[1];
374 if (argc >= 3)
375 port = argv[2];
376 if ((vnc_fd = vnc_connect(host, port)) == -1) {
377 fprintf(stderr, "could not connect!\n");
378 return 1;
380 if (vnc_init(vnc_fd) == -1) {
381 fprintf(stderr, "vnc init failed!\n");
382 return 1;
384 term_setup(&ti);
385 rat_fd = open("/dev/input/mice", O_RDONLY);
387 mainloop(vnc_fd, 0, rat_fd);
389 term_cleanup(&ti);
390 vnc_free();
391 close(vnc_fd);
392 close(rat_fd);
393 return 0;