]>
git.proxmox.com Git - spiceterm.git/blob - spiceterm.c
bcb38c6593c0105cee5ded71b10a9514085bbc9f
3 Copyright (C) 2013 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>
23 Note: most of the code here is copied from vncterm (which is
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <arpa/inet.h>
37 #include <pty.h> /* for openpty and forkpty */
40 #include <sys/ioctl.h>
46 #include "spiceterm.h"
50 #include <spice/enums.h>
51 #include <spice/macros.h>
52 #include <spice/qxl_dev.h>
54 #include "event_loop.h"
55 #include "translations.h"
59 #define DPRINTF(x, format, ...) { \
61 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
67 #define TERMIDCODE "[?1;2c" // vt100 ID
69 /* these colours are from linux kernel drivers/char/vt.c */
71 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
72 8,12,10,14, 9,13,11,15 };
75 draw_char_at(spiceTerm
*vt
, int x
, int y
, gunichar2 ch
, TextAttributes attrib
)
77 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
81 spice_screen_draw_char(vt
->screen
, x
, y
, ch
, attrib
);
85 spiceterm_update_xy(spiceTerm
*vt
, int x
, int y
)
87 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
89 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
90 int y2
= y1
- vt
->y_displ
;
92 y2
+= vt
->total_height
;
94 if (y2
< vt
->height
) {
95 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
96 draw_char_at(vt
, x
, y2
, c
->ch
, c
->attrib
);
101 spiceterm_clear_xy(spiceTerm
*vt
, int x
, int y
)
103 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
105 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
106 int y2
= y1
- vt
->y_displ
;
108 y2
+= vt
->total_height
;
110 if (y2
< vt
->height
) {
111 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
113 c
->attrib
= vt
->default_attrib
;
114 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
115 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
117 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
122 spiceterm_toggle_marked_cell(spiceTerm
*vt
, int pos
)
124 int x
= (pos
%vt
->width
);
125 int y
= (pos
/vt
->width
);
127 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
129 int y1
= (vt
->y_displ
+ y
) % vt
->total_height
;
131 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
132 c
->attrib
.selected
= c
->attrib
.selected
? 0 : 1;
134 if (y
< vt
->height
) {
135 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
140 spiceterm_show_cursor(spiceTerm
*vt
, int show
)
143 if (x
>= vt
->width
) {
147 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
148 int y
= y1
- vt
->y_displ
;
150 y
+= vt
->total_height
;
153 if (y
< vt
->height
) {
155 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
158 TextAttributes attrib
= vt
->default_attrib
;
159 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
160 draw_char_at(vt
, x
, y
, c
->ch
, attrib
);
162 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
168 spiceterm_refresh(spiceTerm
*vt
)
173 for(y
= 0; y
< vt
->height
; y
++) {
174 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
175 for(x
= 0; x
< vt
->width
; x
++) {
176 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
179 if (++y1
== vt
->total_height
)
183 spiceterm_show_cursor(vt
, 1);
187 spiceterm_clear_screen(spiceTerm
*vt
)
191 for (y
= 0; y
<= vt
->height
; y
++) {
192 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
193 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
194 for (x
= 0; x
< vt
->width
; x
++) {
196 c
->attrib
= vt
->default_attrib
;
197 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
198 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
204 spice_screen_clear(vt
->screen
, 0, 0, vt
->screen
->primary_width
,
205 vt
->screen
->primary_height
);
209 spiceterm_unselect_all(spiceTerm
*vt
)
214 for(y
= 0; y
< vt
->total_height
; y
++) {
215 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
216 for(x
= 0; x
< vt
->width
; x
++) {
217 if (c
->attrib
.selected
) {
218 c
->attrib
.selected
= 0;
219 if (y
< vt
->height
) {
220 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
225 if (++y1
== vt
->total_height
)
231 spiceterm_scroll_down(spiceTerm
*vt
, int top
, int bottom
, int lines
)
233 if ((top
+ lines
) >= bottom
) {
234 lines
= bottom
- top
-1;
237 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
242 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
243 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
244 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
246 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
249 for (i
= 0; i
< lines
; i
++) {
251 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
252 for(j
= 0; j
< vt
->width
; j
++) {
253 c
->attrib
= vt
->default_attrib
;
264 spice_screen_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
265 spice_screen_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
269 spiceterm_scroll_up(spiceTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
271 if ((top
+ lines
) >= bottom
) {
272 lines
= bottom
- top
- 1;
275 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
282 int y1
= (top
+ lines
)*16;
285 spice_screen_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
286 spice_screen_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
295 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
296 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
297 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
299 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
302 for (i
= 1; i
<= lines
; i
++) {
304 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
305 for(j
= 0; j
< vt
->width
; j
++) {
306 c
->attrib
= vt
->default_attrib
;
314 spiceterm_virtual_scroll(spiceTerm
*vt
, int lines
)
316 if (vt
->altbuf
|| lines
== 0) return;
320 int i
= vt
->scroll_height
;
321 if (i
> vt
->total_height
- vt
->height
)
322 i
= vt
->total_height
- vt
->height
;
323 int y1
= vt
->y_base
- i
;
325 y1
+= vt
->total_height
;
326 for(i
= 0; i
< lines
; i
++) {
327 if (vt
->y_displ
== y1
) break;
328 if (--vt
->y_displ
< 0) {
329 vt
->y_displ
= vt
->total_height
- 1;
334 for(i
= 0; i
< lines
; i
++) {
335 if (vt
->y_displ
== vt
->y_base
) break;
336 if (++vt
->y_displ
== vt
->total_height
) {
342 spiceterm_refresh(vt
);
346 spiceterm_respond_esc(spiceTerm
*vt
, const char *esc
)
348 int len
= strlen(esc
);
351 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
352 vt
->ibuf
[vt
->ibuf_count
++] = 27;
353 for (i
= 0; i
< len
; i
++) {
354 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
357 fprintf(stderr
, "input buffer oferflow\n");
363 spiceterm_respond_data(spiceTerm
*vt
, int len
, uint8_t *data
)
367 if (vt
->ibuf_count
< (IBUFSIZE
- len
)) {
368 for (i
= 0; i
< len
; i
++) {
369 vt
->ibuf
[vt
->ibuf_count
++] = data
[i
];
372 fprintf(stderr
, "input buffer oferflow\n");
378 spiceterm_put_lf(spiceTerm
*vt
)
380 if (vt
->cy
+ 1 == vt
->region_bottom
) {
382 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
383 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
387 if (vt
->y_displ
== vt
->y_base
) {
388 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
391 if (vt
->y_displ
== vt
->y_base
) {
392 if (++vt
->y_displ
== vt
->total_height
) {
397 if (++vt
->y_base
== vt
->total_height
) {
401 if (vt
->scroll_height
< vt
->total_height
) {
405 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
406 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
408 for (x
= 0; x
< vt
->width
; x
++) {
410 c
->attrib
= vt
->default_attrib
;
414 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
416 } else if (vt
->cy
< vt
->height
- 1) {
422 spiceterm_csi_m(spiceTerm
*vt
)
426 for (i
= 0; i
< vt
->esc_count
; i
++) {
427 switch (vt
->esc_buf
[i
]) {
428 case 0: /* reset all console attributes to default */
429 vt
->cur_attrib
= vt
->default_attrib
;
432 vt
->cur_attrib
.bold
= 1;
435 vt
->cur_attrib
.uline
= 1;
438 vt
->cur_attrib
.blink
= 1;
441 vt
->cur_attrib
.invers
= 1;
444 vt
->cur_attrib
.unvisible
= 1;
447 vt
->cur_enc
= LAT1_MAP
;
448 // fixme: dispaly controls = 0 ?
449 // fixme: toggle meta = 0 ?
452 vt
->cur_enc
= IBMPC_MAP
;
453 // fixme: dispaly controls = 1 ?
454 // fixme: toggle meta = 0 ?
457 vt
->cur_enc
= IBMPC_MAP
;
458 // fixme: dispaly controls = 1 ?
459 // fixme: toggle meta = 1 ?
462 vt
->cur_attrib
.bold
= 0;
465 vt
->cur_attrib
.uline
= 0;
468 vt
->cur_attrib
.blink
= 0;
471 vt
->cur_attrib
.invers
= 0;
474 vt
->cur_attrib
.unvisible
= 0;
484 /* set foreground color */
485 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
488 /* reset color to default, enable underline */
489 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
490 vt
->cur_attrib
.uline
= 1;
493 /* reset color to default, disable underline */
494 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
495 vt
->cur_attrib
.uline
= 0;
505 /* set background color */
506 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
509 /* reset background color */
510 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
513 fprintf(stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
520 spiceterm_save_cursor(spiceTerm
*vt
)
522 vt
->cx_saved
= vt
->cx
;
523 vt
->cy_saved
= vt
->cy
;
524 vt
->cur_attrib_saved
= vt
->cur_attrib
;
525 vt
->charset_saved
= vt
->charset
;
526 vt
->g0enc_saved
= vt
->g0enc
;
527 vt
->g1enc_saved
= vt
->g1enc
;
528 vt
->cur_enc_saved
= vt
->cur_enc
;
532 spiceterm_restore_cursor(spiceTerm
*vt
)
534 vt
->cx
= vt
->cx_saved
;
535 vt
->cy
= vt
->cy_saved
;
536 vt
->cur_attrib
= vt
->cur_attrib_saved
;
537 vt
->charset
= vt
->charset_saved
;
538 vt
->g0enc
= vt
->g0enc_saved
;
539 vt
->g1enc
= vt
->g1enc_saved
;
540 vt
->cur_enc
= vt
->cur_enc_saved
;
544 spiceterm_set_alternate_buffer(spiceTerm
*vt
, int on_off
)
548 vt
->y_displ
= vt
->y_base
;
552 if (vt
->altbuf
) return;
556 /* alternate buffer & cursor */
558 spiceterm_save_cursor(vt
);
559 /* save screen to altcels */
560 for (y
= 0; y
< vt
->height
; y
++) {
561 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
562 for (x
= 0; x
< vt
->width
; x
++) {
563 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
568 for (y
= 0; y
<= vt
->height
; y
++) {
569 for (x
= 0; x
< vt
->width
; x
++) {
570 // spiceterm_clear_xy(vt, x, y);
576 if (vt
->altbuf
== 0) return;
580 /* restore saved data */
581 for (y
= 0; y
< vt
->height
; y
++) {
582 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
583 for (x
= 0; x
< vt
->width
; x
++) {
584 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
588 spiceterm_restore_cursor(vt
);
591 spiceterm_refresh(vt
);
595 spiceterm_set_mode(spiceTerm
*vt
, int on_off
)
599 for (i
= 0; i
<= vt
->esc_count
; i
++) {
600 if (vt
->esc_ques
) { /* DEC private modes set/reset */
601 switch(vt
->esc_buf
[i
]) {
602 case 10: /* X11 mouse reporting on/off */
603 case 1000: /* SET_VT200_MOUSE */
604 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
605 vt
->report_mouse
= on_off
;
607 case 1049: /* start/end special app mode (smcup/rmcup) */
608 spiceterm_set_alternate_buffer (vt
, on_off
);
610 case 25: /* Cursor on/off */
611 case 9: /* X10 mouse reporting on/off */
612 case 6: /* Origin relative/absolute */
613 case 1: /* Cursor keys in appl mode*/
614 case 5: /* Inverted screen on/off */
615 case 7: /* Autowrap on/off */
616 case 8: /* Autorepeat on/off */
619 } else { /* ANSI modes set/reset */
620 //g_assert_not_reached();
622 /* fixme: implement me */
628 spiceterm_gotoxy(spiceTerm
*vt
, int x
, int y
)
630 /* verify all boundaries */
636 if (x
>= vt
->width
) {
646 if (y
>= vt
->height
) {
654 debug_print_escape_buffer(spiceTerm
*vt
, const char *func
, const char *prefix
,
655 const char *qes
, gunichar2 ch
)
658 if (vt
->esc_count
== 0) {
659 printf("%s:%s ESC[%s%c\n", func
, prefix
, qes
, ch
);
660 } else if (vt
->esc_count
== 1) {
661 printf("%s:%s ESC[%s%d%c\n", func
, prefix
, qes
, vt
->esc_buf
[0], ch
);
664 printf("%s:%s ESC[%s%d", func
, prefix
, qes
, vt
->esc_buf
[0]);
665 for (i
= 1; i
< vt
->esc_count
; i
++) {
666 printf(";%d", vt
->esc_buf
[i
]);
673 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
674 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
675 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
678 spiceterm_putchar(spiceTerm
*vt
, gunichar2 ch
)
682 if (debug
&& !vt
->tty_state
) {
683 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
684 vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
687 switch(vt
->tty_state
) {
689 vt
->tty_state
= ESnormal
;
692 vt
->tty_state
= ESsquare
;
695 vt
->tty_state
= ESnonstd
;
698 vt
->tty_state
= ESpercent
;
701 spiceterm_save_cursor(vt
);
704 spiceterm_restore_cursor(vt
);
707 vt
->tty_state
= ESsetG0
; // SET G0
710 vt
->tty_state
= ESsetG1
; // SET G1
714 if (vt
->cy
== vt
->region_top
)
715 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, 1);
716 else if (vt
->cy
> 0) {
721 /* numeric keypad - ignored */
724 /* appl. keypad - ignored */
727 DPRINTF(1, "got unhandled ESC%c %d", ch
, ch
);
731 case ESnonstd
: /* Operating System Controls */
732 vt
->tty_state
= ESnormal
;
735 case 'P': /* palette escape sequence */
736 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
741 vt
->tty_state
= ESpalette
;
743 case 'R': /* reset palette */
744 // fixme: reset_palette(vc);
751 vt
->osc_textbuf
[0] = 0;
752 vt
->tty_state
= ESosc1
;
755 DPRINTF(1, "got unhandled OSC %c", ch
);
756 vt
->tty_state
= ESnormal
;
761 vt
->tty_state
= ESnormal
;
763 vt
->tty_state
= ESosc2
;
765 DPRINTF(1, "got illegal OSC sequence");
769 if (ch
!= 0x9c && ch
!= 7) {
771 while (vt
->osc_textbuf
[i
]) i
++;
772 vt
->osc_textbuf
[i
++] = ch
;
773 vt
->osc_textbuf
[i
] = 0;
775 DPRINTF(1, "OSC:%c:%s", vt
->osc_cmd
, vt
->osc_textbuf
);
776 vt
->tty_state
= ESnormal
;
780 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
781 || (ch
>= 'a' && ch
<= 'f')) {
782 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
783 if (vt
->esc_count
== 7) {
784 // fixme: this does not work - please test
786 rfbColourMap *cmap =&vt->screen->colourMap;
788 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
789 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
790 cmap->data.bytes[i++] += vt->esc_buf[j++];
791 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
792 cmap->data.bytes[i++] += vt->esc_buf[j++];
793 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
794 cmap->data.bytes[i] += vt->esc_buf[j];
798 vt
->tty_state
= ESnormal
;
801 vt
->tty_state
= ESnormal
;
804 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
810 vt
->tty_state
= ESgetpars
;
813 vt
->tty_state
= ESidquery
;
817 if ((vt
->esc_ques
= (ch
== '?'))) {
821 if (ch
>= '0' && ch
<= '9') {
823 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
824 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
827 } else if (ch
== ';') {
831 if (vt
->esc_has_par
) {
834 vt
->tty_state
= ESgotpars
;
838 vt
->tty_state
= ESnormal
;
840 char *qes
= vt
->esc_ques
? "?" : "";
843 debug_print_escape_buffer(vt
, __func__
, "", qes
, ch
);
848 spiceterm_set_mode(vt
, 1);
851 spiceterm_set_mode(vt
, 0);
854 if (!vt
->esc_count
) {
855 vt
->esc_count
++; // default parameter 0
860 /* report cursor position */
861 /* TODO: send ESC[row;colR */
865 if (vt
->esc_buf
[0] == 0) {
868 vt
->cy
-= vt
->esc_buf
[0];
875 /* move cursor down */
876 if (vt
->esc_buf
[0] == 0) {
879 vt
->cy
+= vt
->esc_buf
[0];
880 if (vt
->cy
>= vt
->height
) {
881 vt
->cy
= vt
->height
- 1;
886 /* move cursor right */
887 if (vt
->esc_buf
[0] == 0) {
890 vt
->cx
+= vt
->esc_buf
[0];
891 if (vt
->cx
>= vt
->width
) {
892 vt
->cx
= vt
->width
- 1;
896 /* move cursor left */
897 if (vt
->esc_buf
[0] == 0) {
900 vt
->cx
-= vt
->esc_buf
[0];
907 /* move cursor to column */
908 spiceterm_gotoxy(vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
911 /* move cursor to row */
912 spiceterm_gotoxy(vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
916 /* move cursor to row, column */
917 spiceterm_gotoxy(vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
920 switch (vt
->esc_buf
[0]) {
922 /* clear to end of screen */
923 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
924 for (x
= 0; x
< vt
->width
; x
++) {
925 if (y
== vt
->cy
&& x
< vt
->cx
) {
928 spiceterm_clear_xy (vt
, x
, y
);
933 /* clear from beginning of screen */
934 for (y
= 0; y
<= vt
->cy
; y
++) {
935 for (x
= 0; x
< vt
->width
; x
++) {
936 if (y
== vt
->cy
&& x
> vt
->cx
) {
939 spiceterm_clear_xy(vt
, x
, y
);
944 /* clear entire screen */
945 spiceterm_clear_screen(vt
);
950 switch (vt
->esc_buf
[0]) {
953 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
954 spiceterm_clear_xy(vt
, x
, vt
->cy
);
958 /* clear from beginning of line */
959 for (x
= 0; x
<= vt
->cx
; x
++) {
960 spiceterm_clear_xy(vt
, x
, vt
->cy
);
964 /* clear entire line */
965 for(x
= 0; x
< vt
->width
; x
++) {
966 spiceterm_clear_xy(vt
, x
, vt
->cy
);
975 if (c
> vt
->height
- vt
->cy
)
976 c
= vt
->height
- vt
->cy
;
980 spiceterm_scroll_down(vt
, vt
->cy
, vt
->region_bottom
, c
);
986 if (c
> vt
->height
- vt
->cy
)
987 c
= vt
->height
- vt
->cy
;
991 spiceterm_scroll_up(vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
997 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, c
);
1003 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
1006 /* delete c character */
1009 if (c
> vt
->width
- vt
->cx
)
1010 c
= vt
->width
- vt
->cx
;
1014 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
1015 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1016 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
1017 TextCell
*src
= dst
+ c
;
1019 spiceterm_update_xy(vt
, x
+ c
, vt
->cy
);
1021 src
->attrib
= vt
->default_attrib
;
1022 spiceterm_update_xy(vt
, x
, vt
->cy
);
1026 /* save cursor position */
1027 spiceterm_save_cursor(vt
);
1030 /* restore cursor position */
1031 spiceterm_restore_cursor(vt
);
1034 /* erase c characters */
1038 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1040 for(i
= 0; i
< c
; i
++) {
1041 spiceterm_clear_xy(vt
, vt
->cx
+ i
, vt
->cy
);
1045 /* insert c character */
1047 if (c
> (vt
->width
- vt
->cx
)) {
1048 c
= vt
->width
- vt
->cx
;
1052 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1053 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1054 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1055 TextCell
*dst
= src
+ c
;
1057 spiceterm_update_xy (vt
, x
+ c
, vt
->cy
);
1059 src
->attrib
= vt
->cur_attrib
;
1060 spiceterm_update_xy(vt
, x
, vt
->cy
);
1066 if (!vt
->esc_buf
[0])
1068 if (!vt
->esc_buf
[1])
1069 vt
->esc_buf
[1] = vt
->height
;
1070 /* Minimum allowed region is 2 lines */
1071 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1072 vt
->esc_buf
[1] <= vt
->height
) {
1073 vt
->region_top
= vt
->esc_buf
[0] - 1;
1074 vt
->region_bottom
= vt
->esc_buf
[1];
1076 vt
->cy
= vt
->region_top
;
1077 DPRINTF(1, "set region %d %d", vt
->region_top
, vt
->region_bottom
);
1083 debug_print_escape_buffer(vt
, __func__
, " unhandled escape", qes
, ch
);
1089 case ESsetG0
: // Set G0
1090 vt
->tty_state
= ESnormal
;
1093 vt
->g0enc
= GRAF_MAP
;
1095 vt
->g0enc
= LAT1_MAP
;
1097 vt
->g0enc
= IBMPC_MAP
;
1099 vt
->g0enc
= USER_MAP
;
1101 if (vt
->charset
== 0)
1102 vt
->cur_enc
= vt
->g0enc
;
1105 case ESsetG1
: // Set G1
1106 vt
->tty_state
= ESnormal
;
1109 vt
->g1enc
= GRAF_MAP
;
1111 vt
->g1enc
= LAT1_MAP
;
1113 vt
->g1enc
= IBMPC_MAP
;
1115 vt
->g1enc
= USER_MAP
;
1117 if (vt
->charset
== 1)
1118 vt
->cur_enc
= vt
->g1enc
;
1121 case ESidquery
: // vt100 query id
1122 vt
->tty_state
= ESnormal
;
1125 DPRINTF(1, "ESC[>c Query term ID");
1126 spiceterm_respond_esc(vt
, TERMIDCODE
);
1130 vt
->tty_state
= ESnormal
;
1132 case '@': /* defined in ISO 2022 */
1135 case 'G': /* prelim official escape code */
1136 case '8': /* retained for compatibility */
1141 default: // ESnormal
1142 vt
->tty_state
= ESnormal
;
1147 case 7: /* alert aka. bell */
1149 //rfbSendBell(vt->screen);
1151 case 8: /* backspace */
1155 case 9: /* tabspace */
1156 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1158 spiceterm_put_lf(vt
);
1160 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1166 spiceterm_put_lf(vt
);
1168 case 13: /* carriage return */
1172 /* SI (shift in), select character set 1 */
1174 vt
->cur_enc
= vt
->g1enc
;
1175 /* fixme: display controls = 1 */
1178 /* SO (shift out), select character set 0 */
1180 vt
->cur_enc
= vt
->g0enc
;
1181 /* fixme: display controls = 0 */
1184 vt
->tty_state
= ESesc
;
1186 case 127: /* delete */
1189 case 128+27: /* csi */
1190 vt
->tty_state
= ESsquare
;
1193 if (vt
->cx
>= vt
->width
) {
1196 spiceterm_put_lf(vt
);
1199 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1200 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1201 c
->attrib
= vt
->cur_attrib
;
1203 spiceterm_update_xy(vt
, vt
->cx
, vt
->cy
);
1212 spiceterm_puts(spiceTerm
*vt
, const char *buf
, int len
)
1216 spiceterm_show_cursor(vt
, 0);
1219 unsigned char c
= *buf
;
1223 if (vt
->tty_state
!= ESnormal
) {
1224 // never translate escape sequence
1226 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1228 if(c
& 0x80) { // utf8 multi-byte sequence
1230 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1231 // inside UTF8 sequence
1232 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1234 if (vt
->utf_count
== 0) {
1235 if (vt
->utf_char
<= G_MAXUINT16
) {
1244 // first char of a UTF8 sequence
1245 if ((c
& 0xe0) == 0xc0) {
1247 vt
->utf_char
= (c
& 0x1f);
1248 } else if ((c
& 0xf0) == 0xe0) {
1250 vt
->utf_char
= (c
& 0x0f);
1251 } else if ((c
& 0xf8) == 0xf0) {
1253 vt
->utf_char
= (c
& 0x07);
1254 } else if ((c
& 0xfc) == 0xf8) {
1256 vt
->utf_char
= (c
& 0x03);
1257 } else if ((c
& 0xfe) == 0xfc) {
1259 vt
->utf_char
= (c
& 0x01);
1272 // never translate controls
1273 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1274 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1280 spiceterm_putchar(vt
, tc
);
1283 spiceterm_show_cursor(vt
, 1);
1289 spiceterm_update_watch_mask(spiceTerm
*vt
, gboolean writable
)
1291 g_assert(vt
!= NULL
);
1293 int mask
= SPICE_WATCH_EVENT_READ
;
1296 mask
|= SPICE_WATCH_EVENT_WRITE
;
1299 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, mask
);
1303 mouse_report(spiceTerm
*vt
, int butt
, int mrx
, int mry
)
1307 sprintf(buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1310 spiceterm_respond_esc(vt
, buf
);
1312 spiceterm_update_watch_mask(vt
, TRUE
);
1316 spiceterm_respond_unichar2(spiceTerm
*vt
, gunichar2 uc
)
1320 gint len
= g_unichar_to_utf8(uc
, buf
);
1323 spiceterm_respond_data(vt
, len
, (uint8_t *)buf
);
1326 uint8_t buf
[1] = { (uint8_t)uc
};
1327 spiceterm_respond_data(vt
, 1, buf
);
1332 spiceterm_clear_selection(spiceTerm
*vt
)
1334 DPRINTF(1, "mark_active = %d", vt
->mark_active
);
1336 vt
->mark_active
= 0;
1337 if (vt
->selection
) free (vt
->selection
);
1338 vt
->selection
= NULL
;
1340 spiceterm_unselect_all(vt
);
1344 spiceterm_motion_event(spiceTerm
*vt
, uint32_t x
, uint32_t y
, uint32_t buttons
)
1346 DPRINTF(1, "mask=%08x x=%d y=%d", buttons
, x
,y
);
1348 static int last_mask
= 0;
1349 static int sel_start_pos
= 0;
1350 static int sel_end_pos
= 0;
1351 static int button2_released
= 1;
1358 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
1360 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
1362 if (vt
->report_mouse
&& buttons
!= last_mask
) {
1363 last_mask
= buttons
;
1365 mouse_report(vt
, 0, cx
, cy
);
1368 mouse_report (vt
, 1, cx
, cy
);
1371 mouse_report(vt
, 2, cx
, cy
);
1374 mouse_report(vt
, 3, cx
, cy
);
1380 if(button2_released
) {
1382 if (vdagent_owns_clipboard(vt
)) {
1383 if (vt
->selection
) {
1385 for(i
= 0; i
< vt
->selection_len
; i
++) {
1386 spiceterm_respond_unichar2(vt
, vt
->selection
[i
]);
1388 spiceterm_update_watch_mask(vt
, TRUE
);
1389 if (vt
->y_displ
!= vt
->y_base
) {
1390 vt
->y_displ
= vt
->y_base
;
1391 spiceterm_refresh(vt
);
1395 vdagent_request_clipboard(vt
);
1399 button2_released
= 0;
1401 button2_released
= 1;
1405 int pos
= cy
*vt
->width
+ cx
;
1407 // code borrowed from libvncserver (VNCconsole.c)
1409 if (!vt
->mark_active
) {
1411 spiceterm_unselect_all(vt
);
1413 vt
->mark_active
= 1;
1414 sel_start_pos
= sel_end_pos
= pos
;
1415 spiceterm_toggle_marked_cell(vt
, pos
);
1419 if (pos
!= sel_end_pos
) {
1420 if (pos
> sel_end_pos
) {
1421 cx
= sel_end_pos
; cy
=pos
;
1423 cx
=pos
; cy
=sel_end_pos
;
1426 if (cx
< sel_start_pos
) {
1427 if (cy
< sel_start_pos
) cy
--;
1433 spiceterm_toggle_marked_cell(vt
, cx
);
1441 } else if (vt
->mark_active
) {
1442 vt
->mark_active
= 0;
1444 if (sel_start_pos
> sel_end_pos
) {
1445 int tmp
= sel_start_pos
- 1;
1446 sel_start_pos
= sel_end_pos
;
1450 int len
= sel_end_pos
- sel_start_pos
+ 1;
1452 if (vt
->selection
) free (vt
->selection
);
1453 vt
->selection
= (gunichar2
*)malloc (len
*sizeof(gunichar2
));
1454 vt
->selection_len
= len
;
1456 for (i
= 0; i
< len
; i
++) {
1457 int pos
= sel_start_pos
+ i
;
1458 int x
= pos
% vt
->width
;
1459 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
1460 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
1461 vt
->selection
[i
] = c
->ch
;
1465 DPRINTF(1, "selection length = %d", vt
->selection_len
);
1467 vdagent_grab_clipboard(vt
);
1472 init_spiceterm(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
1476 g_assert(vt
!= NULL
);
1477 g_assert(vt
->screen
!= NULL
);
1479 vt
->width
= width
/ 8;
1480 vt
->height
= height
/ 16;
1482 vt
->total_height
= vt
->height
* 20;
1483 vt
->scroll_height
= 0;
1488 vt
->region_bottom
= vt
->height
;
1490 vt
->g0enc
= LAT1_MAP
;
1491 vt
->g1enc
= GRAF_MAP
;
1492 vt
->cur_enc
= vt
->g0enc
;
1495 /* default text attributes */
1496 vt
->default_attrib
.bold
= 0;
1497 vt
->default_attrib
.uline
= 0;
1498 vt
->default_attrib
.blink
= 0;
1499 vt
->default_attrib
.invers
= 0;
1500 vt
->default_attrib
.unvisible
= 0;
1501 vt
->default_attrib
.fgcol
= 7;
1502 vt
->default_attrib
.bgcol
= 0;
1504 vt
->cur_attrib
= vt
->default_attrib
;
1514 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1516 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1517 vt
->cells
[i
].ch
= ' ';
1518 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1522 g_free(vt
->altcells
);
1525 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1529 spiceterm_resize(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
1531 width
= (width
/8)*8;
1532 height
= (height
/16)*16;
1534 if (vt
->screen
->width
== width
&& vt
->screen
->height
== height
) {
1538 DPRINTF(0, "width=%u height=%u", width
, height
);
1540 spice_screen_resize(vt
->screen
, width
, height
);
1542 init_spiceterm(vt
, width
, height
);
1544 struct winsize dimensions
;
1545 dimensions
.ws_col
= vt
->width
;
1546 dimensions
.ws_row
= vt
->height
;
1548 ioctl(vt
->pty
, TIOCSWINSZ
, &dimensions
);
1552 master_error_callback(GIOChannel
*channel
, GIOCondition condition
,
1555 //spiceTerm *vt = (spiceTerm *)data;
1557 DPRINTF(1, "condition %d", condition
);
1565 master_watch(int master
, int event
, void *opaque
)
1567 spiceTerm
*vt
= (spiceTerm
*)opaque
;
1570 // fixme: if (!vt->mark_active) {
1572 if (event
== SPICE_WATCH_EVENT_READ
) {
1574 while ((c
= read(master
, buffer
, 1024)) == -1) {
1575 if (errno
!= EAGAIN
) break;
1578 perror("master pipe read error"); // fixme
1580 spiceterm_puts (vt
, buffer
, c
);
1582 if (vt
->ibuf_count
> 0) {
1583 DPRINTF(1, "write input %x %d", vt
->ibuf
[0], vt
->ibuf_count
);
1584 if ((c
= write (master
, vt
->ibuf
, vt
->ibuf_count
)) >= 0) {
1585 if (c
== vt
->ibuf_count
) {
1588 // not all data written
1589 memmove(vt
->ibuf
, vt
->ibuf
+ c
, vt
->ibuf_count
- c
);
1590 vt
->ibuf_count
-= c
;
1592 // nothing written -ignore and try later
1595 perror("master pipe write error");
1598 if (vt
->ibuf_count
== 0) {
1599 spiceterm_update_watch_mask(vt
, FALSE
);
1605 spiceterm_print_usage(const char *msg
)
1608 fprintf(stderr
, "ERROR: %s\n", msg
);
1610 fprintf(stderr
, "USAGE: spiceterm [OPTIONS] [-- command [args]]\n");
1611 fprintf(stderr
, " --timeout <seconds> Wait this time before aborting (default is 10 seconds)\n");
1612 fprintf(stderr
, " --authpath <path> Authentication path (PVE AUTH)\n");
1613 fprintf(stderr
, " --permission <perm> Required permissions (PVE AUTH)\n");
1614 fprintf(stderr
, " --port <port> Bind to port <port>\n");
1615 fprintf(stderr
, " --addr <addr> Bind to address <addr>\n");
1616 fprintf(stderr
, " --sasl Enable SASL based authentication\n");
1617 fprintf(stderr
, " --noauth Disable authentication\n");
1618 fprintf(stderr
, " --keymap Spefify keymap (uses kvm keymap files)\n");
1622 main (int argc
, char** argv
)
1625 char **cmdargv
= NULL
;
1626 char *command
= "/bin/bash"; // execute normal shell as default
1630 struct winsize dimensions
;
1631 SpiceTermOptions opts
= {
1639 static struct option long_options
[] = {
1640 { "timeout", required_argument
, 0, 't' },
1641 { "authpath", required_argument
, 0, 'A' },
1642 { "permissions", required_argument
, 0, 'P' },
1643 { "port", required_argument
, 0, 'p' },
1644 { "addr", required_argument
, 0, 'a' },
1645 { "keymap", required_argument
, 0, 'k' },
1646 { "noauth", no_argument
, 0, 'n' },
1647 { "sasl", no_argument
, 0, 's' },
1651 while ((c
= getopt_long(argc
, argv
, "nkst:a:p:P:", long_options
, NULL
)) != -1) {
1661 opts
.keymap
= optarg
;
1664 pve_auth_set_path(optarg
);
1667 pve_auth_set_permissions(optarg
);
1670 opts
.port
= atoi(optarg
);
1676 opts
.timeout
= atoi(optarg
);
1679 spiceterm_print_usage(NULL
);
1683 spiceterm_print_usage("getopt returned unknown character code");
1688 if (optind
< argc
) {
1689 command
= argv
[optind
];
1690 cmdargv
= &argv
[optind
];
1693 spiceTerm
*vt
= spiceterm_create(744, 400, &opts
);
1697 setlocale(LC_ALL
, ""); // set from environment
1699 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1701 // fixme: ist there a standard way to detect utf8 mode ?
1702 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1706 dimensions
.ws_col
= vt
->width
;
1707 dimensions
.ws_row
= vt
->height
;
1709 setenv("TERM", TERM
, 1);
1711 DPRINTF(1, "execute %s", command
);
1713 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1716 // install default signal handlers
1717 signal (SIGQUIT
, SIG_DFL
);
1718 signal (SIGTERM
, SIG_DFL
);
1719 signal (SIGINT
, SIG_DFL
);
1722 execvp (command
, cmdargv
);
1724 execlp (command
, command
, NULL
);
1726 perror ("Error: exec failed\n");
1727 exit (-1); // should not be reached
1728 } else if (pid
== -1) {
1729 perror ("Error: fork failed\n");
1735 /* watch for errors - we need to use glib directly because spice
1736 * does not have SPICE_WATCH_EVENT for this */
1737 GIOChannel
*channel
= g_io_channel_unix_new(master
);
1738 g_io_channel_set_flags(channel
, G_IO_FLAG_NONBLOCK
, NULL
);
1739 g_io_channel_set_encoding(channel
, NULL
, NULL
);
1740 g_io_add_watch(channel
, G_IO_ERR
|G_IO_HUP
, master_error_callback
, vt
);
1742 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1743 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1746 basic_event_loop_mainloop();
1750 waitpid(pid
, &status
, 0);