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>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <arpa/inet.h>
34 #include <pty.h> /* for openpty and forkpty */
37 #include <sys/ioctl.h>
42 #include "spiceterm.h"
47 #include <spice/enums.h>
48 #include <spice/macros.h>
49 #include <spice/qxl_dev.h>
51 #include <gdk/gdkkeysyms.h>
52 #include "test_display_base.h"
54 /* define this for debugging */
59 #define TERMIDCODE "[?1;2c" // vt100 ID
61 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
62 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
67 /* these colours are from linux kernel drivers/char/vt.c */
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 */
83 ucs2_to_utf8 (unicode c, char *out)
86 out[0] = c; // 0*******
89 } else if (c < 0x800) {
90 out[0] = 0xc0 | (c >> 6); // 110***** 10******
91 out[1] = 0x80 | (c & 0x3f);
95 out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10******
96 out[1] = 0x80 | ((c >> 6) & 0x3f);
97 out[2] = 0x80 | (c & 0x3f);
107 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
109 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
113 test_draw_update_char(vt
->screen
, x
, y
, ch
, attrib
);
117 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
119 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
121 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
122 int y2
= y1
- vt
->y_displ
;
124 y2
+= vt
->total_height
;
126 if (y2
< vt
->height
) {
127 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
128 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
);
133 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
135 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
137 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
138 int y2
= y1
- vt
->y_displ
;
140 y2
+= vt
->total_height
;
142 if (y2
< vt
->height
) {
143 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
145 c
->attrib
= vt
->default_attrib
;
146 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
147 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
149 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
154 vncterm_show_cursor (vncTerm
*vt
, int show
)
157 if (x
>= vt
->width
) {
161 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
162 int y
= y1
- vt
->y_displ
;
164 y
+= vt
->total_height
;
167 if (y
< vt
->height
) {
169 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
172 TextAttributes attrib
= vt
->default_attrib
;
173 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
174 draw_char_at (vt
, x
, y
, c
->ch
, attrib
);
176 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
182 vncterm_refresh (vncTerm
*vt
)
186 // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
189 for(y
= 0; y
< vt
->height
; y
++) {
190 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
191 for(x
= 0; x
< vt
->width
; x
++) {
192 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
195 if (++y1
== vt
->total_height
)
198 //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
200 vncterm_show_cursor (vt
, 1);
204 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
206 if ((top
+ lines
) >= bottom
) {
207 lines
= bottom
- top
-1;
210 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
215 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
216 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
217 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
219 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
222 for (i
= 0; i
< lines
; i
++) {
224 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
225 for(j
= 0; j
< vt
->width
; j
++) {
226 c
->attrib
= vt
->default_attrib
;
237 test_spice_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
238 test_spice_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
242 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
244 if ((top
+ lines
) >= bottom
) {
245 lines
= bottom
- top
- 1;
248 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
255 int y1
= (top
+ lines
)*16;
258 test_spice_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
259 test_spice_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
268 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
269 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
270 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
272 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
275 for (i
= 1; i
<= lines
; i
++) {
277 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
278 for(j
= 0; j
< vt
->width
; j
++) {
279 c
->attrib
= vt
->default_attrib
;
287 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
289 if (vt
->altbuf
|| lines
== 0) return;
293 int i
= vt
->scroll_height
;
294 if (i
> vt
->total_height
- vt
->height
)
295 i
= vt
->total_height
- vt
->height
;
296 int y1
= vt
->y_base
- i
;
298 y1
+= vt
->total_height
;
299 for(i
= 0; i
< lines
; i
++) {
300 if (vt
->y_displ
== y1
) break;
301 if (--vt
->y_displ
< 0) {
302 vt
->y_displ
= vt
->total_height
- 1;
307 for(i
= 0; i
< lines
; i
++) {
308 if (vt
->y_displ
== vt
->y_base
) break;
309 if (++vt
->y_displ
== vt
->total_height
) {
316 vncterm_refresh (vt
);
319 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
321 int len
= strlen (esc
);
324 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
325 vt
->ibuf
[vt
->ibuf_count
++] = 27;
326 for (i
= 0; i
< len
; i
++) {
327 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
333 vncterm_put_lf (vncTerm
*vt
)
335 if (vt
->cy
+ 1 == vt
->region_bottom
) {
337 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
338 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
342 if (vt
->y_displ
== vt
->y_base
) {
343 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
346 if (vt
->y_displ
== vt
->y_base
) {
347 if (++vt
->y_displ
== vt
->total_height
) {
352 if (++vt
->y_base
== vt
->total_height
) {
356 if (vt
->scroll_height
< vt
->total_height
) {
360 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
361 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
363 for (x
= 0; x
< vt
->width
; x
++) {
365 c
->attrib
= vt
->default_attrib
;
369 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
371 } else if (vt
->cy
< vt
->height
- 1) {
378 vncterm_csi_m (vncTerm
*vt
)
382 for (i
= 0; i
< vt
->esc_count
; i
++) {
383 switch (vt
->esc_buf
[i
]) {
384 case 0: /* reset all console attributes to default */
385 vt
->cur_attrib
= vt
->default_attrib
;
388 vt
->cur_attrib
.bold
= 1;
391 vt
->cur_attrib
.uline
= 1;
394 vt
->cur_attrib
.blink
= 1;
397 vt
->cur_attrib
.invers
= 1;
400 vt
->cur_attrib
.unvisible
= 1;
403 vt
->cur_enc
= LAT1_MAP
;
404 // fixme: dispaly controls = 0 ?
405 // fixme: toggle meta = 0 ?
408 vt
->cur_enc
= IBMPC_MAP
;
409 // fixme: dispaly controls = 1 ?
410 // fixme: toggle meta = 0 ?
413 vt
->cur_enc
= IBMPC_MAP
;
414 // fixme: dispaly controls = 1 ?
415 // fixme: toggle meta = 1 ?
418 vt
->cur_attrib
.bold
= 0;
421 vt
->cur_attrib
.uline
= 0;
424 vt
->cur_attrib
.blink
= 0;
427 vt
->cur_attrib
.invers
= 0;
430 vt
->cur_attrib
.unvisible
= 0;
440 /* set foreground color */
441 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
444 /* reset color to default, enable underline */
445 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
446 vt
->cur_attrib
.uline
= 1;
449 /* reset color to default, disable underline */
450 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
451 vt
->cur_attrib
.uline
= 0;
461 /* set background color */
462 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
465 /* reset background color */
466 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
469 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
476 vncterm_save_cursor (vncTerm
*vt
)
478 vt
->cx_saved
= vt
->cx
;
479 vt
->cy_saved
= vt
->cy
;
480 vt
->cur_attrib_saved
= vt
->cur_attrib
;
481 vt
->charset_saved
= vt
->charset
;
482 vt
->g0enc_saved
= vt
->g0enc
;
483 vt
->g1enc_saved
= vt
->g1enc
;
484 vt
->cur_enc_saved
= vt
->cur_enc
;
488 vncterm_restore_cursor (vncTerm
*vt
)
490 vt
->cx
= vt
->cx_saved
;
491 vt
->cy
= vt
->cy_saved
;
492 vt
->cur_attrib
= vt
->cur_attrib_saved
;
493 vt
->charset
= vt
->charset_saved
;
494 vt
->g0enc
= vt
->g0enc_saved
;
495 vt
->g1enc
= vt
->g1enc_saved
;
496 vt
->cur_enc
= vt
->cur_enc_saved
;
500 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
504 vt
->y_displ
= vt
->y_base
;
508 if (vt
->altbuf
) return;
512 /* alternate buffer & cursor */
514 vncterm_save_cursor (vt
);
515 /* save screen to altcels */
516 for (y
= 0; y
< vt
->height
; y
++) {
517 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
518 for (x
= 0; x
< vt
->width
; x
++) {
519 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
524 for (y
= 0; y
<= vt
->height
; y
++) {
525 for (x
= 0; x
< vt
->width
; x
++) {
526 vncterm_clear_xy (vt
, x
, y
);
532 if (vt
->altbuf
== 0) return;
536 /* restore saved data */
537 for (y
= 0; y
< vt
->height
; y
++) {
538 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
539 for (x
= 0; x
< vt
->width
; x
++) {
540 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
544 vncterm_restore_cursor (vt
);
547 vncterm_refresh (vt
);
551 vncterm_set_mode (vncTerm
*vt
, int on_off
)
555 for (i
= 0; i
<= vt
->esc_count
; i
++) {
556 if (vt
->esc_ques
) { /* DEC private modes set/reset */
557 switch(vt
->esc_buf
[i
]) {
558 case 10: /* X11 mouse reporting on/off */
560 vt
->report_mouse
= on_off
;
562 case 1049: /* start/end special app mode (smcup/rmcup) */
563 vncterm_set_alternate_buffer (vt
, on_off
);
565 case 25: /* Cursor on/off */
566 case 9: /* X10 mouse reporting on/off */
567 case 6: /* Origin relative/absolute */
568 case 1: /* Cursor keys in appl mode*/
569 case 5: /* Inverted screen on/off */
570 case 7: /* Autowrap on/off */
571 case 8: /* Autorepeat on/off */
574 } else { /* ANSI modes set/reset */
575 /* fixme: implement me */
581 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
583 /* verify all boundaries */
589 if (x
>= vt
->width
) {
599 if (y
>= vt
->height
) {
606 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
607 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
608 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
611 vncterm_putchar (vncTerm
*vt
, unicode ch
)
617 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
620 switch(vt
->tty_state
) {
622 vt
->tty_state
= ESnormal
;
625 vt
->tty_state
= ESsquare
;
628 vt
->tty_state
= ESnonstd
;
631 vt
->tty_state
= ESpercent
;
634 vncterm_save_cursor (vt
);
637 vncterm_restore_cursor (vt
);
640 vt
->tty_state
= ESsetG0
; // SET G0
643 vt
->tty_state
= ESsetG1
; // SET G1
647 if (vt
->cy
== vt
->region_top
)
648 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
649 else if (vt
->cy
> 0) {
654 /* numeric keypad - ignored */
657 /* appl. keypad - ignored */
661 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
666 case ESnonstd
: /* Operating System Controls */
667 vt
->tty_state
= ESnormal
;
670 case 'P': /* palette escape sequence */
671 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
676 vt
->tty_state
= ESpalette
;
678 case 'R': /* reset palette */
679 // fixme: reset_palette(vc);
686 vt
->osc_textbuf
[0] = 0;
687 vt
->tty_state
= ESosc1
;
691 fprintf (stderr
, "unhandled OSC %c\n", ch
);
693 vt
->tty_state
= ESnormal
;
698 vt
->tty_state
= ESnormal
;
700 vt
->tty_state
= ESosc2
;
703 fprintf (stderr
, "got illegal OSC sequence\n");
708 if (ch
!= 0x9c && ch
!= 7) {
710 while (vt
->osc_textbuf
[i
]) i
++;
711 vt
->osc_textbuf
[i
++] = ch
;
712 vt
->osc_textbuf
[i
] = 0;
715 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
717 vt
->tty_state
= ESnormal
;
721 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
722 || (ch
>= 'a' && ch
<= 'f')) {
723 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
724 if (vt
->esc_count
== 7) {
725 // fixme: this does not work - please test
727 rfbColourMap *cmap =&vt->screen->colourMap;
729 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
730 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
731 cmap->data.bytes[i++] += vt->esc_buf[j++];
732 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
733 cmap->data.bytes[i++] += vt->esc_buf[j++];
734 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
735 cmap->data.bytes[i] += vt->esc_buf[j];
739 vt
->tty_state
= ESnormal
;
742 vt
->tty_state
= ESnormal
;
745 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
751 vt
->tty_state
= ESgetpars
;
754 vt
->tty_state
= ESidquery
;
758 if ((vt
->esc_ques
= (ch
== '?'))) {
762 if (ch
>= '0' && ch
<= '9') {
764 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
765 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
768 } else if (ch
== ';') {
772 if (vt
->esc_has_par
) {
775 vt
->tty_state
= ESgotpars
;
779 vt
->tty_state
= ESnormal
;
782 char *qes
= vt
->esc_ques
? "?" : "";
783 if (vt
->esc_count
== 0) {
784 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
785 } else if (vt
->esc_count
== 1) {
786 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
789 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
790 for (i
= 1; i
< vt
->esc_count
; i
++) {
791 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
793 fprintf (stderr
, "%c\n", ch
);
799 vncterm_set_mode (vt
, 1);
802 vncterm_set_mode (vt
, 0);
805 if (!vt
->esc_count
) {
806 vt
->esc_count
++; // default parameter 0
811 /* report cursor position */
812 /* TODO: send ESC[row;colR */
816 if (vt
->esc_buf
[0] == 0) {
819 vt
->cy
-= vt
->esc_buf
[0];
826 /* move cursor down */
827 if (vt
->esc_buf
[0] == 0) {
830 vt
->cy
+= vt
->esc_buf
[0];
831 if (vt
->cy
>= vt
->height
) {
832 vt
->cy
= vt
->height
- 1;
837 /* move cursor right */
838 if (vt
->esc_buf
[0] == 0) {
841 vt
->cx
+= vt
->esc_buf
[0];
842 if (vt
->cx
>= vt
->width
) {
843 vt
->cx
= vt
->width
- 1;
847 /* move cursor left */
848 if (vt
->esc_buf
[0] == 0) {
851 vt
->cx
-= vt
->esc_buf
[0];
858 /* move cursor to column */
859 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
862 /* move cursor to row */
863 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
867 /* move cursor to row, column */
868 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
871 switch (vt
->esc_buf
[0]) {
873 /* clear to end of screen */
874 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
875 for (x
= 0; x
< vt
->width
; x
++) {
876 if (y
== vt
->cy
&& x
< vt
->cx
) {
879 vncterm_clear_xy (vt
, x
, y
);
884 /* clear from beginning of screen */
885 for (y
= 0; y
<= vt
->cy
; y
++) {
886 for (x
= 0; x
< vt
->width
; x
++) {
887 if (y
== vt
->cy
&& x
> vt
->cx
) {
890 vncterm_clear_xy (vt
, x
, y
);
895 /* clear entire screen */
896 for (y
= 0; y
<= vt
->height
; y
++) {
897 for (x
= 0; x
< vt
->width
; x
++) {
898 vncterm_clear_xy (vt
, x
, y
);
905 switch (vt
->esc_buf
[0]) {
908 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
909 vncterm_clear_xy (vt
, x
, vt
->cy
);
913 /* clear from beginning of line */
914 for (x
= 0; x
<= vt
->cx
; x
++) {
915 vncterm_clear_xy (vt
, x
, vt
->cy
);
919 /* clear entire line */
920 for(x
= 0; x
< vt
->width
; x
++) {
921 vncterm_clear_xy (vt
, x
, vt
->cy
);
930 if (c
> vt
->height
- vt
->cy
)
931 c
= vt
->height
- vt
->cy
;
935 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
941 if (c
> vt
->height
- vt
->cy
)
942 c
= vt
->height
- vt
->cy
;
946 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
952 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
958 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
961 /* delete c character */
964 if (c
> vt
->width
- vt
->cx
)
965 c
= vt
->width
- vt
->cx
;
969 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
970 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
971 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
972 TextCell
*src
= dst
+ c
;
974 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
976 src
->attrib
= vt
->default_attrib
;
977 vncterm_update_xy (vt
, x
, vt
->cy
);
981 /* save cursor position */
982 vncterm_save_cursor (vt
);
985 /* restore cursor position */
986 vncterm_restore_cursor (vt
);
989 /* erase c characters */
993 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
995 for(i
= 0; i
< c
; i
++) {
996 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1000 /* insert c character */
1002 if (c
> (vt
->width
- vt
->cx
)) {
1003 c
= vt
->width
- vt
->cx
;
1007 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1008 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1009 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1010 TextCell
*dst
= src
+ c
;
1012 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1014 src
->attrib
= vt
->cur_attrib
;
1015 vncterm_update_xy (vt
, x
, vt
->cy
);
1021 if (!vt
->esc_buf
[0])
1023 if (!vt
->esc_buf
[1])
1024 vt
->esc_buf
[1] = vt
->height
;
1025 /* Minimum allowed region is 2 lines */
1026 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1027 vt
->esc_buf
[1] <= vt
->height
) {
1028 vt
->region_top
= vt
->esc_buf
[0] - 1;
1029 vt
->region_bottom
= vt
->esc_buf
[1];
1031 vt
->cy
= vt
->region_top
;
1033 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1040 if (vt
->esc_count
== 0) {
1041 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1042 } else if (vt
->esc_count
== 1) {
1043 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1046 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1047 for (i
= 1; i
< vt
->esc_count
; i
++) {
1048 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1050 fprintf (stderr
, "%c\n", ch
);
1057 case ESsetG0
: // Set G0
1058 vt
->tty_state
= ESnormal
;
1061 vt
->g0enc
= GRAF_MAP
;
1063 vt
->g0enc
= LAT1_MAP
;
1065 vt
->g0enc
= IBMPC_MAP
;
1067 vt
->g0enc
= USER_MAP
;
1069 if (vt
->charset
== 0)
1070 vt
->cur_enc
= vt
->g0enc
;
1073 case ESsetG1
: // Set G1
1074 vt
->tty_state
= ESnormal
;
1077 vt
->g1enc
= GRAF_MAP
;
1079 vt
->g1enc
= LAT1_MAP
;
1081 vt
->g1enc
= IBMPC_MAP
;
1083 vt
->g1enc
= USER_MAP
;
1085 if (vt
->charset
== 1)
1086 vt
->cur_enc
= vt
->g1enc
;
1089 case ESidquery
: // vt100 query id
1090 vt
->tty_state
= ESnormal
;
1094 fprintf (stderr
, "ESC[>c Query term ID\n");
1096 vncterm_respond_esc (vt
, TERMIDCODE
);
1100 vt
->tty_state
= ESnormal
;
1102 case '@': /* defined in ISO 2022 */
1105 case 'G': /* prelim official escape code */
1106 case '8': /* retained for compatibility */
1111 default: // ESnormal
1112 vt
->tty_state
= ESnormal
;
1117 case 7: /* alert aka. bell */
1119 //rfbSendBell(vt->screen);
1121 case 8: /* backspace */
1125 case 9: /* tabspace */
1126 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1128 vncterm_put_lf (vt
);
1130 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1136 vncterm_put_lf (vt
);
1138 case 13: /* carriage return */
1142 /* SI (shift in), select character set 1 */
1144 vt
->cur_enc
= vt
->g1enc
;
1145 /* fixme: display controls = 1 */
1148 /* SO (shift out), select character set 0 */
1150 vt
->cur_enc
= vt
->g0enc
;
1151 /* fixme: display controls = 0 */
1154 vt
->tty_state
= ESesc
;
1156 case 127: /* delete */
1159 case 128+27: /* csi */
1160 vt
->tty_state
= ESsquare
;
1163 if (vt
->cx
>= vt
->width
) {
1166 vncterm_put_lf (vt
);
1169 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1170 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1171 c
->attrib
= vt
->cur_attrib
;
1173 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1182 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1186 vncterm_show_cursor (vt
, 0);
1189 unsigned char c
= *buf
;
1193 if (vt
->tty_state
!= ESnormal
) {
1194 // never translate escape sequence
1196 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1198 if(c
& 0x80) { // utf8 multi-byte sequence
1200 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1201 // inside UTF8 sequence
1202 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1204 if (vt
->utf_count
== 0) {
1210 // first char of a UTF8 sequence
1211 if ((c
& 0xe0) == 0xc0) {
1213 vt
->utf_char
= (c
& 0x1f);
1214 } else if ((c
& 0xf0) == 0xe0) {
1216 vt
->utf_char
= (c
& 0x0f);
1217 } else if ((c
& 0xf8) == 0xf0) {
1219 vt
->utf_char
= (c
& 0x07);
1220 } else if ((c
& 0xfc) == 0xf8) {
1222 vt
->utf_char
= (c
& 0x03);
1223 } else if ((c
& 0xfe) == 0xfc) {
1225 vt
->utf_char
= (c
& 0x01);
1238 // never translate controls
1239 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1240 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1246 vncterm_putchar (vt
, tc
);
1249 vncterm_show_cursor (vt
, 1);
1256 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1258 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1260 // seems str is Latin-1 encoded
1261 if (vt->selection) free (vt->selection);
1262 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1264 for (i = 0; i < len; i++) {
1265 vt->selection[i] = str[i] & 0xff;
1267 vt->selection_len = len;
1272 mouse_report (vncTerm *vt, int butt, int mrx, int mry)
1276 sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
1279 vncterm_respond_esc (vt, buf);
1284 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1288 int x= (pos%vt->width)*8;
1289 int y= (pos/vt->width)*16;
1292 rfbScreenInfoPtr s=vt->screen;
1294 char *b = s->frameBuffer+y*s->width+x;
1296 for (j=0; j < 16; j++) {
1297 for(i=0; i < 8; i++) {
1298 b[j*s->width+i] ^= 0x0f;
1299 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1308 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
1311 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1312 static int button2_released = 1;
1313 static int last_mask = 0;
1314 static int sel_start_pos = 0;
1315 static int sel_end_pos = 0;
1322 if (cx >= vt->width) cx = vt->width - 1;
1324 if (cy >= vt->height) cy = vt->height - 1;
1326 if (vt->report_mouse && buttonMask != last_mask) {
1327 last_mask = buttonMask;
1328 if (buttonMask & 1) {
1329 mouse_report (vt, 0, cx, cy);
1331 if (buttonMask & 2) {
1332 mouse_report (vt, 1, cx, cy);
1334 if (buttonMask & 4) {
1335 mouse_report (vt, 2, cx, cy);
1338 mouse_report (vt, 3, cx, cy);
1342 if (buttonMask & 2) {
1343 if(button2_released && vt->selection) {
1345 for(i = 0; i < vt->selection_len; i++) {
1346 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1348 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1350 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1354 if (vt->y_displ != vt->y_base) {
1355 vt->y_displ = vt->y_base;
1356 vncterm_refresh (vt);
1359 button2_released = 0;
1361 button2_released = 1;
1364 if (buttonMask & 1) {
1365 int pos = cy*vt->width + cx;
1367 // code borrowed from libvncserver (VNConsole.c)
1369 if (!vt->mark_active) {
1371 vt->mark_active = 1;
1372 sel_start_pos = sel_end_pos = pos;
1373 vncterm_toggle_marked_cell (vt, pos);
1377 if (pos != sel_end_pos) {
1379 if (pos > sel_end_pos) {
1380 cx = sel_end_pos; cy=pos;
1382 cx=pos; cy=sel_end_pos;
1385 if (cx < sel_start_pos) {
1386 if (cy < sel_start_pos) cy--;
1392 vncterm_toggle_marked_cell (vt, cx);
1400 } else if (vt->mark_active) {
1401 vt->mark_active = 0;
1403 if (sel_start_pos > sel_end_pos) {
1404 int tmp = sel_start_pos - 1;
1405 sel_start_pos = sel_end_pos;
1409 int len = sel_end_pos - sel_start_pos + 1;
1411 if (vt->selection) free (vt->selection);
1412 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1413 vt->selection_len = len;
1414 char *sel_latin1 = (char *)malloc (len + 1);
1416 for (i = 0; i < len; i++) {
1417 int pos = sel_start_pos + i;
1418 int x = pos % vt->width;
1419 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1420 TextCell *c = &vt->cells[y1*vt->width + x];
1421 vt->selection[i] = c->ch;
1422 sel_latin1[i] = (char)c->ch;
1425 sel_latin1[len] = 0;
1426 rfbGotXCutText (vt->screen, sel_latin1, len);
1429 while (sel_start_pos <= sel_end_pos) {
1430 vncterm_toggle_marked_cell (vt, sel_start_pos++);
1435 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1440 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1442 // vncTerm *vt = SPICE_CONTAINEROF(sin, vncTerm, keyboard_sin);
1444 /* we no not need this */
1449 static void my_kbd_push_keyval(SpiceKbdInstance
*sin
, uint32_t keySym
, int flags
)
1451 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1452 static int control
= 0;
1453 static int shift
= 0;
1458 fprintf (stderr
, "KEYEVENT:%d: %08x\n", flags
, keySym
);fflush (stderr
);
1460 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1462 } if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1464 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1467 if(keySym
>= 'a' && keySym
<= 'z')
1468 uc
= keySym
- 'a' + 1;
1469 else if (keySym
>= 'A' && keySym
<= 'Z')
1470 uc
= keySym
- 'A' + 1;
1475 //printf("CONTROL: %08x %d\n", keySym, uc);
1479 case GDK_KEY_Escape
:
1481 case GDK_KEY_Return
:
1483 case GDK_KEY_BackSpace
:
1487 case GDK_KEY_Delete
: /* kdch1 */
1488 case GDK_KEY_KP_Delete
:
1490 case GDK_KEY_Home
: /* khome */
1491 case GDK_KEY_KP_Home
:
1494 case GDK_KEY_KP_End
: /* kend */
1496 case GDK_KEY_Insert
: /* kich1 */
1497 case GDK_KEY_KP_Insert
:
1500 case GDK_KEY_KP_Up
: /* kcuu1 */
1502 case GDK_KEY_Down
: /* kcud1 */
1503 case GDK_KEY_KP_Down
:
1506 case GDK_KEY_KP_Right
: /* kcuf1 */
1509 case GDK_KEY_KP_Left
: /* kcub1 */
1511 case GDK_KEY_Page_Up
:
1513 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1517 case GDK_KEY_Page_Down
:
1519 vncterm_virtual_scroll (vt
, vt
->height
/2);
1548 if (keySym
< 0x100) {
1556 fprintf(stderr
, "KEYPRESS OUT:%s: %08x\n", esc
, uc
); fflush (stderr
);
1559 if (vt
->y_displ
!= vt
->y_base
) {
1560 vt
->y_displ
= vt
->y_base
;
1561 vncterm_refresh (vt
);
1565 vncterm_respond_esc(vt
, esc
);
1566 } else if (uc
> 0) {
1569 gint len
= g_unichar_to_utf8(uc
, buf
);
1573 for (i
= 0; i
< len
; i
++) {
1574 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1578 vt
->ibuf
[vt
->ibuf_count
++] = (char)uc
;
1587 if (flags
& 2) { // UP
1588 //printf("KEYRELEASE %08x\n", keySym);
1590 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1592 } else if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1597 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
,
1598 SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1601 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1606 static SpiceKbdInterface my_keyboard_sif
= {
1607 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1608 .base
.description
= "spiceterm keyboard device",
1609 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1610 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1611 .push_keyval
= my_kbd_push_keyval
,
1612 .push_scan_freg
= my_kbd_push_key
,
1613 .get_leds
= my_kbd_get_leds
,
1617 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1623 SpiceCoreInterface
*core
= basic_event_loop_init();
1624 test
= test_new(core
);
1625 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1626 test_add_display_interface(test
);
1627 test_add_agent_interface(test
->server
);
1629 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1631 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1632 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1635 rfbColourMap *cmap =&screen->colourMap;
1636 cmap->data.bytes = malloc (16*3);
1638 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1639 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1640 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1644 screen->serverFormat.trueColour = FALSE;
1646 screen->kbdAddEvent = vncterm_kbd_event;
1648 screen->setXCutText = vncterm_set_xcut_text;
1650 screen->ptrAddEvent = vncterm_pointer_event;
1652 screen->desktopName = "VNC Command Terminal";
1654 screen->newClientHook = new_client;
1658 vt
->maxx
= test
->width
;
1659 vt
->maxy
= test
->height
;
1661 vt
->width
= vt
->maxx
/ 8;
1662 vt
->height
= vt
->maxy
/ 16;
1664 vt
->total_height
= vt
->height
* 20;
1665 vt
->scroll_height
= 0;
1670 vt
->region_bottom
= vt
->height
;
1672 vt
->g0enc
= LAT1_MAP
;
1673 vt
->g1enc
= GRAF_MAP
;
1674 vt
->cur_enc
= vt
->g0enc
;
1677 /* default text attributes */
1678 vt
->default_attrib
.bold
= 0;
1679 vt
->default_attrib
.uline
= 0;
1680 vt
->default_attrib
.blink
= 0;
1681 vt
->default_attrib
.invers
= 0;
1682 vt
->default_attrib
.unvisible
= 0;
1683 vt
->default_attrib
.fgcol
= 7;
1684 vt
->default_attrib
.bgcol
= 0;
1686 vt
->cur_attrib
= vt
->default_attrib
;
1688 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1690 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1691 vt
->cells
[i
].ch
= ' ';
1692 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1695 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1702 static void master_watch(int master
, int event
, void *opaque
)
1704 vncTerm
*vt
= (vncTerm
*)opaque
;
1706 printf("CHANNEL EVENT %d\n", event
);
1708 // fixme: if (!vt->mark_active) {
1710 if (event
== SPICE_WATCH_EVENT_READ
) {
1713 while ((c
= read(master
, buffer
, 1024)) == -1) {
1714 if (errno
!= EAGAIN
) break;
1717 g_error("got read error"); // fixme
1719 vncterm_puts (vt
, buffer
, c
);
1721 if (vt
->ibuf_count
> 0) {
1722 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1723 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1724 vt
->ibuf_count
= 0; // fixme: what if not all data written
1726 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1731 main (int argc
, char** argv
)
1734 char **cmdargv
= NULL
;
1735 char *command
= "/bin/bash"; // execute normal shell as default
1739 struct winsize dimensions
;
1741 g_thread_init(NULL
);
1743 for (i
= 1; i
< argc
; i
++) {
1744 if (!strcmp (argv
[i
], "-c")) {
1745 command
= argv
[i
+1];
1746 cmdargv
= &argv
[i
+1];
1753 if (0) print_usage(NULL
); // fixme:
1755 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1757 setlocale(LC_ALL
, ""); // set from environment
1759 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1761 // fixme: ist there a standard way to detect utf8 mode ?
1762 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1766 dimensions
.ws_col
= vt
->width
;
1767 dimensions
.ws_row
= vt
->height
;
1769 setenv ("TERM", TERM
, 1);
1771 printf("EXEC: %s\n", command
);
1773 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1776 // install default signal handlers
1777 signal (SIGQUIT
, SIG_DFL
);
1778 signal (SIGTERM
, SIG_DFL
);
1779 signal (SIGINT
, SIG_DFL
);
1782 execvp (command
, cmdargv
);
1784 execlp (command
, command
, NULL
);
1786 perror ("Error: exec failed\n");
1787 exit (-1); // should not be reached
1788 } else if (pid
== -1) {
1789 perror ("Error: fork failed\n");
1794 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1795 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1798 basic_event_loop_mainloop();
1800 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1803 if (vt->ibuf_count > 0) {
1804 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1805 write (master, vt->ibuf, vt->ibuf_count);
1807 last_time = time (NULL);
1813 waitpid(pid
, &status
, 0);