]> git.proxmox.com Git - vncterm.git/blame - vncterm.c
do not create a process group with setsid
[vncterm.git] / vncterm.c
CommitLineData
e6d0212c
DM
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
50char *auth_path = "/";
51char *auth_perm = "Sys.Console";
52
53int use_x509 = 1;
54
55static char *
56urlencode(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
81static int
82pve_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
161err:
162 shutdown(sfd, SHUT_RDWR);
163 return -1;
164}
165
166#ifdef DEBUG
167static 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
173static gnutls_dh_params_t dh_params;
174
175typedef struct {
176 gnutls_session_t session;
177} tls_client_t;
178
179static ssize_t
180vnc_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
188retry:
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
198static ssize_t
199vnc_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
207retry:
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
217ssize_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}
232ssize_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
248static gnutls_anon_server_credentials
249tls_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
264static gnutls_certificate_credentials_t
265tls_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
305void 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
313uint32_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
319static void
320vencrypt_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";
383err:
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
399static void
400rfbVncAuthVencrypt(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
569retry:
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
588static 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
606static int idle_timeout = 1;
607
608unsigned 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 */
612int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
613 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
614int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
615 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
616int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
617 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
618
619static void
620print_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 */
627static int
628ucs2_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
650static void
651rfb_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
678static void
679draw_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
718static void
719vncterm_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
734static void
735vncterm_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
755static void
756vncterm_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
783static void
784vncterm_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
805static void
806vncterm_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
849static void
850vncterm_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
898static void
899vncterm_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}
930static void
931vncterm_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
944static void
945vncterm_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
989static void
990vncterm_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
1087static void
1088vncterm_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
1099static void
1100vncterm_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
1111static void
1112vncterm_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
1162static void
1163vncterm_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
1192static void
1193vncterm_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
1218enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
1219 EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
1220 ESpalette, ESidquery, ESosc1, ESosc2};
1221
1222static void
1223vncterm_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
1791static int
1792vncterm_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
1863void
1864vncterm_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
1992void
1993vncterm_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
2007static void
2008mouse_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
2018void
2019vncterm_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
2037void
2038vncterm_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
2167static int client_count = 0;
2168static int client_connected = 0;
2169static int last_client = 1;
2170static time_t last_time = 0;
2171
2172void
2173client_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 */
2185enum rfbNewClientAction
2186new_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
2199vncTerm *
2200create_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
2282int
2283main (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
e6d0212c
DM
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}