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>
29 #include <arpa/inet.h>
32 #include <rfb/keysym.h>
33 #include <pty.h> /* for openpty and forkpty */
36 #include <sys/ioctl.h>
44 #include <gnutls/gnutls.h>
45 #include <gnutls/x509.h>
47 /* define this for debugging */
50 char *auth_path
= "/";
51 char *auth_perm
= "Sys.Console";
56 urlencode(char *buf
, const char *value
)
58 static const char *hexchar
= "0123456789abcdef";
61 int l
= strlen(value
);
62 for (i
= 0; i
< l
; i
++) {
64 if (('a' <= c
&& c
<= 'z') ||
65 ('A' <= c
&& c
<= 'Z') ||
66 ('0' <= c
&& c
<= '9')) {
72 *p
++ = hexchar
[c
>> 4];
73 *p
++ = hexchar
[c
& 15];
82 pve_auth_verify(const char *clientip
, const char *username
, const char *passwd
)
84 struct sockaddr_in server
;
86 int sfd
= socket(AF_INET
, SOCK_STREAM
, 0);
88 perror("pve_auth_verify: socket failed");
93 if ((he
= gethostbyname("localhost")) == NULL
) {
94 fprintf(stderr
, "pve_auth_verify: error resolving hostname\n");
98 memcpy(&server
.sin_addr
, he
->h_addr_list
[0], he
->h_length
);
99 server
.sin_family
= AF_INET
;
100 server
.sin_port
= htons(85);
102 if (connect(sfd
, (struct sockaddr
*)&server
, sizeof(server
))) {
103 perror("pve_auth_verify: error connecting to server");
111 p
= urlencode(p
, "username");
113 p
= urlencode(p
, username
);
116 p
= urlencode(p
, "password");
118 p
= urlencode(p
, passwd
);
121 p
= urlencode(p
, "path");
123 p
= urlencode(p
, auth_path
);
126 p
= urlencode(p
, "privs");
128 p
= urlencode(p
, auth_perm
);
130 sprintf(buf
, "POST /api2/json/access/ticket HTTP/1.1\n"
131 "Host: localhost:85\n"
132 "Connection: close\n"
134 "Content-Type: application/x-www-form-urlencoded\n"
135 "Content-Length: %zd\n\n%s\n", clientip
, strlen(form
), form
);
136 ssize_t len
= strlen(buf
);
137 ssize_t sb
= send(sfd
, buf
, len
, 0);
139 perror("pve_auth_verify: send failed");
143 fprintf(stderr
, "pve_auth_verify: partial send error\n");
147 len
= recv(sfd
, buf
, sizeof(buf
) - 1, 0);
149 perror("pve_auth_verify: recv failed");
155 //printf("DATA:%s\n", buf);
157 shutdown(sfd
, SHUT_RDWR
);
159 return strncmp(buf
, "HTTP/1.1 200 OK", 15);
162 shutdown(sfd
, SHUT_RDWR
);
167 static void vnc_debug_gnutls_log(int level
, const char* str
) {
168 fprintf(stderr
, "%d %s", level
, str
);
173 static gnutls_dh_params_t dh_params
;
176 gnutls_session_t session
;
181 gnutls_transport_ptr_t transport
,
185 rfbClientPtr cl
= (rfbClientPtr
)transport
;
189 n
= send(cl
->sock
, data
, len
, 0);
200 gnutls_transport_ptr_t transport
,
204 rfbClientPtr cl
= (rfbClientPtr
)transport
;
208 n
= recv(cl
->sock
, data
, len
, 0);
217 ssize_t
vnc_tls_read(rfbClientPtr cl
, void *buf
, size_t count
)
219 tls_client_t
*sd
= (tls_client_t
*)cl
->clientData
;
221 int ret
= gnutls_read(sd
->session
, buf
, count
);
223 if (ret
== GNUTLS_E_AGAIN
)
232 ssize_t
vnc_tls_write(rfbClientPtr cl
, void *buf
, size_t count
)
234 tls_client_t
*sd
= (tls_client_t
*)cl
->clientData
;
236 int ret
= gnutls_write(sd
->session
, buf
, count
);
238 if (ret
== GNUTLS_E_AGAIN
)
248 static gnutls_anon_server_credentials
249 tls_initialize_anon_cred(void)
251 gnutls_anon_server_credentials anon_cred
;
254 if ((ret
= gnutls_anon_allocate_server_credentials(&anon_cred
)) < 0) {
255 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret
));
259 gnutls_anon_set_server_dh_params(anon_cred
, dh_params
);
264 static gnutls_certificate_credentials_t
265 tls_initialize_x509_cred(void)
267 gnutls_certificate_credentials_t x509_cred
;
270 /* Paths to x509 certs/keys */
271 char *x509cacert
= "/etc/pve/pve-root-ca.pem";
272 char *x509cert
= "/etc/pve/local/pve-ssl.pem";
273 char *x509key
= "/etc/pve/local/pve-ssl.key";
275 if ((ret
= gnutls_certificate_allocate_credentials(&x509_cred
)) < 0) {
276 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret
));
280 if ((ret
= gnutls_certificate_set_x509_trust_file
281 (x509_cred
, x509cacert
, GNUTLS_X509_FMT_PEM
)) < 0) {
282 rfbLog("can't load CA certificate: %s\n", gnutls_strerror(ret
));
283 gnutls_certificate_free_credentials(x509_cred
);
287 if ((ret
= gnutls_certificate_set_x509_key_file
288 (x509_cred
, x509cert
, x509key
, GNUTLS_X509_FMT_PEM
)) < 0) {
289 rfbLog("can't load certificate & key: %s\n", gnutls_strerror(ret
));
290 gnutls_certificate_free_credentials(x509_cred
);
294 gnutls_certificate_set_dh_params (x509_cred
, dh_params
);
299 /* rfb tls security handler */
301 #define rfbSecTypeVencrypt 19
302 #define rfbVencryptTlsPlain 259
303 #define rfbVencryptX509Plain 262
305 void rfbEncodeU32(char *buf
, uint32_t value
)
307 buf
[0] = (value
>> 24) & 0xFF;
308 buf
[1] = (value
>> 16) & 0xFF;
309 buf
[2] = (value
>> 8) & 0xFF;
310 buf
[3] = value
& 0xFF;
313 uint32_t rfbDecodeU32(char *data
, size_t offset
)
315 return ((data
[offset
] << 24) | (data
[offset
+ 1] << 16) |
316 (data
[offset
+ 2] << 8) | data
[offset
+ 3]);
320 vencrypt_subauth_plain(rfbClientPtr cl
)
322 const char *err
= NULL
;
326 char clientip
[INET6_ADDRSTRLEN
];
328 struct sockaddr_in client
;
329 socklen_t addrlen
= sizeof(client
);
330 if (getpeername(cl
->sock
, &client
, &addrlen
) == 0) {
331 inet_ntop(client
.sin_family
, &client
.sin_addr
,
332 clientip
, sizeof(clientip
));
335 if ((n
= rfbReadExact(cl
, buf
, 8)) <= 0) {
336 err
= n
? "read failed" : "client gone";
340 uint32_t ulen
= rfbDecodeU32(buf
, 0);
341 uint32_t pwlen
= rfbDecodeU32(buf
, 4);
344 err
= "No User name.";
348 err
= "User name too long.";
352 err
= "Password too short";
356 err
= "Password too long.";
360 if ((n
= rfbReadExact(cl
, buf
, ulen
)) <= 0) {
361 err
= n
? "read failed" : "client gone";
365 char *username
= buf
;
366 char *passwd
= buf
+ ulen
+ 1;
367 if ((n
= rfbReadExact(cl
, passwd
, pwlen
)) <= 0) {
368 err
= n
? "read failed" : "client gone";
373 rfbLog("VencryptPlain: username: %s pw: %s\n", username
, passwd
);
375 if (pve_auth_verify(clientip
, username
, passwd
) == 0) {
376 rfbEncodeU32(buf
, 0); /* Accept auth completion */
377 rfbWriteExact(cl
, buf
, 4);
378 cl
->state
= RFB_INITIALISATION
;
382 err
= "Authentication failed";
384 rfbLog("VencryptPlain: %s\n", err
? err
: "no reason specified");
386 rfbEncodeU32(buf
, 1); /* Reject auth */
387 rfbWriteExact(cl
, buf
, 4);
388 if (cl
->protocolMinorVersion
>= 8) {
389 int elen
= strlen(err
);
390 rfbEncodeU32(buf
, elen
);
391 rfbWriteExact(cl
, buf
, 4);
392 rfbWriteExact(cl
, err
, elen
);
400 rfbVncAuthVencrypt(rfbClientPtr cl
)
404 /* Send VeNCrypt version 0.2 */
409 if (rfbWriteExact(cl
, buf
, 2) < 0) {
410 rfbLogPerror("rfbVncAuthVencrypt: write");
415 int n
= rfbReadExact(cl
, buf
, 2);
418 rfbLog("rfbVncAuthVencrypt: client gone\n");
420 rfbLogPerror("rfbVncAuthVencrypt: read");
425 if (buf
[0] != 0 || buf
[1] != 2) {
426 rfbLog("Unsupported VeNCrypt protocol %d.%d\n",
427 (int)buf
[0], (int)buf
[1]);
428 buf
[0] = 1; /* Reject version */
429 rfbWriteExact(cl
, buf
, 1);
434 /* Sending allowed auth */
435 int req_auth
= use_x509
? rfbVencryptX509Plain
: rfbVencryptTlsPlain
;
437 buf
[0] = 0; /* Accept version */
438 buf
[1] = 1; /* number of sub auths */
439 rfbEncodeU32(buf
+2, req_auth
);
440 if (rfbWriteExact(cl
, buf
, 6) < 0) {
441 rfbLogPerror("rfbVncAuthVencrypt: write");
446 n
= rfbReadExact(cl
, buf
, 4);
449 rfbLog("rfbVncAuthVencrypt: client gone\n");
451 rfbLogPerror("rfbVncAuthVencrypt: read");
456 int auth
= rfbDecodeU32(buf
, 0);
457 if (auth
!= req_auth
) {
458 buf
[0] = 1; /* Reject auth*/
459 rfbWriteExact(cl
, buf
, 1);
464 buf
[0] = 1; /* Accept auth */
465 if (rfbWriteExact(cl
, buf
, 1) < 0) {
466 rfbLogPerror("rfbVncAuthVencrypt: write");
471 tls_client_t
*sd
= calloc(1, sizeof(tls_client_t
));
473 if (sd
->session
== NULL
) {
474 if (gnutls_init(&sd
->session
, GNUTLS_SERVER
) < 0) {
475 rfbLog("gnutls_init failed\n");
481 if ((ret
= gnutls_set_default_priority(sd
->session
)) < 0) {
482 rfbLog("gnutls_set_default_priority failed: %s\n", gnutls_strerror(ret
));
488 /* optimize for speed */
489 static const int cipher_priority_performance
[] = {
490 GNUTLS_CIPHER_ARCFOUR_128
,
491 GNUTLS_CIPHER_AES_128_CBC
,
492 GNUTLS_CIPHER_3DES_CBC
, 0
495 if ((ret
= gnutls_cipher_set_priority(sd
->session
, cipher_priority_performance
)) < 0) {
496 rfbLog("gnutls_cipher_set_priority failed: %s\n", gnutls_strerror(ret
));
502 static const int kx_anon
[] = {GNUTLS_KX_ANON_DH
, 0};
503 static const int kx_x509
[] = {GNUTLS_KX_DHE_DSS
, GNUTLS_KX_RSA
, GNUTLS_KX_DHE_RSA
, GNUTLS_KX_SRP
, 0};
504 if ((ret
= gnutls_kx_set_priority(sd
->session
, use_x509
? kx_x509
: kx_anon
)) < 0) {
505 rfbLog("gnutls_kx_set_priority failed: %s\n", gnutls_strerror(ret
));
511 static const int cert_type_priority
[] = { GNUTLS_CRT_X509
, 0 };
512 if ((ret
= gnutls_certificate_type_set_priority(sd
->session
, cert_type_priority
)) < 0) {
513 rfbLog("gnutls_certificate_type_set_priority failed: %s\n",
514 gnutls_strerror(ret
));
520 static const int protocol_priority
[]= { GNUTLS_TLS1_1
, GNUTLS_TLS1_0
, GNUTLS_SSL3
, 0 };
521 if ((ret
= gnutls_protocol_set_priority(sd
->session
, protocol_priority
)) < 0) {
522 rfbLog("gnutls_protocol_set_priority failed: %s\n",
523 gnutls_strerror(ret
));
530 gnutls_certificate_server_credentials x509_cred
;
532 if (!(x509_cred
= tls_initialize_x509_cred())) {
538 if (gnutls_credentials_set(sd
->session
, GNUTLS_CRD_CERTIFICATE
, x509_cred
) < 0) {
540 gnutls_certificate_free_credentials(x509_cred
);
546 gnutls_anon_server_credentials anon_cred
;
548 if (!(anon_cred
= tls_initialize_anon_cred())) {
554 if ((ret
= gnutls_credentials_set(sd
->session
, GNUTLS_CRD_ANON
, anon_cred
)) < 0) {
555 rfbLog("gnutls_credentials_set failed: %s\n", gnutls_strerror(ret
));
556 gnutls_anon_free_server_credentials(anon_cred
);
563 gnutls_transport_set_ptr(sd
->session
, (gnutls_transport_ptr_t
)cl
);
564 gnutls_transport_set_push_function(sd
->session
, vnc_tls_push
);
565 gnutls_transport_set_pull_function(sd
->session
, vnc_tls_pull
);
570 if ((ret
= gnutls_handshake(sd
->session
)) < 0) {
571 if (!gnutls_error_is_fatal(ret
)) {
575 rfbLog("rfbVncAuthVencrypt: handshake failed\n");
580 /* set up TLS read/write hooks */
582 cl
->sock_read_fn
= &vnc_tls_read
;
583 cl
->sock_write_fn
= &vnc_tls_write
;
585 vencrypt_subauth_plain(cl
);
588 static rfbSecurityHandler VncSecurityHandlerVencrypt
= {
596 #define TERMIDCODE "[?1;2c" // vt100 ID
598 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
599 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
600 print_usage (NULL); \
604 /* these colours are from linux kernel drivers/char/vt.c */
606 static int idle_timeout
= 1;
608 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
609 8,12,10,14, 9,13,11,15 };
611 /* the default colour table, for VGA+ colour systems */
612 int default_red
[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
613 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
614 int default_grn
[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
615 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
616 int default_blu
[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
617 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
620 print_usage (const char *msg
)
622 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
623 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
626 /* Convert UCS2 to UTF8 sequence, trailing zero */
628 ucs2_to_utf8 (unicode c
, char *out
)
631 out
[0] = c
; // 0*******
634 } else if (c
< 0x800) {
635 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
636 out
[1] = 0x80 | (c
& 0x3f);
640 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
641 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
642 out
[2] = 0x80 | (c
& 0x3f);
651 rfb_draw_char (rfbScreenInfoPtr rfbScreen
, int x
, int y
,
652 unsigned short c
, rfbPixel col
)
654 if (c
> vt_font_size
) {
655 rfbLog ("undefined font glyph %d\n", c
);
660 unsigned char *data
= vt_font_data
+ c
*16;
661 unsigned char d
=*data
;
662 int rowstride
=rfbScreen
->paddedWidthInBytes
;
663 char *colour
=(char*)&col
;
665 for(j
= 0; j
< 16; j
++) {
666 for(i
= 0; i
< 8; i
++) {
672 *(rfbScreen
->frameBuffer
+(y
+j
)*rowstride
+(x
+i
)) = *colour
;
679 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
681 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
698 int ec
= vt_fontmap
[ch
];
700 rfbFillRect (vt
->screen
, rx
, ry
, rxe
, rye
, bg
);
706 // unsuported attributes = (attrib.blink || attrib.unvisible)
708 rfb_draw_char (vt
->screen
, rx
, ry
, ec
, fg
);
711 rfbDrawLine (vt
->screen
, rx
, ry
+ 14, rxe
, ry
+ 14, fg
);
714 rfbMarkRectAsModified (vt
->screen
, rx
, ry
, rxe
, rye
);
719 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
721 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
723 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
724 int y2
= y1
- vt
->y_displ
;
726 y2
+= vt
->total_height
;
728 if (y2
< vt
->height
) {
729 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
730 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
);
735 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
737 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
739 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
740 int y2
= y1
- vt
->y_displ
;
742 y2
+= vt
->total_height
;
744 if (y2
< vt
->height
) {
745 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
747 c
->attrib
= vt
->default_attrib
;
748 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
749 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
751 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
756 vncterm_show_cursor (vncTerm
*vt
, int show
)
759 if (x
>= vt
->width
) {
763 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
764 int y
= y1
- vt
->y_displ
;
766 y
+= vt
->total_height
;
769 if (y
< vt
->height
) {
771 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
774 TextAttributes attrib
= vt
->default_attrib
;
775 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
776 draw_char_at (vt
, x
, y
, c
->ch
, attrib
);
778 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
784 vncterm_refresh (vncTerm
*vt
)
788 rfbFillRect (vt
->screen
, 0, 0, vt
->maxx
, vt
->maxy
, vt
->default_attrib
.bgcol
);
791 for(y
= 0; y
< vt
->height
; y
++) {
792 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
793 for(x
= 0; x
< vt
->width
; x
++) {
794 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
797 if (++y1
== vt
->total_height
)
800 rfbMarkRectAsModified (vt
->screen
, 0, 0, vt
->maxx
, vt
->maxy
);
802 vncterm_show_cursor (vt
, 1);
806 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
808 if ((top
+ lines
) >= bottom
) {
809 lines
= bottom
- top
-1;
812 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
820 int rowstride
= vt
->screen
->paddedWidthInBytes
;
821 int rows
= (bottom
- top
- lines
)*16;
823 char *in
= vt
->screen
->frameBuffer
+y0
*rowstride
;
824 char *out
= vt
->screen
->frameBuffer
+y1
*rowstride
;
825 memmove(out
,in
, rowstride
*rows
);
827 memset(vt
->screen
->frameBuffer
+y0
*rowstride
, 0, h
*rowstride
);
828 rfbMarkRectAsModified (vt
->screen
, 0, y0
, vt
->screen
->width
, y2
);
831 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
832 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
833 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
835 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
838 for (i
= 0; i
< lines
; i
++) {
840 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
841 for(j
= 0; j
< vt
->width
; j
++) {
842 c
->attrib
= vt
->default_attrib
;
850 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
852 if ((top
+ lines
) >= bottom
) {
853 lines
= bottom
- top
- 1;
856 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
862 int y1
= (top
+ lines
)*16;
864 int rowstride
= vt
->screen
->paddedWidthInBytes
;
865 int rows
= (bottom
- top
- lines
)*16;
867 char *in
= vt
->screen
->frameBuffer
+y1
*rowstride
;
868 char *out
= vt
->screen
->frameBuffer
+y0
*rowstride
;
869 memmove(out
,in
, rowstride
*rows
);
871 memset(vt
->screen
->frameBuffer
+(y2
-h
)*rowstride
, 0, h
*rowstride
);
873 rfbMarkRectAsModified (vt
->screen
, 0, y0
, vt
->screen
->width
, y2
);
875 if (!moveattr
) return;
880 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
881 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
882 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
884 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
887 for (i
= 1; i
<= lines
; i
++) {
889 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
890 for(j
= 0; j
< vt
->width
; j
++) {
891 c
->attrib
= vt
->default_attrib
;
899 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
901 if (vt
->altbuf
|| lines
== 0) return;
905 int i
= vt
->scroll_height
;
906 if (i
> vt
->total_height
- vt
->height
)
907 i
= vt
->total_height
- vt
->height
;
908 int y1
= vt
->y_base
- i
;
910 y1
+= vt
->total_height
;
911 for(i
= 0; i
< lines
; i
++) {
912 if (vt
->y_displ
== y1
) break;
913 if (--vt
->y_displ
< 0) {
914 vt
->y_displ
= vt
->total_height
- 1;
919 for(i
= 0; i
< lines
; i
++) {
920 if (vt
->y_displ
== vt
->y_base
) break;
921 if (++vt
->y_displ
== vt
->total_height
) {
928 vncterm_refresh (vt
);
931 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
933 int len
= strlen (esc
);
936 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
937 vt
->ibuf
[vt
->ibuf_count
++] = 27;
938 for (i
= 0; i
< len
; i
++) {
939 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
945 vncterm_put_lf (vncTerm
*vt
)
947 if (vt
->cy
+ 1 == vt
->region_bottom
) {
949 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
950 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
954 if (vt
->y_displ
== vt
->y_base
) {
955 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
958 if (vt
->y_displ
== vt
->y_base
) {
959 if (++vt
->y_displ
== vt
->total_height
) {
964 if (++vt
->y_base
== vt
->total_height
) {
968 if (vt
->scroll_height
< vt
->total_height
) {
972 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
973 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
975 for (x
= 0; x
< vt
->width
; x
++) {
977 c
->attrib
= vt
->default_attrib
;
981 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
983 } else if (vt
->cy
< vt
->height
- 1) {
990 vncterm_csi_m (vncTerm
*vt
)
994 for (i
= 0; i
< vt
->esc_count
; i
++) {
995 switch (vt
->esc_buf
[i
]) {
996 case 0: /* reset all console attributes to default */
997 vt
->cur_attrib
= vt
->default_attrib
;
1000 vt
->cur_attrib
.bold
= 1;
1003 vt
->cur_attrib
.uline
= 1;
1006 vt
->cur_attrib
.blink
= 1;
1009 vt
->cur_attrib
.invers
= 1;
1012 vt
->cur_attrib
.unvisible
= 1;
1015 vt
->cur_enc
= LAT1_MAP
;
1016 // fixme: dispaly controls = 0 ?
1017 // fixme: toggle meta = 0 ?
1020 vt
->cur_enc
= IBMPC_MAP
;
1021 // fixme: dispaly controls = 1 ?
1022 // fixme: toggle meta = 0 ?
1025 vt
->cur_enc
= IBMPC_MAP
;
1026 // fixme: dispaly controls = 1 ?
1027 // fixme: toggle meta = 1 ?
1030 vt
->cur_attrib
.bold
= 0;
1033 vt
->cur_attrib
.uline
= 0;
1036 vt
->cur_attrib
.blink
= 0;
1039 vt
->cur_attrib
.invers
= 0;
1042 vt
->cur_attrib
.unvisible
= 0;
1052 /* set foreground color */
1053 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
1056 /* reset color to default, enable underline */
1057 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
1058 vt
->cur_attrib
.uline
= 1;
1061 /* reset color to default, disable underline */
1062 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
1063 vt
->cur_attrib
.uline
= 0;
1073 /* set background color */
1074 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
1077 /* reset background color */
1078 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
1081 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
1088 vncterm_save_cursor (vncTerm
*vt
)
1090 vt
->cx_saved
= vt
->cx
;
1091 vt
->cy_saved
= vt
->cy
;
1092 vt
->cur_attrib_saved
= vt
->cur_attrib
;
1093 vt
->charset_saved
= vt
->charset
;
1094 vt
->g0enc_saved
= vt
->g0enc
;
1095 vt
->g1enc_saved
= vt
->g1enc
;
1096 vt
->cur_enc_saved
= vt
->cur_enc
;
1100 vncterm_restore_cursor (vncTerm
*vt
)
1102 vt
->cx
= vt
->cx_saved
;
1103 vt
->cy
= vt
->cy_saved
;
1104 vt
->cur_attrib
= vt
->cur_attrib_saved
;
1105 vt
->charset
= vt
->charset_saved
;
1106 vt
->g0enc
= vt
->g0enc_saved
;
1107 vt
->g1enc
= vt
->g1enc_saved
;
1108 vt
->cur_enc
= vt
->cur_enc_saved
;
1112 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
1116 vt
->y_displ
= vt
->y_base
;
1120 if (vt
->altbuf
) return;
1124 /* alternate buffer & cursor */
1126 vncterm_save_cursor (vt
);
1127 /* save screen to altcels */
1128 for (y
= 0; y
< vt
->height
; y
++) {
1129 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
1130 for (x
= 0; x
< vt
->width
; x
++) {
1131 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
1136 for (y
= 0; y
<= vt
->height
; y
++) {
1137 for (x
= 0; x
< vt
->width
; x
++) {
1138 vncterm_clear_xy (vt
, x
, y
);
1144 if (vt
->altbuf
== 0) return;
1148 /* restore saved data */
1149 for (y
= 0; y
< vt
->height
; y
++) {
1150 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
1151 for (x
= 0; x
< vt
->width
; x
++) {
1152 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
1156 vncterm_restore_cursor (vt
);
1159 vncterm_refresh (vt
);
1163 vncterm_set_mode (vncTerm
*vt
, int on_off
)
1167 for (i
= 0; i
<= vt
->esc_count
; i
++) {
1168 if (vt
->esc_ques
) { /* DEC private modes set/reset */
1169 switch(vt
->esc_buf
[i
]) {
1170 case 10: /* X11 mouse reporting on/off */
1172 vt
->report_mouse
= on_off
;
1174 case 1049: /* start/end special app mode (smcup/rmcup) */
1175 vncterm_set_alternate_buffer (vt
, on_off
);
1177 case 25: /* Cursor on/off */
1178 case 9: /* X10 mouse reporting on/off */
1179 case 6: /* Origin relative/absolute */
1180 case 1: /* Cursor keys in appl mode*/
1181 case 5: /* Inverted screen on/off */
1182 case 7: /* Autowrap on/off */
1183 case 8: /* Autorepeat on/off */
1186 } else { /* ANSI modes set/reset */
1187 /* fixme: implement me */
1193 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
1195 /* verify all boundaries */
1201 if (x
>= vt
->width
) {
1211 if (y
>= vt
->height
) {
1218 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
1219 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
1220 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
1223 vncterm_putchar (vncTerm
*vt
, unicode ch
)
1229 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
1232 switch(vt
->tty_state
) {
1234 vt
->tty_state
= ESnormal
;
1237 vt
->tty_state
= ESsquare
;
1240 vt
->tty_state
= ESnonstd
;
1243 vt
->tty_state
= ESpercent
;
1246 vncterm_save_cursor (vt
);
1249 vncterm_restore_cursor (vt
);
1252 vt
->tty_state
= ESsetG0
; // SET G0
1255 vt
->tty_state
= ESsetG1
; // SET G1
1258 /* cursor up (ri) */
1259 if (vt
->cy
== vt
->region_top
)
1260 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
1261 else if (vt
->cy
> 0) {
1266 /* numeric keypad - ignored */
1269 /* appl. keypad - ignored */
1273 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
1278 case ESnonstd
: /* Operating System Controls */
1279 vt
->tty_state
= ESnormal
;
1282 case 'P': /* palette escape sequence */
1283 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
1288 vt
->tty_state
= ESpalette
;
1290 case 'R': /* reset palette */
1291 // fixme: reset_palette(vc);
1298 vt
->osc_textbuf
[0] = 0;
1299 vt
->tty_state
= ESosc1
;
1303 fprintf (stderr
, "unhandled OSC %c\n", ch
);
1305 vt
->tty_state
= ESnormal
;
1310 vt
->tty_state
= ESnormal
;
1312 vt
->tty_state
= ESosc2
;
1315 fprintf (stderr
, "got illegal OSC sequence\n");
1320 if (ch
!= 0x9c && ch
!= 7) {
1322 while (vt
->osc_textbuf
[i
]) i
++;
1323 vt
->osc_textbuf
[i
++] = ch
;
1324 vt
->osc_textbuf
[i
] = 0;
1327 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
1329 vt
->tty_state
= ESnormal
;
1333 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
1334 || (ch
>= 'a' && ch
<= 'f')) {
1335 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
1336 if (vt
->esc_count
== 7) {
1337 // fixme: this does not work - please test
1338 rfbColourMap
*cmap
=&vt
->screen
->colourMap
;
1340 int i
= color_table
[vt
->esc_buf
[0]] * 3, j
= 1;
1341 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1342 cmap
->data
.bytes
[i
++] += vt
->esc_buf
[j
++];
1343 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1344 cmap
->data
.bytes
[i
++] += vt
->esc_buf
[j
++];
1345 cmap
->data
.bytes
[i
] = 16 * vt
->esc_buf
[j
++];
1346 cmap
->data
.bytes
[i
] += vt
->esc_buf
[j
];
1348 //set_palette(vc); ?
1350 vt
->tty_state
= ESnormal
;
1353 vt
->tty_state
= ESnormal
;
1356 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
1361 vt
->esc_has_par
= 0;
1362 vt
->tty_state
= ESgetpars
;
1365 vt
->tty_state
= ESidquery
;
1369 if ((vt
->esc_ques
= (ch
== '?'))) {
1373 if (ch
>= '0' && ch
<= '9') {
1374 vt
->esc_has_par
= 1;
1375 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
1376 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
1379 } else if (ch
== ';') {
1383 if (vt
->esc_has_par
) {
1386 vt
->tty_state
= ESgotpars
;
1390 vt
->tty_state
= ESnormal
;
1393 char *qes
= vt
->esc_ques
? "?" : "";
1394 if (vt
->esc_count
== 0) {
1395 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
1396 } else if (vt
->esc_count
== 1) {
1397 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1400 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
1401 for (i
= 1; i
< vt
->esc_count
; i
++) {
1402 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1404 fprintf (stderr
, "%c\n", ch
);
1410 vncterm_set_mode (vt
, 1);
1413 vncterm_set_mode (vt
, 0);
1416 if (!vt
->esc_count
) {
1417 vt
->esc_count
++; // default parameter 0
1422 /* report cursor position */
1423 /* TODO: send ESC[row;colR */
1426 /* move cursor up */
1427 if (vt
->esc_buf
[0] == 0) {
1430 vt
->cy
-= vt
->esc_buf
[0];
1437 /* move cursor down */
1438 if (vt
->esc_buf
[0] == 0) {
1441 vt
->cy
+= vt
->esc_buf
[0];
1442 if (vt
->cy
>= vt
->height
) {
1443 vt
->cy
= vt
->height
- 1;
1448 /* move cursor right */
1449 if (vt
->esc_buf
[0] == 0) {
1452 vt
->cx
+= vt
->esc_buf
[0];
1453 if (vt
->cx
>= vt
->width
) {
1454 vt
->cx
= vt
->width
- 1;
1458 /* move cursor left */
1459 if (vt
->esc_buf
[0] == 0) {
1462 vt
->cx
-= vt
->esc_buf
[0];
1469 /* move cursor to column */
1470 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
1473 /* move cursor to row */
1474 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
1478 /* move cursor to row, column */
1479 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
1482 switch (vt
->esc_buf
[0]) {
1484 /* clear to end of screen */
1485 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
1486 for (x
= 0; x
< vt
->width
; x
++) {
1487 if (y
== vt
->cy
&& x
< vt
->cx
) {
1490 vncterm_clear_xy (vt
, x
, y
);
1495 /* clear from beginning of screen */
1496 for (y
= 0; y
<= vt
->cy
; y
++) {
1497 for (x
= 0; x
< vt
->width
; x
++) {
1498 if (y
== vt
->cy
&& x
> vt
->cx
) {
1501 vncterm_clear_xy (vt
, x
, y
);
1506 /* clear entire screen */
1507 for (y
= 0; y
<= vt
->height
; y
++) {
1508 for (x
= 0; x
< vt
->width
; x
++) {
1509 vncterm_clear_xy (vt
, x
, y
);
1516 switch (vt
->esc_buf
[0]) {
1519 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
1520 vncterm_clear_xy (vt
, x
, vt
->cy
);
1524 /* clear from beginning of line */
1525 for (x
= 0; x
<= vt
->cx
; x
++) {
1526 vncterm_clear_xy (vt
, x
, vt
->cy
);
1530 /* clear entire line */
1531 for(x
= 0; x
< vt
->width
; x
++) {
1532 vncterm_clear_xy (vt
, x
, vt
->cy
);
1541 if (c
> vt
->height
- vt
->cy
)
1542 c
= vt
->height
- vt
->cy
;
1546 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
1552 if (c
> vt
->height
- vt
->cy
)
1553 c
= vt
->height
- vt
->cy
;
1557 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
1563 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
1569 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
1572 /* delete c character */
1575 if (c
> vt
->width
- vt
->cx
)
1576 c
= vt
->width
- vt
->cx
;
1580 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
1581 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1582 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
1583 TextCell
*src
= dst
+ c
;
1585 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1587 src
->attrib
= vt
->default_attrib
;
1588 vncterm_update_xy (vt
, x
, vt
->cy
);
1592 /* save cursor position */
1593 vncterm_save_cursor (vt
);
1596 /* restore cursor position */
1597 vncterm_restore_cursor (vt
);
1600 /* erase c characters */
1604 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1606 for(i
= 0; i
< c
; i
++) {
1607 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1611 /* insert c character */
1613 if (c
> (vt
->width
- vt
->cx
)) {
1614 c
= vt
->width
- vt
->cx
;
1618 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1619 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1620 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1621 TextCell
*dst
= src
+ c
;
1623 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1625 src
->attrib
= vt
->cur_attrib
;
1626 vncterm_update_xy (vt
, x
, vt
->cy
);
1632 if (!vt
->esc_buf
[0])
1634 if (!vt
->esc_buf
[1])
1635 vt
->esc_buf
[1] = vt
->height
;
1636 /* Minimum allowed region is 2 lines */
1637 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1638 vt
->esc_buf
[1] <= vt
->height
) {
1639 vt
->region_top
= vt
->esc_buf
[0] - 1;
1640 vt
->region_bottom
= vt
->esc_buf
[1];
1642 vt
->cy
= vt
->region_top
;
1644 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1651 if (vt
->esc_count
== 0) {
1652 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1653 } else if (vt
->esc_count
== 1) {
1654 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1657 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1658 for (i
= 1; i
< vt
->esc_count
; i
++) {
1659 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1661 fprintf (stderr
, "%c\n", ch
);
1668 case ESsetG0
: // Set G0
1669 vt
->tty_state
= ESnormal
;
1672 vt
->g0enc
= GRAF_MAP
;
1674 vt
->g0enc
= LAT1_MAP
;
1676 vt
->g0enc
= IBMPC_MAP
;
1678 vt
->g0enc
= USER_MAP
;
1680 if (vt
->charset
== 0)
1681 vt
->cur_enc
= vt
->g0enc
;
1684 case ESsetG1
: // Set G1
1685 vt
->tty_state
= ESnormal
;
1688 vt
->g1enc
= GRAF_MAP
;
1690 vt
->g1enc
= LAT1_MAP
;
1692 vt
->g1enc
= IBMPC_MAP
;
1694 vt
->g1enc
= USER_MAP
;
1696 if (vt
->charset
== 1)
1697 vt
->cur_enc
= vt
->g1enc
;
1700 case ESidquery
: // vt100 query id
1701 vt
->tty_state
= ESnormal
;
1705 fprintf (stderr
, "ESC[>c Query term ID\n");
1707 vncterm_respond_esc (vt
, TERMIDCODE
);
1711 vt
->tty_state
= ESnormal
;
1713 case '@': /* defined in ISO 2022 */
1716 case 'G': /* prelim official escape code */
1717 case '8': /* retained for compatibility */
1722 default: // ESnormal
1723 vt
->tty_state
= ESnormal
;
1728 case 7: /* alert aka. bell */
1729 rfbSendBell(vt
->screen
);
1731 case 8: /* backspace */
1735 case 9: /* tabspace */
1736 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1738 vncterm_put_lf (vt
);
1740 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1746 vncterm_put_lf (vt
);
1748 case 13: /* carriage return */
1752 /* SI (shift in), select character set 1 */
1754 vt
->cur_enc
= vt
->g1enc
;
1755 /* fixme: display controls = 1 */
1758 /* SO (shift out), select character set 0 */
1760 vt
->cur_enc
= vt
->g0enc
;
1761 /* fixme: display controls = 0 */
1764 vt
->tty_state
= ESesc
;
1766 case 127: /* delete */
1769 case 128+27: /* csi */
1770 vt
->tty_state
= ESsquare
;
1773 if (vt
->cx
>= vt
->width
) {
1776 vncterm_put_lf (vt
);
1779 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1780 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1781 c
->attrib
= vt
->cur_attrib
;
1783 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1792 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1796 vncterm_show_cursor (vt
, 0);
1799 unsigned char c
= *buf
;
1803 if (vt
->tty_state
!= ESnormal
) {
1804 // never translate escape sequence
1806 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1808 if(c
& 0x80) { // utf8 multi-byte sequence
1810 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1811 // inside UTF8 sequence
1812 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1814 if (vt
->utf_count
== 0) {
1820 // first char of a UTF8 sequence
1821 if ((c
& 0xe0) == 0xc0) {
1823 vt
->utf_char
= (c
& 0x1f);
1824 } else if ((c
& 0xf0) == 0xe0) {
1826 vt
->utf_char
= (c
& 0x0f);
1827 } else if ((c
& 0xf8) == 0xf0) {
1829 vt
->utf_char
= (c
& 0x07);
1830 } else if ((c
& 0xfc) == 0xf8) {
1832 vt
->utf_char
= (c
& 0x03);
1833 } else if ((c
& 0xfe) == 0xfc) {
1835 vt
->utf_char
= (c
& 0x01);
1848 // never translate controls
1849 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1850 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1856 vncterm_putchar (vt
, tc
);
1859 vncterm_show_cursor (vt
, 1);
1864 vncterm_kbd_event (rfbBool down
, rfbKeySym keySym
, rfbClientPtr cl
)
1866 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
1867 static int control
= 0;
1868 static int shift
= 0;
1871 //fprintf (stderr, "KEYEVENT:%d: %08x\n", down == 0, keySym);fflush (stderr);
1873 //fprintf (stderr, "KEYPRESS: %d\n", keySym);fflush (stderr);
1875 if (keySym
== XK_Shift_L
|| keySym
== XK_Shift_R
) {
1877 } if (keySym
== XK_Control_L
|| keySym
== XK_Control_R
) {
1879 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1882 if(keySym
>= 'a' && keySym
<= 'z')
1884 else if (keySym
>= 'A' && keySym
<= 'Z')
1898 case XK_Delete
: /* kdch1 */
1901 case XK_Home
: /* khome */
1905 case XK_KP_End
: /* kend */
1907 case XK_Insert
: /* kich1 */
1911 case XK_KP_Up
: /* kcuu1 */
1913 case XK_Down
: /* kcud1 */
1917 case XK_KP_Right
: /* kcuf1 */
1920 case XK_KP_Left
: /* kcub1 */
1924 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1930 vncterm_virtual_scroll (vt
, vt
->height
/2);
1964 fprintf (stderr
, "KEYPRESS OUT:%s: %d\n", esc
, keySym
); fflush (stderr
);
1967 if (vt
->y_displ
!= vt
->y_base
) {
1968 vt
->y_displ
= vt
->y_base
;
1969 vncterm_refresh (vt
);
1973 vncterm_respond_esc (vt
, esc
);
1974 } else if(keySym
<0x100) {
1976 int len
= ucs2_to_utf8 (keySym
& 0x0fff, &vt
->ibuf
[vt
->ibuf_count
]);
1977 vt
->ibuf_count
+= len
;
1979 vt
->ibuf
[vt
->ibuf_count
++] = (char)keySym
;
1984 if (keySym
== XK_Shift_L
|| keySym
== XK_Shift_R
) {
1986 } else if (keySym
== XK_Control_L
|| keySym
== XK_Control_R
) {
1993 vncterm_set_xcut_text (char* str
, int len
, struct _rfbClientRec
* cl
)
1995 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
1997 // seems str is Latin-1 encoded
1998 if (vt
->selection
) free (vt
->selection
);
1999 vt
->selection
= (unicode
*)malloc (len
*sizeof (unicode
));
2001 for (i
= 0; i
< len
; i
++) {
2002 vt
->selection
[i
] = str
[i
] & 0xff;
2004 vt
->selection_len
= len
;
2008 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
2012 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
2015 vncterm_respond_esc (vt
, buf
);
2019 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
2021 int x
= (pos
%vt
->width
)*8;
2022 int y
= (pos
/vt
->width
)*16;
2025 rfbScreenInfoPtr s
=vt
->screen
;
2027 char *b
= s
->frameBuffer
+y
*s
->width
+x
;
2029 for (j
=0; j
< 16; j
++) {
2030 for(i
=0; i
< 8; i
++) {
2031 b
[j
*s
->width
+i
] ^= 0x0f;
2032 rfbMarkRectAsModified (s
, x
, y
, x
+8, y
+16);
2038 vncterm_pointer_event (int buttonMask
, int x
, int y
, rfbClientPtr cl
)
2040 vncTerm
*vt
=(vncTerm
*)cl
->screen
->screenData
;
2041 static int button2_released
= 1;
2042 static int last_mask
= 0;
2043 static int sel_start_pos
= 0;
2044 static int sel_end_pos
= 0;
2051 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
2053 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
2055 if (vt
->report_mouse
&& buttonMask
!= last_mask
) {
2056 last_mask
= buttonMask
;
2057 if (buttonMask
& 1) {
2058 mouse_report (vt
, 0, cx
, cy
);
2060 if (buttonMask
& 2) {
2061 mouse_report (vt
, 1, cx
, cy
);
2063 if (buttonMask
& 4) {
2064 mouse_report (vt
, 2, cx
, cy
);
2067 mouse_report (vt
, 3, cx
, cy
);
2071 if (buttonMask
& 2) {
2072 if(button2_released
&& vt
->selection
) {
2074 for(i
= 0; i
< vt
->selection_len
; i
++) {
2075 if (vt
->ibuf_count
< IBUFSIZE
- 6) { // uft8 is max 6 characters wide
2077 vt
->ibuf_count
+= ucs2_to_utf8 (vt
->selection
[i
], &vt
->ibuf
[vt
->ibuf_count
]);
2079 vt
->ibuf
[vt
->ibuf_count
++] = vt
->selection
[i
];
2083 if (vt
->y_displ
!= vt
->y_base
) {
2084 vt
->y_displ
= vt
->y_base
;
2085 vncterm_refresh (vt
);
2088 button2_released
= 0;
2090 button2_released
= 1;
2093 if (buttonMask
& 1) {
2094 int pos
= cy
*vt
->width
+ cx
;
2096 // code borrowed from libvncserver (VNConsole.c)
2098 if (!vt
->mark_active
) {
2100 vt
->mark_active
= 1;
2101 sel_start_pos
= sel_end_pos
= pos
;
2102 vncterm_toggle_marked_cell (vt
, pos
);
2106 if (pos
!= sel_end_pos
) {
2108 if (pos
> sel_end_pos
) {
2109 cx
= sel_end_pos
; cy
=pos
;
2111 cx
=pos
; cy
=sel_end_pos
;
2114 if (cx
< sel_start_pos
) {
2115 if (cy
< sel_start_pos
) cy
--;
2121 vncterm_toggle_marked_cell (vt
, cx
);
2129 } else if (vt
->mark_active
) {
2130 vt
->mark_active
= 0;
2132 if (sel_start_pos
> sel_end_pos
) {
2133 int tmp
= sel_start_pos
- 1;
2134 sel_start_pos
= sel_end_pos
;
2138 int len
= sel_end_pos
- sel_start_pos
+ 1;
2140 if (vt
->selection
) free (vt
->selection
);
2141 vt
->selection
= (unicode
*)malloc (len
*sizeof (unicode
));
2142 vt
->selection_len
= len
;
2143 char *sel_latin1
= (char *)malloc (len
+ 1);
2145 for (i
= 0; i
< len
; i
++) {
2146 int pos
= sel_start_pos
+ i
;
2147 int x
= pos
% vt
->width
;
2148 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
2149 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
2150 vt
->selection
[i
] = c
->ch
;
2151 sel_latin1
[i
] = (char)c
->ch
;
2154 sel_latin1
[len
] = 0;
2155 rfbGotXCutText (vt
->screen
, sel_latin1
, len
);
2158 while (sel_start_pos
<= sel_end_pos
) {
2159 vncterm_toggle_marked_cell (vt
, sel_start_pos
++);
2164 rfbDefaultPtrAddEvent (buttonMask
, x
, y
, cl
);
2167 static int client_count
= 0;
2168 static int client_connected
= 0;
2169 static int last_client
= 1;
2170 static time_t last_time
= 0;
2173 client_gone (rfbClientPtr client
)
2177 last_time
= time (NULL
);
2179 if (client_count
<= 0) {
2184 /* libvncserver callback for when a new client connects */
2185 enum rfbNewClientAction
2186 new_client (rfbClientPtr client
)
2188 client
->clientGoneHook
= client_gone
;
2191 last_time
= time (NULL
);
2194 client_connected
= 1;
2196 return RFB_CLIENT_ACCEPT
;
2200 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
2204 rfbScreenInfoPtr screen
= rfbGetScreen (&argc
, argv
, maxx
, maxy
, 8, 1, 1);
2205 screen
->frameBuffer
=(char*)calloc(maxx
*maxy
, 1);
2207 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
2209 rfbColourMap
*cmap
=&screen
->colourMap
;
2210 cmap
->data
.bytes
= malloc (16*3);
2212 cmap
->data
.bytes
[i
*3 + 0] = default_red
[color_table
[i
]];
2213 cmap
->data
.bytes
[i
*3 + 1] = default_grn
[color_table
[i
]];
2214 cmap
->data
.bytes
[i
*3 + 2] = default_blu
[color_table
[i
]];
2218 screen
->serverFormat
.trueColour
= FALSE
;
2220 screen
->kbdAddEvent
= vncterm_kbd_event
;
2222 screen
->setXCutText
= vncterm_set_xcut_text
;
2224 screen
->ptrAddEvent
= vncterm_pointer_event
;
2226 screen
->desktopName
= "VNC Command Terminal";
2228 screen
->newClientHook
= new_client
;
2230 vt
->maxx
= screen
->width
;
2231 vt
->maxy
= screen
->height
;
2233 vt
->width
= vt
->maxx
/ 8;
2234 vt
->height
= vt
->maxy
/ 16;
2236 vt
->total_height
= vt
->height
* 20;
2237 vt
->scroll_height
= 0;
2242 vt
->region_bottom
= vt
->height
;
2244 vt
->g0enc
= LAT1_MAP
;
2245 vt
->g1enc
= GRAF_MAP
;
2246 vt
->cur_enc
= vt
->g0enc
;
2249 /* default text attributes */
2250 vt
->default_attrib
.bold
= 0;
2251 vt
->default_attrib
.uline
= 0;
2252 vt
->default_attrib
.blink
= 0;
2253 vt
->default_attrib
.invers
= 0;
2254 vt
->default_attrib
.unvisible
= 0;
2255 vt
->default_attrib
.fgcol
= 7;
2256 vt
->default_attrib
.bgcol
= 0;
2258 vt
->cur_attrib
= vt
->default_attrib
;
2260 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
2262 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
2263 vt
->cells
[i
].ch
= ' ';
2264 vt
->cells
[i
].attrib
= vt
->default_attrib
;
2267 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
2269 vt
->screen
= screen
;
2271 screen
->screenData
= (void*)vt
;
2273 //screen->autoPort = 1;
2275 rfbRegisterSecurityHandler(&VncSecurityHandlerVencrypt
);
2277 rfbInitServer(screen
);
2283 main (int argc
, char** argv
)
2286 char **cmdargv
= NULL
;
2287 char *command
= "/bin/bash"; // execute normal shell as default
2292 struct timeval tv
, tv1
;
2293 time_t elapsed
, cur_time
;
2294 struct winsize dimensions
;
2296 if (gnutls_global_init () < 0) {
2297 fprintf(stderr
, "gnutls_global_init failed\n");
2301 if (gnutls_dh_params_init (&dh_params
) < 0) {
2302 fprintf(stderr
, "gnutls_dh_params_init failed\n");
2306 if (gnutls_dh_params_generate2 (dh_params
, DH_BITS
) < 0) {
2307 fprintf(stderr
, "gnutls_dh_params_init failed\n");
2311 for (i
= 1; i
< argc
; i
++) {
2312 if (!strcmp (argv
[i
], "-c")) {
2313 command
= argv
[i
+1];
2314 cmdargv
= &argv
[i
+1];
2321 for (i
= 1; i
< argc
; i
++) {
2322 if (!strcmp (argv
[i
], "-timeout")) {
2323 CHECK_ARGC (argc
, argv
, i
);
2324 idle_timeout
= atoi(argv
[i
+1]);
2325 rfbPurgeArguments(&argc
, &i
, 2, argv
);
2327 if (!strcmp (argv
[i
], "-authpath")) {
2328 CHECK_ARGC (argc
, argv
, i
);
2329 auth_path
= argv
[i
+1];
2330 rfbPurgeArguments(&argc
, &i
, 2, argv
);
2332 if (!strcmp (argv
[i
], "-perm")) {
2333 CHECK_ARGC (argc
, argv
, i
);
2334 auth_perm
= argv
[i
+1];
2335 rfbPurgeArguments(&argc
, &i
, 2, argv
);
2341 gnutls_global_set_log_level(10);
2342 gnutls_global_set_log_function(vnc_debug_gnutls_log
);
2347 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
2349 setlocale(LC_ALL
, ""); // set from environment
2351 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
2353 // fixme: ist there a standard way to detect utf8 mode ?
2354 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
2358 dimensions
.ws_col
= vt
->width
;
2359 dimensions
.ws_row
= vt
->height
;
2361 setenv ("TERM", TERM
, 1);
2363 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
2366 // install default signal handlers
2367 signal (SIGQUIT
, SIG_DFL
);
2368 signal (SIGTERM
, SIG_DFL
);
2369 signal (SIGINT
, SIG_DFL
);
2372 execvp (command
, cmdargv
);
2374 execlp (command
, command
, NULL
);
2376 perror ("Error: exec failed\n");
2377 exit (-1); // should not be reached
2378 } else if (pid
== -1) {
2379 perror ("Error: fork failed\n");
2384 FD_SET (master
, &fs
);
2386 tv
.tv_usec
= 5000; /* 5 ms */
2388 last_time
= time (NULL
);
2396 cur_time
= time (NULL
);
2398 elapsed
= cur_time
- last_time
;
2399 //printf ("Elapsed %ld\n", elapsed);
2402 if (client_connected
) {
2403 if (idle_timeout
&& (elapsed
>= idle_timeout
)) {
2407 // wait at least 20 seconds for initial connect
2408 if (idle_timeout
&& (elapsed
>= (idle_timeout
> 20 ? idle_timeout
: 20))) {
2413 // exit after 30 minutes idle time
2414 if (elapsed
>= 30*60) {
2419 rfbProcessEvents (vt
->screen
, 40000); /* 40 ms */
2421 if (vt
->ibuf_count
> 0) {
2422 //printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
2423 write (master
, vt
->ibuf
, vt
->ibuf_count
);
2425 last_time
= time (NULL
);
2428 if (!vt
->mark_active
) {
2430 int num_fds
= select (master
+1, &fs1
, NULL
, NULL
, &tv1
);
2432 if (FD_ISSET (master
, &fs1
)) {
2435 while ((c
= read (master
, buffer
, 1024)) == -1) {
2436 if (errno
!= EAGAIN
) break;
2439 vncterm_puts (vt
, buffer
, c
);
2449 waitpid(pid
, &status
, 0);