major rewrite; real mouse supported and no cmd key anymore
[fbvnc.git] / fbvnc.c
blob32f190e000496f4ae0c3b831bb558820f304852f
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 MAXRES (1 << 21)
31 #define MIN(a, b) ((a) < (b) ? (a) : (b))
33 static int cols, rows;
34 static int mr, mc; /* mouse position */
36 static char buf[MAXRES];
38 int vnc_init(char *addr, int port)
40 struct sockaddr_in si;
41 char vncver[] = "RFB 003.003\n";
42 struct vnc_client_init clientinit;
43 struct vnc_server_init serverinit;
44 struct vnc_client_pixelfmt pixfmt_cmd;
45 struct hostent *he;
46 int servsock;
47 int connstat = VNC_CONN_FAILED;
49 si.sin_family = AF_INET;
50 si.sin_port = htons(port);
51 he = gethostbyname(addr);
52 if (he) {
53 si.sin_addr.s_addr = *((unsigned long *)(he->h_addr));
54 } else if (inet_aton(addr, &(si.sin_addr)) < 0) {
55 fprintf(stderr, "cannot resolve hostname");
56 return -1;
59 servsock = socket(PF_INET, SOCK_STREAM, 0);
60 if (servsock == -1) {
61 perror("Cannot create socket");
62 return -1;
64 if (connect(servsock, (void *) &si, sizeof(si)) < 0) {
65 perror("cannot connect");
66 close(servsock);
67 return -1;
69 write(servsock, vncver, 12);
70 read(servsock, vncver, 12);
72 read(servsock, &connstat, sizeof(connstat));
74 switch (ntohl(connstat)) {
75 case VNC_CONN_FAILED:
76 puts("remote server says: connection failed");
77 close(servsock);
78 return -1;
79 case VNC_CONN_NOAUTH:
80 break;
81 case VNC_CONN_AUTH:
82 puts("we don't support DES yet");
83 close(servsock);
84 return -1;
87 clientinit.shared = 1;
88 write(servsock, &clientinit, sizeof(clientinit));
89 read(servsock, &serverinit, sizeof(serverinit));
91 fb_init();
92 cols = MIN(ntohs(serverinit.w), fb_cols());
93 rows = MIN(ntohs(serverinit.h), fb_rows());
94 mr = rows / 2;
95 mc = cols / 2;
97 read(servsock, buf, ntohl(serverinit.len));
98 pixfmt_cmd.type = VNC_CLIENT_PIXFMT;
99 pixfmt_cmd.format.bpp = 8;
100 pixfmt_cmd.format.depth = 8;
101 pixfmt_cmd.format.bigendian = 0;
102 pixfmt_cmd.format.truecolor = 1;
104 pixfmt_cmd.format.rmax = htons(3);
105 pixfmt_cmd.format.gmax = htons(7);
106 pixfmt_cmd.format.bmax = htons(7);
107 pixfmt_cmd.format.rshl = 0;
108 pixfmt_cmd.format.gshl = 2;
109 pixfmt_cmd.format.bshl = 5;
111 write(servsock, &pixfmt_cmd, sizeof(pixfmt_cmd));
112 return servsock;
115 int vnc_free(void)
117 fb_free();
118 return 0;
121 int vnc_refresh(int fd, int inc)
123 struct vnc_client_fbup fbup_req;
124 fbup_req.type = VNC_CLIENT_FBUP;
125 fbup_req.inc = inc;
126 fbup_req.x = htons(0);
127 fbup_req.y = htons(0);
128 fbup_req.w = htons(cols);
129 fbup_req.h = htons(rows);
130 write(fd, &fbup_req, sizeof(fbup_req));
131 return 0;
134 static void drawfb(char *s, int x, int y, int w, int h)
136 fbval_t slice[1 << 14];
137 int i, j;
138 for (i = 0; i < h; i++) {
139 for (j = 0; j < w; j++) {
140 unsigned char *p = (void *) &s[i * w + j];
141 slice[j] = fb_color(*p, *p, *p);
143 fb_set(y + i, x, slice, w);
147 int vnc_event(int fd)
149 struct vnc_rect uprect;
150 char msg[1 << 12];
151 struct vnc_server_fbup *fbup = (void *) msg;
152 struct vnc_server_cuttext *cuttext = (void *) msg;
153 struct vnc_server_colormap *colormap = (void *) msg;
154 int nr, i, j;
155 int n;
157 if ((nr = read(fd, msg, 1)) != 1)
158 return -1;
160 switch (msg[0]) {
161 case VNC_SERVER_FBUP:
162 nr = read(fd, msg + 1, sizeof(*fbup) - 1);
163 n = ntohs(fbup->n);
164 for (j = 0; j < n; j++) {
165 int x, y, w, h;
166 nr = read(fd, &uprect, sizeof(uprect));
167 if (nr != sizeof(uprect))
168 return -1;
169 x = ntohs(uprect.x);
170 y = ntohs(uprect.y);
171 w = ntohs(uprect.w);
172 h = ntohs(uprect.h);
173 if (x >= cols || x + w > cols)
174 return -1;
175 if (y >= rows || y + h > rows)
176 return -1;
177 for (i = 0; i < w * h; ) {
178 nr = read(fd, buf + i, w * h - i);
179 if (nr == -1)
180 return 0;
181 i += nr;
183 drawfb(buf, x, y, w, h);
185 break;
186 case VNC_SERVER_BELL:
187 break;
188 case VNC_SERVER_CUTTEXT:
189 nr = read(fd, msg + 1, sizeof(*cuttext) - 1);
190 nr = read(fd, buf, cuttext->len);
191 break;
192 case VNC_SERVER_COLORMAP:
193 nr = read(fd, msg + 1, sizeof(*colormap) - 1);
194 nr = read(fd, buf, ntohs(colormap->n) * 3 * 2);
195 break;
196 default:
197 printf("unknown msg: %d\n", msg[0]);
198 return -1;
200 return 0;
203 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] & 0x02)
217 mask |= VNC_BUTTON1_MASK;
218 if (ie[0] & 0x04)
219 mask |= VNC_BUTTON1_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 int kbd_event(int fd, int kbdfd)
237 char msg[1024];
238 int i, j;
239 int mod = 0;
241 if ((j = read(kbdfd, msg, sizeof(msg))) <= 0 )
242 return -1;
243 for (i = 0; i < j; i++) {
244 int k = -1;
245 switch (msg[i]) {
246 case 0x08:
247 case 0x7f:
248 k = 0xff08;
249 break;
250 case 0x09:
251 k = 0xff09;
252 break;
253 case 0x1b:
254 k = 0xff1b;
255 break;
256 case 0x0d:
257 k = 0xff0d;
258 break;
259 case 0x03:
260 return -1;
261 default:
262 k = (unsigned char) msg[i];
264 if (isupper(k) || strchr(":\"<>?{}|+_()*&^%$#@!~", k))
265 mod = 0xffe1;
266 if (k >= 1 && k <= 26) {
267 k = 'a' + k - 1;
268 mod = 0xffe3;
270 if (k > 0) {
271 if (mod)
272 press(fd, mod, 1);
273 press(fd, k, 1);
274 press(fd, k, 0);
275 if (mod)
276 press(fd, mod, 0);
279 return 0;
282 static void term_setup(struct termios *ti)
284 struct termios termios;
285 char *hide = "\x1b[?25l";
286 char *clear = "\x1b[2J\x1b[H";
287 char *msg = "\t\t\t*** fbvnc ***\r\n";
289 write(STDIN_FILENO, hide, strlen(hide));
290 write(STDOUT_FILENO, clear, strlen(clear));
291 write(STDOUT_FILENO, msg, strlen(msg));
292 tcgetattr (0, &termios);
293 *ti = termios;
294 cfmakeraw(&termios);
295 tcsetattr(0, TCSANOW, &termios);
298 static void term_cleanup(struct termios *ti)
300 char *show = "\x1b[?25h";
301 tcsetattr(0, TCSANOW, ti);
302 write(STDIN_FILENO, show, strlen(show));
305 static void mainloop(int vnc_fd, int kbd_fd, int rat_fd)
307 struct pollfd ufds[3];
308 int pending = 0;
309 int retval = 0;
310 int update = 1;
311 int err;
312 ufds[0].fd = kbd_fd;
313 ufds[0].events = POLLIN;
314 ufds[1].fd = vnc_fd;
315 ufds[1].events = POLLIN;
316 ufds[2].fd = rat_fd;
317 ufds[2].events = POLLIN;
318 while(1) {
319 if ((update || !retval) && !pending) {
320 if (vnc_refresh(vnc_fd, 1) == -1)
321 break;
322 pending = 1;
323 update = 0;
325 err = poll(ufds, 3, 500);
326 if (err == -1 && errno != EINTR)
327 break;
328 if (!err)
329 continue;
330 if (ufds[0].revents & POLLIN) {
331 if (kbd_event(vnc_fd, kbd_fd) == -1)
332 break;
333 update = 1;
335 if (ufds[1].revents & POLLIN) {
336 if (vnc_event(vnc_fd) == -1)
337 break;
338 pending = 0;
340 if (ufds[2].revents & POLLIN) {
341 if (rat_event(vnc_fd, rat_fd) == -1)
342 break;
343 update = 1;
348 #define VNC_PORT 5900
350 int main(int argc, char * argv[])
352 int port = VNC_PORT;
353 char *host = "127.0.0.1";
354 struct termios ti;
355 int vnc_fd, rat_fd;
356 if (argc >= 2)
357 host = argv[1];
358 if (argc >= 3)
359 port = atoi(argv[2]);
360 if ((vnc_fd = vnc_init(host, port)) == -1)
361 return -1;
362 term_setup(&ti);
363 rat_fd = open("/dev/input/mice", O_RDONLY);
365 mainloop(vnc_fd, 0, rat_fd);
367 term_cleanup(&ti);
368 vnc_free();
369 close(vnc_fd);
370 close(rat_fd);
371 return 0;