3 Copyright (C) 2007-2011 Proxmox Server Solutions GmbH
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 Author: Dietmar Maurer <dietmar@proxmox.com>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
32 #include <pty.h> /* for openpty and forkpty */
35 #include <sys/ioctl.h>
40 #include "spiceterm.h"
45 #include <spice/enums.h>
46 #include <spice/macros.h>
47 #include <spice/qxl_dev.h>
49 #include <gdk/gdkkeysyms.h>
50 #include "test_display_base.h"
52 /* define this for debugging */
57 #define TERMIDCODE "[?1;2c" // vt100 ID
59 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
60 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
65 /* these colours are from linux kernel drivers/char/vt.c */
67 static int idle_timeout
= 1;
69 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
70 8,12,10,14, 9,13,11,15 };
74 print_usage (const char *msg
)
76 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
77 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
80 /* Convert UCS2 to UTF8 sequence, trailing zero */
82 ucs2_to_utf8 (unicode c
, char *out
)
85 out
[0] = c
; // 0*******
88 } else if (c
< 0x800) {
89 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
90 out
[1] = 0x80 | (c
& 0x3f);
94 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
95 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
96 out
[2] = 0x80 | (c
& 0x3f);
106 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
108 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
113 int ec
= vt_fontmap
[ch
];
115 test_draw_update_char(vt
->screen
, x
, y
, ec
, attrib
);
119 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
121 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
123 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
124 int y2
= y1
- vt
->y_displ
;
126 y2
+= vt
->total_height
;
128 if (y2
< vt
->height
) {
129 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
130 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
);
135 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
137 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
139 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
140 int y2
= y1
- vt
->y_displ
;
142 y2
+= vt
->total_height
;
144 if (y2
< vt
->height
) {
145 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
147 c
->attrib
= vt
->default_attrib
;
148 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
149 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
151 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
156 vncterm_show_cursor (vncTerm
*vt
, int show
)
159 if (x
>= vt
->width
) {
163 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
164 int y
= y1
- vt
->y_displ
;
166 y
+= vt
->total_height
;
169 if (y
< vt
->height
) {
171 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
174 TextAttributes attrib
= vt
->default_attrib
;
175 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
176 draw_char_at (vt
, x
, y
, c
->ch
, attrib
);
178 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
184 vncterm_refresh (vncTerm
*vt
)
188 // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
191 for(y
= 0; y
< vt
->height
; y
++) {
192 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
193 for(x
= 0; x
< vt
->width
; x
++) {
194 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
197 if (++y1
== vt
->total_height
)
200 //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
202 vncterm_show_cursor (vt
, 1);
206 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
208 if ((top
+ lines
) >= bottom
) {
209 lines
= bottom
- top
-1;
212 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
216 g_error("vncterm_scroll_down not implemented");
223 int rowstride = vt->screen->paddedWidthInBytes;
224 int rows = (bottom - top - lines)*16;
226 char *in = vt->screen->frameBuffer+y0*rowstride;
227 char *out = vt->screen->frameBuffer+y1*rowstride;
228 memmove(out,in, rowstride*rows);
230 memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
231 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
234 for(i = bottom - top - lines - 1; i >= 0; i--) {
235 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
236 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
238 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
241 for (i = 0; i < lines; i++) {
243 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
244 for(j = 0; j < vt->width; j++) {
245 c->attrib = vt->default_attrib;
254 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
256 if ((top
+ lines
) >= bottom
) {
257 lines
= bottom
- top
- 1;
260 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
264 g_error("vncterm_scroll_down not implemented");
269 int y1 = (top + lines)*16;
271 int rowstride = vt->screen->paddedWidthInBytes;
272 int rows = (bottom - top - lines)*16;
274 char *in = vt->screen->frameBuffer+y1*rowstride;
275 char *out = vt->screen->frameBuffer+y0*rowstride;
276 memmove(out,in, rowstride*rows);
278 memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
280 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
282 if (!moveattr) return;
287 for(i = 0; i < (bottom - top - lines); i++) {
288 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
289 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
291 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
294 for (i = 1; i <= lines; i++) {
296 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
297 for(j = 0; j < vt->width; j++) {
298 c->attrib = vt->default_attrib;
307 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
309 if (vt
->altbuf
|| lines
== 0) return;
313 int i
= vt
->scroll_height
;
314 if (i
> vt
->total_height
- vt
->height
)
315 i
= vt
->total_height
- vt
->height
;
316 int y1
= vt
->y_base
- i
;
318 y1
+= vt
->total_height
;
319 for(i
= 0; i
< lines
; i
++) {
320 if (vt
->y_displ
== y1
) break;
321 if (--vt
->y_displ
< 0) {
322 vt
->y_displ
= vt
->total_height
- 1;
327 for(i
= 0; i
< lines
; i
++) {
328 if (vt
->y_displ
== vt
->y_base
) break;
329 if (++vt
->y_displ
== vt
->total_height
) {
336 vncterm_refresh (vt
);
339 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
341 int len
= strlen (esc
);
344 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
345 vt
->ibuf
[vt
->ibuf_count
++] = 27;
346 for (i
= 0; i
< len
; i
++) {
347 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
353 vncterm_put_lf (vncTerm
*vt
)
355 if (vt
->cy
+ 1 == vt
->region_bottom
) {
357 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
358 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
362 if (vt
->y_displ
== vt
->y_base
) {
363 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
366 if (vt
->y_displ
== vt
->y_base
) {
367 if (++vt
->y_displ
== vt
->total_height
) {
372 if (++vt
->y_base
== vt
->total_height
) {
376 if (vt
->scroll_height
< vt
->total_height
) {
380 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
381 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
383 for (x
= 0; x
< vt
->width
; x
++) {
385 c
->attrib
= vt
->default_attrib
;
389 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
391 } else if (vt
->cy
< vt
->height
- 1) {
398 vncterm_csi_m (vncTerm
*vt
)
402 for (i
= 0; i
< vt
->esc_count
; i
++) {
403 switch (vt
->esc_buf
[i
]) {
404 case 0: /* reset all console attributes to default */
405 vt
->cur_attrib
= vt
->default_attrib
;
408 vt
->cur_attrib
.bold
= 1;
411 vt
->cur_attrib
.uline
= 1;
414 vt
->cur_attrib
.blink
= 1;
417 vt
->cur_attrib
.invers
= 1;
420 vt
->cur_attrib
.unvisible
= 1;
423 vt
->cur_enc
= LAT1_MAP
;
424 // fixme: dispaly controls = 0 ?
425 // fixme: toggle meta = 0 ?
428 vt
->cur_enc
= IBMPC_MAP
;
429 // fixme: dispaly controls = 1 ?
430 // fixme: toggle meta = 0 ?
433 vt
->cur_enc
= IBMPC_MAP
;
434 // fixme: dispaly controls = 1 ?
435 // fixme: toggle meta = 1 ?
438 vt
->cur_attrib
.bold
= 0;
441 vt
->cur_attrib
.uline
= 0;
444 vt
->cur_attrib
.blink
= 0;
447 vt
->cur_attrib
.invers
= 0;
450 vt
->cur_attrib
.unvisible
= 0;
460 /* set foreground color */
461 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
464 /* reset color to default, enable underline */
465 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
466 vt
->cur_attrib
.uline
= 1;
469 /* reset color to default, disable underline */
470 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
471 vt
->cur_attrib
.uline
= 0;
481 /* set background color */
482 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
485 /* reset background color */
486 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
489 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
496 vncterm_save_cursor (vncTerm
*vt
)
498 vt
->cx_saved
= vt
->cx
;
499 vt
->cy_saved
= vt
->cy
;
500 vt
->cur_attrib_saved
= vt
->cur_attrib
;
501 vt
->charset_saved
= vt
->charset
;
502 vt
->g0enc_saved
= vt
->g0enc
;
503 vt
->g1enc_saved
= vt
->g1enc
;
504 vt
->cur_enc_saved
= vt
->cur_enc
;
508 vncterm_restore_cursor (vncTerm
*vt
)
510 vt
->cx
= vt
->cx_saved
;
511 vt
->cy
= vt
->cy_saved
;
512 vt
->cur_attrib
= vt
->cur_attrib_saved
;
513 vt
->charset
= vt
->charset_saved
;
514 vt
->g0enc
= vt
->g0enc_saved
;
515 vt
->g1enc
= vt
->g1enc_saved
;
516 vt
->cur_enc
= vt
->cur_enc_saved
;
520 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
524 vt
->y_displ
= vt
->y_base
;
528 if (vt
->altbuf
) return;
532 /* alternate buffer & cursor */
534 vncterm_save_cursor (vt
);
535 /* save screen to altcels */
536 for (y
= 0; y
< vt
->height
; y
++) {
537 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
538 for (x
= 0; x
< vt
->width
; x
++) {
539 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
544 for (y
= 0; y
<= vt
->height
; y
++) {
545 for (x
= 0; x
< vt
->width
; x
++) {
546 vncterm_clear_xy (vt
, x
, y
);
552 if (vt
->altbuf
== 0) return;
556 /* restore saved data */
557 for (y
= 0; y
< vt
->height
; y
++) {
558 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
559 for (x
= 0; x
< vt
->width
; x
++) {
560 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
564 vncterm_restore_cursor (vt
);
567 vncterm_refresh (vt
);
571 vncterm_set_mode (vncTerm
*vt
, int on_off
)
575 for (i
= 0; i
<= vt
->esc_count
; i
++) {
576 if (vt
->esc_ques
) { /* DEC private modes set/reset */
577 switch(vt
->esc_buf
[i
]) {
578 case 10: /* X11 mouse reporting on/off */
580 vt
->report_mouse
= on_off
;
582 case 1049: /* start/end special app mode (smcup/rmcup) */
583 vncterm_set_alternate_buffer (vt
, on_off
);
585 case 25: /* Cursor on/off */
586 case 9: /* X10 mouse reporting on/off */
587 case 6: /* Origin relative/absolute */
588 case 1: /* Cursor keys in appl mode*/
589 case 5: /* Inverted screen on/off */
590 case 7: /* Autowrap on/off */
591 case 8: /* Autorepeat on/off */
594 } else { /* ANSI modes set/reset */
595 /* fixme: implement me */
601 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
603 /* verify all boundaries */
609 if (x
>= vt
->width
) {
619 if (y
>= vt
->height
) {
626 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
627 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
628 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
631 vncterm_putchar (vncTerm
*vt
, unicode ch
)
637 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
640 switch(vt
->tty_state
) {
642 vt
->tty_state
= ESnormal
;
645 vt
->tty_state
= ESsquare
;
648 vt
->tty_state
= ESnonstd
;
651 vt
->tty_state
= ESpercent
;
654 vncterm_save_cursor (vt
);
657 vncterm_restore_cursor (vt
);
660 vt
->tty_state
= ESsetG0
; // SET G0
663 vt
->tty_state
= ESsetG1
; // SET G1
667 if (vt
->cy
== vt
->region_top
)
668 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
669 else if (vt
->cy
> 0) {
674 /* numeric keypad - ignored */
677 /* appl. keypad - ignored */
681 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
686 case ESnonstd
: /* Operating System Controls */
687 vt
->tty_state
= ESnormal
;
690 case 'P': /* palette escape sequence */
691 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
696 vt
->tty_state
= ESpalette
;
698 case 'R': /* reset palette */
699 // fixme: reset_palette(vc);
706 vt
->osc_textbuf
[0] = 0;
707 vt
->tty_state
= ESosc1
;
711 fprintf (stderr
, "unhandled OSC %c\n", ch
);
713 vt
->tty_state
= ESnormal
;
718 vt
->tty_state
= ESnormal
;
720 vt
->tty_state
= ESosc2
;
723 fprintf (stderr
, "got illegal OSC sequence\n");
728 if (ch
!= 0x9c && ch
!= 7) {
730 while (vt
->osc_textbuf
[i
]) i
++;
731 vt
->osc_textbuf
[i
++] = ch
;
732 vt
->osc_textbuf
[i
] = 0;
735 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
737 vt
->tty_state
= ESnormal
;
741 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
742 || (ch
>= 'a' && ch
<= 'f')) {
743 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
744 if (vt
->esc_count
== 7) {
745 // fixme: this does not work - please test
747 rfbColourMap *cmap =&vt->screen->colourMap;
749 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
750 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
751 cmap->data.bytes[i++] += vt->esc_buf[j++];
752 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
753 cmap->data.bytes[i++] += vt->esc_buf[j++];
754 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
755 cmap->data.bytes[i] += vt->esc_buf[j];
759 vt
->tty_state
= ESnormal
;
762 vt
->tty_state
= ESnormal
;
765 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
771 vt
->tty_state
= ESgetpars
;
774 vt
->tty_state
= ESidquery
;
778 if ((vt
->esc_ques
= (ch
== '?'))) {
782 if (ch
>= '0' && ch
<= '9') {
784 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
785 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
788 } else if (ch
== ';') {
792 if (vt
->esc_has_par
) {
795 vt
->tty_state
= ESgotpars
;
799 vt
->tty_state
= ESnormal
;
802 char *qes
= vt
->esc_ques
? "?" : "";
803 if (vt
->esc_count
== 0) {
804 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
805 } else if (vt
->esc_count
== 1) {
806 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
809 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
810 for (i
= 1; i
< vt
->esc_count
; i
++) {
811 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
813 fprintf (stderr
, "%c\n", ch
);
819 vncterm_set_mode (vt
, 1);
822 vncterm_set_mode (vt
, 0);
825 if (!vt
->esc_count
) {
826 vt
->esc_count
++; // default parameter 0
831 /* report cursor position */
832 /* TODO: send ESC[row;colR */
836 if (vt
->esc_buf
[0] == 0) {
839 vt
->cy
-= vt
->esc_buf
[0];
846 /* move cursor down */
847 if (vt
->esc_buf
[0] == 0) {
850 vt
->cy
+= vt
->esc_buf
[0];
851 if (vt
->cy
>= vt
->height
) {
852 vt
->cy
= vt
->height
- 1;
857 /* move cursor right */
858 if (vt
->esc_buf
[0] == 0) {
861 vt
->cx
+= vt
->esc_buf
[0];
862 if (vt
->cx
>= vt
->width
) {
863 vt
->cx
= vt
->width
- 1;
867 /* move cursor left */
868 if (vt
->esc_buf
[0] == 0) {
871 vt
->cx
-= vt
->esc_buf
[0];
878 /* move cursor to column */
879 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
882 /* move cursor to row */
883 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
887 /* move cursor to row, column */
888 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
891 switch (vt
->esc_buf
[0]) {
893 /* clear to end of screen */
894 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
895 for (x
= 0; x
< vt
->width
; x
++) {
896 if (y
== vt
->cy
&& x
< vt
->cx
) {
899 vncterm_clear_xy (vt
, x
, y
);
904 /* clear from beginning of screen */
905 for (y
= 0; y
<= vt
->cy
; y
++) {
906 for (x
= 0; x
< vt
->width
; x
++) {
907 if (y
== vt
->cy
&& x
> vt
->cx
) {
910 vncterm_clear_xy (vt
, x
, y
);
915 /* clear entire screen */
916 for (y
= 0; y
<= vt
->height
; y
++) {
917 for (x
= 0; x
< vt
->width
; x
++) {
918 vncterm_clear_xy (vt
, x
, y
);
925 switch (vt
->esc_buf
[0]) {
928 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
929 vncterm_clear_xy (vt
, x
, vt
->cy
);
933 /* clear from beginning of line */
934 for (x
= 0; x
<= vt
->cx
; x
++) {
935 vncterm_clear_xy (vt
, x
, vt
->cy
);
939 /* clear entire line */
940 for(x
= 0; x
< vt
->width
; x
++) {
941 vncterm_clear_xy (vt
, x
, vt
->cy
);
950 if (c
> vt
->height
- vt
->cy
)
951 c
= vt
->height
- vt
->cy
;
955 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
961 if (c
> vt
->height
- vt
->cy
)
962 c
= vt
->height
- vt
->cy
;
966 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
972 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
978 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
981 /* delete c character */
984 if (c
> vt
->width
- vt
->cx
)
985 c
= vt
->width
- vt
->cx
;
989 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
990 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
991 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
992 TextCell
*src
= dst
+ c
;
994 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
996 src
->attrib
= vt
->default_attrib
;
997 vncterm_update_xy (vt
, x
, vt
->cy
);
1001 /* save cursor position */
1002 vncterm_save_cursor (vt
);
1005 /* restore cursor position */
1006 vncterm_restore_cursor (vt
);
1009 /* erase c characters */
1013 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1015 for(i
= 0; i
< c
; i
++) {
1016 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1020 /* insert c character */
1022 if (c
> (vt
->width
- vt
->cx
)) {
1023 c
= vt
->width
- vt
->cx
;
1027 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1028 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1029 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1030 TextCell
*dst
= src
+ c
;
1032 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1034 src
->attrib
= vt
->cur_attrib
;
1035 vncterm_update_xy (vt
, x
, vt
->cy
);
1041 if (!vt
->esc_buf
[0])
1043 if (!vt
->esc_buf
[1])
1044 vt
->esc_buf
[1] = vt
->height
;
1045 /* Minimum allowed region is 2 lines */
1046 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1047 vt
->esc_buf
[1] <= vt
->height
) {
1048 vt
->region_top
= vt
->esc_buf
[0] - 1;
1049 vt
->region_bottom
= vt
->esc_buf
[1];
1051 vt
->cy
= vt
->region_top
;
1053 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1060 if (vt
->esc_count
== 0) {
1061 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1062 } else if (vt
->esc_count
== 1) {
1063 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1066 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1067 for (i
= 1; i
< vt
->esc_count
; i
++) {
1068 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1070 fprintf (stderr
, "%c\n", ch
);
1077 case ESsetG0
: // Set G0
1078 vt
->tty_state
= ESnormal
;
1081 vt
->g0enc
= GRAF_MAP
;
1083 vt
->g0enc
= LAT1_MAP
;
1085 vt
->g0enc
= IBMPC_MAP
;
1087 vt
->g0enc
= USER_MAP
;
1089 if (vt
->charset
== 0)
1090 vt
->cur_enc
= vt
->g0enc
;
1093 case ESsetG1
: // Set G1
1094 vt
->tty_state
= ESnormal
;
1097 vt
->g1enc
= GRAF_MAP
;
1099 vt
->g1enc
= LAT1_MAP
;
1101 vt
->g1enc
= IBMPC_MAP
;
1103 vt
->g1enc
= USER_MAP
;
1105 if (vt
->charset
== 1)
1106 vt
->cur_enc
= vt
->g1enc
;
1109 case ESidquery
: // vt100 query id
1110 vt
->tty_state
= ESnormal
;
1114 fprintf (stderr
, "ESC[>c Query term ID\n");
1116 vncterm_respond_esc (vt
, TERMIDCODE
);
1120 vt
->tty_state
= ESnormal
;
1122 case '@': /* defined in ISO 2022 */
1125 case 'G': /* prelim official escape code */
1126 case '8': /* retained for compatibility */
1131 default: // ESnormal
1132 vt
->tty_state
= ESnormal
;
1137 case 7: /* alert aka. bell */
1139 //rfbSendBell(vt->screen);
1141 case 8: /* backspace */
1145 case 9: /* tabspace */
1146 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1148 vncterm_put_lf (vt
);
1150 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1156 vncterm_put_lf (vt
);
1158 case 13: /* carriage return */
1162 /* SI (shift in), select character set 1 */
1164 vt
->cur_enc
= vt
->g1enc
;
1165 /* fixme: display controls = 1 */
1168 /* SO (shift out), select character set 0 */
1170 vt
->cur_enc
= vt
->g0enc
;
1171 /* fixme: display controls = 0 */
1174 vt
->tty_state
= ESesc
;
1176 case 127: /* delete */
1179 case 128+27: /* csi */
1180 vt
->tty_state
= ESsquare
;
1183 if (vt
->cx
>= vt
->width
) {
1186 vncterm_put_lf (vt
);
1189 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1190 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1191 c
->attrib
= vt
->cur_attrib
;
1193 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1202 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1206 vncterm_show_cursor (vt
, 0);
1209 unsigned char c
= *buf
;
1213 if (vt
->tty_state
!= ESnormal
) {
1214 // never translate escape sequence
1216 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1218 if(c
& 0x80) { // utf8 multi-byte sequence
1220 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1221 // inside UTF8 sequence
1222 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1224 if (vt
->utf_count
== 0) {
1230 // first char of a UTF8 sequence
1231 if ((c
& 0xe0) == 0xc0) {
1233 vt
->utf_char
= (c
& 0x1f);
1234 } else if ((c
& 0xf0) == 0xe0) {
1236 vt
->utf_char
= (c
& 0x0f);
1237 } else if ((c
& 0xf8) == 0xf0) {
1239 vt
->utf_char
= (c
& 0x07);
1240 } else if ((c
& 0xfc) == 0xf8) {
1242 vt
->utf_char
= (c
& 0x03);
1243 } else if ((c
& 0xfe) == 0xfc) {
1245 vt
->utf_char
= (c
& 0x01);
1258 // never translate controls
1259 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1260 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1266 vncterm_putchar (vt
, tc
);
1269 vncterm_show_cursor (vt
, 1);
1276 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1278 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1280 // seems str is Latin-1 encoded
1281 if (vt->selection) free (vt->selection);
1282 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1284 for (i = 0; i < len; i++) {
1285 vt->selection[i] = str[i] & 0xff;
1287 vt->selection_len = len;
1291 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
1295 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1298 vncterm_respond_esc (vt
, buf
);
1302 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1304 int x
= (pos
%vt
->width
)*8;
1305 int y
= (pos
/vt
->width
)*16;
1310 rfbScreenInfoPtr s=vt->screen;
1312 char *b = s->frameBuffer+y*s->width+x;
1314 for (j=0; j < 16; j++) {
1315 for(i=0; i < 8; i++) {
1316 b[j*s->width+i] ^= 0x0f;
1317 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1326 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
1329 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1330 static int button2_released = 1;
1331 static int last_mask = 0;
1332 static int sel_start_pos = 0;
1333 static int sel_end_pos = 0;
1340 if (cx >= vt->width) cx = vt->width - 1;
1342 if (cy >= vt->height) cy = vt->height - 1;
1344 if (vt->report_mouse && buttonMask != last_mask) {
1345 last_mask = buttonMask;
1346 if (buttonMask & 1) {
1347 mouse_report (vt, 0, cx, cy);
1349 if (buttonMask & 2) {
1350 mouse_report (vt, 1, cx, cy);
1352 if (buttonMask & 4) {
1353 mouse_report (vt, 2, cx, cy);
1356 mouse_report (vt, 3, cx, cy);
1360 if (buttonMask & 2) {
1361 if(button2_released && vt->selection) {
1363 for(i = 0; i < vt->selection_len; i++) {
1364 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1366 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1368 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1372 if (vt->y_displ != vt->y_base) {
1373 vt->y_displ = vt->y_base;
1374 vncterm_refresh (vt);
1377 button2_released = 0;
1379 button2_released = 1;
1382 if (buttonMask & 1) {
1383 int pos = cy*vt->width + cx;
1385 // code borrowed from libvncserver (VNConsole.c)
1387 if (!vt->mark_active) {
1389 vt->mark_active = 1;
1390 sel_start_pos = sel_end_pos = pos;
1391 vncterm_toggle_marked_cell (vt, pos);
1395 if (pos != sel_end_pos) {
1397 if (pos > sel_end_pos) {
1398 cx = sel_end_pos; cy=pos;
1400 cx=pos; cy=sel_end_pos;
1403 if (cx < sel_start_pos) {
1404 if (cy < sel_start_pos) cy--;
1410 vncterm_toggle_marked_cell (vt, cx);
1418 } else if (vt->mark_active) {
1419 vt->mark_active = 0;
1421 if (sel_start_pos > sel_end_pos) {
1422 int tmp = sel_start_pos - 1;
1423 sel_start_pos = sel_end_pos;
1427 int len = sel_end_pos - sel_start_pos + 1;
1429 if (vt->selection) free (vt->selection);
1430 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1431 vt->selection_len = len;
1432 char *sel_latin1 = (char *)malloc (len + 1);
1434 for (i = 0; i < len; i++) {
1435 int pos = sel_start_pos + i;
1436 int x = pos % vt->width;
1437 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1438 TextCell *c = &vt->cells[y1*vt->width + x];
1439 vt->selection[i] = c->ch;
1440 sel_latin1[i] = (char)c->ch;
1443 sel_latin1[len] = 0;
1444 rfbGotXCutText (vt->screen, sel_latin1, len);
1447 while (sel_start_pos <= sel_end_pos) {
1448 vncterm_toggle_marked_cell (vt, sel_start_pos++);
1453 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1458 static int client_count
= 0;
1459 static int client_connected
= 0;
1460 static int last_client
= 1;
1461 static time_t last_time
= 0;
1463 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1465 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1469 printf("MYKEYCODE %x\n", frag
);
1471 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1473 char keySym
= 'A'; // fixme;
1474 vt
->ibuf
[vt
->ibuf_count
++] = keySym
;
1476 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1480 static void my_kbd_push_keyval(SpiceKbdInstance
*sin
, uint32_t keySym
, int flags
)
1482 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1483 static int control
= 0;
1484 static int shift
= 0;
1487 //fprintf (stderr, "KEYEVENT:%d: %08x\n", flags, keySym);fflush (stderr);
1489 fprintf(stderr
, "KEYPRESS: %08x\n", keySym
);fflush (stderr
);
1491 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1493 } if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1495 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1498 if(keySym
>= 'a' && keySym
<= 'z')
1500 else if (keySym
>= 'A' && keySym
<= 'Z')
1506 case GDK_KEY_Escape
:
1508 case GDK_KEY_Return
:
1510 case GDK_KEY_BackSpace
:
1514 case GDK_KEY_Delete
: /* kdch1 */
1515 case GDK_KEY_KP_Delete
:
1517 case GDK_KEY_Home
: /* khome */
1518 case GDK_KEY_KP_Home
:
1521 case GDK_KEY_KP_End
: /* kend */
1523 case GDK_KEY_Insert
: /* kich1 */
1524 case GDK_KEY_KP_Insert
:
1527 case GDK_KEY_KP_Up
: /* kcuu1 */
1529 case GDK_KEY_Down
: /* kcud1 */
1530 case GDK_KEY_KP_Down
:
1533 case GDK_KEY_KP_Right
: /* kcuf1 */
1536 case GDK_KEY_KP_Left
: /* kcub1 */
1538 case GDK_KEY_Page_Up
:
1540 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1544 case GDK_KEY_Page_Down
:
1546 vncterm_virtual_scroll (vt
, vt
->height
/2);
1580 fprintf(stderr
, "KEYPRESS OUT:%s: %d\n", esc
, keySym
); fflush (stderr
);
1583 if (vt
->y_displ
!= vt
->y_base
) {
1584 vt
->y_displ
= vt
->y_base
;
1585 vncterm_refresh (vt
);
1589 vncterm_respond_esc(vt
, esc
);
1590 } else if(keySym
< 0x100) {
1593 gint len
= g_unichar_to_utf8(keySym
, buf
);
1597 for (i
= 0; i
< len
; i
++) {
1598 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1602 vt
->ibuf
[vt
->ibuf_count
++] = (char)keySym
;
1608 if (flags
& 2) { // UP
1609 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1611 } else if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1617 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
,
1618 SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1621 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1626 static SpiceKbdInterface my_keyboard_sif
= {
1627 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1628 .base
.description
= "spiceterm keyboard device",
1629 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1630 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1631 .push_keyval
= my_kbd_push_keyval
,
1632 .push_scan_freg
= my_kbd_push_key
,
1633 .get_leds
= my_kbd_get_leds
,
1637 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1643 SpiceCoreInterface
*core
= basic_event_loop_init();
1644 test
= test_new(core
);
1645 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1646 test_add_display_interface(test
);
1647 test_add_agent_interface(test
->server
);
1649 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1651 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1652 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1655 rfbColourMap *cmap =&screen->colourMap;
1656 cmap->data.bytes = malloc (16*3);
1658 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1659 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1660 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1664 screen->serverFormat.trueColour = FALSE;
1666 screen->kbdAddEvent = vncterm_kbd_event;
1668 screen->setXCutText = vncterm_set_xcut_text;
1670 screen->ptrAddEvent = vncterm_pointer_event;
1672 screen->desktopName = "VNC Command Terminal";
1674 screen->newClientHook = new_client;
1678 vt
->maxx
= test
->width
;
1679 vt
->maxy
= test
->height
;
1681 vt
->width
= vt
->maxx
/ 8;
1682 vt
->height
= vt
->maxy
/ 16;
1684 vt
->total_height
= vt
->height
* 20;
1685 vt
->scroll_height
= 0;
1690 vt
->region_bottom
= vt
->height
;
1692 vt
->g0enc
= LAT1_MAP
;
1693 vt
->g1enc
= GRAF_MAP
;
1694 vt
->cur_enc
= vt
->g0enc
;
1697 /* default text attributes */
1698 vt
->default_attrib
.bold
= 0;
1699 vt
->default_attrib
.uline
= 0;
1700 vt
->default_attrib
.blink
= 0;
1701 vt
->default_attrib
.invers
= 0;
1702 vt
->default_attrib
.unvisible
= 0;
1703 vt
->default_attrib
.fgcol
= 7;
1704 vt
->default_attrib
.bgcol
= 0;
1706 vt
->cur_attrib
= vt
->default_attrib
;
1708 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1710 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1711 vt
->cells
[i
].ch
= ' ';
1712 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1715 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1722 static void master_watch(int master
, int event
, void *opaque
)
1724 vncTerm
*vt
= (vncTerm
*)opaque
;
1726 printf("CHANNEL EVENT %d\n", event
);
1728 // fixme: if (!vt->mark_active) {
1730 if (event
== SPICE_WATCH_EVENT_READ
) {
1733 while ((c
= read(master
, buffer
, 1024)) == -1) {
1734 if (errno
!= EAGAIN
) break;
1737 g_error("got read error"); // fixme
1739 vncterm_puts (vt
, buffer
, c
);
1741 if (vt
->ibuf_count
> 0) {
1742 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1743 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1744 vt
->ibuf_count
= 0; // fixme: what if not all data written
1746 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1751 main (int argc
, char** argv
)
1754 char **cmdargv
= NULL
;
1755 char *command
= "/bin/bash"; // execute normal shell as default
1760 struct timeval tv
, tv1
;
1761 time_t elapsed
, cur_time
;
1762 struct winsize dimensions
;
1764 g_thread_init(NULL
);
1766 for (i
= 1; i
< argc
; i
++) {
1767 if (!strcmp (argv
[i
], "-c")) {
1768 command
= argv
[i
+1];
1769 cmdargv
= &argv
[i
+1];
1776 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1778 setlocale(LC_ALL
, ""); // set from environment
1780 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1782 // fixme: ist there a standard way to detect utf8 mode ?
1783 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1787 dimensions
.ws_col
= vt
->width
;
1788 dimensions
.ws_row
= vt
->height
;
1790 setenv ("TERM", TERM
, 1);
1792 printf("EXEC: %s\n", command
);
1794 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1797 // install default signal handlers
1798 signal (SIGQUIT
, SIG_DFL
);
1799 signal (SIGTERM
, SIG_DFL
);
1800 signal (SIGINT
, SIG_DFL
);
1803 execvp (command
, cmdargv
);
1805 execlp (command
, command
, NULL
);
1807 perror ("Error: exec failed\n");
1808 exit (-1); // should not be reached
1809 } else if (pid
== -1) {
1810 perror ("Error: fork failed\n");
1815 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1816 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1819 basic_event_loop_mainloop();
1821 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1824 if (vt->ibuf_count > 0) {
1825 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1826 write (master, vt->ibuf, vt->ibuf_count);
1828 last_time = time (NULL);
1834 waitpid(pid
, &status
, 0);