2 * fbvnc - a small linux framebuffer vnc viewer
4 * Copyright (C) 2009-2012 Ali Gholami Rudi
6 * This program is released under the modified BSD license.
13 #include <netinet/in.h>
21 #include <sys/socket.h>
23 #include <sys/types.h>
24 #include <linux/input.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
;
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
))
60 fd
= socket(addrinfo
->ai_family
, addrinfo
->ai_socktype
,
61 addrinfo
->ai_protocol
);
63 if (connect(fd
, addrinfo
->ai_addr
, addrinfo
->ai_addrlen
) == -1) {
65 freeaddrinfo(addrinfo
);
68 freeaddrinfo(addrinfo
);
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);
83 read(fd
, &connstat
, sizeof(connstat
));
85 if (ntohl(connstat
) != VNC_CONN_NOAUTH
)
88 clientinit
.shared
= 1;
89 write(fd
, &clientinit
, sizeof(clientinit
));
90 read(fd
, &serverinit
, sizeof(serverinit
));
94 if (FBM_BPP(fb_mode()) != sizeof(fbval_t
)) {
95 fprintf(stderr
, "fbvnc: fbval_t doesn't match fb depth\n");
98 cols
= MIN(ntohs(serverinit
.w
), fb_cols());
99 rows
= MIN(ntohs(serverinit
.h
), fb_rows());
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
));
121 static int vnc_free(void)
127 static int vnc_refresh(int fd
, int inc
)
129 struct vnc_client_fbup fbup_req
;
130 fbup_req
.type
= VNC_CLIENT_FBUP
;
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
));
140 static void drawfb(char *s
, int x
, int y
, int w
, int h
)
142 fbval_t slice
[1 << 14];
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
)
160 while (nr
< len
&& (n
= read(fd
, buf
+ nr
, len
- nr
)) > 0)
163 printf("partial vnc read!\n");
168 static int vnc_event(int fd
)
170 struct vnc_rect uprect
;
172 struct vnc_server_fbup
*fbup
= (void *) msg
;
173 struct vnc_server_cuttext
*cuttext
= (void *) msg
;
174 struct vnc_server_colormap
*colormap
= (void *) msg
;
178 if (read(fd
, msg
, 1) != 1)
181 case VNC_SERVER_FBUP
:
182 xread(fd
, msg
+ 1, sizeof(*fbup
) - 1);
184 for (j
= 0; j
< n
; j
++) {
186 xread(fd
, &uprect
, sizeof(uprect
));
191 if (x
>= cols
|| x
+ w
> cols
)
193 if (y
>= rows
|| y
+ h
> rows
)
195 xread(fd
, buf
, w
* h
);
197 drawfb(buf
, x
, y
, w
, h
);
200 case VNC_SERVER_BELL
:
202 case VNC_SERVER_CUTTEXT
:
203 xread(fd
, msg
+ 1, sizeof(*cuttext
) - 1);
204 xread(fd
, buf
, ntohl(cuttext
->len
));
206 case VNC_SERVER_COLORMAP
:
207 xread(fd
, msg
+ 1, sizeof(*colormap
) - 1);
208 xread(fd
, buf
, ntohs(colormap
->n
) * 3 * 2);
211 fprintf(stderr
, "unknown vnc msg: %d\n", msg
[0]);
217 static int rat_event(int fd
, int ratfd
)
220 struct vnc_client_ratevent me
= {VNC_CLIENT_RATEVENT
};
222 if (read(ratfd
, &ie
, sizeof(ie
)) != 3)
226 mc
= MAX(0, MIN(cols
- 1, mc
));
227 mr
= MAX(0, MIN(rows
- 1, mr
));
229 mask
|= VNC_BUTTON1_MASK
;
231 mask
|= VNC_BUTTON2_MASK
;
233 mask
|= VNC_BUTTON3_MASK
;
237 write(fd
, &me
, sizeof(me
));
241 static int press(int fd
, int key
, int down
)
243 struct vnc_client_keyevent ke
= {VNC_CLIENT_KEYEVENT
};
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
)
260 if ((nr
= read(kbdfd
, key
, sizeof(key
))) <= 0 )
262 for (i
= 0; i
< nr
; i
++) {
275 if (i
+ 2 < nr
&& key
[i
+ 1] == '[') {
276 if (key
[i
+ 2] == 'A')
278 if (key
[i
+ 2] == 'B')
280 if (key
[i
+ 2] == 'C')
282 if (key
[i
+ 2] == 'D')
284 if (key
[i
+ 2] == 'H')
293 mod
[nmod
++] = 0xffe9;
295 if (k
== 0x03) /* esc-^C: quit */
302 case 0x0: /* c-space: stop/start drawing */
308 if (vnc_refresh(fd
, 0))
312 k
= (unsigned char) key
[i
];
314 if (k
>= 'A' && k
<= 'Z' || strchr(":\"<>?{}|+_()*&^%$#@!~", k
))
315 mod
[nmod
++] = 0xffe1;
316 if (k
>= 1 && k
<= 26) {
318 mod
[nmod
++] = 0xffe3;
322 for (j
= 0; j
< nmod
; j
++)
323 press(fd
, mod
[j
], 1);
326 for (j
= 0; j
< nmod
; j
++)
327 press(fd
, mod
[j
], 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
));
342 tcgetattr(0, &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];
361 ufds
[0].events
= POLLIN
;
363 ufds
[1].events
= POLLIN
;
365 ufds
[2].events
= POLLIN
;
366 if (vnc_refresh(vnc_fd
, 0))
369 err
= poll(ufds
, 3, 500);
370 if (err
== -1 && errno
!= EINTR
)
374 if (ufds
[0].revents
& POLLIN
)
375 if (kbd_event(vnc_fd
, kbd_fd
) == -1)
377 if (ufds
[1].revents
& POLLIN
) {
378 if (vnc_event(vnc_fd
) == -1)
382 if (ufds
[2].revents
& POLLIN
)
383 if (rat_event(vnc_fd
, rat_fd
) == -1)
386 if (vnc_refresh(vnc_fd
, 1))
391 int main(int argc
, char * argv
[])
393 char *port
= VNC_PORT
;
394 char *host
= "127.0.0.1";
401 if ((vnc_fd
= vnc_connect(host
, port
)) == -1) {
402 fprintf(stderr
, "could not connect!\n");
405 if (vnc_init(vnc_fd
) == -1) {
406 fprintf(stderr
, "vnc init failed!\n");
410 rat_fd
= open("/dev/input/mice", O_RDONLY
);
412 mainloop(vnc_fd
, 0, rat_fd
);