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) {
217 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
218 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
219 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
221 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
224 for (i
= 0; i
< lines
; i
++) {
226 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
227 for(j
= 0; j
< vt
->width
; j
++) {
228 c
->attrib
= vt
->default_attrib
;
239 test_spice_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
240 test_spice_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
244 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
246 if ((top
+ lines
) >= bottom
) {
247 lines
= bottom
- top
- 1;
250 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
257 int y1
= (top
+ lines
)*16;
260 test_spice_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
261 test_spice_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
270 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
271 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
272 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
274 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof (TextCell
));
277 for (i
= 1; i
<= lines
; i
++) {
279 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
280 for(j
= 0; j
< vt
->width
; j
++) {
281 c
->attrib
= vt
->default_attrib
;
289 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
291 if (vt
->altbuf
|| lines
== 0) return;
295 int i
= vt
->scroll_height
;
296 if (i
> vt
->total_height
- vt
->height
)
297 i
= vt
->total_height
- vt
->height
;
298 int y1
= vt
->y_base
- i
;
300 y1
+= vt
->total_height
;
301 for(i
= 0; i
< lines
; i
++) {
302 if (vt
->y_displ
== y1
) break;
303 if (--vt
->y_displ
< 0) {
304 vt
->y_displ
= vt
->total_height
- 1;
309 for(i
= 0; i
< lines
; i
++) {
310 if (vt
->y_displ
== vt
->y_base
) break;
311 if (++vt
->y_displ
== vt
->total_height
) {
318 vncterm_refresh (vt
);
321 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
323 int len
= strlen (esc
);
326 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
327 vt
->ibuf
[vt
->ibuf_count
++] = 27;
328 for (i
= 0; i
< len
; i
++) {
329 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
335 vncterm_put_lf (vncTerm
*vt
)
337 if (vt
->cy
+ 1 == vt
->region_bottom
) {
339 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
340 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
344 if (vt
->y_displ
== vt
->y_base
) {
345 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
348 if (vt
->y_displ
== vt
->y_base
) {
349 if (++vt
->y_displ
== vt
->total_height
) {
354 if (++vt
->y_base
== vt
->total_height
) {
358 if (vt
->scroll_height
< vt
->total_height
) {
362 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
363 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
365 for (x
= 0; x
< vt
->width
; x
++) {
367 c
->attrib
= vt
->default_attrib
;
371 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
373 } else if (vt
->cy
< vt
->height
- 1) {
380 vncterm_csi_m (vncTerm
*vt
)
384 for (i
= 0; i
< vt
->esc_count
; i
++) {
385 switch (vt
->esc_buf
[i
]) {
386 case 0: /* reset all console attributes to default */
387 vt
->cur_attrib
= vt
->default_attrib
;
390 vt
->cur_attrib
.bold
= 1;
393 vt
->cur_attrib
.uline
= 1;
396 vt
->cur_attrib
.blink
= 1;
399 vt
->cur_attrib
.invers
= 1;
402 vt
->cur_attrib
.unvisible
= 1;
405 vt
->cur_enc
= LAT1_MAP
;
406 // fixme: dispaly controls = 0 ?
407 // fixme: toggle meta = 0 ?
410 vt
->cur_enc
= IBMPC_MAP
;
411 // fixme: dispaly controls = 1 ?
412 // fixme: toggle meta = 0 ?
415 vt
->cur_enc
= IBMPC_MAP
;
416 // fixme: dispaly controls = 1 ?
417 // fixme: toggle meta = 1 ?
420 vt
->cur_attrib
.bold
= 0;
423 vt
->cur_attrib
.uline
= 0;
426 vt
->cur_attrib
.blink
= 0;
429 vt
->cur_attrib
.invers
= 0;
432 vt
->cur_attrib
.unvisible
= 0;
442 /* set foreground color */
443 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
446 /* reset color to default, enable underline */
447 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
448 vt
->cur_attrib
.uline
= 1;
451 /* reset color to default, disable underline */
452 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
453 vt
->cur_attrib
.uline
= 0;
463 /* set background color */
464 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
467 /* reset background color */
468 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
471 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
478 vncterm_save_cursor (vncTerm
*vt
)
480 vt
->cx_saved
= vt
->cx
;
481 vt
->cy_saved
= vt
->cy
;
482 vt
->cur_attrib_saved
= vt
->cur_attrib
;
483 vt
->charset_saved
= vt
->charset
;
484 vt
->g0enc_saved
= vt
->g0enc
;
485 vt
->g1enc_saved
= vt
->g1enc
;
486 vt
->cur_enc_saved
= vt
->cur_enc
;
490 vncterm_restore_cursor (vncTerm
*vt
)
492 vt
->cx
= vt
->cx_saved
;
493 vt
->cy
= vt
->cy_saved
;
494 vt
->cur_attrib
= vt
->cur_attrib_saved
;
495 vt
->charset
= vt
->charset_saved
;
496 vt
->g0enc
= vt
->g0enc_saved
;
497 vt
->g1enc
= vt
->g1enc_saved
;
498 vt
->cur_enc
= vt
->cur_enc_saved
;
502 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
506 vt
->y_displ
= vt
->y_base
;
510 if (vt
->altbuf
) return;
514 /* alternate buffer & cursor */
516 vncterm_save_cursor (vt
);
517 /* save screen to altcels */
518 for (y
= 0; y
< vt
->height
; y
++) {
519 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
520 for (x
= 0; x
< vt
->width
; x
++) {
521 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
526 for (y
= 0; y
<= vt
->height
; y
++) {
527 for (x
= 0; x
< vt
->width
; x
++) {
528 vncterm_clear_xy (vt
, x
, y
);
534 if (vt
->altbuf
== 0) return;
538 /* restore saved data */
539 for (y
= 0; y
< vt
->height
; y
++) {
540 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
541 for (x
= 0; x
< vt
->width
; x
++) {
542 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
546 vncterm_restore_cursor (vt
);
549 vncterm_refresh (vt
);
553 vncterm_set_mode (vncTerm
*vt
, int on_off
)
557 for (i
= 0; i
<= vt
->esc_count
; i
++) {
558 if (vt
->esc_ques
) { /* DEC private modes set/reset */
559 switch(vt
->esc_buf
[i
]) {
560 case 10: /* X11 mouse reporting on/off */
562 vt
->report_mouse
= on_off
;
564 case 1049: /* start/end special app mode (smcup/rmcup) */
565 vncterm_set_alternate_buffer (vt
, on_off
);
567 case 25: /* Cursor on/off */
568 case 9: /* X10 mouse reporting on/off */
569 case 6: /* Origin relative/absolute */
570 case 1: /* Cursor keys in appl mode*/
571 case 5: /* Inverted screen on/off */
572 case 7: /* Autowrap on/off */
573 case 8: /* Autorepeat on/off */
576 } else { /* ANSI modes set/reset */
577 /* fixme: implement me */
583 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
585 /* verify all boundaries */
591 if (x
>= vt
->width
) {
601 if (y
>= vt
->height
) {
608 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
609 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
610 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
613 vncterm_putchar (vncTerm
*vt
, unicode ch
)
619 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
622 switch(vt
->tty_state
) {
624 vt
->tty_state
= ESnormal
;
627 vt
->tty_state
= ESsquare
;
630 vt
->tty_state
= ESnonstd
;
633 vt
->tty_state
= ESpercent
;
636 vncterm_save_cursor (vt
);
639 vncterm_restore_cursor (vt
);
642 vt
->tty_state
= ESsetG0
; // SET G0
645 vt
->tty_state
= ESsetG1
; // SET G1
649 if (vt
->cy
== vt
->region_top
)
650 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
651 else if (vt
->cy
> 0) {
656 /* numeric keypad - ignored */
659 /* appl. keypad - ignored */
663 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
668 case ESnonstd
: /* Operating System Controls */
669 vt
->tty_state
= ESnormal
;
672 case 'P': /* palette escape sequence */
673 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
678 vt
->tty_state
= ESpalette
;
680 case 'R': /* reset palette */
681 // fixme: reset_palette(vc);
688 vt
->osc_textbuf
[0] = 0;
689 vt
->tty_state
= ESosc1
;
693 fprintf (stderr
, "unhandled OSC %c\n", ch
);
695 vt
->tty_state
= ESnormal
;
700 vt
->tty_state
= ESnormal
;
702 vt
->tty_state
= ESosc2
;
705 fprintf (stderr
, "got illegal OSC sequence\n");
710 if (ch
!= 0x9c && ch
!= 7) {
712 while (vt
->osc_textbuf
[i
]) i
++;
713 vt
->osc_textbuf
[i
++] = ch
;
714 vt
->osc_textbuf
[i
] = 0;
717 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
719 vt
->tty_state
= ESnormal
;
723 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
724 || (ch
>= 'a' && ch
<= 'f')) {
725 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
726 if (vt
->esc_count
== 7) {
727 // fixme: this does not work - please test
729 rfbColourMap *cmap =&vt->screen->colourMap;
731 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
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++];
736 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
737 cmap->data.bytes[i] += vt->esc_buf[j];
741 vt
->tty_state
= ESnormal
;
744 vt
->tty_state
= ESnormal
;
747 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
753 vt
->tty_state
= ESgetpars
;
756 vt
->tty_state
= ESidquery
;
760 if ((vt
->esc_ques
= (ch
== '?'))) {
764 if (ch
>= '0' && ch
<= '9') {
766 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
767 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
770 } else if (ch
== ';') {
774 if (vt
->esc_has_par
) {
777 vt
->tty_state
= ESgotpars
;
781 vt
->tty_state
= ESnormal
;
784 char *qes
= vt
->esc_ques
? "?" : "";
785 if (vt
->esc_count
== 0) {
786 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
787 } else if (vt
->esc_count
== 1) {
788 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
791 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
792 for (i
= 1; i
< vt
->esc_count
; i
++) {
793 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
795 fprintf (stderr
, "%c\n", ch
);
801 vncterm_set_mode (vt
, 1);
804 vncterm_set_mode (vt
, 0);
807 if (!vt
->esc_count
) {
808 vt
->esc_count
++; // default parameter 0
813 /* report cursor position */
814 /* TODO: send ESC[row;colR */
818 if (vt
->esc_buf
[0] == 0) {
821 vt
->cy
-= vt
->esc_buf
[0];
828 /* move cursor down */
829 if (vt
->esc_buf
[0] == 0) {
832 vt
->cy
+= vt
->esc_buf
[0];
833 if (vt
->cy
>= vt
->height
) {
834 vt
->cy
= vt
->height
- 1;
839 /* move cursor right */
840 if (vt
->esc_buf
[0] == 0) {
843 vt
->cx
+= vt
->esc_buf
[0];
844 if (vt
->cx
>= vt
->width
) {
845 vt
->cx
= vt
->width
- 1;
849 /* move cursor left */
850 if (vt
->esc_buf
[0] == 0) {
853 vt
->cx
-= vt
->esc_buf
[0];
860 /* move cursor to column */
861 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
864 /* move cursor to row */
865 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
869 /* move cursor to row, column */
870 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
873 switch (vt
->esc_buf
[0]) {
875 /* clear to end of screen */
876 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
877 for (x
= 0; x
< vt
->width
; x
++) {
878 if (y
== vt
->cy
&& x
< vt
->cx
) {
881 vncterm_clear_xy (vt
, x
, y
);
886 /* clear from beginning of screen */
887 for (y
= 0; y
<= vt
->cy
; y
++) {
888 for (x
= 0; x
< vt
->width
; x
++) {
889 if (y
== vt
->cy
&& x
> vt
->cx
) {
892 vncterm_clear_xy (vt
, x
, y
);
897 /* clear entire screen */
898 for (y
= 0; y
<= vt
->height
; y
++) {
899 for (x
= 0; x
< vt
->width
; x
++) {
900 vncterm_clear_xy (vt
, x
, y
);
907 switch (vt
->esc_buf
[0]) {
910 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
911 vncterm_clear_xy (vt
, x
, vt
->cy
);
915 /* clear from beginning of line */
916 for (x
= 0; x
<= vt
->cx
; x
++) {
917 vncterm_clear_xy (vt
, x
, vt
->cy
);
921 /* clear entire line */
922 for(x
= 0; x
< vt
->width
; x
++) {
923 vncterm_clear_xy (vt
, x
, vt
->cy
);
932 if (c
> vt
->height
- vt
->cy
)
933 c
= vt
->height
- vt
->cy
;
937 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
943 if (c
> vt
->height
- vt
->cy
)
944 c
= vt
->height
- vt
->cy
;
948 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
954 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
960 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
963 /* delete c character */
966 if (c
> vt
->width
- vt
->cx
)
967 c
= vt
->width
- vt
->cx
;
971 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
972 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
973 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
974 TextCell
*src
= dst
+ c
;
976 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
978 src
->attrib
= vt
->default_attrib
;
979 vncterm_update_xy (vt
, x
, vt
->cy
);
983 /* save cursor position */
984 vncterm_save_cursor (vt
);
987 /* restore cursor position */
988 vncterm_restore_cursor (vt
);
991 /* erase c characters */
995 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
997 for(i
= 0; i
< c
; i
++) {
998 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1002 /* insert c character */
1004 if (c
> (vt
->width
- vt
->cx
)) {
1005 c
= vt
->width
- vt
->cx
;
1009 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1010 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1011 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1012 TextCell
*dst
= src
+ c
;
1014 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1016 src
->attrib
= vt
->cur_attrib
;
1017 vncterm_update_xy (vt
, x
, vt
->cy
);
1023 if (!vt
->esc_buf
[0])
1025 if (!vt
->esc_buf
[1])
1026 vt
->esc_buf
[1] = vt
->height
;
1027 /* Minimum allowed region is 2 lines */
1028 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1029 vt
->esc_buf
[1] <= vt
->height
) {
1030 vt
->region_top
= vt
->esc_buf
[0] - 1;
1031 vt
->region_bottom
= vt
->esc_buf
[1];
1033 vt
->cy
= vt
->region_top
;
1035 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1042 if (vt
->esc_count
== 0) {
1043 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1044 } else if (vt
->esc_count
== 1) {
1045 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1048 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1049 for (i
= 1; i
< vt
->esc_count
; i
++) {
1050 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1052 fprintf (stderr
, "%c\n", ch
);
1059 case ESsetG0
: // Set G0
1060 vt
->tty_state
= ESnormal
;
1063 vt
->g0enc
= GRAF_MAP
;
1065 vt
->g0enc
= LAT1_MAP
;
1067 vt
->g0enc
= IBMPC_MAP
;
1069 vt
->g0enc
= USER_MAP
;
1071 if (vt
->charset
== 0)
1072 vt
->cur_enc
= vt
->g0enc
;
1075 case ESsetG1
: // Set G1
1076 vt
->tty_state
= ESnormal
;
1079 vt
->g1enc
= GRAF_MAP
;
1081 vt
->g1enc
= LAT1_MAP
;
1083 vt
->g1enc
= IBMPC_MAP
;
1085 vt
->g1enc
= USER_MAP
;
1087 if (vt
->charset
== 1)
1088 vt
->cur_enc
= vt
->g1enc
;
1091 case ESidquery
: // vt100 query id
1092 vt
->tty_state
= ESnormal
;
1096 fprintf (stderr
, "ESC[>c Query term ID\n");
1098 vncterm_respond_esc (vt
, TERMIDCODE
);
1102 vt
->tty_state
= ESnormal
;
1104 case '@': /* defined in ISO 2022 */
1107 case 'G': /* prelim official escape code */
1108 case '8': /* retained for compatibility */
1113 default: // ESnormal
1114 vt
->tty_state
= ESnormal
;
1119 case 7: /* alert aka. bell */
1121 //rfbSendBell(vt->screen);
1123 case 8: /* backspace */
1127 case 9: /* tabspace */
1128 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1130 vncterm_put_lf (vt
);
1132 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1138 vncterm_put_lf (vt
);
1140 case 13: /* carriage return */
1144 /* SI (shift in), select character set 1 */
1146 vt
->cur_enc
= vt
->g1enc
;
1147 /* fixme: display controls = 1 */
1150 /* SO (shift out), select character set 0 */
1152 vt
->cur_enc
= vt
->g0enc
;
1153 /* fixme: display controls = 0 */
1156 vt
->tty_state
= ESesc
;
1158 case 127: /* delete */
1161 case 128+27: /* csi */
1162 vt
->tty_state
= ESsquare
;
1165 if (vt
->cx
>= vt
->width
) {
1168 vncterm_put_lf (vt
);
1171 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1172 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1173 c
->attrib
= vt
->cur_attrib
;
1175 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1184 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1188 vncterm_show_cursor (vt
, 0);
1191 unsigned char c
= *buf
;
1195 if (vt
->tty_state
!= ESnormal
) {
1196 // never translate escape sequence
1198 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1200 if(c
& 0x80) { // utf8 multi-byte sequence
1202 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1203 // inside UTF8 sequence
1204 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1206 if (vt
->utf_count
== 0) {
1212 // first char of a UTF8 sequence
1213 if ((c
& 0xe0) == 0xc0) {
1215 vt
->utf_char
= (c
& 0x1f);
1216 } else if ((c
& 0xf0) == 0xe0) {
1218 vt
->utf_char
= (c
& 0x0f);
1219 } else if ((c
& 0xf8) == 0xf0) {
1221 vt
->utf_char
= (c
& 0x07);
1222 } else if ((c
& 0xfc) == 0xf8) {
1224 vt
->utf_char
= (c
& 0x03);
1225 } else if ((c
& 0xfe) == 0xfc) {
1227 vt
->utf_char
= (c
& 0x01);
1240 // never translate controls
1241 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1242 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1248 vncterm_putchar (vt
, tc
);
1251 vncterm_show_cursor (vt
, 1);
1258 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1260 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1262 // seems str is Latin-1 encoded
1263 if (vt->selection) free (vt->selection);
1264 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1266 for (i = 0; i < len; i++) {
1267 vt->selection[i] = str[i] & 0xff;
1269 vt->selection_len = len;
1273 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
1277 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1280 vncterm_respond_esc (vt
, buf
);
1284 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1286 int x
= (pos
%vt
->width
)*8;
1287 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 int client_count
= 0;
1441 static int client_connected
= 0;
1442 static int last_client
= 1;
1443 static time_t last_time
= 0;
1445 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1447 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1449 /* we no not need this */
1454 static void my_kbd_push_keyval(SpiceKbdInstance
*sin
, uint32_t keySym
, int flags
)
1456 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1457 static int control
= 0;
1458 static int shift
= 0;
1463 fprintf (stderr
, "KEYEVENT:%d: %08x\n", flags
, keySym
);fflush (stderr
);
1465 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1467 } if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1469 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1472 if(keySym
>= 'a' && keySym
<= 'z')
1473 uc
= keySym
- 'a' + 1;
1474 else if (keySym
>= 'A' && keySym
<= 'Z')
1475 uc
= keySym
- 'A' + 1;
1480 //printf("CONTROL: %08x %d\n", keySym, uc);
1484 case GDK_KEY_Escape
:
1486 case GDK_KEY_Return
:
1488 case GDK_KEY_BackSpace
:
1492 case GDK_KEY_Delete
: /* kdch1 */
1493 case GDK_KEY_KP_Delete
:
1495 case GDK_KEY_Home
: /* khome */
1496 case GDK_KEY_KP_Home
:
1499 case GDK_KEY_KP_End
: /* kend */
1501 case GDK_KEY_Insert
: /* kich1 */
1502 case GDK_KEY_KP_Insert
:
1505 case GDK_KEY_KP_Up
: /* kcuu1 */
1507 case GDK_KEY_Down
: /* kcud1 */
1508 case GDK_KEY_KP_Down
:
1511 case GDK_KEY_KP_Right
: /* kcuf1 */
1514 case GDK_KEY_KP_Left
: /* kcub1 */
1516 case GDK_KEY_Page_Up
:
1518 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1522 case GDK_KEY_Page_Down
:
1524 vncterm_virtual_scroll (vt
, vt
->height
/2);
1553 if (keySym
< 0x100) {
1561 fprintf(stderr
, "KEYPRESS OUT:%s: %08x\n", esc
, uc
); fflush (stderr
);
1564 if (vt
->y_displ
!= vt
->y_base
) {
1565 vt
->y_displ
= vt
->y_base
;
1566 vncterm_refresh (vt
);
1570 vncterm_respond_esc(vt
, esc
);
1571 } else if (uc
> 0) {
1574 gint len
= g_unichar_to_utf8(uc
, buf
);
1578 for (i
= 0; i
< len
; i
++) {
1579 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1583 vt
->ibuf
[vt
->ibuf_count
++] = (char)uc
;
1592 if (flags
& 2) { // UP
1593 //printf("KEYRELEASE %08x\n", keySym);
1595 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1597 } else if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1602 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
,
1603 SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1606 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1611 static SpiceKbdInterface my_keyboard_sif
= {
1612 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1613 .base
.description
= "spiceterm keyboard device",
1614 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1615 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1616 .push_keyval
= my_kbd_push_keyval
,
1617 .push_scan_freg
= my_kbd_push_key
,
1618 .get_leds
= my_kbd_get_leds
,
1622 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1628 SpiceCoreInterface
*core
= basic_event_loop_init();
1629 test
= test_new(core
);
1630 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1631 test_add_display_interface(test
);
1632 test_add_agent_interface(test
->server
);
1634 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1636 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1637 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1640 rfbColourMap *cmap =&screen->colourMap;
1641 cmap->data.bytes = malloc (16*3);
1643 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1644 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1645 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1649 screen->serverFormat.trueColour = FALSE;
1651 screen->kbdAddEvent = vncterm_kbd_event;
1653 screen->setXCutText = vncterm_set_xcut_text;
1655 screen->ptrAddEvent = vncterm_pointer_event;
1657 screen->desktopName = "VNC Command Terminal";
1659 screen->newClientHook = new_client;
1663 vt
->maxx
= test
->width
;
1664 vt
->maxy
= test
->height
;
1666 vt
->width
= vt
->maxx
/ 8;
1667 vt
->height
= vt
->maxy
/ 16;
1669 vt
->total_height
= vt
->height
* 20;
1670 vt
->scroll_height
= 0;
1675 vt
->region_bottom
= vt
->height
;
1677 vt
->g0enc
= LAT1_MAP
;
1678 vt
->g1enc
= GRAF_MAP
;
1679 vt
->cur_enc
= vt
->g0enc
;
1682 /* default text attributes */
1683 vt
->default_attrib
.bold
= 0;
1684 vt
->default_attrib
.uline
= 0;
1685 vt
->default_attrib
.blink
= 0;
1686 vt
->default_attrib
.invers
= 0;
1687 vt
->default_attrib
.unvisible
= 0;
1688 vt
->default_attrib
.fgcol
= 7;
1689 vt
->default_attrib
.bgcol
= 0;
1691 vt
->cur_attrib
= vt
->default_attrib
;
1693 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1695 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1696 vt
->cells
[i
].ch
= ' ';
1697 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1700 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1707 static void master_watch(int master
, int event
, void *opaque
)
1709 vncTerm
*vt
= (vncTerm
*)opaque
;
1711 printf("CHANNEL EVENT %d\n", event
);
1713 // fixme: if (!vt->mark_active) {
1715 if (event
== SPICE_WATCH_EVENT_READ
) {
1718 while ((c
= read(master
, buffer
, 1024)) == -1) {
1719 if (errno
!= EAGAIN
) break;
1722 g_error("got read error"); // fixme
1724 vncterm_puts (vt
, buffer
, c
);
1726 if (vt
->ibuf_count
> 0) {
1727 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1728 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1729 vt
->ibuf_count
= 0; // fixme: what if not all data written
1731 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1736 main (int argc
, char** argv
)
1739 char **cmdargv
= NULL
;
1740 char *command
= "/bin/bash"; // execute normal shell as default
1745 struct timeval tv
, tv1
;
1746 time_t elapsed
, cur_time
;
1747 struct winsize dimensions
;
1749 g_thread_init(NULL
);
1751 for (i
= 1; i
< argc
; i
++) {
1752 if (!strcmp (argv
[i
], "-c")) {
1753 command
= argv
[i
+1];
1754 cmdargv
= &argv
[i
+1];
1761 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1763 setlocale(LC_ALL
, ""); // set from environment
1765 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1767 // fixme: ist there a standard way to detect utf8 mode ?
1768 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1772 dimensions
.ws_col
= vt
->width
;
1773 dimensions
.ws_row
= vt
->height
;
1775 setenv ("TERM", TERM
, 1);
1777 printf("EXEC: %s\n", command
);
1779 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1782 // install default signal handlers
1783 signal (SIGQUIT
, SIG_DFL
);
1784 signal (SIGTERM
, SIG_DFL
);
1785 signal (SIGINT
, SIG_DFL
);
1788 execvp (command
, cmdargv
);
1790 execlp (command
, command
, NULL
);
1792 perror ("Error: exec failed\n");
1793 exit (-1); // should not be reached
1794 } else if (pid
== -1) {
1795 perror ("Error: fork failed\n");
1800 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1801 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1804 basic_event_loop_mainloop();
1806 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1809 if (vt->ibuf_count > 0) {
1810 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1811 write (master, vt->ibuf, vt->ibuf_count);
1813 last_time = time (NULL);
1819 waitpid(pid
, &status
, 0);