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 };
72 print_usage (const char *msg
)
74 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
75 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
78 /* Convert UCS2 to UTF8 sequence, trailing zero */
80 ucs2_to_utf8 (unicode c
, char *out
)
83 out
[0] = c
; // 0*******
86 } else if (c
< 0x800) {
87 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
88 out
[1] = 0x80 | (c
& 0x3f);
92 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
93 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
94 out
[2] = 0x80 | (c
& 0x3f);
104 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
106 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
111 int ec
= vt_fontmap
[ch
];
113 test_draw_update_char(vt
->screen
, x
, y
, ec
, 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) {
214 g_error("vncterm_scroll_down not implemented");
221 int rowstride = vt->screen->paddedWidthInBytes;
222 int rows = (bottom - top - lines)*16;
224 char *in = vt->screen->frameBuffer+y0*rowstride;
225 char *out = vt->screen->frameBuffer+y1*rowstride;
226 memmove(out,in, rowstride*rows);
228 memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
229 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
232 for(i = bottom - top - lines - 1; i >= 0; i--) {
233 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
234 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
236 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
239 for (i = 0; i < lines; i++) {
241 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
242 for(j = 0; j < vt->width; j++) {
243 c->attrib = vt->default_attrib;
252 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
254 if ((top
+ lines
) >= bottom
) {
255 lines
= bottom
- top
- 1;
258 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
262 g_error("vncterm_scroll_down not implemented");
267 int y1 = (top + lines)*16;
269 int rowstride = vt->screen->paddedWidthInBytes;
270 int rows = (bottom - top - lines)*16;
272 char *in = vt->screen->frameBuffer+y1*rowstride;
273 char *out = vt->screen->frameBuffer+y0*rowstride;
274 memmove(out,in, rowstride*rows);
276 memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
278 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
280 if (!moveattr) return;
285 for(i = 0; i < (bottom - top - lines); i++) {
286 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
287 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
289 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
292 for (i = 1; i <= lines; i++) {
294 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
295 for(j = 0; j < vt->width; j++) {
296 c->attrib = vt->default_attrib;
305 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
307 if (vt
->altbuf
|| lines
== 0) return;
311 int i
= vt
->scroll_height
;
312 if (i
> vt
->total_height
- vt
->height
)
313 i
= vt
->total_height
- vt
->height
;
314 int y1
= vt
->y_base
- i
;
316 y1
+= vt
->total_height
;
317 for(i
= 0; i
< lines
; i
++) {
318 if (vt
->y_displ
== y1
) break;
319 if (--vt
->y_displ
< 0) {
320 vt
->y_displ
= vt
->total_height
- 1;
325 for(i
= 0; i
< lines
; i
++) {
326 if (vt
->y_displ
== vt
->y_base
) break;
327 if (++vt
->y_displ
== vt
->total_height
) {
334 vncterm_refresh (vt
);
337 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
339 int len
= strlen (esc
);
342 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
343 vt
->ibuf
[vt
->ibuf_count
++] = 27;
344 for (i
= 0; i
< len
; i
++) {
345 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
351 vncterm_put_lf (vncTerm
*vt
)
353 if (vt
->cy
+ 1 == vt
->region_bottom
) {
355 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
356 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
360 if (vt
->y_displ
== vt
->y_base
) {
361 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
364 if (vt
->y_displ
== vt
->y_base
) {
365 if (++vt
->y_displ
== vt
->total_height
) {
370 if (++vt
->y_base
== vt
->total_height
) {
374 if (vt
->scroll_height
< vt
->total_height
) {
378 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
379 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
381 for (x
= 0; x
< vt
->width
; x
++) {
383 c
->attrib
= vt
->default_attrib
;
387 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
389 } else if (vt
->cy
< vt
->height
- 1) {
396 vncterm_csi_m (vncTerm
*vt
)
400 for (i
= 0; i
< vt
->esc_count
; i
++) {
401 switch (vt
->esc_buf
[i
]) {
402 case 0: /* reset all console attributes to default */
403 vt
->cur_attrib
= vt
->default_attrib
;
406 vt
->cur_attrib
.bold
= 1;
409 vt
->cur_attrib
.uline
= 1;
412 vt
->cur_attrib
.blink
= 1;
415 vt
->cur_attrib
.invers
= 1;
418 vt
->cur_attrib
.unvisible
= 1;
421 vt
->cur_enc
= LAT1_MAP
;
422 // fixme: dispaly controls = 0 ?
423 // fixme: toggle meta = 0 ?
426 vt
->cur_enc
= IBMPC_MAP
;
427 // fixme: dispaly controls = 1 ?
428 // fixme: toggle meta = 0 ?
431 vt
->cur_enc
= IBMPC_MAP
;
432 // fixme: dispaly controls = 1 ?
433 // fixme: toggle meta = 1 ?
436 vt
->cur_attrib
.bold
= 0;
439 vt
->cur_attrib
.uline
= 0;
442 vt
->cur_attrib
.blink
= 0;
445 vt
->cur_attrib
.invers
= 0;
448 vt
->cur_attrib
.unvisible
= 0;
458 /* set foreground color */
459 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
462 /* reset color to default, enable underline */
463 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
464 vt
->cur_attrib
.uline
= 1;
467 /* reset color to default, disable underline */
468 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
469 vt
->cur_attrib
.uline
= 0;
479 /* set background color */
480 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
483 /* reset background color */
484 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
487 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
494 vncterm_save_cursor (vncTerm
*vt
)
496 vt
->cx_saved
= vt
->cx
;
497 vt
->cy_saved
= vt
->cy
;
498 vt
->cur_attrib_saved
= vt
->cur_attrib
;
499 vt
->charset_saved
= vt
->charset
;
500 vt
->g0enc_saved
= vt
->g0enc
;
501 vt
->g1enc_saved
= vt
->g1enc
;
502 vt
->cur_enc_saved
= vt
->cur_enc
;
506 vncterm_restore_cursor (vncTerm
*vt
)
508 vt
->cx
= vt
->cx_saved
;
509 vt
->cy
= vt
->cy_saved
;
510 vt
->cur_attrib
= vt
->cur_attrib_saved
;
511 vt
->charset
= vt
->charset_saved
;
512 vt
->g0enc
= vt
->g0enc_saved
;
513 vt
->g1enc
= vt
->g1enc_saved
;
514 vt
->cur_enc
= vt
->cur_enc_saved
;
518 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
522 vt
->y_displ
= vt
->y_base
;
526 if (vt
->altbuf
) return;
530 /* alternate buffer & cursor */
532 vncterm_save_cursor (vt
);
533 /* save screen to altcels */
534 for (y
= 0; y
< vt
->height
; y
++) {
535 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
536 for (x
= 0; x
< vt
->width
; x
++) {
537 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
542 for (y
= 0; y
<= vt
->height
; y
++) {
543 for (x
= 0; x
< vt
->width
; x
++) {
544 vncterm_clear_xy (vt
, x
, y
);
550 if (vt
->altbuf
== 0) return;
554 /* restore saved data */
555 for (y
= 0; y
< vt
->height
; y
++) {
556 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
557 for (x
= 0; x
< vt
->width
; x
++) {
558 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
562 vncterm_restore_cursor (vt
);
565 vncterm_refresh (vt
);
569 vncterm_set_mode (vncTerm
*vt
, int on_off
)
573 for (i
= 0; i
<= vt
->esc_count
; i
++) {
574 if (vt
->esc_ques
) { /* DEC private modes set/reset */
575 switch(vt
->esc_buf
[i
]) {
576 case 10: /* X11 mouse reporting on/off */
578 vt
->report_mouse
= on_off
;
580 case 1049: /* start/end special app mode (smcup/rmcup) */
581 vncterm_set_alternate_buffer (vt
, on_off
);
583 case 25: /* Cursor on/off */
584 case 9: /* X10 mouse reporting on/off */
585 case 6: /* Origin relative/absolute */
586 case 1: /* Cursor keys in appl mode*/
587 case 5: /* Inverted screen on/off */
588 case 7: /* Autowrap on/off */
589 case 8: /* Autorepeat on/off */
592 } else { /* ANSI modes set/reset */
593 /* fixme: implement me */
599 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
601 /* verify all boundaries */
607 if (x
>= vt
->width
) {
617 if (y
>= vt
->height
) {
624 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
625 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
626 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
629 vncterm_putchar (vncTerm
*vt
, unicode ch
)
635 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
638 switch(vt
->tty_state
) {
640 vt
->tty_state
= ESnormal
;
643 vt
->tty_state
= ESsquare
;
646 vt
->tty_state
= ESnonstd
;
649 vt
->tty_state
= ESpercent
;
652 vncterm_save_cursor (vt
);
655 vncterm_restore_cursor (vt
);
658 vt
->tty_state
= ESsetG0
; // SET G0
661 vt
->tty_state
= ESsetG1
; // SET G1
665 if (vt
->cy
== vt
->region_top
)
666 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
667 else if (vt
->cy
> 0) {
672 /* numeric keypad - ignored */
675 /* appl. keypad - ignored */
679 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
684 case ESnonstd
: /* Operating System Controls */
685 vt
->tty_state
= ESnormal
;
688 case 'P': /* palette escape sequence */
689 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
694 vt
->tty_state
= ESpalette
;
696 case 'R': /* reset palette */
697 // fixme: reset_palette(vc);
704 vt
->osc_textbuf
[0] = 0;
705 vt
->tty_state
= ESosc1
;
709 fprintf (stderr
, "unhandled OSC %c\n", ch
);
711 vt
->tty_state
= ESnormal
;
716 vt
->tty_state
= ESnormal
;
718 vt
->tty_state
= ESosc2
;
721 fprintf (stderr
, "got illegal OSC sequence\n");
726 if (ch
!= 0x9c && ch
!= 7) {
728 while (vt
->osc_textbuf
[i
]) i
++;
729 vt
->osc_textbuf
[i
++] = ch
;
730 vt
->osc_textbuf
[i
] = 0;
733 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
735 vt
->tty_state
= ESnormal
;
739 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
740 || (ch
>= 'a' && ch
<= 'f')) {
741 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
742 if (vt
->esc_count
== 7) {
743 // fixme: this does not work - please test
745 rfbColourMap *cmap =&vt->screen->colourMap;
747 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
748 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
749 cmap->data.bytes[i++] += vt->esc_buf[j++];
750 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
751 cmap->data.bytes[i++] += vt->esc_buf[j++];
752 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
753 cmap->data.bytes[i] += vt->esc_buf[j];
757 vt
->tty_state
= ESnormal
;
760 vt
->tty_state
= ESnormal
;
763 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
769 vt
->tty_state
= ESgetpars
;
772 vt
->tty_state
= ESidquery
;
776 if ((vt
->esc_ques
= (ch
== '?'))) {
780 if (ch
>= '0' && ch
<= '9') {
782 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
783 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
786 } else if (ch
== ';') {
790 if (vt
->esc_has_par
) {
793 vt
->tty_state
= ESgotpars
;
797 vt
->tty_state
= ESnormal
;
800 char *qes
= vt
->esc_ques
? "?" : "";
801 if (vt
->esc_count
== 0) {
802 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
803 } else if (vt
->esc_count
== 1) {
804 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
807 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
808 for (i
= 1; i
< vt
->esc_count
; i
++) {
809 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
811 fprintf (stderr
, "%c\n", ch
);
817 vncterm_set_mode (vt
, 1);
820 vncterm_set_mode (vt
, 0);
823 if (!vt
->esc_count
) {
824 vt
->esc_count
++; // default parameter 0
829 /* report cursor position */
830 /* TODO: send ESC[row;colR */
834 if (vt
->esc_buf
[0] == 0) {
837 vt
->cy
-= vt
->esc_buf
[0];
844 /* move cursor down */
845 if (vt
->esc_buf
[0] == 0) {
848 vt
->cy
+= vt
->esc_buf
[0];
849 if (vt
->cy
>= vt
->height
) {
850 vt
->cy
= vt
->height
- 1;
855 /* move cursor right */
856 if (vt
->esc_buf
[0] == 0) {
859 vt
->cx
+= vt
->esc_buf
[0];
860 if (vt
->cx
>= vt
->width
) {
861 vt
->cx
= vt
->width
- 1;
865 /* move cursor left */
866 if (vt
->esc_buf
[0] == 0) {
869 vt
->cx
-= vt
->esc_buf
[0];
876 /* move cursor to column */
877 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
880 /* move cursor to row */
881 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
885 /* move cursor to row, column */
886 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
889 switch (vt
->esc_buf
[0]) {
891 /* clear to end of screen */
892 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
893 for (x
= 0; x
< vt
->width
; x
++) {
894 if (y
== vt
->cy
&& x
< vt
->cx
) {
897 vncterm_clear_xy (vt
, x
, y
);
902 /* clear from beginning of screen */
903 for (y
= 0; y
<= vt
->cy
; y
++) {
904 for (x
= 0; x
< vt
->width
; x
++) {
905 if (y
== vt
->cy
&& x
> vt
->cx
) {
908 vncterm_clear_xy (vt
, x
, y
);
913 /* clear entire screen */
914 for (y
= 0; y
<= vt
->height
; y
++) {
915 for (x
= 0; x
< vt
->width
; x
++) {
916 vncterm_clear_xy (vt
, x
, y
);
923 switch (vt
->esc_buf
[0]) {
926 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
927 vncterm_clear_xy (vt
, x
, vt
->cy
);
931 /* clear from beginning of line */
932 for (x
= 0; x
<= vt
->cx
; x
++) {
933 vncterm_clear_xy (vt
, x
, vt
->cy
);
937 /* clear entire line */
938 for(x
= 0; x
< vt
->width
; x
++) {
939 vncterm_clear_xy (vt
, x
, vt
->cy
);
948 if (c
> vt
->height
- vt
->cy
)
949 c
= vt
->height
- vt
->cy
;
953 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
959 if (c
> vt
->height
- vt
->cy
)
960 c
= vt
->height
- vt
->cy
;
964 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
970 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
976 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
979 /* delete c character */
982 if (c
> vt
->width
- vt
->cx
)
983 c
= vt
->width
- vt
->cx
;
987 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
988 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
989 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
990 TextCell
*src
= dst
+ c
;
992 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
994 src
->attrib
= vt
->default_attrib
;
995 vncterm_update_xy (vt
, x
, vt
->cy
);
999 /* save cursor position */
1000 vncterm_save_cursor (vt
);
1003 /* restore cursor position */
1004 vncterm_restore_cursor (vt
);
1007 /* erase c characters */
1011 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1013 for(i
= 0; i
< c
; i
++) {
1014 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1018 /* insert c character */
1020 if (c
> (vt
->width
- vt
->cx
)) {
1021 c
= vt
->width
- vt
->cx
;
1025 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1026 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1027 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1028 TextCell
*dst
= src
+ c
;
1030 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1032 src
->attrib
= vt
->cur_attrib
;
1033 vncterm_update_xy (vt
, x
, vt
->cy
);
1039 if (!vt
->esc_buf
[0])
1041 if (!vt
->esc_buf
[1])
1042 vt
->esc_buf
[1] = vt
->height
;
1043 /* Minimum allowed region is 2 lines */
1044 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1045 vt
->esc_buf
[1] <= vt
->height
) {
1046 vt
->region_top
= vt
->esc_buf
[0] - 1;
1047 vt
->region_bottom
= vt
->esc_buf
[1];
1049 vt
->cy
= vt
->region_top
;
1051 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1058 if (vt
->esc_count
== 0) {
1059 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1060 } else if (vt
->esc_count
== 1) {
1061 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1064 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1065 for (i
= 1; i
< vt
->esc_count
; i
++) {
1066 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1068 fprintf (stderr
, "%c\n", ch
);
1075 case ESsetG0
: // Set G0
1076 vt
->tty_state
= ESnormal
;
1079 vt
->g0enc
= GRAF_MAP
;
1081 vt
->g0enc
= LAT1_MAP
;
1083 vt
->g0enc
= IBMPC_MAP
;
1085 vt
->g0enc
= USER_MAP
;
1087 if (vt
->charset
== 0)
1088 vt
->cur_enc
= vt
->g0enc
;
1091 case ESsetG1
: // Set G1
1092 vt
->tty_state
= ESnormal
;
1095 vt
->g1enc
= GRAF_MAP
;
1097 vt
->g1enc
= LAT1_MAP
;
1099 vt
->g1enc
= IBMPC_MAP
;
1101 vt
->g1enc
= USER_MAP
;
1103 if (vt
->charset
== 1)
1104 vt
->cur_enc
= vt
->g1enc
;
1107 case ESidquery
: // vt100 query id
1108 vt
->tty_state
= ESnormal
;
1112 fprintf (stderr
, "ESC[>c Query term ID\n");
1114 vncterm_respond_esc (vt
, TERMIDCODE
);
1118 vt
->tty_state
= ESnormal
;
1120 case '@': /* defined in ISO 2022 */
1123 case 'G': /* prelim official escape code */
1124 case '8': /* retained for compatibility */
1129 default: // ESnormal
1130 vt
->tty_state
= ESnormal
;
1135 case 7: /* alert aka. bell */
1137 //rfbSendBell(vt->screen);
1139 case 8: /* backspace */
1143 case 9: /* tabspace */
1144 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1146 vncterm_put_lf (vt
);
1148 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1154 vncterm_put_lf (vt
);
1156 case 13: /* carriage return */
1160 /* SI (shift in), select character set 1 */
1162 vt
->cur_enc
= vt
->g1enc
;
1163 /* fixme: display controls = 1 */
1166 /* SO (shift out), select character set 0 */
1168 vt
->cur_enc
= vt
->g0enc
;
1169 /* fixme: display controls = 0 */
1172 vt
->tty_state
= ESesc
;
1174 case 127: /* delete */
1177 case 128+27: /* csi */
1178 vt
->tty_state
= ESsquare
;
1181 if (vt
->cx
>= vt
->width
) {
1184 vncterm_put_lf (vt
);
1187 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1188 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1189 c
->attrib
= vt
->cur_attrib
;
1191 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1200 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1204 vncterm_show_cursor (vt
, 0);
1207 unsigned char c
= *buf
;
1211 if (vt
->tty_state
!= ESnormal
) {
1212 // never translate escape sequence
1214 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1216 if(c
& 0x80) { // utf8 multi-byte sequence
1218 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1219 // inside UTF8 sequence
1220 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1222 if (vt
->utf_count
== 0) {
1228 // first char of a UTF8 sequence
1229 if ((c
& 0xe0) == 0xc0) {
1231 vt
->utf_char
= (c
& 0x1f);
1232 } else if ((c
& 0xf0) == 0xe0) {
1234 vt
->utf_char
= (c
& 0x0f);
1235 } else if ((c
& 0xf8) == 0xf0) {
1237 vt
->utf_char
= (c
& 0x07);
1238 } else if ((c
& 0xfc) == 0xf8) {
1240 vt
->utf_char
= (c
& 0x03);
1241 } else if ((c
& 0xfe) == 0xfc) {
1243 vt
->utf_char
= (c
& 0x01);
1256 // never translate controls
1257 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1258 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1264 vncterm_putchar (vt
, tc
);
1267 vncterm_show_cursor (vt
, 1);
1274 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1276 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1278 // seems str is Latin-1 encoded
1279 if (vt->selection) free (vt->selection);
1280 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1282 for (i = 0; i < len; i++) {
1283 vt->selection[i] = str[i] & 0xff;
1285 vt->selection_len = len;
1289 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
1293 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1296 vncterm_respond_esc (vt
, buf
);
1300 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1302 int x
= (pos
%vt
->width
)*8;
1303 int y
= (pos
/vt
->width
)*16;
1308 rfbScreenInfoPtr s=vt->screen;
1310 char *b = s->frameBuffer+y*s->width+x;
1312 for (j=0; j < 16; j++) {
1313 for(i=0; i < 8; i++) {
1314 b[j*s->width+i] ^= 0x0f;
1315 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1324 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
1327 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1328 static int button2_released = 1;
1329 static int last_mask = 0;
1330 static int sel_start_pos = 0;
1331 static int sel_end_pos = 0;
1338 if (cx >= vt->width) cx = vt->width - 1;
1340 if (cy >= vt->height) cy = vt->height - 1;
1342 if (vt->report_mouse && buttonMask != last_mask) {
1343 last_mask = buttonMask;
1344 if (buttonMask & 1) {
1345 mouse_report (vt, 0, cx, cy);
1347 if (buttonMask & 2) {
1348 mouse_report (vt, 1, cx, cy);
1350 if (buttonMask & 4) {
1351 mouse_report (vt, 2, cx, cy);
1354 mouse_report (vt, 3, cx, cy);
1358 if (buttonMask & 2) {
1359 if(button2_released && vt->selection) {
1361 for(i = 0; i < vt->selection_len; i++) {
1362 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1364 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1366 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1370 if (vt->y_displ != vt->y_base) {
1371 vt->y_displ = vt->y_base;
1372 vncterm_refresh (vt);
1375 button2_released = 0;
1377 button2_released = 1;
1380 if (buttonMask & 1) {
1381 int pos = cy*vt->width + cx;
1383 // code borrowed from libvncserver (VNConsole.c)
1385 if (!vt->mark_active) {
1387 vt->mark_active = 1;
1388 sel_start_pos = sel_end_pos = pos;
1389 vncterm_toggle_marked_cell (vt, pos);
1393 if (pos != sel_end_pos) {
1395 if (pos > sel_end_pos) {
1396 cx = sel_end_pos; cy=pos;
1398 cx=pos; cy=sel_end_pos;
1401 if (cx < sel_start_pos) {
1402 if (cy < sel_start_pos) cy--;
1408 vncterm_toggle_marked_cell (vt, cx);
1416 } else if (vt->mark_active) {
1417 vt->mark_active = 0;
1419 if (sel_start_pos > sel_end_pos) {
1420 int tmp = sel_start_pos - 1;
1421 sel_start_pos = sel_end_pos;
1425 int len = sel_end_pos - sel_start_pos + 1;
1427 if (vt->selection) free (vt->selection);
1428 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1429 vt->selection_len = len;
1430 char *sel_latin1 = (char *)malloc (len + 1);
1432 for (i = 0; i < len; i++) {
1433 int pos = sel_start_pos + i;
1434 int x = pos % vt->width;
1435 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1436 TextCell *c = &vt->cells[y1*vt->width + x];
1437 vt->selection[i] = c->ch;
1438 sel_latin1[i] = (char)c->ch;
1441 sel_latin1[len] = 0;
1442 rfbGotXCutText (vt->screen, sel_latin1, len);
1445 while (sel_start_pos <= sel_end_pos) {
1446 vncterm_toggle_marked_cell (vt, sel_start_pos++);
1451 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1456 static int client_count
= 0;
1457 static int client_connected
= 0;
1458 static int last_client
= 1;
1459 static time_t last_time
= 0;
1461 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1463 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1466 printf("MYKEYCODE %x\n", frag
);
1468 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1470 char keySym
= 'A'; // fixme;
1471 vt
->ibuf
[vt
->ibuf_count
++] = keySym
;
1473 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1477 static void my_kbd_push_unicode(SpiceKbdInstance
*sin
, uint32_t uc
)
1479 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1481 printf("MYKEYVAL %x\n", uc
);
1483 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1485 gint len
= g_unichar_to_utf8(uc
, buf
);
1489 for (i
= 0; i
< len
; i
++) {
1490 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1493 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1497 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1502 static SpiceKbdInterface my_keyboard_sif
= {
1503 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1504 .base
.description
= "spiceterm keyboard device",
1505 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1506 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1507 .push_unicode
= my_kbd_push_unicode
,
1508 .push_scan_freg
= my_kbd_push_key
,
1509 .get_leds
= my_kbd_get_leds
,
1513 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1519 SpiceCoreInterface
*core
= basic_event_loop_init();
1520 test
= test_new(core
);
1521 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1522 test_add_display_interface(test
);
1523 test_add_agent_interface(test
->server
);
1525 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1527 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1528 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1531 rfbColourMap *cmap =&screen->colourMap;
1532 cmap->data.bytes = malloc (16*3);
1534 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1535 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1536 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1540 screen->serverFormat.trueColour = FALSE;
1542 screen->kbdAddEvent = vncterm_kbd_event;
1544 screen->setXCutText = vncterm_set_xcut_text;
1546 screen->ptrAddEvent = vncterm_pointer_event;
1548 screen->desktopName = "VNC Command Terminal";
1550 screen->newClientHook = new_client;
1554 vt
->maxx
= test
->width
;
1555 vt
->maxy
= test
->height
;
1557 vt
->width
= vt
->maxx
/ 8;
1558 vt
->height
= vt
->maxy
/ 16;
1560 vt
->total_height
= vt
->height
* 20;
1561 vt
->scroll_height
= 0;
1566 vt
->region_bottom
= vt
->height
;
1568 vt
->g0enc
= LAT1_MAP
;
1569 vt
->g1enc
= GRAF_MAP
;
1570 vt
->cur_enc
= vt
->g0enc
;
1573 /* default text attributes */
1574 vt
->default_attrib
.bold
= 0;
1575 vt
->default_attrib
.uline
= 0;
1576 vt
->default_attrib
.blink
= 0;
1577 vt
->default_attrib
.invers
= 0;
1578 vt
->default_attrib
.unvisible
= 0;
1579 vt
->default_attrib
.fgcol
= 7;
1580 vt
->default_attrib
.bgcol
= 0;
1582 vt
->cur_attrib
= vt
->default_attrib
;
1584 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1586 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1587 vt
->cells
[i
].ch
= ' ';
1588 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1591 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1598 static void master_watch(int master
, int event
, void *opaque
)
1600 vncTerm
*vt
= (vncTerm
*)opaque
;
1602 printf("CHANNEL EVENT %d\n", event
);
1604 // fixme: if (!vt->mark_active) {
1606 if (event
== SPICE_WATCH_EVENT_READ
) {
1609 while ((c
= read(master
, buffer
, 1024)) == -1) {
1610 if (errno
!= EAGAIN
) break;
1613 g_error("got read error"); // fixme
1615 vncterm_puts (vt
, buffer
, c
);
1617 if (vt
->ibuf_count
> 0) {
1618 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1619 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1620 vt
->ibuf_count
= 0; // fixme: what if not all data written
1622 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1627 main (int argc
, char** argv
)
1630 char **cmdargv
= NULL
;
1631 char *command
= "/bin/bash"; // execute normal shell as default
1636 struct timeval tv
, tv1
;
1637 time_t elapsed
, cur_time
;
1638 struct winsize dimensions
;
1640 for (i
= 1; i
< argc
; i
++) {
1641 if (!strcmp (argv
[i
], "-c")) {
1642 command
= argv
[i
+1];
1643 cmdargv
= &argv
[i
+1];
1650 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1652 setlocale(LC_ALL
, ""); // set from environment
1654 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1656 // fixme: ist there a standard way to detect utf8 mode ?
1657 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1661 dimensions
.ws_col
= vt
->width
;
1662 dimensions
.ws_row
= vt
->height
;
1664 setenv ("TERM", TERM
, 1);
1666 printf("EXEC: %s\n", command
);
1668 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1671 // install default signal handlers
1672 signal (SIGQUIT
, SIG_DFL
);
1673 signal (SIGTERM
, SIG_DFL
);
1674 signal (SIGINT
, SIG_DFL
);
1677 execvp (command
, cmdargv
);
1679 execlp (command
, command
, NULL
);
1681 perror ("Error: exec failed\n");
1682 exit (-1); // should not be reached
1683 } else if (pid
== -1) {
1684 perror ("Error: fork failed\n");
1689 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1690 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1693 basic_event_loop_mainloop();
1695 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1698 if (vt->ibuf_count > 0) {
1699 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1700 write (master, vt->ibuf, vt->ibuf_count);
1702 last_time = time (NULL);
1708 waitpid(pid
, &status
, 0);