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 <gdk/gdkkeysyms.h>
49 #include "test_display_base.h"
51 /* define this for debugging */
56 #define TERMIDCODE "[?1;2c" // vt100 ID
58 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
59 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
64 /* these colours are from linux kernel drivers/char/vt.c */
66 static int idle_timeout
= 1;
68 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
69 8,12,10,14, 9,13,11,15 };
73 print_usage (const char *msg
)
75 if (msg
) { fprintf (stderr
, "ERROR: %s\n", msg
); }
76 fprintf (stderr
, "USAGE: vncterm [vncopts] [-c command [args]]\n");
79 /* Convert UCS2 to UTF8 sequence, trailing zero */
81 ucs2_to_utf8 (unicode c
, char *out
)
84 out
[0] = c
; // 0*******
87 } else if (c
< 0x800) {
88 out
[0] = 0xc0 | (c
>> 6); // 110***** 10******
89 out
[1] = 0x80 | (c
& 0x3f);
93 out
[0] = 0xe0 | (c
>> 12); // 1110**** 10****** 10******
94 out
[1] = 0x80 | ((c
>> 6) & 0x3f);
95 out
[2] = 0x80 | (c
& 0x3f);
105 draw_char_at (vncTerm
*vt
, int x
, int y
, unicode ch
, TextAttributes attrib
)
107 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
112 int ec
= vt_fontmap
[ch
];
114 test_draw_update_char(vt
->screen
, x
, y
, ec
, attrib
);
118 vncterm_update_xy (vncTerm
*vt
, int x
, int y
)
120 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
122 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
123 int y2
= y1
- vt
->y_displ
;
125 y2
+= vt
->total_height
;
127 if (y2
< vt
->height
) {
128 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
129 draw_char_at (vt
, x
, y2
, c
->ch
, c
->attrib
);
134 vncterm_clear_xy (vncTerm
*vt
, int x
, int y
)
136 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
138 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
139 int y2
= y1
- vt
->y_displ
;
141 y2
+= vt
->total_height
;
143 if (y2
< vt
->height
) {
144 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
146 c
->attrib
= vt
->default_attrib
;
147 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
148 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
150 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
155 vncterm_show_cursor (vncTerm
*vt
, int show
)
158 if (x
>= vt
->width
) {
162 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
163 int y
= y1
- vt
->y_displ
;
165 y
+= vt
->total_height
;
168 if (y
< vt
->height
) {
170 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
173 TextAttributes attrib
= vt
->default_attrib
;
174 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
175 draw_char_at (vt
, x
, y
, c
->ch
, attrib
);
177 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
183 vncterm_refresh (vncTerm
*vt
)
187 // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
190 for(y
= 0; y
< vt
->height
; y
++) {
191 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
192 for(x
= 0; x
< vt
->width
; x
++) {
193 draw_char_at (vt
, x
, y
, c
->ch
, c
->attrib
);
196 if (++y1
== vt
->total_height
)
199 //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
201 vncterm_show_cursor (vt
, 1);
205 vncterm_scroll_down (vncTerm
*vt
, int top
, int bottom
, int lines
)
207 if ((top
+ lines
) >= bottom
) {
208 lines
= bottom
- top
-1;
211 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
215 g_error("vncterm_scroll_down not implemented");
222 int rowstride = vt->screen->paddedWidthInBytes;
223 int rows = (bottom - top - lines)*16;
225 char *in = vt->screen->frameBuffer+y0*rowstride;
226 char *out = vt->screen->frameBuffer+y1*rowstride;
227 memmove(out,in, rowstride*rows);
229 memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
230 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
233 for(i = bottom - top - lines - 1; i >= 0; i--) {
234 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
235 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
237 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
240 for (i = 0; i < lines; i++) {
242 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
243 for(j = 0; j < vt->width; j++) {
244 c->attrib = vt->default_attrib;
253 vncterm_scroll_up (vncTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
255 if ((top
+ lines
) >= bottom
) {
256 lines
= bottom
- top
- 1;
259 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
263 g_error("vncterm_scroll_down not implemented");
268 int y1 = (top + lines)*16;
270 int rowstride = vt->screen->paddedWidthInBytes;
271 int rows = (bottom - top - lines)*16;
273 char *in = vt->screen->frameBuffer+y1*rowstride;
274 char *out = vt->screen->frameBuffer+y0*rowstride;
275 memmove(out,in, rowstride*rows);
277 memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
279 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
281 if (!moveattr) return;
286 for(i = 0; i < (bottom - top - lines); i++) {
287 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
288 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
290 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
293 for (i = 1; i <= lines; i++) {
295 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
296 for(j = 0; j < vt->width; j++) {
297 c->attrib = vt->default_attrib;
306 vncterm_virtual_scroll (vncTerm
*vt
, int lines
)
308 if (vt
->altbuf
|| lines
== 0) return;
312 int i
= vt
->scroll_height
;
313 if (i
> vt
->total_height
- vt
->height
)
314 i
= vt
->total_height
- vt
->height
;
315 int y1
= vt
->y_base
- i
;
317 y1
+= vt
->total_height
;
318 for(i
= 0; i
< lines
; i
++) {
319 if (vt
->y_displ
== y1
) break;
320 if (--vt
->y_displ
< 0) {
321 vt
->y_displ
= vt
->total_height
- 1;
326 for(i
= 0; i
< lines
; i
++) {
327 if (vt
->y_displ
== vt
->y_base
) break;
328 if (++vt
->y_displ
== vt
->total_height
) {
335 vncterm_refresh (vt
);
338 vncterm_respond_esc (vncTerm
*vt
, const char *esc
)
340 int len
= strlen (esc
);
343 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
344 vt
->ibuf
[vt
->ibuf_count
++] = 27;
345 for (i
= 0; i
< len
; i
++) {
346 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
352 vncterm_put_lf (vncTerm
*vt
)
354 if (vt
->cy
+ 1 == vt
->region_bottom
) {
356 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
357 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
361 if (vt
->y_displ
== vt
->y_base
) {
362 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
365 if (vt
->y_displ
== vt
->y_base
) {
366 if (++vt
->y_displ
== vt
->total_height
) {
371 if (++vt
->y_base
== vt
->total_height
) {
375 if (vt
->scroll_height
< vt
->total_height
) {
379 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
380 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
382 for (x
= 0; x
< vt
->width
; x
++) {
384 c
->attrib
= vt
->default_attrib
;
388 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
390 } else if (vt
->cy
< vt
->height
- 1) {
397 vncterm_csi_m (vncTerm
*vt
)
401 for (i
= 0; i
< vt
->esc_count
; i
++) {
402 switch (vt
->esc_buf
[i
]) {
403 case 0: /* reset all console attributes to default */
404 vt
->cur_attrib
= vt
->default_attrib
;
407 vt
->cur_attrib
.bold
= 1;
410 vt
->cur_attrib
.uline
= 1;
413 vt
->cur_attrib
.blink
= 1;
416 vt
->cur_attrib
.invers
= 1;
419 vt
->cur_attrib
.unvisible
= 1;
422 vt
->cur_enc
= LAT1_MAP
;
423 // fixme: dispaly controls = 0 ?
424 // fixme: toggle meta = 0 ?
427 vt
->cur_enc
= IBMPC_MAP
;
428 // fixme: dispaly controls = 1 ?
429 // fixme: toggle meta = 0 ?
432 vt
->cur_enc
= IBMPC_MAP
;
433 // fixme: dispaly controls = 1 ?
434 // fixme: toggle meta = 1 ?
437 vt
->cur_attrib
.bold
= 0;
440 vt
->cur_attrib
.uline
= 0;
443 vt
->cur_attrib
.blink
= 0;
446 vt
->cur_attrib
.invers
= 0;
449 vt
->cur_attrib
.unvisible
= 0;
459 /* set foreground color */
460 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
463 /* reset color to default, enable underline */
464 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
465 vt
->cur_attrib
.uline
= 1;
468 /* reset color to default, disable underline */
469 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
470 vt
->cur_attrib
.uline
= 0;
480 /* set background color */
481 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
484 /* reset background color */
485 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
488 fprintf (stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
495 vncterm_save_cursor (vncTerm
*vt
)
497 vt
->cx_saved
= vt
->cx
;
498 vt
->cy_saved
= vt
->cy
;
499 vt
->cur_attrib_saved
= vt
->cur_attrib
;
500 vt
->charset_saved
= vt
->charset
;
501 vt
->g0enc_saved
= vt
->g0enc
;
502 vt
->g1enc_saved
= vt
->g1enc
;
503 vt
->cur_enc_saved
= vt
->cur_enc
;
507 vncterm_restore_cursor (vncTerm
*vt
)
509 vt
->cx
= vt
->cx_saved
;
510 vt
->cy
= vt
->cy_saved
;
511 vt
->cur_attrib
= vt
->cur_attrib_saved
;
512 vt
->charset
= vt
->charset_saved
;
513 vt
->g0enc
= vt
->g0enc_saved
;
514 vt
->g1enc
= vt
->g1enc_saved
;
515 vt
->cur_enc
= vt
->cur_enc_saved
;
519 vncterm_set_alternate_buffer (vncTerm
*vt
, int on_off
)
523 vt
->y_displ
= vt
->y_base
;
527 if (vt
->altbuf
) return;
531 /* alternate buffer & cursor */
533 vncterm_save_cursor (vt
);
534 /* save screen to altcels */
535 for (y
= 0; y
< vt
->height
; y
++) {
536 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
537 for (x
= 0; x
< vt
->width
; x
++) {
538 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
543 for (y
= 0; y
<= vt
->height
; y
++) {
544 for (x
= 0; x
< vt
->width
; x
++) {
545 vncterm_clear_xy (vt
, x
, y
);
551 if (vt
->altbuf
== 0) return;
555 /* restore saved data */
556 for (y
= 0; y
< vt
->height
; y
++) {
557 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
558 for (x
= 0; x
< vt
->width
; x
++) {
559 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
563 vncterm_restore_cursor (vt
);
566 vncterm_refresh (vt
);
570 vncterm_set_mode (vncTerm
*vt
, int on_off
)
574 for (i
= 0; i
<= vt
->esc_count
; i
++) {
575 if (vt
->esc_ques
) { /* DEC private modes set/reset */
576 switch(vt
->esc_buf
[i
]) {
577 case 10: /* X11 mouse reporting on/off */
579 vt
->report_mouse
= on_off
;
581 case 1049: /* start/end special app mode (smcup/rmcup) */
582 vncterm_set_alternate_buffer (vt
, on_off
);
584 case 25: /* Cursor on/off */
585 case 9: /* X10 mouse reporting on/off */
586 case 6: /* Origin relative/absolute */
587 case 1: /* Cursor keys in appl mode*/
588 case 5: /* Inverted screen on/off */
589 case 7: /* Autowrap on/off */
590 case 8: /* Autorepeat on/off */
593 } else { /* ANSI modes set/reset */
594 /* fixme: implement me */
600 vncterm_gotoxy (vncTerm
*vt
, int x
, int y
)
602 /* verify all boundaries */
608 if (x
>= vt
->width
) {
618 if (y
>= vt
->height
) {
625 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
626 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
627 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
630 vncterm_putchar (vncTerm
*vt
, unicode ch
)
636 fprintf (stderr
, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
639 switch(vt
->tty_state
) {
641 vt
->tty_state
= ESnormal
;
644 vt
->tty_state
= ESsquare
;
647 vt
->tty_state
= ESnonstd
;
650 vt
->tty_state
= ESpercent
;
653 vncterm_save_cursor (vt
);
656 vncterm_restore_cursor (vt
);
659 vt
->tty_state
= ESsetG0
; // SET G0
662 vt
->tty_state
= ESsetG1
; // SET G1
666 if (vt
->cy
== vt
->region_top
)
667 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, 1);
668 else if (vt
->cy
> 0) {
673 /* numeric keypad - ignored */
676 /* appl. keypad - ignored */
680 fprintf(stderr
, "got unhandled ESC%c %d\n", ch
, ch
);
685 case ESnonstd
: /* Operating System Controls */
686 vt
->tty_state
= ESnormal
;
689 case 'P': /* palette escape sequence */
690 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
695 vt
->tty_state
= ESpalette
;
697 case 'R': /* reset palette */
698 // fixme: reset_palette(vc);
705 vt
->osc_textbuf
[0] = 0;
706 vt
->tty_state
= ESosc1
;
710 fprintf (stderr
, "unhandled OSC %c\n", ch
);
712 vt
->tty_state
= ESnormal
;
717 vt
->tty_state
= ESnormal
;
719 vt
->tty_state
= ESosc2
;
722 fprintf (stderr
, "got illegal OSC sequence\n");
727 if (ch
!= 0x9c && ch
!= 7) {
729 while (vt
->osc_textbuf
[i
]) i
++;
730 vt
->osc_textbuf
[i
++] = ch
;
731 vt
->osc_textbuf
[i
] = 0;
734 fprintf (stderr
, "OSC:%c:%s\n", vt
->osc_cmd
, vt
->osc_textbuf
);
736 vt
->tty_state
= ESnormal
;
740 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
741 || (ch
>= 'a' && ch
<= 'f')) {
742 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
743 if (vt
->esc_count
== 7) {
744 // fixme: this does not work - please test
746 rfbColourMap *cmap =&vt->screen->colourMap;
748 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
749 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
750 cmap->data.bytes[i++] += vt->esc_buf[j++];
751 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
752 cmap->data.bytes[i++] += vt->esc_buf[j++];
753 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
754 cmap->data.bytes[i] += vt->esc_buf[j];
758 vt
->tty_state
= ESnormal
;
761 vt
->tty_state
= ESnormal
;
764 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
770 vt
->tty_state
= ESgetpars
;
773 vt
->tty_state
= ESidquery
;
777 if ((vt
->esc_ques
= (ch
== '?'))) {
781 if (ch
>= '0' && ch
<= '9') {
783 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
784 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
787 } else if (ch
== ';') {
791 if (vt
->esc_has_par
) {
794 vt
->tty_state
= ESgotpars
;
798 vt
->tty_state
= ESnormal
;
801 char *qes
= vt
->esc_ques
? "?" : "";
802 if (vt
->esc_count
== 0) {
803 fprintf(stderr
, "ESC[%s%c\n", qes
, ch
);
804 } else if (vt
->esc_count
== 1) {
805 fprintf(stderr
, "ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
808 fprintf(stderr
, "ESC[%s%d", qes
, vt
->esc_buf
[0]);
809 for (i
= 1; i
< vt
->esc_count
; i
++) {
810 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
812 fprintf (stderr
, "%c\n", ch
);
818 vncterm_set_mode (vt
, 1);
821 vncterm_set_mode (vt
, 0);
824 if (!vt
->esc_count
) {
825 vt
->esc_count
++; // default parameter 0
830 /* report cursor position */
831 /* TODO: send ESC[row;colR */
835 if (vt
->esc_buf
[0] == 0) {
838 vt
->cy
-= vt
->esc_buf
[0];
845 /* move cursor down */
846 if (vt
->esc_buf
[0] == 0) {
849 vt
->cy
+= vt
->esc_buf
[0];
850 if (vt
->cy
>= vt
->height
) {
851 vt
->cy
= vt
->height
- 1;
856 /* move cursor right */
857 if (vt
->esc_buf
[0] == 0) {
860 vt
->cx
+= vt
->esc_buf
[0];
861 if (vt
->cx
>= vt
->width
) {
862 vt
->cx
= vt
->width
- 1;
866 /* move cursor left */
867 if (vt
->esc_buf
[0] == 0) {
870 vt
->cx
-= vt
->esc_buf
[0];
877 /* move cursor to column */
878 vncterm_gotoxy (vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
881 /* move cursor to row */
882 vncterm_gotoxy (vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
886 /* move cursor to row, column */
887 vncterm_gotoxy (vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
890 switch (vt
->esc_buf
[0]) {
892 /* clear to end of screen */
893 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
894 for (x
= 0; x
< vt
->width
; x
++) {
895 if (y
== vt
->cy
&& x
< vt
->cx
) {
898 vncterm_clear_xy (vt
, x
, y
);
903 /* clear from beginning of screen */
904 for (y
= 0; y
<= vt
->cy
; y
++) {
905 for (x
= 0; x
< vt
->width
; x
++) {
906 if (y
== vt
->cy
&& x
> vt
->cx
) {
909 vncterm_clear_xy (vt
, x
, y
);
914 /* clear entire screen */
915 for (y
= 0; y
<= vt
->height
; y
++) {
916 for (x
= 0; x
< vt
->width
; x
++) {
917 vncterm_clear_xy (vt
, x
, y
);
924 switch (vt
->esc_buf
[0]) {
927 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
928 vncterm_clear_xy (vt
, x
, vt
->cy
);
932 /* clear from beginning of line */
933 for (x
= 0; x
<= vt
->cx
; x
++) {
934 vncterm_clear_xy (vt
, x
, vt
->cy
);
938 /* clear entire line */
939 for(x
= 0; x
< vt
->width
; x
++) {
940 vncterm_clear_xy (vt
, x
, vt
->cy
);
949 if (c
> vt
->height
- vt
->cy
)
950 c
= vt
->height
- vt
->cy
;
954 vncterm_scroll_down (vt
, vt
->cy
, vt
->region_bottom
, c
);
960 if (c
> vt
->height
- vt
->cy
)
961 c
= vt
->height
- vt
->cy
;
965 vncterm_scroll_up (vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
971 vncterm_scroll_down (vt
, vt
->region_top
, vt
->region_bottom
, c
);
977 vncterm_scroll_up (vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
980 /* delete c character */
983 if (c
> vt
->width
- vt
->cx
)
984 c
= vt
->width
- vt
->cx
;
988 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
989 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
990 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
991 TextCell
*src
= dst
+ c
;
993 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
995 src
->attrib
= vt
->default_attrib
;
996 vncterm_update_xy (vt
, x
, vt
->cy
);
1000 /* save cursor position */
1001 vncterm_save_cursor (vt
);
1004 /* restore cursor position */
1005 vncterm_restore_cursor (vt
);
1008 /* erase c characters */
1012 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1014 for(i
= 0; i
< c
; i
++) {
1015 vncterm_clear_xy (vt
, vt
->cx
+ i
, vt
->cy
);
1019 /* insert c character */
1021 if (c
> (vt
->width
- vt
->cx
)) {
1022 c
= vt
->width
- vt
->cx
;
1026 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1027 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1028 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1029 TextCell
*dst
= src
+ c
;
1031 vncterm_update_xy (vt
, x
+ c
, vt
->cy
);
1033 src
->attrib
= vt
->cur_attrib
;
1034 vncterm_update_xy (vt
, x
, vt
->cy
);
1040 if (!vt
->esc_buf
[0])
1042 if (!vt
->esc_buf
[1])
1043 vt
->esc_buf
[1] = vt
->height
;
1044 /* Minimum allowed region is 2 lines */
1045 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1046 vt
->esc_buf
[1] <= vt
->height
) {
1047 vt
->region_top
= vt
->esc_buf
[0] - 1;
1048 vt
->region_bottom
= vt
->esc_buf
[1];
1050 vt
->cy
= vt
->region_top
;
1052 fprintf (stderr
, "set region %d %d\n", vt
->region_top
, vt
->region_bottom
);
1059 if (vt
->esc_count
== 0) {
1060 fprintf(stderr
, "unhandled escape ESC[%s%c\n", qes
, ch
);
1061 } else if (vt
->esc_count
== 1) {
1062 fprintf(stderr
, "unhandled escape ESC[%s%d%c\n", qes
, vt
->esc_buf
[0], ch
);
1065 fprintf(stderr
, "unhandled escape ESC[%s%d", qes
, vt
->esc_buf
[0]);
1066 for (i
= 1; i
< vt
->esc_count
; i
++) {
1067 fprintf(stderr
, ";%d", vt
->esc_buf
[i
]);
1069 fprintf (stderr
, "%c\n", ch
);
1076 case ESsetG0
: // Set G0
1077 vt
->tty_state
= ESnormal
;
1080 vt
->g0enc
= GRAF_MAP
;
1082 vt
->g0enc
= LAT1_MAP
;
1084 vt
->g0enc
= IBMPC_MAP
;
1086 vt
->g0enc
= USER_MAP
;
1088 if (vt
->charset
== 0)
1089 vt
->cur_enc
= vt
->g0enc
;
1092 case ESsetG1
: // Set G1
1093 vt
->tty_state
= ESnormal
;
1096 vt
->g1enc
= GRAF_MAP
;
1098 vt
->g1enc
= LAT1_MAP
;
1100 vt
->g1enc
= IBMPC_MAP
;
1102 vt
->g1enc
= USER_MAP
;
1104 if (vt
->charset
== 1)
1105 vt
->cur_enc
= vt
->g1enc
;
1108 case ESidquery
: // vt100 query id
1109 vt
->tty_state
= ESnormal
;
1113 fprintf (stderr
, "ESC[>c Query term ID\n");
1115 vncterm_respond_esc (vt
, TERMIDCODE
);
1119 vt
->tty_state
= ESnormal
;
1121 case '@': /* defined in ISO 2022 */
1124 case 'G': /* prelim official escape code */
1125 case '8': /* retained for compatibility */
1130 default: // ESnormal
1131 vt
->tty_state
= ESnormal
;
1136 case 7: /* alert aka. bell */
1138 //rfbSendBell(vt->screen);
1140 case 8: /* backspace */
1144 case 9: /* tabspace */
1145 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1147 vncterm_put_lf (vt
);
1149 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1155 vncterm_put_lf (vt
);
1157 case 13: /* carriage return */
1161 /* SI (shift in), select character set 1 */
1163 vt
->cur_enc
= vt
->g1enc
;
1164 /* fixme: display controls = 1 */
1167 /* SO (shift out), select character set 0 */
1169 vt
->cur_enc
= vt
->g0enc
;
1170 /* fixme: display controls = 0 */
1173 vt
->tty_state
= ESesc
;
1175 case 127: /* delete */
1178 case 128+27: /* csi */
1179 vt
->tty_state
= ESsquare
;
1182 if (vt
->cx
>= vt
->width
) {
1185 vncterm_put_lf (vt
);
1188 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1189 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1190 c
->attrib
= vt
->cur_attrib
;
1192 vncterm_update_xy (vt
, vt
->cx
, vt
->cy
);
1201 vncterm_puts (vncTerm
*vt
, const char *buf
, int len
)
1205 vncterm_show_cursor (vt
, 0);
1208 unsigned char c
= *buf
;
1212 if (vt
->tty_state
!= ESnormal
) {
1213 // never translate escape sequence
1215 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1217 if(c
& 0x80) { // utf8 multi-byte sequence
1219 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1220 // inside UTF8 sequence
1221 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1223 if (vt
->utf_count
== 0) {
1229 // first char of a UTF8 sequence
1230 if ((c
& 0xe0) == 0xc0) {
1232 vt
->utf_char
= (c
& 0x1f);
1233 } else if ((c
& 0xf0) == 0xe0) {
1235 vt
->utf_char
= (c
& 0x0f);
1236 } else if ((c
& 0xf8) == 0xf0) {
1238 vt
->utf_char
= (c
& 0x07);
1239 } else if ((c
& 0xfc) == 0xf8) {
1241 vt
->utf_char
= (c
& 0x03);
1242 } else if ((c
& 0xfe) == 0xfc) {
1244 vt
->utf_char
= (c
& 0x01);
1257 // never translate controls
1258 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1259 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1265 vncterm_putchar (vt
, tc
);
1268 vncterm_show_cursor (vt
, 1);
1275 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
1277 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1279 // seems str is Latin-1 encoded
1280 if (vt->selection) free (vt->selection);
1281 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1283 for (i = 0; i < len; i++) {
1284 vt->selection[i] = str[i] & 0xff;
1286 vt->selection_len = len;
1290 mouse_report (vncTerm
*vt
, int butt
, int mrx
, int mry
)
1294 sprintf (buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1297 vncterm_respond_esc (vt
, buf
);
1301 vncterm_toggle_marked_cell (vncTerm
*vt
, int pos
)
1303 int x
= (pos
%vt
->width
)*8;
1304 int y
= (pos
/vt
->width
)*16;
1309 rfbScreenInfoPtr s=vt->screen;
1311 char *b = s->frameBuffer+y*s->width+x;
1313 for (j=0; j < 16; j++) {
1314 for(i=0; i < 8; i++) {
1315 b[j*s->width+i] ^= 0x0f;
1316 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1325 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
1328 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1329 static int button2_released = 1;
1330 static int last_mask = 0;
1331 static int sel_start_pos = 0;
1332 static int sel_end_pos = 0;
1339 if (cx >= vt->width) cx = vt->width - 1;
1341 if (cy >= vt->height) cy = vt->height - 1;
1343 if (vt->report_mouse && buttonMask != last_mask) {
1344 last_mask = buttonMask;
1345 if (buttonMask & 1) {
1346 mouse_report (vt, 0, cx, cy);
1348 if (buttonMask & 2) {
1349 mouse_report (vt, 1, cx, cy);
1351 if (buttonMask & 4) {
1352 mouse_report (vt, 2, cx, cy);
1355 mouse_report (vt, 3, cx, cy);
1359 if (buttonMask & 2) {
1360 if(button2_released && vt->selection) {
1362 for(i = 0; i < vt->selection_len; i++) {
1363 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1365 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1367 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1371 if (vt->y_displ != vt->y_base) {
1372 vt->y_displ = vt->y_base;
1373 vncterm_refresh (vt);
1376 button2_released = 0;
1378 button2_released = 1;
1381 if (buttonMask & 1) {
1382 int pos = cy*vt->width + cx;
1384 // code borrowed from libvncserver (VNConsole.c)
1386 if (!vt->mark_active) {
1388 vt->mark_active = 1;
1389 sel_start_pos = sel_end_pos = pos;
1390 vncterm_toggle_marked_cell (vt, pos);
1394 if (pos != sel_end_pos) {
1396 if (pos > sel_end_pos) {
1397 cx = sel_end_pos; cy=pos;
1399 cx=pos; cy=sel_end_pos;
1402 if (cx < sel_start_pos) {
1403 if (cy < sel_start_pos) cy--;
1409 vncterm_toggle_marked_cell (vt, cx);
1417 } else if (vt->mark_active) {
1418 vt->mark_active = 0;
1420 if (sel_start_pos > sel_end_pos) {
1421 int tmp = sel_start_pos - 1;
1422 sel_start_pos = sel_end_pos;
1426 int len = sel_end_pos - sel_start_pos + 1;
1428 if (vt->selection) free (vt->selection);
1429 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1430 vt->selection_len = len;
1431 char *sel_latin1 = (char *)malloc (len + 1);
1433 for (i = 0; i < len; i++) {
1434 int pos = sel_start_pos + i;
1435 int x = pos % vt->width;
1436 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1437 TextCell *c = &vt->cells[y1*vt->width + x];
1438 vt->selection[i] = c->ch;
1439 sel_latin1[i] = (char)c->ch;
1442 sel_latin1[len] = 0;
1443 rfbGotXCutText (vt->screen, sel_latin1, len);
1446 while (sel_start_pos <= sel_end_pos) {
1447 vncterm_toggle_marked_cell (vt, sel_start_pos++);
1452 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1457 static int client_count
= 0;
1458 static int client_connected
= 0;
1459 static int last_client
= 1;
1460 static time_t last_time
= 0;
1462 static void my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1464 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1468 printf("MYKEYCODE %x\n", frag
);
1470 if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1472 char keySym
= 'A'; // fixme;
1473 vt
->ibuf
[vt
->ibuf_count
++] = keySym
;
1475 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1479 static void my_kbd_push_keyval(SpiceKbdInstance
*sin
, uint32_t keySym
, int flags
)
1481 vncTerm
*vt
= SPICE_CONTAINEROF(sin
, vncTerm
, keyboard_sin
);
1482 static int control
= 0;
1483 static int shift
= 0;
1486 //fprintf (stderr, "KEYEVENT:%d: %08x\n", flags, keySym);fflush (stderr);
1488 fprintf(stderr
, "KEYPRESS: %08x\n", keySym
);fflush (stderr
);
1490 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1492 } if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1494 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1497 if(keySym
>= 'a' && keySym
<= 'z')
1499 else if (keySym
>= 'A' && keySym
<= 'Z')
1505 case GDK_KEY_Escape
:
1507 case GDK_KEY_Return
:
1509 case GDK_KEY_BackSpace
:
1513 case GDK_KEY_Delete
: /* kdch1 */
1514 case GDK_KEY_KP_Delete
:
1516 case GDK_KEY_Home
: /* khome */
1517 case GDK_KEY_KP_Home
:
1520 case GDK_KEY_KP_End
: /* kend */
1522 case GDK_KEY_Insert
: /* kich1 */
1523 case GDK_KEY_KP_Insert
:
1526 case GDK_KEY_KP_Up
: /* kcuu1 */
1528 case GDK_KEY_Down
: /* kcud1 */
1529 case GDK_KEY_KP_Down
:
1532 case GDK_KEY_KP_Right
: /* kcuf1 */
1535 case GDK_KEY_KP_Left
: /* kcub1 */
1537 case GDK_KEY_Page_Up
:
1539 vncterm_virtual_scroll (vt
, -vt
->height
/2);
1543 case GDK_KEY_Page_Down
:
1545 vncterm_virtual_scroll (vt
, vt
->height
/2);
1579 fprintf(stderr
, "KEYPRESS OUT:%s: %d\n", esc
, keySym
); fflush (stderr
);
1582 if (vt
->y_displ
!= vt
->y_base
) {
1583 vt
->y_displ
= vt
->y_base
;
1584 vncterm_refresh (vt
);
1588 vncterm_respond_esc(vt
, esc
);
1589 } else if(keySym
< 0x100) {
1592 gint len
= g_unichar_to_utf8(keySym
, buf
);
1596 for (i
= 0; i
< len
; i
++) {
1597 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1601 vt
->ibuf
[vt
->ibuf_count
++] = (char)keySym
;
1604 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
,
1605 SPICE_WATCH_EVENT_READ
|SPICE_WATCH_EVENT_WRITE
);
1609 if (flags
& 2) { // UP
1610 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1612 } else if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1618 static uint8_t my_kbd_get_leds(SpiceKbdInstance
*sin
)
1623 static SpiceKbdInterface my_keyboard_sif
= {
1624 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1625 .base
.description
= "spiceterm keyboard device",
1626 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1627 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1628 .push_keyval
= my_kbd_push_keyval
,
1629 .push_scan_freg
= my_kbd_push_key
,
1630 .get_leds
= my_kbd_get_leds
,
1634 create_vncterm (int argc
, char** argv
, int maxx
, int maxy
)
1640 SpiceCoreInterface
*core
= basic_event_loop_init();
1641 test
= test_new(core
);
1642 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1643 test_add_display_interface(test
);
1644 test_add_agent_interface(test
->server
);
1646 vncTerm
*vt
= (vncTerm
*)calloc (sizeof(vncTerm
), 1);
1648 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1649 spice_server_add_interface(test
->server
, &vt
->keyboard_sin
.base
);
1652 rfbColourMap *cmap =&screen->colourMap;
1653 cmap->data.bytes = malloc (16*3);
1655 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
1656 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
1657 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
1661 screen->serverFormat.trueColour = FALSE;
1663 screen->kbdAddEvent = vncterm_kbd_event;
1665 screen->setXCutText = vncterm_set_xcut_text;
1667 screen->ptrAddEvent = vncterm_pointer_event;
1669 screen->desktopName = "VNC Command Terminal";
1671 screen->newClientHook = new_client;
1675 vt
->maxx
= test
->width
;
1676 vt
->maxy
= test
->height
;
1678 vt
->width
= vt
->maxx
/ 8;
1679 vt
->height
= vt
->maxy
/ 16;
1681 vt
->total_height
= vt
->height
* 20;
1682 vt
->scroll_height
= 0;
1687 vt
->region_bottom
= vt
->height
;
1689 vt
->g0enc
= LAT1_MAP
;
1690 vt
->g1enc
= GRAF_MAP
;
1691 vt
->cur_enc
= vt
->g0enc
;
1694 /* default text attributes */
1695 vt
->default_attrib
.bold
= 0;
1696 vt
->default_attrib
.uline
= 0;
1697 vt
->default_attrib
.blink
= 0;
1698 vt
->default_attrib
.invers
= 0;
1699 vt
->default_attrib
.unvisible
= 0;
1700 vt
->default_attrib
.fgcol
= 7;
1701 vt
->default_attrib
.bgcol
= 0;
1703 vt
->cur_attrib
= vt
->default_attrib
;
1705 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1707 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1708 vt
->cells
[i
].ch
= ' ';
1709 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1712 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1719 static void master_watch(int master
, int event
, void *opaque
)
1721 vncTerm
*vt
= (vncTerm
*)opaque
;
1723 printf("CHANNEL EVENT %d\n", event
);
1725 // fixme: if (!vt->mark_active) {
1727 if (event
== SPICE_WATCH_EVENT_READ
) {
1730 while ((c
= read(master
, buffer
, 1024)) == -1) {
1731 if (errno
!= EAGAIN
) break;
1734 g_error("got read error"); // fixme
1736 vncterm_puts (vt
, buffer
, c
);
1738 if (vt
->ibuf_count
> 0) {
1739 printf ("DEBUG: WRITE %x %d\n", vt
->ibuf
[0], vt
->ibuf_count
);
1740 write (master
, vt
->ibuf
, vt
->ibuf_count
);
1741 vt
->ibuf_count
= 0; // fixme: what if not all data written
1743 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, SPICE_WATCH_EVENT_READ
);
1748 main (int argc
, char** argv
)
1751 char **cmdargv
= NULL
;
1752 char *command
= "/bin/bash"; // execute normal shell as default
1757 struct timeval tv
, tv1
;
1758 time_t elapsed
, cur_time
;
1759 struct winsize dimensions
;
1761 for (i
= 1; i
< argc
; i
++) {
1762 if (!strcmp (argv
[i
], "-c")) {
1763 command
= argv
[i
+1];
1764 cmdargv
= &argv
[i
+1];
1771 vncTerm
*vt
= create_vncterm (argc
, argv
, 745, 400);
1773 setlocale(LC_ALL
, ""); // set from environment
1775 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1777 // fixme: ist there a standard way to detect utf8 mode ?
1778 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1782 dimensions
.ws_col
= vt
->width
;
1783 dimensions
.ws_row
= vt
->height
;
1785 setenv ("TERM", TERM
, 1);
1787 printf("EXEC: %s\n", command
);
1789 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1792 // install default signal handlers
1793 signal (SIGQUIT
, SIG_DFL
);
1794 signal (SIGTERM
, SIG_DFL
);
1795 signal (SIGINT
, SIG_DFL
);
1798 execvp (command
, cmdargv
);
1800 execlp (command
, command
, NULL
);
1802 perror ("Error: exec failed\n");
1803 exit (-1); // should not be reached
1804 } else if (pid
== -1) {
1805 perror ("Error: fork failed\n");
1810 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1811 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1814 basic_event_loop_mainloop();
1816 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1819 if (vt->ibuf_count > 0) {
1820 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1821 write (master, vt->ibuf, vt->ibuf_count);
1823 last_time = time (NULL);
1829 waitpid(pid
, &status
, 0);