do not create a process group with setsid
[vncterm.git] / vncterm.c
1 /*
2
3      Copyright (C) 2007-2011 Proxmox Server Solutions GmbH
4
5      Copyright: vzdump is under GNU GPL, the GNU General Public License.
6
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.
10
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.
15
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
19      02111-1307, USA.
20
21      Author: Dietmar Maurer <dietmar@proxmox.com>
22
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h> 
28 #include <sys/socket.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <rfb/rfb.h>
32 #include <rfb/keysym.h>
33 #include <pty.h>  /* for openpty and forkpty */
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/wait.h>
38 #include <signal.h>
39 #include <locale.h>
40
41 #include "vncterm.h"
42 #include "glyphs.h"
43
44 #include <gnutls/gnutls.h>
45 #include <gnutls/x509.h>
46
47 /* define this for debugging */
48 //#define DEBUG
49
50 char *auth_path = "/";
51 char *auth_perm = "Sys.Console";
52
53 int use_x509 = 1;
54
55 static char *
56 urlencode(char *buf, const char *value)
57 {
58         static const char *hexchar = "0123456789abcdef";
59         char *p = buf;
60         int i;
61         int l = strlen(value);
62         for (i = 0; i < l; i++) {
63                 char c = value[i];
64                 if (('a' <= c && c <= 'z') ||
65                     ('A' <= c && c <= 'Z') ||
66                     ('0' <= c && c <= '9')) {
67                         *p++ = c;
68                 } else if (c == 32) {
69                         *p++ = '+';
70                 } else {
71                         *p++ = '%';
72                         *p++ = hexchar[c >> 4];
73                         *p++ = hexchar[c & 15];
74                 }
75         }
76         *p = 0;
77
78         return p;
79 }
80
81 static int 
82 pve_auth_verify(const char *clientip, const char *username, const char *passwd)
83 {
84         struct sockaddr_in server;
85
86         int sfd = socket(AF_INET, SOCK_STREAM, 0);
87         if (sfd == -1) {
88                 perror("pve_auth_verify: socket failed");
89                 return -1;
90         }
91
92         struct hostent *he;
93         if ((he = gethostbyname("localhost")) == NULL) {
94                 fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
95                 goto err;
96         }
97
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);
101
102         if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
103                 perror("pve_auth_verify: error connecting to server");
104                 goto err;
105         }
106
107         char buf[8192];
108         char form[8192];
109
110         char *p = form;
111         p = urlencode(p, "username");
112         *p++ = '=';
113         p = urlencode(p, username);
114
115         *p++ = '&';
116         p = urlencode(p, "password");
117         *p++ = '=';
118         p = urlencode(p, passwd);
119
120         *p++ = '&';
121         p = urlencode(p, "path");
122         *p++ = '=';
123         p = urlencode(p, auth_path);
124
125         *p++ = '&';
126         p = urlencode(p, "privs");
127         *p++ = '=';
128         p = urlencode(p, auth_perm);
129
130         sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
131                 "Host: localhost:85\n"
132                 "Connection: close\n"
133                 "PVEClientIP: %s\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);
138         if (sb < 0) {
139                 perror("pve_auth_verify: send failed");
140                 goto err;
141         }
142         if (sb != len) {
143                 fprintf(stderr, "pve_auth_verify: partial send error\n");
144                 goto err;
145         }
146
147         len = recv(sfd, buf, sizeof(buf) - 1, 0);
148         if (len < 0) {
149                 perror("pve_auth_verify: recv failed");
150                 goto err;
151         }
152
153         buf[len] = 0;
154
155         //printf("DATA:%s\n", buf);
156
157         shutdown(sfd, SHUT_RDWR);
158
159         return strncmp(buf, "HTTP/1.1 200 OK", 15);
160
161 err:
162         shutdown(sfd, SHUT_RDWR);
163         return -1;
164 }
165
166 #ifdef DEBUG
167 static void vnc_debug_gnutls_log(int level, const char* str) {
168         fprintf(stderr, "%d %s", level, str);
169 }
170 #endif
171
172 #define DH_BITS 1024
173 static gnutls_dh_params_t dh_params;
174
175 typedef struct {
176         gnutls_session_t session;
177 } tls_client_t;
178
179 static ssize_t
180 vnc_tls_push(
181         gnutls_transport_ptr_t transport,
182         const void *data,
183         size_t len)
184 {
185         rfbClientPtr cl = (rfbClientPtr)transport;
186         int n;
187
188 retry:
189         n = send(cl->sock, data, len, 0);
190         if (n < 0) {
191                 if (errno == EINTR)
192                         goto retry;
193                 return -1;
194         }
195         return n;
196 }
197
198 static ssize_t
199 vnc_tls_pull(
200         gnutls_transport_ptr_t transport,
201         void *data,
202         size_t len)
203 {
204         rfbClientPtr cl = (rfbClientPtr)transport;
205         int n;
206
207 retry:
208         n = recv(cl->sock, data, len, 0);
209         if (n < 0) {
210                 if (errno == EINTR)
211                         goto retry;
212                 return -1;
213         }
214         return n;
215 }
216
217 ssize_t vnc_tls_read(rfbClientPtr cl, void *buf, size_t count)
218 {
219         tls_client_t *sd = (tls_client_t *)cl->clientData;
220
221         int ret = gnutls_read(sd->session, buf, count);
222         if (ret < 0) {
223                 if (ret == GNUTLS_E_AGAIN)
224                         errno = EAGAIN;
225                 else
226                         errno = EIO;
227                 ret = -1;
228         }
229
230         return ret;
231 }
232 ssize_t vnc_tls_write(rfbClientPtr cl, void *buf, size_t count)
233 {
234         tls_client_t *sd = (tls_client_t *)cl->clientData;
235
236         int ret = gnutls_write(sd->session, buf, count);
237         if (ret < 0) {
238                 if (ret == GNUTLS_E_AGAIN)
239                         errno = EAGAIN;
240                 else
241                         errno = EIO;
242                 ret = -1;
243         }
244
245         return ret;
246 }
247
248 static gnutls_anon_server_credentials
249 tls_initialize_anon_cred(void)
250 {
251         gnutls_anon_server_credentials anon_cred;
252         int ret;
253
254         if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
255                 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret));
256                 return NULL;
257         }
258
259         gnutls_anon_set_server_dh_params(anon_cred, dh_params);
260
261         return anon_cred;
262 }
263
264 static gnutls_certificate_credentials_t 
265 tls_initialize_x509_cred(void)
266 {
267         gnutls_certificate_credentials_t x509_cred;
268         int ret;
269
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";
274
275         if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
276                 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret));
277                 return NULL;
278         }
279
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);
284                 return NULL;
285         }
286
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);
291                 return NULL;
292         }
293
294         gnutls_certificate_set_dh_params (x509_cred, dh_params);
295
296         return x509_cred;
297 }
298
299 /* rfb tls security handler */
300
301 #define rfbSecTypeVencrypt  19
302 #define rfbVencryptTlsPlain 259
303 #define rfbVencryptX509Plain 262
304
305 void rfbEncodeU32(char *buf, uint32_t value)
306 {
307     buf[0] = (value >> 24) & 0xFF;
308     buf[1] = (value >> 16) & 0xFF;
309     buf[2] = (value >>  8) & 0xFF;
310     buf[3] = value & 0xFF;
311 }
312
313 uint32_t rfbDecodeU32(char *data, size_t offset)
314 {
315         return ((data[offset] << 24) | (data[offset + 1] << 16) |
316                 (data[offset + 2] << 8) | data[offset + 3]);
317 }
318
319 static void
320 vencrypt_subauth_plain(rfbClientPtr cl)
321 {
322         const char *err = NULL;
323         char buf[4096];
324         int n;
325
326         char clientip[INET6_ADDRSTRLEN];
327         clientip[0] = 0;
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));
333         }
334
335         if ((n = rfbReadExact(cl, buf, 8)) <= 0) {
336                 err = n ? "read failed" : "client gone";
337                 goto err;
338         }
339
340         uint32_t ulen = rfbDecodeU32(buf, 0);
341         uint32_t pwlen = rfbDecodeU32(buf, 4);
342
343         if (!ulen) {
344                 err = "No User name.";
345                 goto err;
346         }
347         if (ulen >= 255) {
348                 err = "User name too long.";
349                 goto err;
350         }
351         if (!pwlen) {
352                 err = "Password too short";
353                 goto err;
354         }
355         if (pwlen >= 511) {
356                 err = "Password too long.";
357                 goto err;
358         }
359
360         if ((n = rfbReadExact(cl, buf, ulen)) <= 0) {
361                 err = n ? "read failed" : "client gone";
362                 goto err;
363         }
364         buf[ulen] = 0;
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";
369                 goto err;
370         }
371         passwd[pwlen] = 0;
372
373         rfbLog("VencryptPlain: username: %s pw: %s\n", username, passwd);
374
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;
379                 return;
380         }
381
382         err = "Authentication failed";
383 err:
384         rfbLog("VencryptPlain: %s\n", err ? err : "no reason specified");
385         if (err) {
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);
393                 }
394         }
395         rfbCloseClient(cl);
396         return;
397 }
398
399 static void
400 rfbVncAuthVencrypt(rfbClientPtr cl)
401 {
402         int ret;
403
404         /* Send VeNCrypt version 0.2 */
405         char buf[256];
406         buf[0] = 0;
407         buf[1] = 2;
408
409         if (rfbWriteExact(cl, buf, 2) < 0) {
410             rfbLogPerror("rfbVncAuthVencrypt: write");
411             rfbCloseClient(cl);
412             return;
413         }
414
415         int n = rfbReadExact(cl, buf, 2);
416         if (n <= 0) {
417                 if (n == 0)
418                         rfbLog("rfbVncAuthVencrypt: client gone\n");
419                 else
420                         rfbLogPerror("rfbVncAuthVencrypt: read");
421                 rfbCloseClient(cl);
422                 return;
423         }
424
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);
430                 rfbCloseClient(cl);
431                 return;
432         }
433
434         /* Sending allowed auth */
435         int req_auth = use_x509 ? rfbVencryptX509Plain : rfbVencryptTlsPlain;
436
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");
442                 rfbCloseClient(cl);
443                 return;
444         }
445
446         n = rfbReadExact(cl, buf, 4);
447         if (n <= 0) {
448                 if (n == 0)
449                         rfbLog("rfbVncAuthVencrypt: client gone\n");
450                 else
451                         rfbLogPerror("rfbVncAuthVencrypt: read");
452                 rfbCloseClient(cl);
453                 return;
454         }
455
456         int auth = rfbDecodeU32(buf, 0);
457         if (auth != req_auth) {
458                 buf[0] = 1; /* Reject auth*/
459                 rfbWriteExact(cl, buf, 1);
460                 rfbCloseClient(cl);
461                 return;
462         }
463
464         buf[0] = 1; /* Accept auth */
465         if (rfbWriteExact(cl, buf, 1) < 0) {
466                 rfbLogPerror("rfbVncAuthVencrypt: write");
467                 rfbCloseClient(cl);
468                 return;
469         }
470
471         tls_client_t *sd = calloc(1, sizeof(tls_client_t));
472
473         if (sd->session == NULL) {
474                 if (gnutls_init(&sd->session, GNUTLS_SERVER) < 0) {
475                         rfbLog("gnutls_init failed\n");
476                         rfbCloseClient(cl);
477                         return;
478
479                 }
480
481                 if ((ret = gnutls_set_default_priority(sd->session)) < 0) {
482                         rfbLog("gnutls_set_default_priority failed: %s\n", gnutls_strerror(ret));
483                         sd->session = NULL;
484                         rfbCloseClient(cl);
485                         return;
486                 }
487
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
493                 };
494
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));
497                         sd->session = NULL;
498                         rfbCloseClient(cl);
499                         return;
500                 }
501
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));
506                         sd->session = NULL;
507                         rfbCloseClient(cl);
508                         return;
509                 }
510
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));
515                         sd->session = NULL;
516                         rfbCloseClient(cl);
517                         return;
518                 }
519
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));
524                         sd->session = NULL;
525                         rfbCloseClient(cl);
526                         return;
527                 }
528
529                 if (use_x509) {
530                         gnutls_certificate_server_credentials x509_cred;
531                         
532                         if (!(x509_cred = tls_initialize_x509_cred())) {
533                                 sd->session = NULL;
534                                 rfbCloseClient(cl);
535                                 return;
536                         }
537  
538                         if (gnutls_credentials_set(sd->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
539                                 sd->session = NULL;
540                                 gnutls_certificate_free_credentials(x509_cred);
541                                 rfbCloseClient(cl);
542                                 return;
543                         }
544                         
545                 } else {
546                         gnutls_anon_server_credentials anon_cred;
547
548                         if (!(anon_cred = tls_initialize_anon_cred())) {
549                                 sd->session = NULL;
550                                 rfbCloseClient(cl);
551                                 return;
552                         }
553
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);
557                                 sd->session = NULL;
558                                 rfbCloseClient(cl);
559                                 return;
560                         }
561                 }
562
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);
566         }
567
568
569 retry:
570         if ((ret = gnutls_handshake(sd->session)) < 0) {
571                 if (!gnutls_error_is_fatal(ret)) {
572                         usleep(100000);
573                         goto retry;
574                 }
575                 rfbLog("rfbVncAuthVencrypt: handshake failed\n");
576                 rfbCloseClient(cl);
577                 return;
578         }
579
580         /* set up TLS read/write hooks */
581         cl->clientData = sd;
582         cl->sock_read_fn = &vnc_tls_read;
583         cl->sock_write_fn = &vnc_tls_write;
584
585         vencrypt_subauth_plain(cl);
586 }
587
588 static rfbSecurityHandler VncSecurityHandlerVencrypt = {
589     rfbSecTypeVencrypt,
590     rfbVncAuthVencrypt,
591     NULL
592 };
593
594 #define TERM "xterm"
595
596 #define TERMIDCODE "[?1;2c" // vt100 ID
597
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); \
601    exit(1); \
602 }
603
604 /* these colours are from linux kernel drivers/char/vt.c */
605
606 static int idle_timeout = 1;
607
608 unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
609                                 8,12,10,14, 9,13,11,15 };
610
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};
618
619 static void
620 print_usage (const char *msg)
621 {
622   if (msg) { fprintf (stderr, "ERROR: %s\n", msg); }
623   fprintf (stderr, "USAGE: vncterm [vncopts] [-c command [args]]\n");
624 }
625
626 /* Convert UCS2 to UTF8 sequence, trailing zero */
627 static int
628 ucs2_to_utf8 (unicode c, char *out)
629 {
630   if (c < 0x80) {
631     out[0] = c;                 //  0*******
632     out[1] = 0;
633     return 1;
634   } else if (c < 0x800) {
635     out[0] = 0xc0 | (c >> 6);   //  110***** 10******
636     out[1] = 0x80 | (c & 0x3f);
637     out[2] = 0;
638     return 2;
639   } else {
640     out[0] = 0xe0 | (c >> 12);  //  1110**** 10****** 10******
641     out[1] = 0x80 | ((c >> 6) & 0x3f);
642     out[2] = 0x80 | (c & 0x3f);
643     out[3] = 0;
644     return 3;
645   }
646
647   return 0;
648 }
649
650 static void
651 rfb_draw_char (rfbScreenInfoPtr rfbScreen, int x, int y,
652                unsigned short c, rfbPixel col)
653 {
654   if (c > vt_font_size) {
655     rfbLog ("undefined font glyph %d\n", c);
656     return;
657   }
658
659   int i,j;
660   unsigned char *data= vt_font_data + c*16;
661   unsigned char d=*data;
662   int rowstride=rfbScreen->paddedWidthInBytes;
663   char *colour=(char*)&col;
664
665   for(j = 0; j < 16; j++) {
666     for(i = 0; i < 8; i++) {
667       if ((i&7) == 0) {
668         d=*data;
669         data++;
670       }
671       if (d&0x80)
672         *(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)) = *colour;
673       d<<=1;
674     }
675   }
676 }
677
678 static void
679 draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
680 {
681   if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
682
683   int rx = x*8;
684   int ry = y*16;
685   int rxe = x*8+8;
686   int rye = y*16+16;
687
688   int fg, bg;
689
690   if (attrib.invers) {
691     bg = attrib.fgcol;
692     fg = attrib.bgcol;
693   } else {
694     bg = attrib.bgcol;
695     fg = attrib.fgcol;
696   }
697
698   int ec = vt_fontmap[ch];
699
700   rfbFillRect (vt->screen, rx, ry, rxe, rye, bg);
701
702   if (attrib.bold) {
703     fg += 8;
704   }
705
706   // unsuported attributes = (attrib.blink || attrib.unvisible)
707
708   rfb_draw_char (vt->screen, rx, ry, ec, fg);
709
710   if (attrib.uline) {
711     rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
712   }
713
714   rfbMarkRectAsModified (vt->screen, rx, ry, rxe, rye);
715
716 }
717
718 static void
719 vncterm_update_xy (vncTerm *vt, int x, int y)
720 {
721   if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
722
723   int y1 = (vt->y_base + y) % vt->total_height;
724   int y2 = y1 - vt->y_displ;
725   if (y2 < 0) {
726     y2 += vt->total_height;
727   }
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);
731   }
732 }
733
734 static void
735 vncterm_clear_xy (vncTerm *vt, int x, int y)
736 {
737   if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
738
739   int y1 = (vt->y_base + y) % vt->total_height;
740   int y2 = y1 - vt->y_displ;
741   if (y2 < 0) {
742     y2 += vt->total_height;
743   }
744   if (y2 < vt->height) {
745     TextCell *c = &vt->cells[y1 * vt->width + x];
746     c->ch = ' ';
747     c->attrib = vt->default_attrib;
748     c->attrib.fgcol = vt->cur_attrib.fgcol;
749     c->attrib.bgcol = vt->cur_attrib.bgcol;
750
751     draw_char_at (vt, x, y, c->ch, c->attrib);
752   }
753 }
754
755 static void
756 vncterm_show_cursor (vncTerm *vt, int show)
757 {
758   int x = vt->cx;
759   if (x >= vt->width) {
760     x = vt->width - 1;
761   }
762
763   int y1 = (vt->y_base + vt->cy) % vt->total_height;
764   int y = y1 - vt->y_displ;
765   if (y < 0) {
766     y += vt->total_height;
767   }
768
769   if (y < vt->height) {
770
771     TextCell *c = &vt->cells[y1 * vt->width + x];
772
773     if (show) {
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);
777     } else {
778       draw_char_at (vt, x, y, c->ch, c->attrib);
779     }
780   }
781 }
782
783 static void
784 vncterm_refresh (vncTerm *vt)
785 {
786   int x, y, y1;
787
788   rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
789
790   y1 = vt->y_displ;
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);
795       c++;
796     }
797     if (++y1 == vt->total_height)
798       y1 = 0;
799   }
800   rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
801
802   vncterm_show_cursor (vt, 1);
803 }
804
805 static void
806 vncterm_scroll_down (vncTerm *vt, int top, int bottom, int lines)
807 {
808   if ((top + lines) >= bottom) {
809     lines = bottom - top -1;
810   }
811
812   if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
813     return;
814   }
815
816   int h = lines * 16;
817   int y0 = top*16;
818   int y1 = y0 + h;
819   int y2 = bottom*16;
820   int rowstride = vt->screen->paddedWidthInBytes;
821   int rows = (bottom - top - lines)*16;
822
823   char *in = vt->screen->frameBuffer+y0*rowstride;
824   char *out = vt->screen->frameBuffer+y1*rowstride;
825   memmove(out,in, rowstride*rows);
826
827   memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
828   rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
829
830   int i;
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;
834
835     memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
836   }
837
838   for (i = 0; i < lines; i++) {
839     int j;
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;
843       c->ch = ' ';
844       c++;
845     }
846   }
847 }
848
849 static void
850 vncterm_scroll_up (vncTerm *vt, int top, int bottom, int lines, int moveattr)
851 {
852   if ((top + lines) >= bottom) {
853     lines = bottom - top - 1;
854   }
855
856   if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
857     return;
858   }
859
860   int h = lines * 16;
861   int y0 = top*16;
862   int y1 = (top + lines)*16;
863   int y2 = bottom*16;
864   int rowstride = vt->screen->paddedWidthInBytes;
865   int rows = (bottom - top - lines)*16;
866
867   char *in = vt->screen->frameBuffer+y1*rowstride;
868   char *out = vt->screen->frameBuffer+y0*rowstride;
869   memmove(out,in, rowstride*rows);
870
871   memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
872
873   rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
874
875   if (!moveattr) return;
876
877   // move attributes
878
879   int i;
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;
883
884     memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
885   }
886
887   for (i = 1; i <= lines; i++) {
888     int j;
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;
892       c->ch = ' ';
893       c++;
894     }
895   }
896 }
897
898 static void
899 vncterm_virtual_scroll (vncTerm *vt, int lines)
900 {
901   if (vt->altbuf || lines == 0) return;
902
903   if (lines < 0) {
904     lines = -lines;
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;
909     if (y1 < 0)
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;
915       }
916     }
917   } else {
918     int i;
919     for(i = 0; i < lines; i++) {
920       if (vt->y_displ == vt->y_base) break;
921       if (++vt->y_displ == vt->total_height) {
922         vt->y_displ = 0;
923       }
924     }
925
926   }
927
928   vncterm_refresh (vt);
929 }
930 static void
931 vncterm_respond_esc (vncTerm *vt, const char *esc)
932 {
933   int len = strlen (esc);
934   int i;
935
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];
940     }
941   }
942 }
943
944 static void
945 vncterm_put_lf (vncTerm *vt)
946 {
947   if (vt->cy + 1 == vt->region_bottom) {
948
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);
951       return;
952     }
953
954     if (vt->y_displ == vt->y_base) {
955       vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0);
956     }
957
958     if (vt->y_displ == vt->y_base) {
959       if (++vt->y_displ == vt->total_height) {
960         vt->y_displ = 0;
961       }
962     }
963
964     if (++vt->y_base == vt->total_height) {
965       vt->y_base = 0;
966     }
967
968     if (vt->scroll_height < vt->total_height) {
969       vt->scroll_height++;
970     }
971
972     int y1 = (vt->y_base + vt->height - 1) % vt->total_height;
973     TextCell *c = &vt->cells[y1 * vt->width];
974     int x;
975     for (x = 0; x < vt->width; x++) {
976       c->ch = ' ';
977       c->attrib = vt->default_attrib;
978       c++;
979     }
980
981     // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
982
983   } else if (vt->cy < vt->height - 1) {
984     vt->cy += 1;
985   }
986 }
987
988
989 static void
990 vncterm_csi_m (vncTerm *vt)
991 {
992   int i;
993
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;
998       break;
999     case 1:
1000       vt->cur_attrib.bold = 1;
1001       break;
1002     case 4:
1003       vt->cur_attrib.uline = 1;
1004       break;
1005     case 5:
1006       vt->cur_attrib.blink = 1;
1007       break;
1008     case 7:
1009       vt->cur_attrib.invers = 1;
1010       break;
1011     case 8:
1012       vt->cur_attrib.unvisible = 1;
1013       break;
1014     case 10:
1015       vt->cur_enc = LAT1_MAP;
1016       // fixme: dispaly controls = 0 ?
1017       // fixme: toggle meta = 0 ?
1018       break;
1019     case 11:
1020       vt->cur_enc = IBMPC_MAP;
1021       // fixme: dispaly controls = 1 ?
1022       // fixme: toggle meta = 0 ?
1023       break;
1024     case 12:
1025       vt->cur_enc = IBMPC_MAP;
1026       // fixme: dispaly controls = 1 ?
1027       // fixme: toggle meta = 1 ?
1028       break;
1029     case 22:
1030       vt->cur_attrib.bold = 0;
1031       break;
1032     case 24:
1033       vt->cur_attrib.uline = 0;
1034       break;
1035     case 25:
1036       vt->cur_attrib.blink = 0;
1037       break;
1038     case 27:
1039       vt->cur_attrib.invers = 0;
1040       break;
1041     case 28:
1042       vt->cur_attrib.unvisible = 0;
1043       break;
1044     case 30:
1045     case 31:
1046     case 32:
1047     case 33:
1048     case 34:
1049     case 35:
1050     case 36:
1051     case 37:
1052       /* set foreground color */
1053       vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30];
1054       break;
1055     case 38:
1056       /* reset color to default, enable underline */
1057       vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
1058       vt->cur_attrib.uline = 1;
1059       break;
1060     case 39:
1061       /* reset color to default, disable underline */
1062       vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
1063       vt->cur_attrib.uline = 0;
1064       break;
1065     case 40:
1066     case 41:
1067     case 42:
1068     case 43:
1069     case 44:
1070     case 45:
1071     case 46:
1072     case 47:
1073       /* set background color */
1074       vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40];
1075       break;
1076     case 49:
1077       /* reset background color */
1078       vt->cur_attrib.bgcol = vt->default_attrib.bgcol;
1079       break;
1080     default:
1081       fprintf (stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]);
1082       //fixme: implement
1083      }
1084   }
1085 }
1086
1087 static void
1088 vncterm_save_cursor (vncTerm *vt)
1089 {
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;
1097 }
1098
1099 static void
1100 vncterm_restore_cursor (vncTerm *vt)
1101 {
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;
1109 }
1110
1111 static void
1112 vncterm_set_alternate_buffer (vncTerm *vt, int on_off)
1113 {
1114   int x, y;
1115
1116   vt->y_displ = vt->y_base;
1117
1118   if (on_off) {
1119
1120     if (vt->altbuf) return;
1121
1122     vt->altbuf = 1;
1123
1124     /* alternate buffer & cursor */
1125
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];
1132       }
1133     }
1134
1135     /* clear screen */
1136     for (y = 0; y <= vt->height; y++) {
1137       for (x = 0; x < vt->width; x++) {
1138         vncterm_clear_xy (vt, x, y);
1139       }
1140     }
1141
1142   } else {
1143
1144     if (vt->altbuf == 0) return;
1145
1146     vt->altbuf = 0;
1147
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];
1153       }
1154     }
1155
1156     vncterm_restore_cursor (vt);
1157   }
1158
1159   vncterm_refresh (vt);
1160 }
1161
1162 static void
1163 vncterm_set_mode (vncTerm *vt, int on_off)
1164 {
1165   int i;
1166
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 */
1171       case 1000:
1172         vt->report_mouse = on_off;
1173         break;
1174       case 1049:                /* start/end special app mode (smcup/rmcup) */
1175         vncterm_set_alternate_buffer (vt, on_off);
1176         break;
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 */
1184         break;
1185       }
1186     } else { /* ANSI modes set/reset */
1187       /* fixme: implement me */
1188     }
1189   }
1190 }
1191
1192 static void
1193 vncterm_gotoxy (vncTerm *vt, int x, int y)
1194 {
1195   /* verify all boundaries */
1196
1197   if (x < 0) {
1198     x = 0;
1199   }
1200
1201   if (x >= vt->width) {
1202     x = vt->width - 1;
1203   }
1204
1205   vt->cx = x;
1206
1207   if (y < 0) {
1208     y = 0;
1209   }
1210
1211   if (y >= vt->height) {
1212     y = vt->height - 1;
1213   }
1214
1215   vt->cy = y;
1216 }
1217
1218 enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
1219        EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
1220        ESpalette, ESidquery, ESosc1, ESosc2};
1221
1222 static void
1223 vncterm_putchar (vncTerm *vt, unicode ch)
1224 {
1225   int x, y, i, c;
1226
1227 #ifdef DEBUG
1228   if (!vt->tty_state)
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);
1230 #endif
1231
1232   switch(vt->tty_state) {
1233   case ESesc:
1234     vt->tty_state = ESnormal;
1235     switch (ch) {
1236     case '[':
1237       vt->tty_state = ESsquare;
1238       break;
1239     case ']':
1240       vt->tty_state = ESnonstd;
1241       break;
1242     case '%':
1243       vt->tty_state = ESpercent;
1244       break;
1245     case '7':
1246       vncterm_save_cursor (vt);
1247       break;
1248     case '8':
1249       vncterm_restore_cursor (vt);
1250       break;
1251     case '(':
1252       vt->tty_state = ESsetG0; // SET G0
1253       break;
1254     case ')':
1255       vt->tty_state = ESsetG1; // SET G1
1256       break;
1257     case 'M':
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) {
1262         vt->cy--;
1263       }
1264       break;
1265     case '>':
1266       /* numeric keypad  - ignored */
1267       break;
1268     case '=':
1269       /* appl. keypad - ignored */
1270       break;
1271     default:
1272 #ifdef DEBUG
1273       fprintf(stderr, "got unhandled ESC%c  %d\n", ch, ch);
1274 #endif
1275       break;
1276     }
1277     break;
1278   case ESnonstd: /* Operating System Controls */
1279     vt->tty_state = ESnormal;
1280
1281     switch (ch) {
1282     case 'P':   /* palette escape sequence */
1283       for(i = 0; i < MAX_ESC_PARAMS; i++) {
1284         vt->esc_buf[i] = 0;
1285       }
1286
1287       vt->esc_count = 0;
1288       vt->tty_state = ESpalette;
1289       break;
1290     case 'R':   /* reset palette */
1291       // fixme: reset_palette(vc);
1292       break;
1293     case '0':
1294     case '1':
1295     case '2':
1296     case '4':
1297       vt->osc_cmd = ch;
1298       vt->osc_textbuf[0] = 0;
1299       vt->tty_state = ESosc1;
1300       break;
1301     default:
1302 #ifdef DEBUG
1303       fprintf (stderr, "unhandled OSC %c\n", ch);
1304 #endif
1305       vt->tty_state = ESnormal;
1306       break;
1307     }
1308     break;
1309   case ESosc1:
1310     vt->tty_state = ESnormal;
1311     if (ch == ';') {
1312       vt->tty_state = ESosc2;
1313     } else {
1314 #ifdef DEBUG
1315       fprintf (stderr, "got illegal OSC sequence\n");
1316 #endif
1317     }
1318     break;
1319   case ESosc2:
1320     if (ch != 0x9c && ch != 7) {
1321       int i = 0;
1322       while (vt->osc_textbuf[i]) i++;
1323       vt->osc_textbuf[i++] = ch;
1324       vt->osc_textbuf[i] = 0;
1325     } else {
1326 #ifdef DEBUG
1327       fprintf (stderr, "OSC:%c:%s\n", vt->osc_cmd, vt->osc_textbuf);
1328 #endif
1329       vt->tty_state = ESnormal;
1330     }
1331     break;
1332   case ESpalette:
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;
1339
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];
1347
1348         //set_palette(vc); ?
1349
1350         vt->tty_state = ESnormal;
1351       }
1352     } else
1353        vt->tty_state = ESnormal;
1354     break;
1355   case ESsquare:
1356     for(i = 0; i < MAX_ESC_PARAMS; i++) {
1357       vt->esc_buf[i] = 0;
1358     }
1359
1360     vt->esc_count = 0;
1361     vt->esc_has_par = 0;
1362     vt->tty_state = ESgetpars;
1363
1364     if (ch == '>') {
1365       vt->tty_state = ESidquery;
1366       break;
1367     }
1368
1369     if ((vt->esc_ques = (ch == '?'))) {
1370       break;
1371     }
1372   case ESgetpars:
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';
1377       }
1378       break;
1379     } else if (ch == ';') {
1380       vt->esc_count++;
1381       break;
1382     } else {
1383       if (vt->esc_has_par) {
1384         vt->esc_count++;
1385       }
1386       vt->tty_state = ESgotpars;
1387     }
1388   case ESgotpars:
1389
1390     vt->tty_state = ESnormal;
1391
1392 #ifdef DEBUG
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);
1398     } else {
1399       int i;
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]);
1403       }
1404       fprintf (stderr, "%c\n", ch);
1405     }
1406 #endif
1407
1408     switch (ch) {
1409     case 'h':
1410       vncterm_set_mode (vt, 1);
1411       break;
1412     case 'l':
1413       vncterm_set_mode (vt, 0);
1414       break;
1415     case 'm':
1416       if (!vt->esc_count) {
1417         vt->esc_count++; // default parameter 0
1418       }
1419       vncterm_csi_m (vt);
1420       break;
1421     case 'n':
1422       /* report cursor position */
1423       /* TODO: send ESC[row;colR */
1424       break;
1425     case 'A':
1426       /* move cursor up */
1427       if (vt->esc_buf[0] == 0) {
1428         vt->esc_buf[0] = 1;
1429       }
1430       vt->cy -= vt->esc_buf[0];
1431       if (vt->cy < 0) {
1432         vt->cy = 0;
1433       }
1434       break;
1435     case 'B':
1436     case 'e':
1437       /* move cursor down */
1438       if (vt->esc_buf[0] == 0) {
1439         vt->esc_buf[0] = 1;
1440       }
1441       vt->cy += vt->esc_buf[0];
1442       if (vt->cy >= vt->height) {
1443         vt->cy = vt->height - 1;
1444       }
1445       break;
1446     case 'C':
1447     case 'a':
1448       /* move cursor right */
1449       if (vt->esc_buf[0] == 0) {
1450         vt->esc_buf[0] = 1;
1451       }
1452       vt->cx += vt->esc_buf[0];
1453       if (vt->cx >= vt->width) {
1454         vt->cx = vt->width - 1;
1455       }
1456       break;
1457     case 'D':
1458       /* move cursor left */
1459       if (vt->esc_buf[0] == 0) {
1460         vt->esc_buf[0] = 1;
1461       }
1462       vt->cx -= vt->esc_buf[0];
1463       if (vt->cx < 0) {
1464         vt->cx = 0;
1465       }
1466       break;
1467     case 'G':
1468     case '`':
1469       /* move cursor to column */
1470       vncterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy);
1471       break;
1472     case 'd':
1473       /* move cursor to row */
1474       vncterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1);
1475       break;
1476     case 'f':
1477     case 'H':
1478       /* move cursor to row, column */
1479       vncterm_gotoxy (vt, vt->esc_buf[1] - 1,  vt->esc_buf[0] - 1);
1480       break;
1481     case 'J':
1482       switch (vt->esc_buf[0]) {
1483       case 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) {
1488               continue;
1489             }
1490             vncterm_clear_xy (vt, x, y);
1491           }
1492         }
1493         break;
1494       case 1:
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) {
1499               break;
1500             }
1501             vncterm_clear_xy (vt, x, y);
1502           }
1503         }
1504         break;
1505       case 2:
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);
1510           }
1511         }
1512         break;
1513       }
1514       break;
1515     case 'K':
1516       switch (vt->esc_buf[0]) {
1517       case 0:
1518         /* clear to eol */
1519         for(x = vt->cx; x < vt->width; x++) {
1520           vncterm_clear_xy (vt, x, vt->cy);
1521         }
1522         break;
1523       case 1:
1524         /* clear from beginning of line */
1525         for (x = 0; x <= vt->cx; x++) {
1526           vncterm_clear_xy (vt, x, vt->cy);
1527         }
1528         break;
1529       case 2:
1530         /* clear entire line */
1531         for(x = 0; x < vt->width; x++) {
1532           vncterm_clear_xy (vt, x, vt->cy);
1533         }
1534         break;
1535       }
1536       break;
1537     case 'L':
1538       /* insert line */
1539       c = vt->esc_buf[0];
1540
1541       if (c > vt->height - vt->cy)
1542         c = vt->height - vt->cy;
1543       else if (!c)
1544         c = 1;
1545
1546       vncterm_scroll_down (vt, vt->cy, vt->region_bottom, c);
1547       break;
1548     case 'M':
1549       /* delete line */
1550       c = vt->esc_buf[0];
1551
1552       if (c > vt->height - vt->cy)
1553         c = vt->height - vt->cy;
1554       else if (!c)
1555         c = 1;
1556
1557       vncterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1);
1558       break;
1559     case 'T':
1560       /* scroll down */
1561       c = vt->esc_buf[0];
1562       if (!c) c = 1;
1563       vncterm_scroll_down (vt, vt->region_top, vt->region_bottom, c);
1564       break;
1565     case 'S':
1566       /* scroll up */
1567       c = vt->esc_buf[0];
1568       if (!c) c = 1;
1569       vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1);
1570       break;
1571     case 'P':
1572       /* delete c character */
1573       c = vt->esc_buf[0];
1574
1575       if (c > vt->width - vt->cx)
1576         c = vt->width - vt->cx;
1577       else if (!c)
1578         c = 1;
1579
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;
1584         *dst = *src;
1585         vncterm_update_xy (vt, x + c, vt->cy);
1586         src->ch = ' ';
1587         src->attrib = vt->default_attrib;
1588         vncterm_update_xy (vt, x, vt->cy);
1589       }
1590       break;
1591     case 's':
1592       /* save cursor position */
1593       vncterm_save_cursor (vt);
1594       break;
1595     case 'u':
1596       /* restore cursor position */
1597       vncterm_restore_cursor (vt);
1598       break;
1599     case 'X':
1600       /* erase c characters */
1601       c = vt->esc_buf[0];
1602       if (!c) c = 1;
1603
1604       if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
1605
1606       for(i = 0; i < c; i++) {
1607         vncterm_clear_xy (vt, vt->cx + i, vt->cy);
1608       }
1609       break;
1610     case '@':
1611       /* insert c character */
1612       c = vt->esc_buf[0];
1613       if (c > (vt->width - vt->cx)) {
1614         c = vt->width - vt->cx;
1615       }
1616       if (!c) c = 1;
1617
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;
1622         *dst = *src;
1623         vncterm_update_xy (vt, x + c, vt->cy);
1624         src->ch = ' ';
1625         src->attrib = vt->cur_attrib;
1626         vncterm_update_xy (vt, x, vt->cy);
1627       }
1628
1629       break;
1630     case 'r':
1631       /* set region */
1632       if (!vt->esc_buf[0])
1633         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];
1641         vt->cx = 0;
1642         vt->cy = vt->region_top;
1643 #ifdef DEBUG
1644         fprintf (stderr, "set region %d %d\n", vt->region_top, vt->region_bottom);
1645 #endif
1646       }
1647
1648       break;
1649     default:
1650 #ifdef DEBUG
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);
1655       } else {
1656         int i;
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]);
1660         }
1661         fprintf (stderr, "%c\n", ch);
1662       }
1663 #endif
1664       break;
1665     }
1666     vt->esc_ques = 0;
1667     break;
1668   case ESsetG0: // Set G0
1669     vt->tty_state = ESnormal;
1670
1671     if (ch == '0')
1672       vt->g0enc = GRAF_MAP;
1673     else if (ch == 'B')
1674       vt->g0enc = LAT1_MAP;
1675     else if (ch == 'U')
1676       vt->g0enc = IBMPC_MAP;
1677     else if (ch == 'K')
1678       vt->g0enc = USER_MAP;
1679
1680     if (vt->charset == 0)
1681       vt->cur_enc = vt->g0enc;
1682
1683     break;
1684   case ESsetG1: // Set G1
1685     vt->tty_state = ESnormal;
1686
1687     if (ch == '0')
1688       vt->g1enc = GRAF_MAP;
1689     else if (ch == 'B')
1690       vt->g1enc = LAT1_MAP;
1691     else if (ch == 'U')
1692       vt->g1enc = IBMPC_MAP;
1693     else if (ch == 'K')
1694       vt->g1enc = USER_MAP;
1695
1696     if (vt->charset == 1)
1697       vt->cur_enc = vt->g1enc;
1698
1699     break;
1700   case ESidquery: // vt100 query id
1701     vt->tty_state = ESnormal;
1702
1703     if (ch == 'c') {
1704 #ifdef DEBUG
1705       fprintf (stderr, "ESC[>c   Query term ID\n");
1706 #endif
1707       vncterm_respond_esc (vt, TERMIDCODE);
1708     }
1709     break;
1710   case ESpercent:
1711     vt->tty_state = ESnormal;
1712     switch (ch) {
1713     case '@':  /* defined in ISO 2022 */
1714       vt->utf8 = 0;
1715       break;
1716     case 'G':  /* prelim official escape code */
1717     case '8':  /* retained for compatibility */
1718       vt->utf8 = 1;
1719       break;
1720     }
1721     break;
1722   default: // ESnormal
1723     vt->tty_state = ESnormal;
1724
1725     switch(ch) {
1726     case 0:
1727       break;
1728     case 7:  /* alert aka. bell */
1729       rfbSendBell(vt->screen);
1730       break;
1731     case 8:  /* backspace */
1732       if (vt->cx > 0)
1733         vt->cx--;
1734       break;
1735     case 9:  /* tabspace */
1736       if (vt->cx + (8 - (vt->cx % 8)) > vt->width) {
1737         vt->cx = 0;
1738         vncterm_put_lf (vt);
1739       } else {
1740         vt->cx = vt->cx + (8 - (vt->cx % 8));
1741       }
1742       break;
1743     case 10:  /* LF,*/
1744     case 11:  /* VT */
1745     case 12:  /* FF */
1746       vncterm_put_lf (vt);
1747       break;
1748     case 13:  /* carriage return */
1749       vt->cx = 0;
1750       break;
1751     case 14:
1752       /* SI (shift in), select character set 1 */
1753       vt->charset = 1;
1754       vt->cur_enc = vt->g1enc;
1755       /* fixme: display controls = 1 */
1756       break;
1757     case 15:
1758       /* SO (shift out), select character set 0 */
1759       vt->charset = 0;
1760       vt->cur_enc = vt->g0enc;
1761       /* fixme: display controls = 0 */
1762       break;
1763     case 27:    /* esc */
1764       vt->tty_state = ESesc;
1765       break;
1766     case 127: /* delete */
1767       /* ignore */
1768       break;
1769     case 128+27:    /* csi */
1770       vt->tty_state = ESsquare;
1771       break;
1772     default:
1773       if (vt->cx >= vt->width) {
1774         /* line wrap */
1775         vt->cx = 0;
1776         vncterm_put_lf (vt);
1777       }
1778
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;
1782       c->ch = ch;
1783       vncterm_update_xy (vt, vt->cx, vt->cy);
1784       vt->cx++;
1785       break;
1786     }
1787     break;
1788   }
1789 }
1790
1791 static int
1792 vncterm_puts (vncTerm *vt, const char *buf, int len)
1793 {
1794     unicode tc;
1795
1796     vncterm_show_cursor (vt, 0);
1797
1798     while (len) {
1799       unsigned char c = *buf;
1800       len--;
1801       buf++;
1802
1803       if (vt->tty_state != ESnormal) {
1804         // never translate escape sequence
1805         tc = c;
1806       } else if (vt->utf8 && !vt->cur_enc) {
1807
1808         if(c & 0x80) { // utf8 multi-byte sequence
1809
1810           if (vt->utf_count > 0 && (c & 0xc0) == 0x80) {
1811             // inside UTF8 sequence
1812             vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
1813             vt->utf_count--;
1814             if (vt->utf_count == 0) {
1815               tc = vt->utf_char;
1816             } else {
1817               continue;
1818             }
1819           } else {
1820             //  first char of a UTF8 sequence
1821             if ((c & 0xe0) == 0xc0) {
1822               vt->utf_count = 1;
1823               vt->utf_char = (c & 0x1f);
1824             } else if ((c & 0xf0) == 0xe0) {
1825               vt->utf_count = 2;
1826               vt->utf_char = (c & 0x0f);
1827             } else if ((c & 0xf8) == 0xf0) {
1828               vt->utf_count = 3;
1829               vt->utf_char = (c & 0x07);
1830             } else if ((c & 0xfc) == 0xf8) {
1831               vt->utf_count = 4;
1832               vt->utf_char = (c & 0x03);
1833             } else if ((c & 0xfe) == 0xfc) {
1834               vt->utf_count = 5;
1835               vt->utf_char = (c & 0x01);
1836             } else
1837               vt->utf_count = 0;
1838
1839             continue;
1840           }
1841         } else {
1842           // utf8 single byte
1843           tc = c;
1844           vt->utf_count = 0;
1845         }
1846
1847       } else {
1848         // never translate controls
1849         if (c >= 32 && c != 127 && c != (128+27)) {
1850           tc = translations[vt->cur_enc][c & 0x0ff];
1851         } else {
1852           tc = c;
1853         }
1854       }
1855
1856       vncterm_putchar (vt, tc);
1857     }
1858
1859     vncterm_show_cursor (vt, 1);
1860     return len;
1861 }
1862
1863 void
1864 vncterm_kbd_event (rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
1865 {
1866   vncTerm *vt =(vncTerm *)cl->screen->screenData;
1867   static int control = 0;
1868   static int shift = 0;
1869   char *esc = NULL;
1870
1871   //fprintf (stderr, "KEYEVENT:%d: %08x\n", down == 0, keySym);fflush (stderr);
1872   if (down) {
1873     //fprintf (stderr, "KEYPRESS: %d\n", keySym);fflush (stderr);
1874
1875     if (keySym == XK_Shift_L || keySym == XK_Shift_R) {
1876       shift = 1;
1877     } if (keySym == XK_Control_L || keySym == XK_Control_R) {
1878       control = 1;
1879     } else if (vt->ibuf_count < (IBUFSIZE - 32)) {
1880
1881       if (control) {
1882         if(keySym >= 'a' && keySym <= 'z')
1883           keySym -= 'a' -1;
1884         else if (keySym >= 'A' && keySym <= 'Z')
1885           keySym -= 'A'-1;
1886         else
1887           keySym=0xffff;
1888       } else {
1889         switch (keySym) {
1890         case XK_Escape:
1891           keySym=27; break;
1892         case XK_Return:
1893           keySym='\r'; break;
1894         case XK_BackSpace:
1895           keySym=8; break;
1896         case XK_Tab:
1897           keySym='\t'; break;
1898         case XK_Delete: /* kdch1 */
1899         case XK_KP_Delete:
1900           esc = "[3~";break;
1901         case XK_Home: /* khome */
1902         case XK_KP_Home:
1903           esc = "OH";break;
1904         case XK_End:
1905         case XK_KP_End: /* kend */
1906           esc = "OF";break;
1907         case XK_Insert: /* kich1 */
1908         case XK_KP_Insert:
1909           esc = "[2~";break;
1910         case XK_Up:
1911         case XK_KP_Up:  /* kcuu1 */
1912           esc = "OA";break;
1913         case XK_Down: /* kcud1 */
1914         case XK_KP_Down:
1915           esc = "OB";break;
1916         case XK_Right:
1917         case XK_KP_Right: /* kcuf1 */
1918           esc = "OC";break;
1919         case XK_Left:
1920         case XK_KP_Left: /* kcub1 */
1921           esc = "OD";break;
1922         case XK_Page_Up:
1923           if (shift) {
1924             vncterm_virtual_scroll (vt, -vt->height/2);
1925             return;
1926           }
1927           esc = "[5~";break;
1928         case XK_Page_Down:
1929           if (shift) {
1930             vncterm_virtual_scroll (vt, vt->height/2);
1931             return;
1932           }
1933           esc = "[6~";break;
1934         case XK_F1:
1935           esc = "OP";break;
1936         case XK_F2:
1937           esc = "OQ";break;
1938         case XK_F3:
1939           esc = "OR";break;
1940         case XK_F4:
1941           esc = "OS";break;
1942         case XK_F5:
1943           esc = "[15~";break;
1944         case XK_F6:
1945           esc = "[17~";break;
1946         case XK_F7:
1947           esc = "[18~";break;
1948         case XK_F8:
1949           esc = "[19~";break;
1950         case XK_F9:
1951           esc = "[20~";break;
1952         case XK_F10:
1953           esc = "[21~";break;
1954         case XK_F11:
1955           esc = "[23~";break;
1956         case XK_F12:
1957           esc = "[24~";break;
1958         default:
1959           break;
1960         }
1961       }
1962
1963 #ifdef DEBUG
1964       fprintf (stderr, "KEYPRESS OUT:%s: %d\n", esc, keySym); fflush (stderr);
1965 #endif
1966
1967       if (vt->y_displ != vt->y_base) {
1968         vt->y_displ = vt->y_base;
1969         vncterm_refresh (vt);
1970       }
1971
1972       if (esc) {
1973         vncterm_respond_esc (vt, esc);
1974       } else if(keySym<0x100) {
1975         if (vt->utf8) {
1976           int len = ucs2_to_utf8 (keySym & 0x0fff, &vt->ibuf[vt->ibuf_count]);
1977           vt->ibuf_count += len;
1978         } else {
1979           vt->ibuf[vt->ibuf_count++] = (char)keySym;
1980         }
1981       }
1982     }
1983   } else {
1984     if (keySym == XK_Shift_L || keySym == XK_Shift_R) {
1985       shift = 0;
1986     } else if (keySym == XK_Control_L || keySym == XK_Control_R) {
1987       control = 0;
1988     }
1989   }
1990 }
1991
1992 void
1993 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1994 {
1995   vncTerm *vt =(vncTerm *)cl->screen->screenData;
1996
1997   // seems str is Latin-1 encoded
1998   if (vt->selection) free (vt->selection);
1999   vt->selection = (unicode *)malloc (len*sizeof (unicode));
2000   int i;
2001   for (i = 0; i < len; i++) {
2002     vt->selection[i] = str[i] & 0xff;
2003   }
2004   vt->selection_len = len;
2005 }
2006
2007 static void
2008 mouse_report (vncTerm *vt, int butt, int mrx, int mry)
2009 {
2010   char buf[8];
2011
2012   sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
2013            (char)('!' + mry));
2014
2015   vncterm_respond_esc (vt, buf);
2016 }
2017
2018 void
2019 vncterm_toggle_marked_cell (vncTerm *vt, int pos)
2020 {
2021   int x= (pos%vt->width)*8;
2022   int y= (pos/vt->width)*16;
2023
2024   int i,j;
2025   rfbScreenInfoPtr s=vt->screen;
2026
2027   char *b = s->frameBuffer+y*s->width+x;
2028
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);
2033     }
2034   }
2035 }
2036
2037 void
2038 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
2039 {
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;
2045   int i;
2046
2047   int cx = x/8;
2048   int cy = y/16;
2049
2050   if (cx < 0) cx = 0;
2051   if (cx >= vt->width) cx = vt->width - 1;
2052   if (cy < 0) cy = 0;
2053   if (cy >= vt->height) cy = vt->height - 1;
2054
2055   if (vt->report_mouse && buttonMask != last_mask) {
2056     last_mask = buttonMask;
2057     if (buttonMask & 1) {
2058       mouse_report (vt, 0, cx, cy);
2059     }
2060     if (buttonMask & 2) {
2061       mouse_report (vt, 1, cx, cy);
2062     }
2063     if (buttonMask & 4) {
2064       mouse_report (vt, 2, cx, cy);
2065     }
2066     if (!buttonMask) {
2067       mouse_report (vt, 3, cx, cy);
2068     }
2069   }
2070
2071   if (buttonMask & 2) {
2072     if(button2_released && vt->selection) {
2073       int i;
2074       for(i = 0; i < vt->selection_len; i++) {
2075         if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
2076           if (vt->utf8) {
2077             vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
2078           } else  {
2079             vt->ibuf[vt->ibuf_count++] = vt->selection[i];
2080           }
2081         }
2082       }
2083       if (vt->y_displ != vt->y_base) {
2084         vt->y_displ = vt->y_base;
2085         vncterm_refresh (vt);
2086       }
2087     }
2088     button2_released = 0;
2089   } else {
2090     button2_released = 1;
2091   }
2092
2093   if (buttonMask & 1) {
2094     int pos = cy*vt->width + cx;
2095
2096     // code borrowed from libvncserver (VNConsole.c)
2097
2098     if (!vt->mark_active) {
2099
2100       vt->mark_active = 1;
2101       sel_start_pos = sel_end_pos = pos;
2102       vncterm_toggle_marked_cell (vt, pos);
2103
2104     } else {
2105
2106       if (pos != sel_end_pos) {
2107
2108         if (pos > sel_end_pos) {
2109           cx = sel_end_pos; cy=pos;
2110         } else {
2111           cx=pos; cy=sel_end_pos;
2112         }
2113
2114         if (cx < sel_start_pos) {
2115           if (cy < sel_start_pos) cy--;
2116         } else {
2117           cx++;
2118         }
2119
2120         while (cx <= cy) {
2121           vncterm_toggle_marked_cell (vt, cx);
2122           cx++;
2123         }
2124
2125         sel_end_pos = pos;
2126       }
2127     }
2128
2129   } else if (vt->mark_active) {
2130     vt->mark_active = 0;
2131
2132     if (sel_start_pos > sel_end_pos) {
2133       int tmp = sel_start_pos - 1;
2134       sel_start_pos = sel_end_pos;
2135       sel_end_pos = tmp;
2136     }
2137
2138     int len = sel_end_pos - sel_start_pos + 1;
2139
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);
2144
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;
2152       c++;
2153     }
2154     sel_latin1[len] = 0;
2155     rfbGotXCutText (vt->screen, sel_latin1, len);
2156     free (sel_latin1);
2157
2158     while (sel_start_pos <= sel_end_pos) {
2159       vncterm_toggle_marked_cell (vt, sel_start_pos++);
2160     }
2161
2162   }
2163
2164   rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
2165 }
2166
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;
2171
2172 void
2173 client_gone (rfbClientPtr client)
2174 {
2175   client_count--;
2176
2177   last_time = time (NULL);
2178
2179   if (client_count <= 0) {
2180     last_client = 1;
2181   }
2182 }
2183
2184 /* libvncserver callback for when a new client connects */
2185 enum rfbNewClientAction
2186 new_client (rfbClientPtr client)
2187 {
2188   client->clientGoneHook = client_gone;
2189   client_count++;
2190
2191   last_time = time (NULL);
2192
2193   last_client = 0;
2194   client_connected = 1;
2195
2196   return RFB_CLIENT_ACCEPT;
2197 }
2198
2199 vncTerm *
2200 create_vncterm (int argc, char** argv, int maxx, int maxy)
2201 {
2202   int i;
2203
2204   rfbScreenInfoPtr screen = rfbGetScreen (&argc, argv, maxx, maxy, 8, 1, 1);
2205   screen->frameBuffer=(char*)calloc(maxx*maxy, 1);
2206
2207   vncTerm *vt = (vncTerm *)calloc (sizeof(vncTerm), 1);
2208
2209   rfbColourMap *cmap =&screen->colourMap;
2210   cmap->data.bytes = malloc (16*3);
2211   for(i=0;i<16;i++) {
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]];
2215   }
2216   cmap->count = 16;
2217   cmap->is16 = FALSE;
2218   screen->serverFormat.trueColour = FALSE;
2219
2220   screen->kbdAddEvent = vncterm_kbd_event;
2221
2222   screen->setXCutText = vncterm_set_xcut_text;
2223
2224   screen->ptrAddEvent = vncterm_pointer_event;
2225
2226   screen->desktopName = "VNC Command Terminal";
2227
2228   screen->newClientHook = new_client;
2229
2230   vt->maxx = screen->width;
2231   vt->maxy = screen->height;
2232
2233   vt->width = vt->maxx / 8;
2234   vt->height = vt->maxy / 16;
2235
2236   vt->total_height = vt->height * 20;
2237   vt->scroll_height = 0;
2238   vt->y_base =  0;
2239   vt->y_displ =  0;
2240
2241   vt->region_top = 0;
2242   vt->region_bottom = vt->height;
2243
2244   vt->g0enc = LAT1_MAP;
2245   vt->g1enc = GRAF_MAP;
2246   vt->cur_enc = vt->g0enc;
2247   vt->charset = 0;
2248
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;
2257
2258   vt->cur_attrib = vt->default_attrib;
2259
2260   vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
2261
2262   for (i = 0; i < vt->width*vt->total_height; i++) {
2263     vt->cells[i].ch = ' ';
2264     vt->cells[i].attrib = vt->default_attrib;
2265   }
2266
2267   vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
2268
2269   vt->screen = screen;
2270
2271   screen->screenData = (void*)vt;
2272
2273   //screen->autoPort = 1;
2274
2275   rfbRegisterSecurityHandler(&VncSecurityHandlerVencrypt);
2276
2277   rfbInitServer(screen);
2278
2279   return vt;
2280 }
2281
2282 int
2283 main (int argc, char** argv)
2284 {
2285   int i;
2286   char **cmdargv = NULL;
2287   char *command = "/bin/bash"; // execute normal shell as default
2288   int pid;
2289   int master;
2290   char ptyname[1024];
2291   fd_set fs, fs1;
2292   struct timeval tv, tv1;
2293   time_t elapsed, cur_time;
2294   struct winsize dimensions;
2295
2296   if (gnutls_global_init () < 0) {
2297           fprintf(stderr, "gnutls_global_init failed\n");
2298           exit(-1);
2299   }
2300
2301   if (gnutls_dh_params_init (&dh_params) < 0) {
2302           fprintf(stderr, "gnutls_dh_params_init failed\n");
2303           exit(-1);
2304   }
2305
2306   if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) {
2307           fprintf(stderr, "gnutls_dh_params_init failed\n");
2308           exit(-1);
2309   }
2310
2311   for (i = 1; i < argc; i++) {
2312     if (!strcmp (argv[i], "-c")) {
2313       command = argv[i+1];
2314       cmdargv = &argv[i+1];
2315       argc = i;
2316       argv[i] = NULL;
2317       break;
2318     }
2319   }
2320
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);
2326     }
2327     if (!strcmp (argv[i], "-authpath")) {
2328       CHECK_ARGC (argc, argv, i);
2329       auth_path = argv[i+1];
2330       rfbPurgeArguments(&argc, &i, 2, argv);
2331     }
2332     if (!strcmp (argv[i], "-perm")) {
2333       CHECK_ARGC (argc, argv, i);
2334       auth_perm = argv[i+1];
2335       rfbPurgeArguments(&argc, &i, 2, argv);
2336     }
2337   }
2338
2339 #ifdef DEBUG
2340   rfbLogEnable (1);
2341   gnutls_global_set_log_level(10);
2342   gnutls_global_set_log_function(vnc_debug_gnutls_log);
2343 #else
2344   rfbLogEnable (0);
2345 #endif
2346
2347   vncTerm *vt = create_vncterm (argc, argv, 745, 400);
2348
2349   setlocale(LC_ALL, ""); // set from environment
2350
2351   char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
2352
2353   // fixme: ist there a standard way to detect utf8 mode ?
2354   if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) {
2355     vt->utf8 = 1;
2356   }
2357
2358   dimensions.ws_col = vt->width;
2359   dimensions.ws_row = vt->height;
2360
2361   setenv ("TERM", TERM, 1);
2362
2363   pid = forkpty (&master, ptyname, NULL, &dimensions);
2364   if(!pid) {
2365
2366     // install default signal handlers
2367     signal (SIGQUIT, SIG_DFL);
2368     signal (SIGTERM, SIG_DFL);
2369     signal (SIGINT, SIG_DFL);
2370
2371     if (cmdargv) {
2372       execvp (command, cmdargv);
2373     } else {
2374       execlp (command, command, NULL);
2375     }
2376     perror ("Error: exec failed\n");
2377     exit (-1); // should not be reached
2378   } else if (pid == -1) {
2379     perror ("Error: fork failed\n");
2380     exit (-1);
2381   }
2382
2383   FD_ZERO (&fs);
2384   FD_SET (master, &fs);
2385   tv.tv_sec = 0;
2386   tv.tv_usec = 5000; /* 5 ms */
2387
2388   last_time = time (NULL);
2389
2390   int count = 0;
2391   while (1) {
2392     count ++;
2393     tv1 = tv;
2394     fs1 = fs;
2395
2396     cur_time = time (NULL);
2397
2398     elapsed = cur_time - last_time;
2399     //printf ("Elapsed %ld\n", elapsed);
2400
2401     if (last_client) {
2402       if (client_connected) {
2403         if (idle_timeout && (elapsed >= idle_timeout)) {
2404           break;
2405         }
2406       } else {
2407         // wait at least 20 seconds for initial connect
2408         if (idle_timeout && (elapsed >= (idle_timeout > 20 ? idle_timeout : 20))) {
2409           break;
2410         }
2411       }
2412     } else {
2413       // exit after 30 minutes idle time
2414       if (elapsed >= 30*60) {
2415         break;
2416       }
2417     }
2418
2419     rfbProcessEvents (vt->screen, 40000); /* 40 ms */
2420
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);
2424       vt->ibuf_count = 0;
2425       last_time = time (NULL);
2426     }
2427
2428     if (!vt->mark_active) {
2429
2430       int num_fds = select (master+1, &fs1, NULL, NULL, &tv1);
2431       if (num_fds >= 0) {
2432         if (FD_ISSET (master, &fs1)) {
2433           char buffer[1024];
2434           int c;
2435           while ((c = read (master, buffer, 1024)) == -1) {
2436             if (errno != EAGAIN) break;
2437           }
2438           if (c == -1) break;
2439           vncterm_puts (vt, buffer, c);
2440         }
2441       } else {
2442         break;
2443       }
2444     }
2445   }
2446
2447   kill (pid, 9);
2448   int status;
2449   waitpid(pid, &status, 0);
2450
2451   exit (0);
2452 }