2 * fbvnc - a small linux framebuffer vnc viewer
4 * Copyright (C) 2009-2013 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 #define MIN(a, b) ((a) < (b) ? (a) : (b))
29 #define MAX(a, b) ((a) > (b) ? (a) : (b))
30 #define OUT(msg) write(1, (msg), strlen(msg))
32 #define VNC_PORT "5900"
34 #define MAXRES (1 << 16)
36 static int cols
, rows
; /* framebuffer dimensions */
37 static int bpp
; /* bytes per pixel */
38 static int srv_cols
, srv_rows
; /* server screen dimensions */
39 static int or, oc
; /* visible screen offset */
40 static int mr
, mc
; /* mouse position */
41 static int nodraw
; /* don't draw anything */
43 static char buf
[MAXRES
];
45 static int vnc_connect(char *addr
, char *port
)
47 struct addrinfo hints
, *addrinfo
;
50 memset(&hints
, 0, sizeof(hints
));
51 hints
.ai_family
= AF_UNSPEC
;
52 hints
.ai_socktype
= SOCK_STREAM
;
53 hints
.ai_flags
= AI_PASSIVE
;
55 if (getaddrinfo(addr
, port
, &hints
, &addrinfo
))
57 fd
= socket(addrinfo
->ai_family
, addrinfo
->ai_socktype
,
58 addrinfo
->ai_protocol
);
60 if (connect(fd
, addrinfo
->ai_addr
, addrinfo
->ai_addrlen
) == -1) {
62 freeaddrinfo(addrinfo
);
65 freeaddrinfo(addrinfo
);
69 static void fbmode_bits(int *rr
, int *rg
, int *rb
)
71 int mode
= FBM_COLORS(fb_mode());
72 *rr
= (mode
>> 8) & 0xf;
73 *rg
= (mode
>> 4) & 0xf;
74 *rb
= (mode
>> 0) & 0xf;
77 static int vnc_init(int fd
)
79 char vncver
[] = "RFB 003.003\n";
82 struct vnc_client_init clientinit
;
83 struct vnc_server_init serverinit
;
84 struct vnc_client_pixelfmt pixfmt_cmd
;
85 int connstat
= VNC_CONN_FAILED
;
87 write(fd
, vncver
, 12);
90 read(fd
, &connstat
, sizeof(connstat
));
92 if (ntohl(connstat
) != VNC_CONN_NOAUTH
)
95 clientinit
.shared
= 1;
96 write(fd
, &clientinit
, sizeof(clientinit
));
97 read(fd
, &serverinit
, sizeof(serverinit
));
101 srv_cols
= ntohs(serverinit
.w
);
102 srv_rows
= ntohs(serverinit
.h
);
103 cols
= MIN(srv_cols
, fb_cols());
104 rows
= MIN(srv_rows
, fb_rows());
105 bpp
= FBM_BPP(fb_mode());
109 read(fd
, buf
, ntohl(serverinit
.len
));
110 pixfmt_cmd
.type
= VNC_CLIENT_PIXFMT
;
111 pixfmt_cmd
.format
.bpp
= bpp
<< 3;
112 pixfmt_cmd
.format
.depth
= bpp
<< 3;
113 pixfmt_cmd
.format
.bigendian
= 0;
114 pixfmt_cmd
.format
.truecolor
= 1;
116 fbmode_bits(&rr
, &rg
, &rb
);
117 pixfmt_cmd
.format
.rmax
= htons((1 << rr
) - 1);
118 pixfmt_cmd
.format
.gmax
= htons((1 << rg
) - 1);
119 pixfmt_cmd
.format
.bmax
= htons((1 << rb
) - 1);
120 /* assuming colors packed as RGB; shall handle other cases later */
121 pixfmt_cmd
.format
.rshl
= rg
+ rb
;
122 pixfmt_cmd
.format
.gshl
= rb
;
123 pixfmt_cmd
.format
.bshl
= 0;
124 write(fd
, &pixfmt_cmd
, sizeof(pixfmt_cmd
));
128 static int vnc_free(void)
134 static int vnc_refresh(int fd
, int inc
)
136 struct vnc_client_fbup fbup_req
;
137 fbup_req
.type
= VNC_CLIENT_FBUP
;
139 fbup_req
.x
= htons(oc
);
140 fbup_req
.y
= htons(or);
141 fbup_req
.w
= htons(cols
);
142 fbup_req
.h
= htons(rows
);
143 return write(fd
, &fbup_req
, sizeof(fbup_req
)) != sizeof(fbup_req
);
146 static void drawfb(char *s
, int x
, int y
, int w
, int h
)
148 int sc
; /* screen column offset */
149 int bc
, bw
; /* buffer column offset / row width */
152 bc
= x
> oc
? 0 : oc
- x
;
153 bw
= x
+ w
< oc
+ cols
? w
- bc
: w
- bc
- (x
+ w
- oc
- cols
);
154 for (i
= y
; i
< y
+ h
; i
++)
155 if (i
- or >= 0 && i
- or < rows
&& bw
> 0)
156 fb_set(i
- or, sc
, s
+ ((i
- y
) * w
+ bc
) * bpp
, bw
);
159 static void xread(int fd
, void *buf
, int len
)
163 while (nr
< len
&& (n
= read(fd
, buf
+ nr
, len
- nr
)) > 0)
166 printf("partial vnc read!\n");
171 static int vnc_event(int fd
)
173 struct vnc_rect uprect
;
175 struct vnc_server_fbup
*fbup
= (void *) msg
;
176 struct vnc_server_cuttext
*cuttext
= (void *) msg
;
177 struct vnc_server_colormap
*colormap
= (void *) msg
;
181 if (read(fd
, msg
, 1) != 1)
184 case VNC_SERVER_FBUP
:
185 xread(fd
, msg
+ 1, sizeof(*fbup
) - 1);
187 for (j
= 0; j
< n
; j
++) {
189 xread(fd
, &uprect
, sizeof(uprect
));
194 if (x
>= srv_cols
|| x
+ w
> srv_cols
)
196 if (y
>= srv_rows
|| y
+ h
> srv_rows
)
198 for (i
= 0; i
< h
; i
++) {
199 xread(fd
, buf
, w
* bpp
);
201 drawfb(buf
, x
, y
+ i
, w
, 1);
205 case VNC_SERVER_BELL
:
207 case VNC_SERVER_CUTTEXT
:
208 xread(fd
, msg
+ 1, sizeof(*cuttext
) - 1);
209 xread(fd
, buf
, ntohl(cuttext
->len
));
211 case VNC_SERVER_COLORMAP
:
212 xread(fd
, msg
+ 1, sizeof(*colormap
) - 1);
213 xread(fd
, buf
, ntohs(colormap
->n
) * 3 * 2);
216 fprintf(stderr
, "unknown vnc msg: %d\n", msg
[0]);
222 static int rat_event(int fd
, int ratfd
)
225 struct vnc_client_ratevent me
= {VNC_CLIENT_RATEVENT
};
227 int or_
= or, oc_
= oc
;
228 if (read(ratfd
, &ie
, sizeof(ie
)) != 4)
230 /* ignore mouse movements when nodraw */
237 oc
= MAX(0, oc
- cols
/ SCRSCRL
);
238 if (mc
>= oc
+ cols
&& oc
+ cols
< srv_cols
)
239 oc
= MIN(srv_cols
- cols
, oc
+ cols
/ SCRSCRL
);
241 or = MAX(0, or - rows
/ SCRSCRL
);
242 if (mr
>= or + rows
&& or + rows
< srv_rows
)
243 or = MIN(srv_rows
- rows
, or + rows
/ SCRSCRL
);
244 mc
= MAX(oc
, MIN(oc
+ cols
- 1, mc
));
245 mr
= MAX(or, MIN(or + rows
- 1, mr
));
247 mask
|= VNC_BUTTON1_MASK
;
249 mask
|= VNC_BUTTON2_MASK
;
251 mask
|= VNC_BUTTON3_MASK
;
252 if (ie
[3] > 0) /* wheel up */
253 mask
|= VNC_BUTTON4_MASK
;
254 if (ie
[3] < 0) /* wheel down */
255 mask
|= VNC_BUTTON5_MASK
;
260 write(fd
, &me
, sizeof(me
));
261 if (or != or_
|| oc
!= oc_
)
262 if (vnc_refresh(fd
, 0))
267 static int press(int fd
, int key
, int down
)
269 struct vnc_client_keyevent ke
= {VNC_CLIENT_KEYEVENT
};
272 return write(fd
, &ke
, sizeof(ke
));
275 static void showmsg(void)
277 OUT("\x1b[H\t\t\t*** fbvnc ***\r");
280 static int kbd_event(int fd
, int kbdfd
)
285 if ((nr
= read(kbdfd
, key
, sizeof(key
))) <= 0 )
287 for (i
= 0; i
< nr
; i
++) {
300 if (i
+ 2 < nr
&& key
[i
+ 1] == '[') {
301 if (key
[i
+ 2] == 'A')
303 if (key
[i
+ 2] == 'B')
305 if (key
[i
+ 2] == 'C')
307 if (key
[i
+ 2] == 'D')
309 if (key
[i
+ 2] == 'H')
318 mod
[nmod
++] = 0xffe9;
320 if (k
== 0x03) /* esc-^C: quit */
327 case 0x0: /* c-space: stop/start drawing */
333 if (vnc_refresh(fd
, 0))
337 k
= (unsigned char) key
[i
];
339 if (k
>= 'A' && k
<= 'Z' || strchr(":\"<>?{}|+_()*&^%$#@!~", k
))
340 mod
[nmod
++] = 0xffe1;
341 if (k
>= 1 && k
<= 26) {
343 mod
[nmod
++] = 0xffe3;
347 for (j
= 0; j
< nmod
; j
++)
348 press(fd
, mod
[j
], 1);
351 for (j
= 0; j
< nmod
; j
++)
352 press(fd
, mod
[j
], 0);
358 static void term_setup(struct termios
*ti
)
360 struct termios termios
;
361 OUT("\033[2J"); /* clear the screen */
362 OUT("\033[?25l"); /* hide the cursor */
364 tcgetattr(0, &termios
);
367 tcsetattr(0, TCSANOW
, &termios
);
370 static void term_cleanup(struct termios
*ti
)
372 tcsetattr(0, TCSANOW
, ti
);
373 OUT("\r\n\033[?25h"); /* show the cursor */
376 static void mainloop(int vnc_fd
, int kbd_fd
, int rat_fd
)
378 struct pollfd ufds
[3];
382 ufds
[0].events
= POLLIN
;
384 ufds
[1].events
= POLLIN
;
386 ufds
[2].events
= POLLIN
;
387 if (vnc_refresh(vnc_fd
, 0))
390 err
= poll(ufds
, 3, 500);
391 if (err
== -1 && errno
!= EINTR
)
395 if (ufds
[0].revents
& POLLIN
)
396 if (kbd_event(vnc_fd
, kbd_fd
) == -1)
398 if (ufds
[1].revents
& POLLIN
) {
399 if (vnc_event(vnc_fd
) == -1)
403 if (ufds
[2].revents
& POLLIN
)
404 if (rat_event(vnc_fd
, rat_fd
) == -1)
407 if (vnc_refresh(vnc_fd
, 1))
412 int main(int argc
, char * argv
[])
414 char *port
= VNC_PORT
;
415 char *host
= "127.0.0.1";
422 if ((vnc_fd
= vnc_connect(host
, port
)) < 0) {
423 fprintf(stderr
, "could not connect!\n");
426 if (vnc_init(vnc_fd
) < 0) {
428 fprintf(stderr
, "vnc init failed!\n");
433 /* entering intellimouse for using mouse wheel */
434 rat_fd
= open("/dev/input/mice", O_RDWR
);
435 write(rat_fd
, "\xf3\xc8\xf3\x64\xf3\x50", 6);
436 read(rat_fd
, buf
, 1);
438 mainloop(vnc_fd
, 0, rat_fd
);