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