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"
44 #include <spice/enums.h>
45 #include <spice/macros.h>
46 #include <spice/qxl_dev.h>
48 #include "test_display_base.h"
50 /* define this for debugging */
55 #define TERMIDCODE "[?1;2c" // vt100 ID
57 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
58 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
63 /* these colours are from linux kernel drivers/char/vt.c */
65 static int idle_timeout
= 1;
67 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
68 8,12,10,14, 9,13,11,15 };
70 /* the default colour table, for VGA+ colour systems */
71 int default_red
[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
72 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
73 int default_grn
[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
74 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
75 int default_blu
[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
76 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
79 print_usage (const char *msg
)
81 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
82 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
85 /* Convert UCS2 to UTF8 sequence, trailing zero */
87 ucs2_to_utf8 (unicode c
, char *out
)
90 out
[0] = c
; // 0*******
93 } else if (c
< 0x800) {
94 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
95 out
[1] = 0x80 | (c
& 0x3f);
99 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
100 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
101 out
[2] = 0x80 | (c
& 0x3f);
111 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
113 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
118 int ec
= vt_fontmap
[ch
];
120 test_draw_update_char(vt
->screen
, x
, y
, ec
, attrib
);
124 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
126 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
128 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
129 int y2
= y1
- vt
->y_displ
;
131 y2
+= vt
->total_height
;
133 if (y2
< vt
->height
) {
134 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
135 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
);
140 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
142 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
144 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
145 int y2
= y1
- vt
->y_displ
;
147 y2
+= vt
->total_height
;
149 if (y2
< vt
->height
) {
150 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
152 c
->attrib
= vt
->default_attrib
;
153 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
154 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
156 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
161 vncterm_show_cursor (vncTerm
*vt
, int show
)
164 if (x
>= vt
->width
) {
168 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
169 int y
= y1
- vt
->y_displ
;
171 y
+= vt
->total_height
;
174 if (y
< vt
->height
) {
176 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
179 TextAttributes attrib
= vt
->default_attrib
;
180 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
181 draw_char_at (vt
, x
, y
, c
->ch
, attrib
);
183 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
189 vncterm_refresh (vncTerm
*vt
)
193 // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
196 for(y
= 0; y
< vt
->height
; y
++) {
197 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
198 for(x
= 0; x
< vt
->width
; x
++) {
199 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
202 if (++y1
== vt
->total_height
)
205 //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
207 vncterm_show_cursor (vt
, 1);
211 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
213 if ((top
+ lines
) >= bottom
) {
214 lines
= bottom
- top
-1;
217 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
221 g_error("vncterm_scroll_down not implemented");
228 int rowstride = vt->screen->paddedWidthInBytes;
229 int rows = (bottom - top - lines)*16;
231 char *in = vt->screen->frameBuffer+y0*rowstride;
232 char *out = vt->screen->frameBuffer+y1*rowstride;
233 memmove(out,in, rowstride*rows);
235 memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
236 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
239 for(i = bottom - top - lines - 1; i >= 0; i--) {
240 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
241 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
243 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
246 for (i = 0; i < lines; i++) {
248 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
249 for(j = 0; j < vt->width; j++) {
250 c->attrib = vt->default_attrib;
259 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
261 if ((top
+ lines
) >= bottom
) {
262 lines
= bottom
- top
- 1;
265 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
269 g_error("vncterm_scroll_down not implemented");
274 int y1 = (top + lines)*16;
276 int rowstride = vt->screen->paddedWidthInBytes;
277 int rows = (bottom - top - lines)*16;
279 char *in = vt->screen->frameBuffer+y1*rowstride;
280 char *out = vt->screen->frameBuffer+y0*rowstride;
281 memmove(out,in, rowstride*rows);
283 memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
285 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
287 if (!moveattr) return;
292 for(i = 0; i < (bottom - top - lines); i++) {
293 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
294 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
296 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
299 for (i = 1; i <= lines; i++) {
301 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
302 for(j = 0; j < vt->width; j++) {
303 c->attrib = vt->default_attrib;
312 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
314 if (vt
->altbuf
|| lines
== 0) return;
318 int i
= vt
->scroll_height
;
319 if (i
> vt
->total_height
- vt
->height
)
320 i
= vt
->total_height
- vt
->height
;
321 int y1
= vt
->y_base
- i
;
323 y1
+= vt
->total_height
;
324 for(i
= 0; i
< lines
; i
++) {
325 if (vt
->y_displ
== y1
) break;
326 if (--vt
->y_displ
< 0) {
327 vt
->y_displ
= vt
->total_height
- 1;
332 for(i
= 0; i
< lines
; i
++) {
333 if (vt
->y_displ
== vt
->y_base
) break;
334 if (++vt
->y_displ
== vt
->total_height
) {
341 vncterm_refresh (vt
);
344 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
346 int len
= strlen (esc
);
349 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
350 vt
->ibuf
[vt
->ibuf_count
++] = 27;
351 for (i
= 0; i
< len
; i
++) {
352 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
358 vncterm_put_lf (vncTerm
*vt
)
360 if (vt
->cy
+ 1 == vt
->region_bottom
) {
362 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
363 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
367 if (vt
->y_displ
== vt
->y_base
) {
368 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
371 if (vt
->y_displ
== vt
->y_base
) {
372 if (++vt
->y_displ
== vt
->total_height
) {
377 if (++vt
->y_base
== vt
->total_height
) {
381 if (vt
->scroll_height
< vt
->total_height
) {
385 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
386 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
388 for (x
= 0; x
< vt
->width
; x
++) {
390 c
->attrib
= vt
->default_attrib
;
394 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
396 } else if (vt
->cy
< vt
->height
- 1) {
403 vncterm_csi_m (vncTerm
*vt
)
407 for (i
= 0; i
< vt
->esc_count
; i
++) {
408 switch (vt
->esc_buf
[i
]) {
409 case 0: /* reset all console attributes to default */
410 vt
->cur_attrib
= vt
->default_attrib
;
413 vt
->cur_attrib
.bold
= 1;
416 vt
->cur_attrib
.uline
= 1;
419 vt
->cur_attrib
.blink
= 1;
422 vt
->cur_attrib
.invers
= 1;
425 vt
->cur_attrib
.unvisible
= 1;
428 vt
->cur_enc
= LAT1_MAP
;
429 // fixme: dispaly controls = 0 ?
430 // fixme: toggle meta = 0 ?
433 vt
->cur_enc
= IBMPC_MAP
;
434 // fixme: dispaly controls = 1 ?
435 // fixme: toggle meta = 0 ?
438 vt
->cur_enc
= IBMPC_MAP
;
439 // fixme: dispaly controls = 1 ?
440 // fixme: toggle meta = 1 ?
443 vt
->cur_attrib
.bold
= 0;
446 vt
->cur_attrib
.uline
= 0;
449 vt
->cur_attrib
.blink
= 0;
452 vt
->cur_attrib
.invers
= 0;
455 vt
->cur_attrib
.unvisible
= 0;
465 /* set foreground color */
466 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
469 /* reset color to default, enable underline */
470 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
471 vt
->cur_attrib
.uline
= 1;
474 /* reset color to default, disable underline */
475 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
476 vt
->cur_attrib
.uline
= 0;
486 /* set background color */
487 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
490 /* reset background color */
491 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
494 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
501 vncterm_save_cursor (vncTerm
*vt
)
503 vt
->cx_saved
= vt
->cx
;
504 vt
->cy_saved
= vt
->cy
;
505 vt
->cur_attrib_saved
= vt
->cur_attrib
;
506 vt
->charset_saved
= vt
->charset
;
507 vt
->g0enc_saved
= vt
->g0enc
;
508 vt
->g1enc_saved
= vt
->g1enc
;
509 vt
->cur_enc_saved
= vt
->cur_enc
;
513 vncterm_restore_cursor (vncTerm
*vt
)
515 vt
->cx
= vt
->cx_saved
;
516 vt
->cy
= vt
->cy_saved
;
517 vt
->cur_attrib
= vt
->cur_attrib_saved
;
518 vt
->charset
= vt
->charset_saved
;
519 vt
->g0enc
= vt
->g0enc_saved
;
520 vt
->g1enc
= vt
->g1enc_saved
;
521 vt
->cur_enc
= vt
->cur_enc_saved
;
525 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
529 vt
->y_displ
= vt
->y_base
;
533 if (vt
->altbuf
) return;
537 /* alternate buffer & cursor */
539 vncterm_save_cursor (vt
);
540 /* save screen to altcels */
541 for (y
= 0; y
< vt
->height
; y
++) {
542 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
543 for (x
= 0; x
< vt
->width
; x
++) {
544 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
549 for (y
= 0; y
<= vt
->height
; y
++) {
550 for (x
= 0; x
< vt
->width
; x
++) {
551 vncterm_clear_xy (vt
, x
, y
);
557 if (vt
->altbuf
== 0) return;
561 /* restore saved data */
562 for (y
= 0; y
< vt
->height
; y
++) {
563 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
564 for (x
= 0; x
< vt
->width
; x
++) {
565 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
569 vncterm_restore_cursor (vt
);
572 vncterm_refresh (vt
);
576 vncterm_set_mode (vncTerm
*vt
, int on_off
)
580 for (i
= 0; i
<= vt
->esc_count
; i
++) {
581 if (vt
->esc_ques
) { /* DEC private modes set/reset */
582 switch(vt
->esc_buf
[i
]) {
583 case 10: /* X11 mouse reporting on/off */
585 vt
->report_mouse
= on_off
;
587 case 1049: /* start/end special app mode (smcup/rmcup) */
588 vncterm_set_alternate_buffer (vt
, on_off
);
590 case 25: /* Cursor on/off */
591 case 9: /* X10 mouse reporting on/off */
592 case 6: /* Origin relative/absolute */
593 case 1: /* Cursor keys in appl mode*/
594 case 5: /* Inverted screen on/off */
595 case 7: /* Autowrap on/off */
596 case 8: /* Autorepeat on/off */
599 } else { /* ANSI modes set/reset */
600 /* fixme: implement me */
606 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
608 /* verify all boundaries */
614 if (x
>= vt
->width
) {
624 if (y
>= vt
->height
) {
631 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
632 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
633 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
636 vncterm_putchar (vncTerm
*vt
, unicode ch
)
642 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
645 switch(vt
->tty_state
) {
647 vt
->tty_state
= ESnormal
;
650 vt
->tty_state
= ESsquare
;
653 vt
->tty_state
= ESnonstd
;
656 vt
->tty_state
= ESpercent
;
659 vncterm_save_cursor (vt
);
662 vncterm_restore_cursor (vt
);
665 vt
->tty_state
= ESsetG0
; // SET G0
668 vt
->tty_state
= ESsetG1
; // SET G1
672 if (vt
->cy
== vt
->region_top
)
673 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
674 else if (vt
->cy
> 0) {
679 /* numeric keypad - ignored */
682 /* appl. keypad - ignored */
686 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
691 case ESnonstd
: /* Operating System Controls */
692 vt
->tty_state
= ESnormal
;
695 case 'P': /* palette escape sequence */
696 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
701 vt
->tty_state
= ESpalette
;
703 case 'R': /* reset palette */
704 // fixme: reset_palette(vc);
711 vt
->osc_textbuf
[0] = 0;
712 vt
->tty_state
= ESosc1
;
716 fprintf (stderr
, "unhandled OSC %c\n", ch
);
718 vt
->tty_state
= ESnormal
;
723 vt
->tty_state
= ESnormal
;
725 vt
->tty_state
= ESosc2
;
728 fprintf (stderr
, "got illegal OSC sequence\n");
733 if (ch
!= 0x9c && ch
!= 7) {
735 while (vt
->osc_textbuf
[i
]) i
++;
736 vt
->osc_textbuf
[i
++] = ch
;
737 vt
->osc_textbuf
[i
] = 0;
740 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
742 vt
->tty_state
= ESnormal
;
746 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
747 || (ch
>= 'a' && ch
<= 'f')) {
748 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
749 if (vt
->esc_count
== 7) {
750 // fixme: this does not work - please test
752 rfbColourMap *cmap =&vt->screen->colourMap;
754 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
755 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
756 cmap->data.bytes[i++] += vt->esc_buf[j++];
757 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
758 cmap->data.bytes[i++] += vt->esc_buf[j++];
759 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
760 cmap->data.bytes[i] += vt->esc_buf[j];
764 vt
->tty_state
= ESnormal
;
767 vt
->tty_state
= ESnormal
;
770 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
776 vt
->tty_state
= ESgetpars
;
779 vt
->tty_state
= ESidquery
;
783 if ((vt
->esc_ques
= (ch
== '?'))) {
787 if (ch
>= '0' && ch
<= '9') {
789 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
790 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
793 } else if (ch
== ';') {
797 if (vt
->esc_has_par
) {
800 vt
->tty_state
= ESgotpars
;
804 vt
->tty_state
= ESnormal
;
807 char *qes
= vt
->esc_ques
? "?" : "";
808 if (vt
->esc_count
== 0) {
809 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
810 } else if (vt
->esc_count
== 1) {
811 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
814 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
815 for (i
= 1; i
< vt
->esc_count
; i
++) {
816 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
818 fprintf (stderr
, "%c\n", ch
);
824 vncterm_set_mode (vt
, 1);
827 vncterm_set_mode (vt
, 0);
830 if (!vt
->esc_count
) {
831 vt
->esc_count
++; // default parameter 0
836 /* report cursor position */
837 /* TODO: send ESC[row;colR */
841 if (vt
->esc_buf
[0] == 0) {
844 vt
->cy
-= vt
->esc_buf
[0];
851 /* move cursor down */
852 if (vt
->esc_buf
[0] == 0) {
855 vt
->cy
+= vt
->esc_buf
[0];
856 if (vt
->cy
>= vt
->height
) {
857 vt
->cy
= vt
->height
- 1;
862 /* move cursor right */
863 if (vt
->esc_buf
[0] == 0) {
866 vt
->cx
+= vt
->esc_buf
[0];
867 if (vt
->cx
>= vt
->width
) {
868 vt
->cx
= vt
->width
- 1;
872 /* move cursor left */
873 if (vt
->esc_buf
[0] == 0) {
876 vt
->cx
-= vt
->esc_buf
[0];
883 /* move cursor to column */
884 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
887 /* move cursor to row */
888 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
892 /* move cursor to row, column */
893 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
896 switch (vt
->esc_buf
[0]) {
898 /* clear to end of screen */
899 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
900 for (x
= 0; x
< vt
->width
; x
++) {
901 if (y
== vt
->cy
&& x
< vt
->cx
) {
904 vncterm_clear_xy (vt
, x
, y
);
909 /* clear from beginning of screen */
910 for (y
= 0; y
<= vt
->cy
; y
++) {
911 for (x
= 0; x
< vt
->width
; x
++) {
912 if (y
== vt
->cy
&& x
> vt
->cx
) {
915 vncterm_clear_xy (vt
, x
, y
);
920 /* clear entire screen */
921 for (y
= 0; y
<= vt
->height
; y
++) {
922 for (x
= 0; x
< vt
->width
; x
++) {
923 vncterm_clear_xy (vt
, x
, y
);
930 switch (vt
->esc_buf
[0]) {
933 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
934 vncterm_clear_xy (vt
, x
, vt
->cy
);
938 /* clear from beginning of line */
939 for (x
= 0; x
<= vt
->cx
; x
++) {
940 vncterm_clear_xy (vt
, x
, vt
->cy
);
944 /* clear entire line */
945 for(x
= 0; x
< vt
->width
; x
++) {
946 vncterm_clear_xy (vt
, x
, vt
->cy
);
955 if (c
> vt
->height
- vt
->cy
)
956 c
= vt
->height
- vt
->cy
;
960 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
966 if (c
> vt
->height
- vt
->cy
)
967 c
= vt
->height
- vt
->cy
;
971 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
977 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
983 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
986 /* delete c character */
989 if (c
> vt
->width
- vt
->cx
)
990 c
= vt
->width
- vt
->cx
;
994 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
995 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
996 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
997 TextCell
*src
= dst
+ c
;
999 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1001 src
->attrib
= vt
->default_attrib
;
1002 vncterm_update_xy (vt
, x
, vt
->cy
);
1006 /* save cursor position */
1007 vncterm_save_cursor (vt
);
1010 /* restore cursor position */
1011 vncterm_restore_cursor (vt
);
1014 /* erase c characters */
1018 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1020 for(i
= 0; i
< c
; i
++) {
1021 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1025 /* insert c character */
1027 if (c
> (vt
->width
- vt
->cx
)) {
1028 c
= vt
->width
- vt
->cx
;
1032 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1033 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1034 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1035 TextCell
*dst
= src
+ c
;
1037 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1039 src
->attrib
= vt
->cur_attrib
;
1040 vncterm_update_xy (vt
, x
, vt
->cy
);
1046 if (!vt
->esc_buf
[0])
1048 if (!vt
->esc_buf
[1])
1049 vt
->esc_buf
[1] = vt
->height
;
1050 /* Minimum allowed region is 2 lines */
1051 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1052 vt
->esc_buf
[1] <= vt
->height
) {
1053 vt
->region_top
= vt
->esc_buf
[0] - 1;
1054 vt
->region_bottom
= vt
->esc_buf
[1];
1056 vt
->cy
= vt
->region_top
;
1058 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1065 if (vt
->esc_count
== 0) {
1066 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1067 } else if (vt
->esc_count
== 1) {
1068 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1071 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1072 for (i
= 1; i
< vt
->esc_count
; i
++) {
1073 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1075 fprintf (stderr
, "%c\n", ch
);
1082 case ESsetG0
: // Set G0
1083 vt
->tty_state
= ESnormal
;
1086 vt
->g0enc
= GRAF_MAP
;
1088 vt
->g0enc
= LAT1_MAP
;
1090 vt
->g0enc
= IBMPC_MAP
;
1092 vt
->g0enc
= USER_MAP
;
1094 if (vt
->charset
== 0)
1095 vt
->cur_enc
= vt
->g0enc
;
1098 case ESsetG1
: // Set G1
1099 vt
->tty_state
= ESnormal
;
1102 vt
->g1enc
= GRAF_MAP
;
1104 vt
->g1enc
= LAT1_MAP
;
1106 vt
->g1enc
= IBMPC_MAP
;
1108 vt
->g1enc
= USER_MAP
;
1110 if (vt
->charset
== 1)
1111 vt
->cur_enc
= vt
->g1enc
;
1114 case ESidquery
: // vt100 query id
1115 vt
->tty_state
= ESnormal
;
1119 fprintf (stderr
, "ESC[>c Query term ID\n");
1121 vncterm_respond_esc (vt
, TERMIDCODE
);
1125 vt
->tty_state
= ESnormal
;
1127 case '@': /* defined in ISO 2022 */
1130 case 'G': /* prelim official escape code */
1131 case '8': /* retained for compatibility */
1136 default: // ESnormal
1137 vt
->tty_state
= ESnormal
;
1142 case 7: /* alert aka. bell */
1144 //rfbSendBell(vt->screen);
1146 case 8: /* backspace */
1150 case 9: /* tabspace */
1151 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1153 vncterm_put_lf (vt
);
1155 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1161 vncterm_put_lf (vt
);
1163 case 13: /* carriage return */
1167 /* SI (shift in), select character set 1 */
1169 vt
->cur_enc
= vt
->g1enc
;
1170 /* fixme: display controls = 1 */
1173 /* SO (shift out), select character set 0 */
1175 vt
->cur_enc
= vt
->g0enc
;
1176 /* fixme: display controls = 0 */
1179 vt
->tty_state
= ESesc
;
1181 case 127: /* delete */
1184 case 128+27: /* csi */
1185 vt
->tty_state
= ESsquare
;
1188 if (vt
->cx
>= vt
->width
) {
1191 vncterm_put_lf (vt
);
1194 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1195 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1196 c
->attrib
= vt
->cur_attrib
;
1198 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1207 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1211 vncterm_show_cursor (vt
, 0);
1214 unsigned char c
= *buf
;
1218 if (vt
->tty_state
!= ESnormal
) {
1219 // never translate escape sequence
1221 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1223 if(c
& 0x80) { // utf8 multi-byte sequence
1225 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1226 // inside UTF8 sequence
1227 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1229 if (vt
->utf_count
== 0) {
1235 // first char of a UTF8 sequence
1236 if ((c
& 0xe0) == 0xc0) {
1238 vt
->utf_char
= (c
& 0x1f);
1239 } else if ((c
& 0xf0) == 0xe0) {
1241 vt
->utf_char
= (c
& 0x0f);
1242 } else if ((c
& 0xf8) == 0xf0) {
1244 vt
->utf_char
= (c
& 0x07);
1245 } else if ((c
& 0xfc) == 0xf8) {
1247 vt
->utf_char
= (c
& 0x03);
1248 } else if ((c
& 0xfe) == 0xfc) {
1250 vt
->utf_char
= (c
& 0x01);
1263 // never translate controls
1264 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1265 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1271 vncterm_putchar (vt
, tc
);
1274 vncterm_show_cursor (vt
, 1);
1281 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1283 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1285 // seems str is Latin-1 encoded
1286 if (vt->selection) free (vt->selection);
1287 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1289 for (i = 0; i < len; i++) {
1290 vt->selection[i] = str[i] & 0xff;
1292 vt->selection_len = len;
1296 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
1300 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1303 vncterm_respond_esc (vt
, buf
);
1307 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1309 int x
= (pos
%vt
->width
)*8;
1310 int y
= (pos
/vt
->width
)*16;
1315 rfbScreenInfoPtr s=vt->screen;
1317 char *b = s->frameBuffer+y*s->width+x;
1319 for (j=0; j < 16; j++) {
1320 for(i=0; i < 8; i++) {
1321 b[j*s->width+i] ^= 0x0f;
1322 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1331 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
1334 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1335 static int button2_released = 1;
1336 static int last_mask = 0;
1337 static int sel_start_pos = 0;
1338 static int sel_end_pos = 0;
1345 if (cx >= vt->width) cx = vt->width - 1;
1347 if (cy >= vt->height) cy = vt->height - 1;
1349 if (vt->report_mouse && buttonMask != last_mask) {
1350 last_mask = buttonMask;
1351 if (buttonMask & 1) {
1352 mouse_report (vt, 0, cx, cy);
1354 if (buttonMask & 2) {
1355 mouse_report (vt, 1, cx, cy);
1357 if (buttonMask & 4) {
1358 mouse_report (vt, 2, cx, cy);
1361 mouse_report (vt, 3, cx, cy);
1365 if (buttonMask & 2) {
1366 if(button2_released && vt->selection) {
1368 for(i = 0; i < vt->selection_len; i++) {
1369 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1371 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1373 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1377 if (vt->y_displ != vt->y_base) {
1378 vt->y_displ = vt->y_base;
1379 vncterm_refresh (vt);
1382 button2_released = 0;
1384 button2_released = 1;
1387 if (buttonMask & 1) {
1388 int pos = cy*vt->width + cx;
1390 // code borrowed from libvncserver (VNConsole.c)
1392 if (!vt->mark_active) {
1394 vt->mark_active = 1;
1395 sel_start_pos = sel_end_pos = pos;
1396 vncterm_toggle_marked_cell (vt, pos);
1400 if (pos != sel_end_pos) {
1402 if (pos > sel_end_pos) {
1403 cx = sel_end_pos; cy=pos;
1405 cx=pos; cy=sel_end_pos;
1408 if (cx < sel_start_pos) {
1409 if (cy < sel_start_pos) cy--;
1415 vncterm_toggle_marked_cell (vt, cx);
1423 } else if (vt->mark_active) {
1424 vt->mark_active = 0;
1426 if (sel_start_pos > sel_end_pos) {
1427 int tmp = sel_start_pos - 1;
1428 sel_start_pos = sel_end_pos;
1432 int len = sel_end_pos - sel_start_pos + 1;
1434 if (vt->selection) free (vt->selection);
1435 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1436 vt->selection_len = len;
1437 char *sel_latin1 = (char *)malloc (len + 1);
1439 for (i = 0; i < len; i++) {
1440 int pos = sel_start_pos + i;
1441 int x = pos % vt->width;
1442 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1443 TextCell *c = &vt->cells[y1*vt->width + x];
1444 vt->selection[i] = c->ch;
1445 sel_latin1[i] = (char)c->ch;
1448 sel_latin1[len] = 0;
1449 rfbGotXCutText (vt->screen, sel_latin1, len);
1452 while (sel_start_pos <= sel_end_pos) {
1453 vncterm_toggle_marked_cell (vt, sel_start_pos++);
1458 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1463 static int client_count
= 0;
1464 static int client_connected
= 0;
1465 static int last_client
= 1;
1466 static time_t last_time
= 0;
1468 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1470 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1473 printf("MYKEYCODE %x\n", frag
);
1475 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1477 char keySym
= 'A'; // fixme;
1478 vt
->ibuf
[vt
->ibuf_count
++] = keySym
;
1480 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1484 static void my_kbd_push_unicode(SpiceKbdInstance
*sin
, uint32_t uc
)
1486 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1488 printf("MYKEYVAL %x\n", uc
);
1490 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1492 gint len
= g_unichar_to_utf8(uc
, buf
);
1496 for (i
= 0; i
< len
; i
++) {
1497 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1500 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1504 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1509 static SpiceKbdInterface my_keyboard_sif
= {
1510 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1511 .base
.description
= "spiceterm keyboard device",
1512 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1513 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1514 .push_unicode
= my_kbd_push_unicode
,
1515 .push_scan_freg
= my_kbd_push_key
,
1516 .get_leds
= my_kbd_get_leds
,
1520 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1526 SpiceCoreInterface
*core
= basic_event_loop_init();
1527 test
= test_new(core
);
1528 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1529 test_add_display_interface(test
);
1530 test_add_agent_interface(test
->server
);
1532 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1534 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1535 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1538 rfbColourMap *cmap =&screen->colourMap;
1539 cmap->data.bytes = malloc (16*3);
1541 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1542 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1543 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1547 screen->serverFormat.trueColour = FALSE;
1549 screen->kbdAddEvent = vncterm_kbd_event;
1551 screen->setXCutText = vncterm_set_xcut_text;
1553 screen->ptrAddEvent = vncterm_pointer_event;
1555 screen->desktopName = "VNC Command Terminal";
1557 screen->newClientHook = new_client;
1561 vt
->maxx
= test
->width
;
1562 vt
->maxy
= test
->height
;
1564 vt
->width
= vt
->maxx
/ 8;
1565 vt
->height
= vt
->maxy
/ 16;
1567 vt
->total_height
= vt
->height
* 20;
1568 vt
->scroll_height
= 0;
1573 vt
->region_bottom
= vt
->height
;
1575 vt
->g0enc
= LAT1_MAP
;
1576 vt
->g1enc
= GRAF_MAP
;
1577 vt
->cur_enc
= vt
->g0enc
;
1580 /* default text attributes */
1581 vt
->default_attrib
.bold
= 0;
1582 vt
->default_attrib
.uline
= 0;
1583 vt
->default_attrib
.blink
= 0;
1584 vt
->default_attrib
.invers
= 0;
1585 vt
->default_attrib
.unvisible
= 0;
1586 vt
->default_attrib
.fgcol
= 7;
1587 vt
->default_attrib
.bgcol
= 0;
1589 vt
->cur_attrib
= vt
->default_attrib
;
1591 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1593 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1594 vt
->cells
[i
].ch
= ' ';
1595 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1598 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1605 static void master_watch(int master
, int event
, void *opaque
)
1607 vncTerm
*vt
= (vncTerm
*)opaque
;
1609 printf("CHANNEL EVENT %d\n", event
);
1611 // fixme: if (!vt->mark_active) {
1613 if (event
== SPICE_WATCH_EVENT_READ
) {
1616 while ((c
= read(master
, buffer
, 1024)) == -1) {
1617 if (errno
!= EAGAIN
) break;
1620 g_error("got read error"); // fixme
1622 vncterm_puts (vt
, buffer
, c
);
1624 if (vt
->ibuf_count
> 0) {
1625 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1626 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1627 vt
->ibuf_count
= 0; // fixme: what if not all data written
1629 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1634 main (int argc
, char** argv
)
1637 char **cmdargv
= NULL
;
1638 char *command
= "/bin/bash"; // execute normal shell as default
1643 struct timeval tv
, tv1
;
1644 time_t elapsed
, cur_time
;
1645 struct winsize dimensions
;
1647 for (i
= 1; i
< argc
; i
++) {
1648 if (!strcmp (argv
[i
], "-c")) {
1649 command
= argv
[i
+1];
1650 cmdargv
= &argv
[i
+1];
1657 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1659 setlocale(LC_ALL
, ""); // set from environment
1661 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1663 // fixme: ist there a standard way to detect utf8 mode ?
1664 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1668 dimensions
.ws_col
= vt
->width
;
1669 dimensions
.ws_row
= vt
->height
;
1671 setenv ("TERM", TERM
, 1);
1673 printf("EXEC: %s\n", command
);
1675 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1678 // install default signal handlers
1679 signal (SIGQUIT
, SIG_DFL
);
1680 signal (SIGTERM
, SIG_DFL
);
1681 signal (SIGINT
, SIG_DFL
);
1684 execvp (command
, cmdargv
);
1686 execlp (command
, command
, NULL
);
1688 perror ("Error: exec failed\n");
1689 exit (-1); // should not be reached
1690 } else if (pid
== -1) {
1691 perror ("Error: fork failed\n");
1696 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1697 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1700 basic_event_loop_mainloop();
1702 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1705 if (vt->ibuf_count > 0) {
1706 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1707 write (master, vt->ibuf, vt->ibuf_count);
1709 last_time = time (NULL);
1715 waitpid(pid
, &status
, 0);