3 Copyright (C) 2007-2011 Proxmox Server Solutions GmbH
5 Copyright: vzdump is under GNU GPL, the GNU General Public License.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 Author: Dietmar Maurer <dietmar@proxmox.com>
27 #include <sys/types.h>
28 #include <sys/socket.h>
32 #include <arpa/inet.h>
35 #include <rfb/keysym.h>
36 #include <pty.h> /* for openpty and forkpty */
39 #include <sys/ioctl.h>
46 #include <gnutls/gnutls.h>
47 #include <gnutls/x509.h>
49 /* define this for debugging */
52 char *auth_path
= "/";
53 char *auth_perm
= "Sys.Console";
55 uint16_t screen_width
= 744;
56 uint16_t screen_height
= 400;
60 extern int wcwidth (wchar_t wc
);
61 unsigned char *fontdata
;
63 #define FONTFILE "/usr/share/vncterm/font.data"
67 urlencode(char *buf
, const char *value
)
69 static const char *hexchar
= "0123456789abcdef";
72 int l
= strlen(value
);
73 for (i
= 0; i
< l
; i
++) {
75 if (('a' <= c
&& c
<= 'z') ||
76 ('A' <= c
&& c
<= 'Z') ||
77 ('0' <= c
&& c
<= '9')) {
83 *p
++ = hexchar
[c
>> 4];
84 *p
++ = hexchar
[c
& 15];
93 pve_auth_verify(const char *clientip
, const char *username
, const char *passwd
)
95 struct sockaddr_in server
;
97 int sfd
= socket(AF_INET
, SOCK_STREAM
, 0);
99 perror("pve_auth_verify: socket failed");
104 if ((he
= gethostbyname("localhost")) == NULL
) {
105 fprintf(stderr
, "pve_auth_verify: error resolving hostname\n");
109 memcpy(&server
.sin_addr
, he
->h_addr_list
[0], he
->h_length
);
110 server
.sin_family
= AF_INET
;
111 server
.sin_port
= htons(85);
113 if (connect(sfd
, (struct sockaddr
*)&server
, sizeof(server
))) {
114 perror("pve_auth_verify: error connecting to server");
122 p
= urlencode(p
, "username");
124 p
= urlencode(p
, username
);
127 p
= urlencode(p
, "password");
129 p
= urlencode(p
, passwd
);
132 p
= urlencode(p
, "path");
134 p
= urlencode(p
, auth_path
);
137 p
= urlencode(p
, "privs");
139 p
= urlencode(p
, auth_perm
);
141 sprintf(buf
, "POST /api2/json/access/ticket HTTP/1.1\n"
142 "Host: localhost:85\n"
143 "Connection: close\n"
145 "Content-Type: application/x-www-form-urlencoded\n"
146 "Content-Length: %zd\n\n%s\n", clientip
, strlen(form
), form
);
147 ssize_t len
= strlen(buf
);
148 ssize_t sb
= send(sfd
, buf
, len
, 0);
150 perror("pve_auth_verify: send failed");
154 fprintf(stderr
, "pve_auth_verify: partial send error\n");
158 len
= recv(sfd
, buf
, sizeof(buf
) - 1, 0);
160 perror("pve_auth_verify: recv failed");
166 //printf("DATA:%s\n", buf);
168 shutdown(sfd
, SHUT_RDWR
);
170 return strncmp(buf
, "HTTP/1.1 200 OK", 15);
173 shutdown(sfd
, SHUT_RDWR
);
178 static void vnc_debug_gnutls_log(int level
, const char* str
) {
179 fprintf(stderr
, "%d %s", level
, str
);
184 static gnutls_dh_params_t dh_params
;
187 gnutls_session_t session
;
192 gnutls_transport_ptr_t transport
,
196 rfbClientPtr cl
= (rfbClientPtr
)transport
;
200 n
= send(cl
->sock
, data
, len
, 0);
211 gnutls_transport_ptr_t transport
,
215 rfbClientPtr cl
= (rfbClientPtr
)transport
;
219 n
= recv(cl
->sock
, data
, len
, 0);
228 ssize_t
vnc_tls_read(rfbClientPtr cl
, void *buf
, size_t count
)
230 tls_client_t
*sd
= (tls_client_t
*)cl
->clientData
;
232 int ret
= gnutls_read(sd
->session
, buf
, count
);
234 if (ret
== GNUTLS_E_AGAIN
)
243 ssize_t
vnc_tls_write(rfbClientPtr cl
, void *buf
, size_t count
)
245 tls_client_t
*sd
= (tls_client_t
*)cl
->clientData
;
247 int ret
= gnutls_write(sd
->session
, buf
, count
);
249 if (ret
== GNUTLS_E_AGAIN
)
259 static gnutls_anon_server_credentials
260 tls_initialize_anon_cred(void)
262 gnutls_anon_server_credentials anon_cred
;
265 if ((ret
= gnutls_anon_allocate_server_credentials(&anon_cred
)) < 0) {
266 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret
));
270 #if GNUTLS_VERSION_NUMBER >= 0x030506
271 gnutls_anon_set_server_known_dh_params(anon_cred
, GNUTLS_SEC_PARAM_MEDIUM
);
273 gnutls_anon_set_server_dh_params(anon_cred
, dh_params
);
279 static gnutls_certificate_credentials_t
280 tls_initialize_x509_cred(void)
282 gnutls_certificate_credentials_t x509_cred
;
285 /* Paths to x509 certs/keys */
286 char *x509cacert
= "/etc/pve/pve-root-ca.pem";
287 char *x509cert
= "/etc/pve/local/pve-ssl.pem";
288 char *x509key
= "/etc/pve/local/pve-ssl.key";
290 if ((ret
= gnutls_certificate_allocate_credentials(&x509_cred
)) < 0) {
291 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret
));
295 if ((ret
= gnutls_certificate_set_x509_trust_file
296 (x509_cred
, x509cacert
, GNUTLS_X509_FMT_PEM
)) < 0) {
297 rfbLog("can't load CA certificate: %s\n", gnutls_strerror(ret
));
298 gnutls_certificate_free_credentials(x509_cred
);
302 if ((ret
= gnutls_certificate_set_x509_key_file
303 (x509_cred
, x509cert
, x509key
, GNUTLS_X509_FMT_PEM
)) < 0) {
304 rfbLog("can't load certificate & key: %s\n", gnutls_strerror(ret
));
305 gnutls_certificate_free_credentials(x509_cred
);
308 #if GNUTLS_VERSION_NUMBER >= 0x030506
309 /* only available since GnuTLS 3.5.6, on previous versions see
310 * gnutls_certificate_set_dh_params(). */
311 gnutls_certificate_set_known_dh_params(x509_cred
, GNUTLS_SEC_PARAM_MEDIUM
);
313 gnutls_certificate_set_dh_params (x509_cred
, dh_params
);
319 /* rfb tls security handler */
321 #define rfbSecTypeVencrypt 19
322 #define rfbVencryptTlsPlain 259
323 #define rfbVencryptX509Plain 262
325 void rfbEncodeU32(char *buf
, uint32_t value
)
327 buf
[0] = (value
>> 24) & 0xFF;
328 buf
[1] = (value
>> 16) & 0xFF;
329 buf
[2] = (value
>> 8) & 0xFF;
330 buf
[3] = value
& 0xFF;
333 uint32_t rfbDecodeU32(char *data
, size_t offset
)
335 return ((data
[offset
] << 24) | (data
[offset
+ 1] << 16) |
336 (data
[offset
+ 2] << 8) | data
[offset
+ 3]);
340 vencrypt_subauth_plain(rfbClientPtr cl
)
342 const char *err
= NULL
;
346 char clientip
[INET6_ADDRSTRLEN
];
348 struct sockaddr_in client
;
349 socklen_t addrlen
= sizeof(client
);
350 if (getpeername(cl
->sock
, &client
, &addrlen
) == 0) {
351 inet_ntop(client
.sin_family
, &client
.sin_addr
,
352 clientip
, sizeof(clientip
));
355 if ((n
= rfbReadExact(cl
, buf
, 8)) <= 0) {
356 err
= n
? "read failed" : "client gone";
360 uint32_t ulen
= rfbDecodeU32(buf
, 0);
361 uint32_t pwlen
= rfbDecodeU32(buf
, 4);
364 err
= "No User name.";
368 err
= "User name too long.";
372 err
= "Password too short";
376 err
= "Password too long.";
380 if ((n
= rfbReadExact(cl
, buf
, ulen
)) <= 0) {
381 err
= n
? "read failed" : "client gone";
385 char *username
= buf
;
386 char *passwd
= buf
+ ulen
+ 1;
387 if ((n
= rfbReadExact(cl
, passwd
, pwlen
)) <= 0) {
388 err
= n
? "read failed" : "client gone";
393 rfbLog("VencryptPlain: username: %s pw: %s\n", username
, passwd
);
395 if (pve_auth_verify(clientip
, username
, passwd
) == 0) {
396 rfbEncodeU32(buf
, 0); /* Accept auth completion */
397 rfbWriteExact(cl
, buf
, 4);
398 cl
->state
= RFB_INITIALISATION
;
402 err
= "Authentication failed";
404 rfbLog("VencryptPlain: %s\n", err
? err
: "no reason specified");
406 rfbEncodeU32(buf
, 1); /* Reject auth */
407 rfbWriteExact(cl
, buf
, 4);
408 if (cl
->protocolMinorVersion
>= 8) {
409 int elen
= strlen(err
);
410 rfbEncodeU32(buf
, elen
);
411 rfbWriteExact(cl
, buf
, 4);
412 rfbWriteExact(cl
, err
, elen
);
420 rfbVncAuthVencrypt(rfbClientPtr cl
)
424 /* Send VeNCrypt version 0.2 */
429 if (rfbWriteExact(cl
, buf
, 2) < 0) {
430 rfbLogPerror("rfbVncAuthVencrypt: write");
435 int n
= rfbReadExact(cl
, buf
, 2);
438 rfbLog("rfbVncAuthVencrypt: client gone\n");
440 rfbLogPerror("rfbVncAuthVencrypt: read");
445 if (buf
[0] != 0 || buf
[1] != 2) {
446 rfbLog("Unsupported VeNCrypt protocol %d.%d\n",
447 (int)buf
[0], (int)buf
[1]);
448 buf
[0] = 1; /* Reject version */
449 rfbWriteExact(cl
, buf
, 1);
454 /* Sending allowed auth */
455 int req_auth
= use_x509
? rfbVencryptX509Plain
: rfbVencryptTlsPlain
;
457 buf
[0] = 0; /* Accept version */
458 buf
[1] = 1; /* number of sub auths */
459 rfbEncodeU32(buf
+2, req_auth
);
460 if (rfbWriteExact(cl
, buf
, 6) < 0) {
461 rfbLogPerror("rfbVncAuthVencrypt: write");
466 n
= rfbReadExact(cl
, buf
, 4);
469 rfbLog("rfbVncAuthVencrypt: client gone\n");
471 rfbLogPerror("rfbVncAuthVencrypt: read");
476 int auth
= rfbDecodeU32(buf
, 0);
477 if (auth
!= req_auth
) {
478 buf
[0] = 1; /* Reject auth*/
479 rfbWriteExact(cl
, buf
, 1);
484 buf
[0] = 1; /* Accept auth */
485 if (rfbWriteExact(cl
, buf
, 1) < 0) {
486 rfbLogPerror("rfbVncAuthVencrypt: write");
491 tls_client_t
*sd
= calloc(1, sizeof(tls_client_t
));
493 if (sd
->session
== NULL
) {
494 if (gnutls_init(&sd
->session
, GNUTLS_SERVER
) < 0) {
495 rfbLog("gnutls_init failed\n");
501 if ((ret
= gnutls_set_default_priority(sd
->session
)) < 0) {
502 rfbLog("gnutls_set_default_priority failed: %s\n", gnutls_strerror(ret
));
508 static const char *priority_str_x509
= "NORMAL";
509 static const char *priority_str_anon
= "NORMAL:+ANON-ECDH:+ANON-DH";
510 if ((ret
= gnutls_priority_set_direct(sd
->session
, use_x509
? priority_str_x509
: priority_str_anon
, NULL
)) < 0) {
511 rfbLog("gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret
));
518 gnutls_certificate_server_credentials x509_cred
;
520 if (!(x509_cred
= tls_initialize_x509_cred())) {
526 if (gnutls_credentials_set(sd
->session
, GNUTLS_CRD_CERTIFICATE
, x509_cred
) < 0) {
528 gnutls_certificate_free_credentials(x509_cred
);
534 gnutls_anon_server_credentials anon_cred
;
536 if (!(anon_cred
= tls_initialize_anon_cred())) {
542 if ((ret
= gnutls_credentials_set(sd
->session
, GNUTLS_CRD_ANON
, anon_cred
)) < 0) {
543 rfbLog("gnutls_credentials_set failed: %s\n", gnutls_strerror(ret
));
544 gnutls_anon_free_server_credentials(anon_cred
);
551 gnutls_transport_set_ptr(sd
->session
, (gnutls_transport_ptr_t
)cl
);
552 gnutls_transport_set_push_function(sd
->session
, vnc_tls_push
);
553 gnutls_transport_set_pull_function(sd
->session
, vnc_tls_pull
);
558 if ((ret
= gnutls_handshake(sd
->session
)) < 0) {
559 if (!gnutls_error_is_fatal(ret
)) {
563 rfbLog("rfbVncAuthVencrypt: handshake failed\n");
568 /* set up TLS read/write hooks */
570 cl
->sock_read_fn
= &vnc_tls_read
;
571 cl
->sock_write_fn
= &vnc_tls_write
;
573 vencrypt_subauth_plain(cl
);
576 static rfbSecurityHandler VncSecurityHandlerVencrypt
= {
584 #define TERMIDCODE "[?1;2c" // vt100 ID
586 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
587 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
588 print_usage (NULL); \
592 /* these colours are from linux kernel drivers/char/vt.c */
594 static int idle_timeout
= 1;
596 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
597 8,12,10,14, 9,13,11,15 };
599 /* the default colour table, for VGA+ colour systems */
600 int default_red
[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
601 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
602 int default_grn
[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
603 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
604 int default_blu
[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
605 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
608 print_usage (const char *msg
)
610 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
611 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
614 /* Convert UCS2 to UTF8 sequence, trailing zero */
616 ucs2_to_utf8 (unicode c
, char *out
)
619 out
[0] = c
; // 0*******
622 } else if (c
< 0x800) {
623 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
624 out
[1] = 0x80 | (c
& 0x3f);
628 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
629 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
630 out
[2] = 0x80 | (c
& 0x3f);
639 rfb_draw_char (rfbScreenInfoPtr rfbScreen
, int x
, int y
,
640 unicode c
, rfbPixel col
, short width
)
643 unsigned char *data
= fontdata
+ c
*(GLYPHLINES
*2);
644 unsigned char d
=*data
;
645 int rowstride
=rfbScreen
->paddedWidthInBytes
;
646 char *colour
=(char*)&col
;
648 for(j
= 0; j
< GLYPHLINES
; j
++) {
649 for(i
= 0; i
< 8*width
; i
++) {
655 *(rfbScreen
->frameBuffer
+(y
+j
)*rowstride
+(x
+i
)) = *colour
;
662 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
, short width
, unicode combiningglyph
)
664 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
666 // non printable character
667 if (width
< 1) return;
671 int rxe
= x
*8+8*width
;
684 rfbFillRect (vt
->screen
, rx
, ry
, rxe
, rye
, bg
);
690 // unsuported attributes = (attrib.blink || attrib.unvisible)
692 rfb_draw_char (vt
->screen
, rx
, ry
, ch
, fg
, width
);
694 if (combiningglyph
) {
695 rfb_draw_char (vt
->screen
, rx
, ry
, combiningglyph
, fg
, 1);
699 rfbDrawLine (vt
->screen
, rx
, ry
+ 14, rxe
, ry
+ 14, fg
);
702 rfbMarkRectAsModified (vt
->screen
, rx
, ry
, rxe
, rye
);
707 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
709 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
711 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
712 int y2
= y1
- vt
->y_displ
;
714 y2
+= vt
->total_height
;
716 if (y2
< vt
->height
) {
717 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
718 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
, c
->width
, c
->combiningglyph
);
723 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
725 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
727 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
728 int y2
= y1
- vt
->y_displ
;
730 y2
+= vt
->total_height
;
732 if (y2
< vt
->height
) {
733 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
735 c
->attrib
= vt
->default_attrib
;
736 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
737 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
739 c
->combiningglyph
= 0;
741 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
, c
->width
, c
->combiningglyph
);
746 vncterm_show_cursor (vncTerm
*vt
, int show
)
749 if (x
>= vt
->width
) {
753 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
754 int y
= y1
- vt
->y_displ
;
756 y
+= vt
->total_height
;
759 if (y
< vt
->height
) {
761 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
764 TextAttributes attrib
= vt
->default_attrib
;
765 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
766 draw_char_at (vt
, x
, y
, c
->ch
, attrib
, c
->width
, c
->combiningglyph
);
768 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
, c
->width
, c
->combiningglyph
);
774 vncterm_refresh (vncTerm
*vt
)
778 rfbFillRect (vt
->screen
, 0, 0, vt
->maxx
, vt
->maxy
, vt
->default_attrib
.bgcol
);
781 for(y
= 0; y
< vt
->height
; y
++) {
782 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
783 for(x
= 0; x
< vt
->width
; x
++) {
784 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
, c
->width
, c
->combiningglyph
);
787 if (++y1
== vt
->total_height
)
790 rfbMarkRectAsModified (vt
->screen
, 0, 0, vt
->maxx
, vt
->maxy
);
792 vncterm_show_cursor (vt
, 1);
796 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
798 if ((top
+ lines
) >= bottom
) {
799 lines
= bottom
- top
-1;
802 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
810 int rowstride
= vt
->screen
->paddedWidthInBytes
;
811 int rows
= (bottom
- top
- lines
)*16;
813 char *in
= vt
->screen
->frameBuffer
+y0
*rowstride
;
814 char *out
= vt
->screen
->frameBuffer
+y1
*rowstride
;
815 memmove(out
,in
, rowstride
*rows
);
817 memset(vt
->screen
->frameBuffer
+y0
*rowstride
, 0, h
*rowstride
);
818 rfbMarkRectAsModified (vt
->screen
, 0, y0
, vt
->screen
->width
, y2
);
821 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
822 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
823 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
825 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
828 for (i
= 0; i
< lines
; i
++) {
830 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
831 for(j
= 0; j
< vt
->width
; j
++) {
832 c
->attrib
= vt
->default_attrib
;
835 c
->combiningglyph
= 0;
842 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
844 if ((top
+ lines
) >= bottom
) {
845 lines
= bottom
- top
- 1;
848 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
854 int y1
= (top
+ lines
)*16;
856 int rowstride
= vt
->screen
->paddedWidthInBytes
;
857 int rows
= (bottom
- top
- lines
)*16;
859 char *in
= vt
->screen
->frameBuffer
+y1
*rowstride
;
860 char *out
= vt
->screen
->frameBuffer
+y0
*rowstride
;
861 memmove(out
,in
, rowstride
*rows
);
863 memset(vt
->screen
->frameBuffer
+(y2
-h
)*rowstride
, 0, h
*rowstride
);
865 rfbMarkRectAsModified (vt
->screen
, 0, y0
, vt
->screen
->width
, y2
);
867 if (!moveattr
) return;
872 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
873 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
874 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
876 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
879 for (i
= 1; i
<= lines
; i
++) {
881 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
882 for(j
= 0; j
< vt
->width
; j
++) {
883 c
->attrib
= vt
->default_attrib
;
886 c
->combiningglyph
= 0;
893 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
895 if (vt
->altbuf
|| lines
== 0) return;
899 int i
= vt
->scroll_height
;
900 if (i
> vt
->total_height
- vt
->height
)
901 i
= vt
->total_height
- vt
->height
;
902 int y1
= vt
->y_base
- i
;
904 y1
+= vt
->total_height
;
905 for(i
= 0; i
< lines
; i
++) {
906 if (vt
->y_displ
== y1
) break;
907 if (--vt
->y_displ
< 0) {
908 vt
->y_displ
= vt
->total_height
- 1;
913 for(i
= 0; i
< lines
; i
++) {
914 if (vt
->y_displ
== vt
->y_base
) break;
915 if (++vt
->y_displ
== vt
->total_height
) {
922 vncterm_refresh (vt
);
925 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
927 int len
= strlen (esc
);
930 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
931 vt
->ibuf
[vt
->ibuf_count
++] = 27;
932 for (i
= 0; i
< len
; i
++) {
933 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
939 vncterm_put_lf (vncTerm
*vt
)
941 if (vt
->cy
+ 1 == vt
->region_bottom
) {
943 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
944 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
948 if (vt
->y_displ
== vt
->y_base
) {
949 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
952 if (vt
->y_displ
== vt
->y_base
) {
953 if (++vt
->y_displ
== vt
->total_height
) {
958 if (++vt
->y_base
== vt
->total_height
) {
962 if (vt
->scroll_height
< vt
->total_height
) {
966 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
967 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
969 for (x
= 0; x
< vt
->width
; x
++) {
972 c
->combiningglyph
= 0;
973 c
->attrib
= vt
->default_attrib
;
977 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
979 } else if (vt
->cy
< vt
->height
- 1) {
986 vncterm_csi_m (vncTerm
*vt
)
990 for (i
= 0; i
< vt
->esc_count
; i
++) {
991 switch (vt
->esc_buf
[i
]) {
992 case 0: /* reset all console attributes to default */
993 vt
->cur_attrib
= vt
->default_attrib
;
996 vt
->cur_attrib
.bold
= 1;
999 vt
->cur_attrib
.uline
= 1;
1002 vt
->cur_attrib
.blink
= 1;
1005 vt
->cur_attrib
.invers
= 1;
1008 vt
->cur_attrib
.unvisible
= 1;
1011 vt
->cur_enc
= LAT1_MAP
;
1012 // fixme: dispaly controls = 0 ?
1013 // fixme: toggle meta = 0 ?
1016 vt
->cur_enc
= IBMPC_MAP
;
1017 // fixme: dispaly controls = 1 ?
1018 // fixme: toggle meta = 0 ?
1021 vt
->cur_enc
= IBMPC_MAP
;
1022 // fixme: dispaly controls = 1 ?
1023 // fixme: toggle meta = 1 ?
1026 vt
->cur_attrib
.bold
= 0;
1029 vt
->cur_attrib
.uline
= 0;
1032 vt
->cur_attrib
.blink
= 0;
1035 vt
->cur_attrib
.invers
= 0;
1038 vt
->cur_attrib
.unvisible
= 0;
1048 /* set foreground color */
1049 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
1052 /* reset color to default, enable underline */
1053 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
1054 vt
->cur_attrib
.uline
= 1;
1057 /* reset color to default, disable underline */
1058 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
1059 vt
->cur_attrib
.uline
= 0;
1069 /* set background color */
1070 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
1073 /* reset background color */
1074 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
1077 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
1084 vncterm_save_cursor (vncTerm
*vt
)
1086 vt
->cx_saved
= vt
->cx
;
1087 vt
->cy_saved
= vt
->cy
;
1088 vt
->cur_attrib_saved
= vt
->cur_attrib
;
1089 vt
->charset_saved
= vt
->charset
;
1090 vt
->g0enc_saved
= vt
->g0enc
;
1091 vt
->g1enc_saved
= vt
->g1enc
;
1092 vt
->cur_enc_saved
= vt
->cur_enc
;
1096 vncterm_restore_cursor (vncTerm
*vt
)
1098 vt
->cx
= vt
->cx_saved
;
1099 vt
->cy
= vt
->cy_saved
;
1100 vt
->cur_attrib
= vt
->cur_attrib_saved
;
1101 vt
->charset
= vt
->charset_saved
;
1102 vt
->g0enc
= vt
->g0enc_saved
;
1103 vt
->g1enc
= vt
->g1enc_saved
;
1104 vt
->cur_enc
= vt
->cur_enc_saved
;
1108 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
1112 vt
->y_displ
= vt
->y_base
;
1116 if (vt
->altbuf
) return;
1120 /* alternate buffer & cursor */
1122 vncterm_save_cursor (vt
);
1123 /* save screen to altcels */
1124 for (y
= 0; y
< vt
->height
; y
++) {
1125 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
1126 for (x
= 0; x
< vt
->width
; x
++) {
1127 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
1132 for (y
= 0; y
<= vt
->height
; y
++) {
1133 for (x
= 0; x
< vt
->width
; x
++) {
1134 vncterm_clear_xy (vt
, x
, y
);
1140 if (vt
->altbuf
== 0) return;
1144 /* restore saved data */
1145 for (y
= 0; y
< vt
->height
; y
++) {
1146 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
1147 for (x
= 0; x
< vt
->width
; x
++) {
1148 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
1152 vncterm_restore_cursor (vt
);
1155 vncterm_refresh (vt
);
1159 vncterm_set_mode (vncTerm
*vt
, int on_off
)
1163 for (i
= 0; i
<= vt
->esc_count
; i
++) {
1164 if (vt
->esc_ques
) { /* DEC private modes set/reset */
1165 switch(vt
->esc_buf
[i
]) {
1166 case 10: /* X11 mouse reporting on/off */
1168 vt
->report_mouse
= on_off
;
1170 case 1049: /* start/end special app mode (smcup/rmcup) */
1171 vncterm_set_alternate_buffer (vt
, on_off
);
1173 case 25: /* Cursor on/off */
1174 case 9: /* X10 mouse reporting on/off */
1175 case 6: /* Origin relative/absolute */
1176 case 1: /* Cursor keys in appl mode*/
1177 case 5: /* Inverted screen on/off */
1178 case 7: /* Autowrap on/off */
1179 case 8: /* Autorepeat on/off */
1182 } else { /* ANSI modes set/reset */
1183 /* fixme: implement me */
1189 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
1191 /* verify all boundaries */
1195 } else if (x
>= vt
->width
) {
1203 } else if (y
>= vt
->height
) {
1210 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
1211 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
1212 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
1215 vncterm_putchar (vncTerm
*vt
, unicode ch
)
1221 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
1224 switch(vt
->tty_state
) {
1226 vt
->tty_state
= ESnormal
;
1229 vt
->tty_state
= ESsquare
;
1232 vt
->tty_state
= ESnonstd
;
1235 vt
->tty_state
= ESpercent
;
1238 vncterm_save_cursor (vt
);
1241 vncterm_restore_cursor (vt
);
1244 vt
->tty_state
= ESsetG0
; // SET G0
1247 vt
->tty_state
= ESsetG1
; // SET G1
1250 /* cursor up (ri) */
1251 if (vt
->cy
== vt
->region_top
)
1252 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
1253 else if (vt
->cy
> 0) {
1258 /* numeric keypad - ignored */
1261 /* appl. keypad - ignored */
1265 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
1270 case ESnonstd
: /* Operating System Controls */
1271 vt
->tty_state
= ESnormal
;
1274 case 'P': /* palette escape sequence */
1275 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
1280 vt
->tty_state
= ESpalette
;
1282 case 'R': /* reset palette */
1283 // fixme: reset_palette(vc);
1290 vt
->osc_textbuf
[0] = 0;
1291 vt
->tty_state
= ESosc1
;
1295 fprintf (stderr
, "unhandled OSC %c\n", ch
);
1297 vt
->tty_state
= ESnormal
;
1302 vt
->tty_state
= ESnormal
;
1304 vt
->tty_state
= ESosc2
;
1307 fprintf (stderr
, "got illegal OSC sequence\n");
1312 if (ch
!= 0x9c && ch
!= 7) {
1314 while (vt
->osc_textbuf
[i
]) i
++;
1315 vt
->osc_textbuf
[i
++] = ch
;
1316 vt
->osc_textbuf
[i
] = 0;
1319 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
1321 vt
->tty_state
= ESnormal
;
1325 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
1326 || (ch
>= 'a' && ch
<= 'f')) {
1327 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
1328 if (vt
->esc_count
== 7) {
1329 // fixme: this does not work - please test
1330 rfbColourMap
*cmap
=&vt
->screen
->colourMap
;
1332 int i
= color_table
[vt
->esc_buf
[0]] * 3, j
= 1;
1333 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1334 cmap
->data
.bytes
[i
++] += vt
->esc_buf
[j
++];
1335 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1336 cmap
->data
.bytes
[i
++] += vt
->esc_buf
[j
++];
1337 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1338 cmap
->data
.bytes
[i
] += vt
->esc_buf
[j
];
1340 //set_palette(vc); ?
1342 vt
->tty_state
= ESnormal
;
1345 vt
->tty_state
= ESnormal
;
1348 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
1353 vt
->esc_has_par
= 0;
1354 vt
->tty_state
= ESgetpars
;
1357 vt
->tty_state
= ESidquery
;
1361 if ((vt
->esc_ques
= (ch
== '?'))) {
1365 if (ch
>= '0' && ch
<= '9') {
1366 vt
->esc_has_par
= 1;
1367 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
1368 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
1371 } else if (ch
== ';') {
1372 vt
->esc_has_par
= 1;
1376 if (vt
->esc_has_par
) {
1379 vt
->tty_state
= ESgotpars
;
1383 vt
->tty_state
= ESnormal
;
1386 char *qes
= vt
->esc_ques
? "?" : "";
1387 if (vt
->esc_count
== 0) {
1388 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
1389 } else if (vt
->esc_count
== 1) {
1390 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1393 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
1394 for (i
= 1; i
< vt
->esc_count
; i
++) {
1395 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1397 fprintf (stderr
, "%c\n", ch
);
1403 vncterm_set_mode (vt
, 1);
1406 vncterm_set_mode (vt
, 0);
1409 if (!vt
->esc_count
) {
1410 vt
->esc_count
++; // default parameter 0
1415 /* report cursor position */
1416 /* TODO: send ESC[row;colR */
1419 /* move cursor up */
1420 if (vt
->esc_buf
[0] == 0) {
1423 vncterm_gotoxy (vt
, vt
->cx
, vt
->cy
- vt
->esc_buf
[0]);
1427 /* move cursor down */
1428 if (vt
->esc_buf
[0] == 0) {
1431 vncterm_gotoxy (vt
, vt
->cx
, vt
->cy
+ vt
->esc_buf
[0]);
1435 /* move cursor right */
1436 if (vt
->esc_buf
[0] == 0) {
1439 vncterm_gotoxy (vt
, vt
->cx
+ vt
->esc_buf
[0], vt
->cy
);
1442 /* move cursor left */
1443 if (vt
->esc_buf
[0] == 0) {
1446 vncterm_gotoxy (vt
, vt
->cx
- vt
->esc_buf
[0], vt
->cy
);
1450 /* move cursor to column */
1451 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
1454 /* move cursor to row */
1455 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
1459 /* move cursor to row, column */
1460 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
1463 switch (vt
->esc_buf
[0]) {
1465 /* clear to end of screen */
1466 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
1467 for (x
= 0; x
< vt
->width
; x
++) {
1468 if (y
== vt
->cy
&& x
< vt
->cx
) {
1471 vncterm_clear_xy (vt
, x
, y
);
1476 /* clear from beginning of screen */
1477 for (y
= 0; y
<= vt
->cy
; y
++) {
1478 for (x
= 0; x
< vt
->width
; x
++) {
1479 if (y
== vt
->cy
&& x
> vt
->cx
) {
1482 vncterm_clear_xy (vt
, x
, y
);
1487 /* clear entire screen */
1488 for (y
= 0; y
<= vt
->height
; y
++) {
1489 for (x
= 0; x
< vt
->width
; x
++) {
1490 vncterm_clear_xy (vt
, x
, y
);
1497 switch (vt
->esc_buf
[0]) {
1500 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
1501 vncterm_clear_xy (vt
, x
, vt
->cy
);
1505 /* clear from beginning of line */
1506 for (x
= 0; x
<= vt
->cx
; x
++) {
1507 vncterm_clear_xy (vt
, x
, vt
->cy
);
1511 /* clear entire line */
1512 for(x
= 0; x
< vt
->width
; x
++) {
1513 vncterm_clear_xy (vt
, x
, vt
->cy
);
1522 if (c
> vt
->height
- vt
->cy
)
1523 c
= vt
->height
- vt
->cy
;
1527 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
1533 if (c
> vt
->height
- vt
->cy
)
1534 c
= vt
->height
- vt
->cy
;
1538 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
1544 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
1550 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
1553 /* delete c character */
1556 if (c
> vt
->width
- vt
->cx
)
1557 c
= vt
->width
- vt
->cx
;
1561 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
1562 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1563 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
1564 TextCell
*src
= dst
+ c
;
1566 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1569 src
->combiningglyph
= 0;
1570 src
->attrib
= vt
->default_attrib
;
1571 vncterm_update_xy (vt
, x
, vt
->cy
);
1575 /* save cursor position */
1576 vncterm_save_cursor (vt
);
1579 /* restore cursor position */
1580 vncterm_restore_cursor (vt
);
1583 /* erase c characters */
1587 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1589 for(i
= 0; i
< c
; i
++) {
1590 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1594 /* insert c character */
1596 if (c
> (vt
->width
- vt
->cx
)) {
1597 c
= vt
->width
- vt
->cx
;
1601 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1602 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1603 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1604 TextCell
*dst
= src
+ c
;
1606 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1609 src
->combiningglyph
= 0;
1610 src
->attrib
= vt
->cur_attrib
;
1611 vncterm_update_xy (vt
, x
, vt
->cy
);
1617 if (!vt
->esc_buf
[0])
1619 if (!vt
->esc_buf
[1])
1620 vt
->esc_buf
[1] = vt
->height
;
1621 /* Minimum allowed region is 2 lines */
1622 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1623 vt
->esc_buf
[1] <= vt
->height
) {
1624 vt
->region_top
= vt
->esc_buf
[0] - 1;
1625 vt
->region_bottom
= vt
->esc_buf
[1];
1627 vt
->cy
= vt
->region_top
;
1629 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1636 if (vt
->esc_count
== 0) {
1637 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1638 } else if (vt
->esc_count
== 1) {
1639 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1642 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1643 for (i
= 1; i
< vt
->esc_count
; i
++) {
1644 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1646 fprintf (stderr
, "%c\n", ch
);
1653 case ESsetG0
: // Set G0
1654 vt
->tty_state
= ESnormal
;
1657 vt
->g0enc
= GRAF_MAP
;
1659 vt
->g0enc
= LAT1_MAP
;
1661 vt
->g0enc
= IBMPC_MAP
;
1663 vt
->g0enc
= USER_MAP
;
1665 if (vt
->charset
== 0)
1666 vt
->cur_enc
= vt
->g0enc
;
1669 case ESsetG1
: // Set G1
1670 vt
->tty_state
= ESnormal
;
1673 vt
->g1enc
= GRAF_MAP
;
1675 vt
->g1enc
= LAT1_MAP
;
1677 vt
->g1enc
= IBMPC_MAP
;
1679 vt
->g1enc
= USER_MAP
;
1681 if (vt
->charset
== 1)
1682 vt
->cur_enc
= vt
->g1enc
;
1685 case ESidquery
: // vt100 query id
1686 vt
->tty_state
= ESnormal
;
1690 fprintf (stderr
, "ESC[>c Query term ID\n");
1692 vncterm_respond_esc (vt
, TERMIDCODE
);
1696 vt
->tty_state
= ESnormal
;
1698 case '@': /* defined in ISO 2022 */
1701 case 'G': /* prelim official escape code */
1702 case '8': /* retained for compatibility */
1707 default: // ESnormal
1708 vt
->tty_state
= ESnormal
;
1713 case 7: /* alert aka. bell */
1714 rfbSendBell(vt
->screen
);
1716 case 8: /* backspace */
1720 case 9: /* tabspace */
1721 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1723 vncterm_put_lf (vt
);
1725 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1731 vncterm_put_lf (vt
);
1733 case 13: /* carriage return */
1737 /* SI (shift in), select character set 1 */
1739 vt
->cur_enc
= vt
->g1enc
;
1740 /* fixme: display controls = 1 */
1743 /* SO (shift out), select character set 0 */
1745 vt
->cur_enc
= vt
->g0enc
;
1746 /* fixme: display controls = 0 */
1749 vt
->tty_state
= ESesc
;
1751 case 127: /* delete */
1754 case 128+27: /* csi */
1755 vt
->tty_state
= ESsquare
;
1758 if (vt
->cx
>= vt
->width
) {
1761 vncterm_put_lf (vt
);
1764 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1765 int width
= wcwidth(ch
);
1767 // normal/wide character
1768 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1769 c
->attrib
= vt
->cur_attrib
;
1772 c
->combiningglyph
= 0;
1773 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1775 } else if (width
== 0) {
1777 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
- 1];
1778 c
->attrib
= vt
->cur_attrib
;
1779 c
->combiningglyph
= ch
;
1780 vncterm_update_xy (vt
, vt
->cx
- 1, vt
->cy
);
1782 // non printable character, so we do not save them
1791 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1795 vncterm_show_cursor (vt
, 0);
1798 unsigned char c
= *buf
;
1802 if (vt
->tty_state
!= ESnormal
) {
1803 // never translate escape sequence
1805 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1807 if(c
& 0x80) { // utf8 multi-byte sequence
1809 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1810 // inside UTF8 sequence
1811 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1813 if (vt
->utf_count
== 0) {
1814 if (vt
->utf_char
<= USHRT_MAX
) {
1823 // first char of a UTF8 sequence
1824 if ((c
& 0xe0) == 0xc0) {
1826 vt
->utf_char
= (c
& 0x1f);
1827 } else if ((c
& 0xf0) == 0xe0) {
1829 vt
->utf_char
= (c
& 0x0f);
1830 } else if ((c
& 0xf8) == 0xf0) {
1832 vt
->utf_char
= (c
& 0x07);
1833 } else if ((c
& 0xfc) == 0xf8) {
1835 vt
->utf_char
= (c
& 0x03);
1836 } else if ((c
& 0xfe) == 0xfc) {
1838 vt
->utf_char
= (c
& 0x01);
1851 // never translate controls
1852 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1853 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1859 vncterm_putchar (vt
, tc
);
1862 vncterm_show_cursor (vt
, 1);
1867 vncterm_kbd_event (rfbBool down
, rfbKeySym keySym
, rfbClientPtr cl
)
1869 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
1870 static int control
= 0;
1871 static int shift
= 0;
1874 //fprintf (stderr, "KEYEVENT:%d: %08x\n", down == 0, keySym);fflush (stderr);
1876 //fprintf (stderr, "KEYPRESS: %d\n", keySym);fflush (stderr);
1878 if (keySym
== XK_Shift_L
|| keySym
== XK_Shift_R
) {
1880 } if (keySym
== XK_Control_L
|| keySym
== XK_Control_R
) {
1882 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1885 if(keySym
>= 'a' && keySym
<= 'z')
1887 else if (keySym
>= 'A' && keySym
<= 'Z')
1901 case XK_Delete
: /* kdch1 */
1904 case XK_Home
: /* khome */
1908 case XK_KP_End
: /* kend */
1910 case XK_Insert
: /* kich1 */
1914 case XK_KP_Up
: /* kcuu1 */
1916 case XK_Down
: /* kcud1 */
1920 case XK_KP_Right
: /* kcuf1 */
1923 case XK_KP_Left
: /* kcub1 */
1927 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1933 vncterm_virtual_scroll (vt
, vt
->height
/2);
1967 fprintf (stderr
, "KEYPRESS OUT:%s: %d\n", esc
, keySym
); fflush (stderr
);
1970 if (vt
->y_displ
!= vt
->y_base
) {
1971 vt
->y_displ
= vt
->y_base
;
1972 vncterm_refresh (vt
);
1976 vncterm_respond_esc (vt
, esc
);
1977 } else if(keySym
<0x100) {
1979 int len
= ucs2_to_utf8 (keySym
& 0x0fff, &vt
->ibuf
[vt
->ibuf_count
]);
1980 vt
->ibuf_count
+= len
;
1982 vt
->ibuf
[vt
->ibuf_count
++] = (char)keySym
;
1987 if (keySym
== XK_Shift_L
|| keySym
== XK_Shift_R
) {
1989 } else if (keySym
== XK_Control_L
|| keySym
== XK_Control_R
) {
1996 vncterm_set_xcut_text (char* str
, int len
, struct _rfbClientRec
* cl
)
1998 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
2000 // seems str is Latin-1 encoded
2001 if (vt
->selection
) free (vt
->selection
);
2002 vt
->selection
= (unicode
*)malloc (len
*sizeof (unicode
));
2004 for (i
= 0; i
< len
; i
++) {
2005 vt
->selection
[i
] = str
[i
] & 0xff;
2007 vt
->selection_len
= len
;
2011 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
2015 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
2018 vncterm_respond_esc (vt
, buf
);
2022 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
2024 int x
= (pos
%vt
->width
)*8;
2025 int y
= (pos
/vt
->width
)*16;
2028 rfbScreenInfoPtr s
=vt
->screen
;
2030 char *b
= s
->frameBuffer
+y
*s
->width
+x
;
2032 for (j
=0; j
< 16; j
++) {
2033 for(i
=0; i
< 8; i
++) {
2034 b
[j
*s
->width
+i
] ^= 0x0f;
2035 rfbMarkRectAsModified (s
, x
, y
, x
+8, y
+16);
2041 vncterm_pointer_event (int buttonMask
, int x
, int y
, rfbClientPtr cl
)
2043 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
2044 static int button2_released
= 1;
2045 static int last_mask
= 0;
2046 static int sel_start_pos
= 0;
2047 static int sel_end_pos
= 0;
2054 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
2056 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
2058 if (vt
->report_mouse
&& buttonMask
!= last_mask
) {
2059 last_mask
= buttonMask
;
2060 if (buttonMask
& 1) {
2061 mouse_report (vt
, 0, cx
, cy
);
2063 if (buttonMask
& 2) {
2064 mouse_report (vt
, 1, cx
, cy
);
2066 if (buttonMask
& 4) {
2067 mouse_report (vt
, 2, cx
, cy
);
2070 mouse_report (vt
, 3, cx
, cy
);
2074 if (buttonMask
& 2) {
2075 if(button2_released
&& vt
->selection
) {
2077 for(i
= 0; i
< vt
->selection_len
; i
++) {
2078 if (vt
->ibuf_count
< IBUFSIZE
- 6) { // uft8 is max 6 characters wide
2080 vt
->ibuf_count
+= ucs2_to_utf8 (vt
->selection
[i
], &vt
->ibuf
[vt
->ibuf_count
]);
2082 vt
->ibuf
[vt
->ibuf_count
++] = vt
->selection
[i
];
2086 if (vt
->y_displ
!= vt
->y_base
) {
2087 vt
->y_displ
= vt
->y_base
;
2088 vncterm_refresh (vt
);
2091 button2_released
= 0;
2093 button2_released
= 1;
2096 if (buttonMask
& 1) {
2097 int pos
= cy
*vt
->width
+ cx
;
2099 // code borrowed from libvncserver (VNConsole.c)
2101 if (!vt
->mark_active
) {
2103 vt
->mark_active
= 1;
2104 sel_start_pos
= sel_end_pos
= pos
;
2105 vncterm_toggle_marked_cell (vt
, pos
);
2109 if (pos
!= sel_end_pos
) {
2111 if (pos
> sel_end_pos
) {
2112 cx
= sel_end_pos
; cy
=pos
;
2114 cx
=pos
; cy
=sel_end_pos
;
2117 if (cx
< sel_start_pos
) {
2118 if (cy
< sel_start_pos
) cy
--;
2124 vncterm_toggle_marked_cell (vt
, cx
);
2132 } else if (vt
->mark_active
) {
2133 vt
->mark_active
= 0;
2135 if (sel_start_pos
> sel_end_pos
) {
2136 int tmp
= sel_start_pos
- 1;
2137 sel_start_pos
= sel_end_pos
;
2141 int len
= sel_end_pos
- sel_start_pos
+ 1;
2143 if (vt
->selection
) free (vt
->selection
);
2144 vt
->selection
= (unicode
*)malloc (len
*sizeof (unicode
));
2145 vt
->selection_len
= len
;
2146 char *sel_latin1
= (char *)malloc (len
+ 1);
2148 for (i
= 0; i
< len
; i
++) {
2149 int pos
= sel_start_pos
+ i
;
2150 int x
= pos
% vt
->width
;
2151 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
2152 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
2153 vt
->selection
[i
] = c
->ch
;
2154 sel_latin1
[i
] = (char)c
->ch
;
2157 sel_latin1
[len
] = 0;
2158 rfbGotXCutText (vt
->screen
, sel_latin1
, len
);
2161 while (sel_start_pos
<= sel_end_pos
) {
2162 vncterm_toggle_marked_cell (vt
, sel_start_pos
++);
2167 rfbDefaultPtrAddEvent (buttonMask
, x
, y
, cl
);
2170 static int client_count
= 0;
2171 static int client_connected
= 0;
2172 static int last_client
= 1;
2173 static time_t last_time
= 0;
2176 client_gone (rfbClientPtr client
)
2180 last_time
= time (NULL
);
2182 if (client_count
<= 0) {
2187 /* libvncserver callback for when a new client connects */
2188 enum rfbNewClientAction
2189 new_client (rfbClientPtr client
)
2191 client
->clientGoneHook
= client_gone
;
2194 last_time
= time (NULL
);
2197 client_connected
= 1;
2199 return RFB_CLIENT_ACCEPT
;
2202 static char *vncticket
= NULL
;
2205 MakeRichCursor(rfbScreenInfoPtr rfbScreen
)
2209 rfbCursorPtr c
= rfbScreen
->cursor
;
2245 c
= rfbScreen
->cursor
= rfbMakeXCursor(w
,h
,bitmap
,bitmap
);
2246 c
->richSource
= (unsigned char*)calloc(w
*h
, 1);
2247 c
->cleanupRichSource
= TRUE
;
2249 for(int j
=0;j
<h
;j
++) {
2250 for(int i
=0;i
<w
;i
++) {
2251 unsigned int pos
= j
*w
+i
;
2252 if (edge
[pos
] == 'x') {
2253 c
->richSource
[pos
] = 15; // white
2255 c
->richSource
[pos
] = 0; // black
2262 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
2266 rfbScreenInfoPtr screen
= rfbGetScreen (&argc
, argv
, maxx
, maxy
, 8, 1, 1);
2267 screen
->frameBuffer
=(char*)calloc(maxx
*maxy
, 1);
2268 MakeRichCursor(screen
);
2270 char **passwds
= calloc(sizeof(char**), 2);
2272 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
2274 rfbColourMap
*cmap
=&screen
->colourMap
;
2275 cmap
->data
.bytes
= malloc (16*3);
2277 cmap
->data
.bytes
[i
*3 + 0] = default_red
[color_table
[i
]];
2278 cmap
->data
.bytes
[i
*3 + 1] = default_grn
[color_table
[i
]];
2279 cmap
->data
.bytes
[i
*3 + 2] = default_blu
[color_table
[i
]];
2283 screen
->serverFormat
.trueColour
= FALSE
;
2285 screen
->kbdAddEvent
= vncterm_kbd_event
;
2287 screen
->setXCutText
= vncterm_set_xcut_text
;
2289 screen
->ptrAddEvent
= vncterm_pointer_event
;
2291 screen
->desktopName
= "VNC Command Terminal";
2293 screen
->newClientHook
= new_client
;
2295 vt
->maxx
= screen
->width
;
2296 vt
->maxy
= screen
->height
;
2298 vt
->width
= vt
->maxx
/ 8;
2299 vt
->height
= vt
->maxy
/ 16;
2301 vt
->total_height
= vt
->height
* 20;
2302 vt
->scroll_height
= 0;
2307 vt
->region_bottom
= vt
->height
;
2309 vt
->g0enc
= LAT1_MAP
;
2310 vt
->g1enc
= GRAF_MAP
;
2311 vt
->cur_enc
= vt
->g0enc
;
2314 /* default text attributes */
2315 vt
->default_attrib
.bold
= 0;
2316 vt
->default_attrib
.uline
= 0;
2317 vt
->default_attrib
.blink
= 0;
2318 vt
->default_attrib
.invers
= 0;
2319 vt
->default_attrib
.unvisible
= 0;
2320 vt
->default_attrib
.fgcol
= 7;
2321 vt
->default_attrib
.bgcol
= 0;
2323 vt
->cur_attrib
= vt
->default_attrib
;
2325 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
2327 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
2328 vt
->cells
[i
].ch
= ' ';
2329 vt
->cells
[i
].attrib
= vt
->default_attrib
;
2332 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
2334 vt
->screen
= screen
;
2336 screen
->screenData
= (void*)vt
;
2338 //screen->autoPort = 1;
2341 passwds
[0] = vncticket
;
2344 screen
->authPasswdData
= (void *)passwds
;
2345 screen
->passwordCheck
= rfbCheckPasswordByList
;
2347 rfbRegisterSecurityHandler(&VncSecurityHandlerVencrypt
);
2350 rfbInitServer(screen
);
2356 main (int argc
, char** argv
)
2359 char **cmdargv
= NULL
;
2360 char *command
= "/bin/bash"; // execute normal shell as default
2367 struct timeval tv
, tv1
;
2368 time_t elapsed
, cur_time
;
2369 struct winsize dimensions
;
2370 unsigned long width
= 0;
2371 unsigned long height
= 0;
2373 if (gnutls_global_init () < 0) {
2374 fprintf(stderr
, "gnutls_global_init failed\n");
2378 if (gnutls_dh_params_init (&dh_params
) < 0) {
2379 fprintf(stderr
, "gnutls_dh_params_init failed\n");
2383 if (gnutls_dh_params_generate2 (dh_params
, DH_BITS
) < 0) {
2384 fprintf(stderr
, "gnutls_dh_params_init failed\n");
2388 for (i
= 1; i
< argc
; i
++) {
2389 if (!strcmp (argv
[i
], "-c")) {
2390 command
= argv
[i
+1];
2391 cmdargv
= &argv
[i
+1];
2398 for (i
= 1; i
< argc
; i
++) {
2399 if (!strcmp (argv
[i
], "-timeout")) {
2400 CHECK_ARGC (argc
, argv
, i
);
2401 idle_timeout
= atoi(argv
[i
+1]);
2402 rfbPurgeArguments(&argc
, &i
, 2, argv
); i
--;
2403 } else if (!strcmp (argv
[i
], "-authpath")) {
2404 CHECK_ARGC (argc
, argv
, i
);
2405 auth_path
= argv
[i
+1];
2406 rfbPurgeArguments(&argc
, &i
, 2, argv
); i
--;
2407 } else if (!strcmp (argv
[i
], "-perm")) {
2408 CHECK_ARGC (argc
, argv
, i
);
2409 auth_perm
= argv
[i
+1];
2410 rfbPurgeArguments(&argc
, &i
, 2, argv
); i
--;
2411 } else if (!strcmp (argv
[i
], "-width")) {
2412 CHECK_ARGC (argc
, argv
, i
);
2414 width
= strtoul(argv
[i
+1], NULL
, 10);
2415 if (errno
== 0 && width
>= 16 && width
< 0xFFFF) {
2416 screen_width
= width
;
2418 rfbPurgeArguments(&argc
, &i
, 2, argv
); i
--;
2419 } else if (!strcmp (argv
[i
], "-height")) {
2420 CHECK_ARGC (argc
, argv
, i
);
2422 height
= strtoul(argv
[i
+1], NULL
, 10);
2423 if (errno
== 0 && height
>= 32 && height
< 0xFFFF) {
2424 screen_height
= height
;
2426 rfbPurgeArguments(&argc
, &i
, 2, argv
); i
--;
2427 } else if (!strcmp (argv
[i
], "-notls")) {
2428 rfbPurgeArguments(&argc
, &i
, 1, argv
); i
--;
2429 if ((vncticket
= getenv("PVE_VNC_TICKET")) == NULL
) {
2430 fprintf(stderr
, "missing env PVE_VNC_TICKET (-notls)\n");
2436 unsetenv("PVE_VNC_TICKET"); // do not expose this to child
2440 gnutls_global_set_log_level(10);
2441 gnutls_global_set_log_function(vnc_debug_gnutls_log
);
2447 fontfd
= open(FONTFILE
, O_RDONLY
);
2449 perror("Error opening Fontfile 'FONTFILE'");
2452 if (fstat(fontfd
, &sb
) == -1) {
2453 perror("Stat on 'FONTFILE' failed");
2456 fontdata
= mmap(NULL
, sb
.st_size
, PROT_READ
, MAP_SHARED
, fontfd
, 0);
2457 if (fontdata
== MAP_FAILED
) {
2458 perror("Could not mmap 'FONTFILE'");
2463 vncTerm
*vt
= create_vncterm (argc
, argv
, screen_width
, screen_height
);
2465 setlocale(LC_ALL
, ""); // set from environment
2467 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
2469 // fixme: ist there a standard way to detect utf8 mode ?
2470 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
2474 dimensions
.ws_col
= vt
->width
;
2475 dimensions
.ws_row
= vt
->height
;
2477 setenv ("TERM", TERM
, 1);
2479 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
2482 // install default signal handlers
2483 signal (SIGQUIT
, SIG_DFL
);
2484 signal (SIGTERM
, SIG_DFL
);
2485 signal (SIGINT
, SIG_DFL
);
2488 execvp (command
, cmdargv
);
2490 execlp (command
, command
, NULL
);
2492 perror ("Error: exec failed\n");
2493 exit (-1); // should not be reached
2494 } else if (pid
== -1) {
2495 perror ("Error: fork failed\n");
2500 FD_SET (master
, &fs
);
2502 tv
.tv_usec
= 5000; /* 5 ms */
2504 last_time
= time (NULL
);
2512 cur_time
= time (NULL
);
2514 elapsed
= cur_time
- last_time
;
2515 //printf ("Elapsed %ld\n", elapsed);
2518 if (client_connected
) {
2519 if (idle_timeout
&& (elapsed
>= idle_timeout
)) {
2523 // wait at least 20 seconds for initial connect
2524 if (idle_timeout
&& (elapsed
>= (idle_timeout
> 20 ? idle_timeout
: 20))) {
2529 // exit after 30 minutes idle time
2530 if (elapsed
>= 30*60) {
2535 rfbProcessEvents (vt
->screen
, 40000); /* 40 ms */
2537 if (vt
->ibuf_count
> 0) {
2538 //printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
2539 write (master
, vt
->ibuf
, vt
->ibuf_count
);
2541 last_time
= time (NULL
);
2544 if (!vt
->mark_active
) {
2546 int num_fds
= select (master
+1, &fs1
, NULL
, NULL
, &tv1
);
2548 if (FD_ISSET (master
, &fs1
)) {
2551 while ((c
= read (master
, buffer
, 1024)) == -1) {
2552 if (errno
!= EAGAIN
) break;
2555 vncterm_puts (vt
, buffer
, c
);
2563 rfbScreenCleanup(vt
->screen
);
2567 waitpid(pid
, &status
, 0);
2569 munmap(fontdata
, sb
.st_size
);