]>
git.proxmox.com Git - spiceterm.git/blob - spiceterm.c
85067901033dcee33245724c58a6d02b90e08bdc
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>
45 #include "spiceterm.h"
49 #include <spice/enums.h>
50 #include <spice/macros.h>
51 #include <spice/qxl_dev.h>
53 #include <gdk/gdkkeysyms.h>
55 #include "event_loop.h"
56 #include "translations.h"
60 #define DPRINTF(x, format, ...) { \
62 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
68 #define TERMIDCODE "[?1;2c" // vt100 ID
70 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
71 fprintf(stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
76 /* these colours are from linux kernel drivers/char/vt.c */
78 unsigned char color_table
[] = { 0, 4, 2, 6, 1, 5, 3, 7,
79 8,12,10,14, 9,13,11,15 };
82 print_usage(const char *msg
)
84 if (msg
) { fprintf(stderr
, "ERROR: %s\n", msg
); }
85 fprintf(stderr
, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
89 draw_char_at(spiceTerm
*vt
, int x
, int y
, gunichar2 ch
, TextAttributes attrib
)
91 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
95 spice_screen_draw_char(vt
->screen
, x
, y
, ch
, attrib
);
99 spiceterm_update_xy(spiceTerm
*vt
, int x
, int y
)
101 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
103 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
104 int y2
= y1
- vt
->y_displ
;
106 y2
+= vt
->total_height
;
108 if (y2
< vt
->height
) {
109 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
110 draw_char_at(vt
, x
, y2
, c
->ch
, c
->attrib
);
115 spiceterm_clear_xy(spiceTerm
*vt
, int x
, int y
)
117 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
119 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
120 int y2
= y1
- vt
->y_displ
;
122 y2
+= vt
->total_height
;
124 if (y2
< vt
->height
) {
125 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
127 c
->attrib
= vt
->default_attrib
;
128 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
129 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
131 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
136 spiceterm_toggle_marked_cell(spiceTerm
*vt
, int pos
)
138 int x
= (pos
%vt
->width
);
139 int y
= (pos
/vt
->width
);
141 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
143 int y1
= (vt
->y_displ
+ y
) % vt
->total_height
;
145 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
146 c
->attrib
.selected
= c
->attrib
.selected
? 0 : 1;
148 if (y
< vt
->height
) {
149 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
154 spiceterm_show_cursor(spiceTerm
*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 spiceterm_refresh(spiceTerm
*vt
)
187 for(y
= 0; y
< vt
->height
; y
++) {
188 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
189 for(x
= 0; x
< vt
->width
; x
++) {
190 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
193 if (++y1
== vt
->total_height
)
197 spiceterm_show_cursor(vt
, 1);
201 spiceterm_clear_screen(spiceTerm
*vt
)
205 for (y
= 0; y
<= vt
->height
; y
++) {
206 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
207 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
208 for (x
= 0; x
< vt
->width
; x
++) {
210 c
->attrib
= vt
->default_attrib
;
211 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
212 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
218 spice_screen_clear(vt
->screen
, 0, 0, vt
->screen
->primary_width
,
219 vt
->screen
->primary_height
);
223 spiceterm_unselect_all(spiceTerm
*vt
)
228 for(y
= 0; y
< vt
->total_height
; y
++) {
229 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
230 for(x
= 0; x
< vt
->width
; x
++) {
231 if (c
->attrib
.selected
) {
232 c
->attrib
.selected
= 0;
233 if (y
< vt
->height
) {
234 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
239 if (++y1
== vt
->total_height
)
245 spiceterm_scroll_down(spiceTerm
*vt
, int top
, int bottom
, int lines
)
247 if ((top
+ lines
) >= bottom
) {
248 lines
= bottom
- top
-1;
251 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
256 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
257 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
258 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
260 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
263 for (i
= 0; i
< lines
; i
++) {
265 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
266 for(j
= 0; j
< vt
->width
; j
++) {
267 c
->attrib
= vt
->default_attrib
;
278 spice_screen_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
279 spice_screen_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
283 spiceterm_scroll_up(spiceTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
285 if ((top
+ lines
) >= bottom
) {
286 lines
= bottom
- top
- 1;
289 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
296 int y1
= (top
+ lines
)*16;
299 spice_screen_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
300 spice_screen_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
309 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
310 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
311 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
313 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
316 for (i
= 1; i
<= lines
; i
++) {
318 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
319 for(j
= 0; j
< vt
->width
; j
++) {
320 c
->attrib
= vt
->default_attrib
;
328 spiceterm_virtual_scroll(spiceTerm
*vt
, int lines
)
330 if (vt
->altbuf
|| lines
== 0) return;
334 int i
= vt
->scroll_height
;
335 if (i
> vt
->total_height
- vt
->height
)
336 i
= vt
->total_height
- vt
->height
;
337 int y1
= vt
->y_base
- i
;
339 y1
+= vt
->total_height
;
340 for(i
= 0; i
< lines
; i
++) {
341 if (vt
->y_displ
== y1
) break;
342 if (--vt
->y_displ
< 0) {
343 vt
->y_displ
= vt
->total_height
- 1;
348 for(i
= 0; i
< lines
; i
++) {
349 if (vt
->y_displ
== vt
->y_base
) break;
350 if (++vt
->y_displ
== vt
->total_height
) {
356 spiceterm_refresh(vt
);
360 spiceterm_respond_esc(spiceTerm
*vt
, const char *esc
)
362 int len
= strlen(esc
);
365 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
366 vt
->ibuf
[vt
->ibuf_count
++] = 27;
367 for (i
= 0; i
< len
; i
++) {
368 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
371 fprintf(stderr
, "input buffer oferflow\n");
377 spiceterm_respond_data(spiceTerm
*vt
, int len
, uint8_t *data
)
381 if (vt
->ibuf_count
< (IBUFSIZE
- len
)) {
382 for (i
= 0; i
< len
; i
++) {
383 vt
->ibuf
[vt
->ibuf_count
++] = data
[i
];
386 fprintf(stderr
, "input buffer oferflow\n");
392 spiceterm_put_lf(spiceTerm
*vt
)
394 if (vt
->cy
+ 1 == vt
->region_bottom
) {
396 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
397 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
401 if (vt
->y_displ
== vt
->y_base
) {
402 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
405 if (vt
->y_displ
== vt
->y_base
) {
406 if (++vt
->y_displ
== vt
->total_height
) {
411 if (++vt
->y_base
== vt
->total_height
) {
415 if (vt
->scroll_height
< vt
->total_height
) {
419 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
420 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
422 for (x
= 0; x
< vt
->width
; x
++) {
424 c
->attrib
= vt
->default_attrib
;
428 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
430 } else if (vt
->cy
< vt
->height
- 1) {
436 spiceterm_csi_m(spiceTerm
*vt
)
440 for (i
= 0; i
< vt
->esc_count
; i
++) {
441 switch (vt
->esc_buf
[i
]) {
442 case 0: /* reset all console attributes to default */
443 vt
->cur_attrib
= vt
->default_attrib
;
446 vt
->cur_attrib
.bold
= 1;
449 vt
->cur_attrib
.uline
= 1;
452 vt
->cur_attrib
.blink
= 1;
455 vt
->cur_attrib
.invers
= 1;
458 vt
->cur_attrib
.unvisible
= 1;
461 vt
->cur_enc
= LAT1_MAP
;
462 // fixme: dispaly controls = 0 ?
463 // fixme: toggle meta = 0 ?
466 vt
->cur_enc
= IBMPC_MAP
;
467 // fixme: dispaly controls = 1 ?
468 // fixme: toggle meta = 0 ?
471 vt
->cur_enc
= IBMPC_MAP
;
472 // fixme: dispaly controls = 1 ?
473 // fixme: toggle meta = 1 ?
476 vt
->cur_attrib
.bold
= 0;
479 vt
->cur_attrib
.uline
= 0;
482 vt
->cur_attrib
.blink
= 0;
485 vt
->cur_attrib
.invers
= 0;
488 vt
->cur_attrib
.unvisible
= 0;
498 /* set foreground color */
499 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
502 /* reset color to default, enable underline */
503 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
504 vt
->cur_attrib
.uline
= 1;
507 /* reset color to default, disable underline */
508 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
509 vt
->cur_attrib
.uline
= 0;
519 /* set background color */
520 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
523 /* reset background color */
524 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
527 fprintf(stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
534 spiceterm_save_cursor(spiceTerm
*vt
)
536 vt
->cx_saved
= vt
->cx
;
537 vt
->cy_saved
= vt
->cy
;
538 vt
->cur_attrib_saved
= vt
->cur_attrib
;
539 vt
->charset_saved
= vt
->charset
;
540 vt
->g0enc_saved
= vt
->g0enc
;
541 vt
->g1enc_saved
= vt
->g1enc
;
542 vt
->cur_enc_saved
= vt
->cur_enc
;
546 spiceterm_restore_cursor(spiceTerm
*vt
)
548 vt
->cx
= vt
->cx_saved
;
549 vt
->cy
= vt
->cy_saved
;
550 vt
->cur_attrib
= vt
->cur_attrib_saved
;
551 vt
->charset
= vt
->charset_saved
;
552 vt
->g0enc
= vt
->g0enc_saved
;
553 vt
->g1enc
= vt
->g1enc_saved
;
554 vt
->cur_enc
= vt
->cur_enc_saved
;
558 spiceterm_set_alternate_buffer(spiceTerm
*vt
, int on_off
)
562 vt
->y_displ
= vt
->y_base
;
566 if (vt
->altbuf
) return;
570 /* alternate buffer & cursor */
572 spiceterm_save_cursor(vt
);
573 /* save screen to altcels */
574 for (y
= 0; y
< vt
->height
; y
++) {
575 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
576 for (x
= 0; x
< vt
->width
; x
++) {
577 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
582 for (y
= 0; y
<= vt
->height
; y
++) {
583 for (x
= 0; x
< vt
->width
; x
++) {
584 // spiceterm_clear_xy(vt, x, y);
590 if (vt
->altbuf
== 0) return;
594 /* restore saved data */
595 for (y
= 0; y
< vt
->height
; y
++) {
596 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
597 for (x
= 0; x
< vt
->width
; x
++) {
598 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
602 spiceterm_restore_cursor(vt
);
605 spiceterm_refresh(vt
);
609 spiceterm_set_mode(spiceTerm
*vt
, int on_off
)
613 for (i
= 0; i
<= vt
->esc_count
; i
++) {
614 if (vt
->esc_ques
) { /* DEC private modes set/reset */
615 switch(vt
->esc_buf
[i
]) {
616 case 10: /* X11 mouse reporting on/off */
617 case 1000: /* SET_VT200_MOUSE */
618 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
619 vt
->report_mouse
= on_off
;
621 case 1049: /* start/end special app mode (smcup/rmcup) */
622 spiceterm_set_alternate_buffer (vt
, on_off
);
624 case 25: /* Cursor on/off */
625 case 9: /* X10 mouse reporting on/off */
626 case 6: /* Origin relative/absolute */
627 case 1: /* Cursor keys in appl mode*/
628 case 5: /* Inverted screen on/off */
629 case 7: /* Autowrap on/off */
630 case 8: /* Autorepeat on/off */
633 } else { /* ANSI modes set/reset */
634 //g_assert_not_reached();
636 /* fixme: implement me */
642 spiceterm_gotoxy(spiceTerm
*vt
, int x
, int y
)
644 /* verify all boundaries */
650 if (x
>= vt
->width
) {
660 if (y
>= vt
->height
) {
668 debug_print_escape_buffer(spiceTerm
*vt
, const char *func
, const char *prefix
,
669 const char *qes
, gunichar2 ch
)
672 if (vt
->esc_count
== 0) {
673 printf("%s:%s ESC[%s%c\n", func
, prefix
, qes
, ch
);
674 } else if (vt
->esc_count
== 1) {
675 printf("%s:%s ESC[%s%d%c\n", func
, prefix
, qes
, vt
->esc_buf
[0], ch
);
678 printf("%s:%s ESC[%s%d", func
, prefix
, qes
, vt
->esc_buf
[0]);
679 for (i
= 1; i
< vt
->esc_count
; i
++) {
680 printf(";%d", vt
->esc_buf
[i
]);
687 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
688 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
689 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
692 spiceterm_putchar(spiceTerm
*vt
, gunichar2 ch
)
696 if (debug
&& !vt
->tty_state
) {
697 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
698 vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
701 switch(vt
->tty_state
) {
703 vt
->tty_state
= ESnormal
;
706 vt
->tty_state
= ESsquare
;
709 vt
->tty_state
= ESnonstd
;
712 vt
->tty_state
= ESpercent
;
715 spiceterm_save_cursor(vt
);
718 spiceterm_restore_cursor(vt
);
721 vt
->tty_state
= ESsetG0
; // SET G0
724 vt
->tty_state
= ESsetG1
; // SET G1
728 if (vt
->cy
== vt
->region_top
)
729 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, 1);
730 else if (vt
->cy
> 0) {
735 /* numeric keypad - ignored */
738 /* appl. keypad - ignored */
741 DPRINTF(1, "got unhandled ESC%c %d", ch
, ch
);
745 case ESnonstd
: /* Operating System Controls */
746 vt
->tty_state
= ESnormal
;
749 case 'P': /* palette escape sequence */
750 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
755 vt
->tty_state
= ESpalette
;
757 case 'R': /* reset palette */
758 // fixme: reset_palette(vc);
765 vt
->osc_textbuf
[0] = 0;
766 vt
->tty_state
= ESosc1
;
769 DPRINTF(1, "got unhandled OSC %c", ch
);
770 vt
->tty_state
= ESnormal
;
775 vt
->tty_state
= ESnormal
;
777 vt
->tty_state
= ESosc2
;
779 DPRINTF(1, "got illegal OSC sequence");
783 if (ch
!= 0x9c && ch
!= 7) {
785 while (vt
->osc_textbuf
[i
]) i
++;
786 vt
->osc_textbuf
[i
++] = ch
;
787 vt
->osc_textbuf
[i
] = 0;
789 DPRINTF(1, "OSC:%c:%s", vt
->osc_cmd
, vt
->osc_textbuf
);
790 vt
->tty_state
= ESnormal
;
794 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
795 || (ch
>= 'a' && ch
<= 'f')) {
796 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
797 if (vt
->esc_count
== 7) {
798 // fixme: this does not work - please test
800 rfbColourMap *cmap =&vt->screen->colourMap;
802 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
803 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
804 cmap->data.bytes[i++] += vt->esc_buf[j++];
805 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
806 cmap->data.bytes[i++] += vt->esc_buf[j++];
807 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
808 cmap->data.bytes[i] += vt->esc_buf[j];
812 vt
->tty_state
= ESnormal
;
815 vt
->tty_state
= ESnormal
;
818 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
824 vt
->tty_state
= ESgetpars
;
827 vt
->tty_state
= ESidquery
;
831 if ((vt
->esc_ques
= (ch
== '?'))) {
835 if (ch
>= '0' && ch
<= '9') {
837 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
838 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
841 } else if (ch
== ';') {
845 if (vt
->esc_has_par
) {
848 vt
->tty_state
= ESgotpars
;
852 vt
->tty_state
= ESnormal
;
854 char *qes
= vt
->esc_ques
? "?" : "";
857 debug_print_escape_buffer(vt
, __func__
, "", qes
, ch
);
862 spiceterm_set_mode(vt
, 1);
865 spiceterm_set_mode(vt
, 0);
868 if (!vt
->esc_count
) {
869 vt
->esc_count
++; // default parameter 0
874 /* report cursor position */
875 /* TODO: send ESC[row;colR */
879 if (vt
->esc_buf
[0] == 0) {
882 vt
->cy
-= vt
->esc_buf
[0];
889 /* move cursor down */
890 if (vt
->esc_buf
[0] == 0) {
893 vt
->cy
+= vt
->esc_buf
[0];
894 if (vt
->cy
>= vt
->height
) {
895 vt
->cy
= vt
->height
- 1;
900 /* move cursor right */
901 if (vt
->esc_buf
[0] == 0) {
904 vt
->cx
+= vt
->esc_buf
[0];
905 if (vt
->cx
>= vt
->width
) {
906 vt
->cx
= vt
->width
- 1;
910 /* move cursor left */
911 if (vt
->esc_buf
[0] == 0) {
914 vt
->cx
-= vt
->esc_buf
[0];
921 /* move cursor to column */
922 spiceterm_gotoxy(vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
925 /* move cursor to row */
926 spiceterm_gotoxy(vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
930 /* move cursor to row, column */
931 spiceterm_gotoxy(vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
934 switch (vt
->esc_buf
[0]) {
936 /* clear to end of screen */
937 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
938 for (x
= 0; x
< vt
->width
; x
++) {
939 if (y
== vt
->cy
&& x
< vt
->cx
) {
942 spiceterm_clear_xy (vt
, x
, y
);
947 /* clear from beginning of screen */
948 for (y
= 0; y
<= vt
->cy
; y
++) {
949 for (x
= 0; x
< vt
->width
; x
++) {
950 if (y
== vt
->cy
&& x
> vt
->cx
) {
953 spiceterm_clear_xy(vt
, x
, y
);
958 /* clear entire screen */
959 spiceterm_clear_screen(vt
);
964 switch (vt
->esc_buf
[0]) {
967 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
968 spiceterm_clear_xy(vt
, x
, vt
->cy
);
972 /* clear from beginning of line */
973 for (x
= 0; x
<= vt
->cx
; x
++) {
974 spiceterm_clear_xy(vt
, x
, vt
->cy
);
978 /* clear entire line */
979 for(x
= 0; x
< vt
->width
; x
++) {
980 spiceterm_clear_xy(vt
, x
, vt
->cy
);
989 if (c
> vt
->height
- vt
->cy
)
990 c
= vt
->height
- vt
->cy
;
994 spiceterm_scroll_down(vt
, vt
->cy
, vt
->region_bottom
, c
);
1000 if (c
> vt
->height
- vt
->cy
)
1001 c
= vt
->height
- vt
->cy
;
1005 spiceterm_scroll_up(vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
1011 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, c
);
1017 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
1020 /* delete c character */
1023 if (c
> vt
->width
- vt
->cx
)
1024 c
= vt
->width
- vt
->cx
;
1028 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
1029 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1030 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
1031 TextCell
*src
= dst
+ c
;
1033 spiceterm_update_xy(vt
, x
+ c
, vt
->cy
);
1035 src
->attrib
= vt
->default_attrib
;
1036 spiceterm_update_xy(vt
, x
, vt
->cy
);
1040 /* save cursor position */
1041 spiceterm_save_cursor(vt
);
1044 /* restore cursor position */
1045 spiceterm_restore_cursor(vt
);
1048 /* erase c characters */
1052 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1054 for(i
= 0; i
< c
; i
++) {
1055 spiceterm_clear_xy(vt
, vt
->cx
+ i
, vt
->cy
);
1059 /* insert c character */
1061 if (c
> (vt
->width
- vt
->cx
)) {
1062 c
= vt
->width
- vt
->cx
;
1066 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1067 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1068 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1069 TextCell
*dst
= src
+ c
;
1071 spiceterm_update_xy (vt
, x
+ c
, vt
->cy
);
1073 src
->attrib
= vt
->cur_attrib
;
1074 spiceterm_update_xy(vt
, x
, vt
->cy
);
1080 if (!vt
->esc_buf
[0])
1082 if (!vt
->esc_buf
[1])
1083 vt
->esc_buf
[1] = vt
->height
;
1084 /* Minimum allowed region is 2 lines */
1085 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1086 vt
->esc_buf
[1] <= vt
->height
) {
1087 vt
->region_top
= vt
->esc_buf
[0] - 1;
1088 vt
->region_bottom
= vt
->esc_buf
[1];
1090 vt
->cy
= vt
->region_top
;
1091 DPRINTF(1, "set region %d %d", vt
->region_top
, vt
->region_bottom
);
1097 debug_print_escape_buffer(vt
, __func__
, " unhandled escape", qes
, ch
);
1103 case ESsetG0
: // Set G0
1104 vt
->tty_state
= ESnormal
;
1107 vt
->g0enc
= GRAF_MAP
;
1109 vt
->g0enc
= LAT1_MAP
;
1111 vt
->g0enc
= IBMPC_MAP
;
1113 vt
->g0enc
= USER_MAP
;
1115 if (vt
->charset
== 0)
1116 vt
->cur_enc
= vt
->g0enc
;
1119 case ESsetG1
: // Set G1
1120 vt
->tty_state
= ESnormal
;
1123 vt
->g1enc
= GRAF_MAP
;
1125 vt
->g1enc
= LAT1_MAP
;
1127 vt
->g1enc
= IBMPC_MAP
;
1129 vt
->g1enc
= USER_MAP
;
1131 if (vt
->charset
== 1)
1132 vt
->cur_enc
= vt
->g1enc
;
1135 case ESidquery
: // vt100 query id
1136 vt
->tty_state
= ESnormal
;
1139 DPRINTF(1, "ESC[>c Query term ID");
1140 spiceterm_respond_esc(vt
, TERMIDCODE
);
1144 vt
->tty_state
= ESnormal
;
1146 case '@': /* defined in ISO 2022 */
1149 case 'G': /* prelim official escape code */
1150 case '8': /* retained for compatibility */
1155 default: // ESnormal
1156 vt
->tty_state
= ESnormal
;
1161 case 7: /* alert aka. bell */
1163 //rfbSendBell(vt->screen);
1165 case 8: /* backspace */
1169 case 9: /* tabspace */
1170 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1172 spiceterm_put_lf(vt
);
1174 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1180 spiceterm_put_lf(vt
);
1182 case 13: /* carriage return */
1186 /* SI (shift in), select character set 1 */
1188 vt
->cur_enc
= vt
->g1enc
;
1189 /* fixme: display controls = 1 */
1192 /* SO (shift out), select character set 0 */
1194 vt
->cur_enc
= vt
->g0enc
;
1195 /* fixme: display controls = 0 */
1198 vt
->tty_state
= ESesc
;
1200 case 127: /* delete */
1203 case 128+27: /* csi */
1204 vt
->tty_state
= ESsquare
;
1207 if (vt
->cx
>= vt
->width
) {
1210 spiceterm_put_lf(vt
);
1213 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1214 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1215 c
->attrib
= vt
->cur_attrib
;
1217 spiceterm_update_xy(vt
, vt
->cx
, vt
->cy
);
1226 spiceterm_puts(spiceTerm
*vt
, const char *buf
, int len
)
1230 spiceterm_show_cursor(vt
, 0);
1233 unsigned char c
= *buf
;
1237 if (vt
->tty_state
!= ESnormal
) {
1238 // never translate escape sequence
1240 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1242 if(c
& 0x80) { // utf8 multi-byte sequence
1244 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1245 // inside UTF8 sequence
1246 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1248 if (vt
->utf_count
== 0) {
1254 // first char of a UTF8 sequence
1255 if ((c
& 0xe0) == 0xc0) {
1257 vt
->utf_char
= (c
& 0x1f);
1258 } else if ((c
& 0xf0) == 0xe0) {
1260 vt
->utf_char
= (c
& 0x0f);
1261 } else if ((c
& 0xf8) == 0xf0) {
1263 vt
->utf_char
= (c
& 0x07);
1264 } else if ((c
& 0xfc) == 0xf8) {
1266 vt
->utf_char
= (c
& 0x03);
1267 } else if ((c
& 0xfe) == 0xfc) {
1269 vt
->utf_char
= (c
& 0x01);
1282 // never translate controls
1283 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1284 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1290 spiceterm_putchar(vt
, tc
);
1293 spiceterm_show_cursor(vt
, 1);
1299 spiceterm_update_watch_mask(spiceTerm
*vt
, gboolean writable
)
1301 g_assert(vt
!= NULL
);
1303 int mask
= SPICE_WATCH_EVENT_READ
;
1306 mask
|= SPICE_WATCH_EVENT_WRITE
;
1309 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, mask
);
1313 mouse_report(spiceTerm
*vt
, int butt
, int mrx
, int mry
)
1317 sprintf(buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1320 spiceterm_respond_esc(vt
, buf
);
1322 spiceterm_update_watch_mask(vt
, TRUE
);
1326 spiceterm_respond_unichar2(spiceTerm
*vt
, gunichar2 uc
)
1330 gint len
= g_unichar_to_utf8(uc
, buf
);
1333 spiceterm_respond_data(vt
, len
, (uint8_t *)buf
);
1336 uint8_t buf
[1] = { (uint8_t)uc
};
1337 spiceterm_respond_data(vt
, 1, buf
);
1342 spiceterm_clear_selection(spiceTerm
*vt
)
1344 DPRINTF(1, "mark_active = %d", vt
->mark_active
);
1346 vt
->mark_active
= 0;
1347 if (vt
->selection
) free (vt
->selection
);
1348 vt
->selection
= NULL
;
1350 spiceterm_unselect_all(vt
);
1354 spiceterm_motion_event(spiceTerm
*vt
, uint32_t x
, uint32_t y
, uint32_t buttons
)
1356 DPRINTF(1, "mask=%08x x=%d y=%d", buttons
, x
,y
);
1358 static int last_mask
= 0;
1359 static int sel_start_pos
= 0;
1360 static int sel_end_pos
= 0;
1361 static int button2_released
= 1;
1368 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
1370 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
1372 if (vt
->report_mouse
&& buttons
!= last_mask
) {
1373 last_mask
= buttons
;
1375 mouse_report(vt
, 0, cx
, cy
);
1378 mouse_report (vt
, 1, cx
, cy
);
1381 mouse_report(vt
, 2, cx
, cy
);
1384 mouse_report(vt
, 3, cx
, cy
);
1390 if(button2_released
) {
1392 if (vdagent_owns_clipboard(vt
)) {
1393 if (vt
->selection
) {
1395 for(i
= 0; i
< vt
->selection_len
; i
++) {
1396 spiceterm_respond_unichar2(vt
, vt
->selection
[i
]);
1398 spiceterm_update_watch_mask(vt
, TRUE
);
1399 if (vt
->y_displ
!= vt
->y_base
) {
1400 vt
->y_displ
= vt
->y_base
;
1401 spiceterm_refresh(vt
);
1405 vdagent_request_clipboard(vt
);
1409 button2_released
= 0;
1411 button2_released
= 1;
1415 int pos
= cy
*vt
->width
+ cx
;
1417 // code borrowed from libvncserver (VNCconsole.c)
1419 if (!vt
->mark_active
) {
1421 spiceterm_unselect_all(vt
);
1423 vt
->mark_active
= 1;
1424 sel_start_pos
= sel_end_pos
= pos
;
1425 spiceterm_toggle_marked_cell(vt
, pos
);
1429 if (pos
!= sel_end_pos
) {
1430 if (pos
> sel_end_pos
) {
1431 cx
= sel_end_pos
; cy
=pos
;
1433 cx
=pos
; cy
=sel_end_pos
;
1436 if (cx
< sel_start_pos
) {
1437 if (cy
< sel_start_pos
) cy
--;
1443 spiceterm_toggle_marked_cell(vt
, cx
);
1451 } else if (vt
->mark_active
) {
1452 vt
->mark_active
= 0;
1454 if (sel_start_pos
> sel_end_pos
) {
1455 int tmp
= sel_start_pos
- 1;
1456 sel_start_pos
= sel_end_pos
;
1460 int len
= sel_end_pos
- sel_start_pos
+ 1;
1462 if (vt
->selection
) free (vt
->selection
);
1463 vt
->selection
= (gunichar2
*)malloc (len
*sizeof(gunichar2
));
1464 vt
->selection_len
= len
;
1466 for (i
= 0; i
< len
; i
++) {
1467 int pos
= sel_start_pos
+ i
;
1468 int x
= pos
% vt
->width
;
1469 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
1470 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
1471 vt
->selection
[i
] = c
->ch
;
1475 DPRINTF(1, "selection length = %d", vt
->selection_len
);
1477 vdagent_grab_clipboard(vt
);
1482 init_spiceterm(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
1486 g_assert(vt
!= NULL
);
1487 g_assert(vt
->screen
!= NULL
);
1489 vt
->width
= width
/ 8;
1490 vt
->height
= height
/ 16;
1492 vt
->total_height
= vt
->height
* 20;
1493 vt
->scroll_height
= 0;
1498 vt
->region_bottom
= vt
->height
;
1500 vt
->g0enc
= LAT1_MAP
;
1501 vt
->g1enc
= GRAF_MAP
;
1502 vt
->cur_enc
= vt
->g0enc
;
1505 /* default text attributes */
1506 vt
->default_attrib
.bold
= 0;
1507 vt
->default_attrib
.uline
= 0;
1508 vt
->default_attrib
.blink
= 0;
1509 vt
->default_attrib
.invers
= 0;
1510 vt
->default_attrib
.unvisible
= 0;
1511 vt
->default_attrib
.fgcol
= 7;
1512 vt
->default_attrib
.bgcol
= 0;
1514 vt
->cur_attrib
= vt
->default_attrib
;
1524 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1526 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1527 vt
->cells
[i
].ch
= ' ';
1528 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1532 g_free(vt
->altcells
);
1535 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1539 spiceterm_resize(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
1541 width
= (width
/8)*8;
1542 height
= (height
/16)*16;
1544 if (vt
->screen
->width
== width
&& vt
->screen
->height
== height
) {
1548 DPRINTF(0, "width=%u height=%u", width
, height
);
1550 spice_screen_resize(vt
->screen
, width
, height
);
1552 init_spiceterm(vt
, width
, height
);
1554 struct winsize dimensions
;
1555 dimensions
.ws_col
= vt
->width
;
1556 dimensions
.ws_row
= vt
->height
;
1558 ioctl(vt
->pty
, TIOCSWINSZ
, &dimensions
);
1562 master_error_callback(GIOChannel
*channel
, GIOCondition condition
,
1565 //spiceTerm *vt = (spiceTerm *)data;
1567 DPRINTF(1, "condition %d", condition
);
1575 master_watch(int master
, int event
, void *opaque
)
1577 spiceTerm
*vt
= (spiceTerm
*)opaque
;
1580 // fixme: if (!vt->mark_active) {
1582 if (event
== SPICE_WATCH_EVENT_READ
) {
1584 while ((c
= read(master
, buffer
, 1024)) == -1) {
1585 if (errno
!= EAGAIN
) break;
1588 perror("master pipe read error"); // fixme
1590 spiceterm_puts (vt
, buffer
, c
);
1592 if (vt
->ibuf_count
> 0) {
1593 DPRINTF(1, "write input %x %d", vt
->ibuf
[0], vt
->ibuf_count
);
1594 if ((c
= write (master
, vt
->ibuf
, vt
->ibuf_count
)) >= 0) {
1595 if (c
== vt
->ibuf_count
) {
1598 // not all data written
1599 memmove(vt
->ibuf
, vt
->ibuf
+ c
, vt
->ibuf_count
- c
);
1600 vt
->ibuf_count
-= c
;
1602 // nothing written -ignore and try later
1605 perror("master pipe write error");
1608 if (vt
->ibuf_count
== 0) {
1609 spiceterm_update_watch_mask(vt
, FALSE
);
1615 main (int argc
, char** argv
)
1618 char **cmdargv
= NULL
;
1619 char *command
= "/bin/bash"; // execute normal shell as default
1623 struct winsize dimensions
;
1625 g_thread_init(NULL
);
1627 for (i
= 1; i
< argc
; i
++) {
1628 if (!strcmp (argv
[i
], "-c")) {
1629 command
= argv
[i
+1];
1630 cmdargv
= &argv
[i
+1];
1637 if (0) print_usage(NULL
); // fixme:
1639 spiceTerm
*vt
= create_spiceterm (argc
, argv
, 744, 400, 10);
1641 setlocale(LC_ALL
, ""); // set from environment
1643 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
1645 // fixme: ist there a standard way to detect utf8 mode ?
1646 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
1650 dimensions
.ws_col
= vt
->width
;
1651 dimensions
.ws_row
= vt
->height
;
1653 setenv("TERM", TERM
, 1);
1655 DPRINTF(1, "execute %s", command
);
1657 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
1660 // install default signal handlers
1661 signal (SIGQUIT
, SIG_DFL
);
1662 signal (SIGTERM
, SIG_DFL
);
1663 signal (SIGINT
, SIG_DFL
);
1666 execvp (command
, cmdargv
);
1668 execlp (command
, command
, NULL
);
1670 perror ("Error: exec failed\n");
1671 exit (-1); // should not be reached
1672 } else if (pid
== -1) {
1673 perror ("Error: fork failed\n");
1679 /* watch for errors - we need to use glib directly because spice
1680 * does not have SPICE_WATCH_EVENT for this */
1681 GIOChannel
*channel
= g_io_channel_unix_new(master
);
1682 g_io_channel_set_flags(channel
, G_IO_FLAG_NONBLOCK
, NULL
);
1683 g_io_channel_set_encoding(channel
, NULL
, NULL
);
1684 g_io_add_watch(channel
, G_IO_ERR
|G_IO_HUP
, master_error_callback
, vt
);
1686 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
1687 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
1690 basic_event_loop_mainloop();
1694 waitpid(pid
, &status
, 0);