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 static void spiceterm_resize(spiceTerm
*vt
, uint32_t width
, uint32_t height
);
84 static void vdagent_grab_clipboard(spiceTerm
*vt
, uint8_t selection
);
85 static void vdagent_request_clipboard(spiceTerm
*vt
, uint8_t selection
);
88 print_usage(const char *msg
)
90 if (msg
) { fprintf(stderr
, "ERROR: %s\n", msg
); }
91 fprintf(stderr
, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
95 draw_char_at(spiceTerm
*vt
, int x
, int y
, gunichar2 ch
, TextAttributes attrib
)
97 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
101 spice_screen_draw_char(vt
->screen
, x
, y
, ch
, attrib
);
105 spiceterm_update_xy(spiceTerm
*vt
, int x
, int y
)
107 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
109 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
110 int y2
= y1
- vt
->y_displ
;
112 y2
+= vt
->total_height
;
114 if (y2
< vt
->height
) {
115 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
116 draw_char_at(vt
, x
, y2
, c
->ch
, c
->attrib
);
121 spiceterm_clear_xy(spiceTerm
*vt
, int x
, int y
)
123 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
125 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
126 int y2
= y1
- vt
->y_displ
;
128 y2
+= vt
->total_height
;
130 if (y2
< vt
->height
) {
131 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
133 c
->attrib
= vt
->default_attrib
;
134 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
135 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
137 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
142 spiceterm_toggle_marked_cell(spiceTerm
*vt
, int pos
)
144 int x
= (pos
%vt
->width
);
145 int y
= (pos
/vt
->width
);
147 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
149 int y1
= (vt
->y_displ
+ y
) % vt
->total_height
;
151 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
152 c
->attrib
.selected
= c
->attrib
.selected
? 0 : 1;
154 if (y
< vt
->height
) {
155 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
160 spiceterm_show_cursor(spiceTerm
*vt
, int show
)
163 if (x
>= vt
->width
) {
167 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
168 int y
= y1
- vt
->y_displ
;
170 y
+= vt
->total_height
;
173 if (y
< vt
->height
) {
175 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
178 TextAttributes attrib
= vt
->default_attrib
;
179 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
180 draw_char_at(vt
, x
, y
, c
->ch
, attrib
);
182 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
188 spiceterm_refresh(spiceTerm
*vt
)
193 for(y
= 0; y
< vt
->height
; y
++) {
194 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
195 for(x
= 0; x
< vt
->width
; x
++) {
196 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
199 if (++y1
== vt
->total_height
)
203 spiceterm_show_cursor(vt
, 1);
207 spiceterm_clear_screen(spiceTerm
*vt
)
211 for (y
= 0; y
<= vt
->height
; y
++) {
212 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
213 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
214 for (x
= 0; x
< vt
->width
; x
++) {
216 c
->attrib
= vt
->default_attrib
;
217 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
218 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
224 spice_screen_clear(vt
->screen
, 0, 0, vt
->screen
->primary_width
,
225 vt
->screen
->primary_height
);
229 spiceterm_unselect_all(spiceTerm
*vt
)
234 for(y
= 0; y
< vt
->total_height
; y
++) {
235 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
236 for(x
= 0; x
< vt
->width
; x
++) {
237 if (c
->attrib
.selected
) {
238 c
->attrib
.selected
= 0;
239 if (y
< vt
->height
) {
240 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
245 if (++y1
== vt
->total_height
)
251 spiceterm_scroll_down(spiceTerm
*vt
, int top
, int bottom
, int lines
)
253 if ((top
+ lines
) >= bottom
) {
254 lines
= bottom
- top
-1;
257 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
262 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
263 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
264 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
266 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
269 for (i
= 0; i
< lines
; i
++) {
271 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
272 for(j
= 0; j
< vt
->width
; j
++) {
273 c
->attrib
= vt
->default_attrib
;
284 spice_screen_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
285 spice_screen_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
289 spiceterm_scroll_up(spiceTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
291 if ((top
+ lines
) >= bottom
) {
292 lines
= bottom
- top
- 1;
295 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
302 int y1
= (top
+ lines
)*16;
305 spice_screen_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
306 spice_screen_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
315 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
316 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
317 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
319 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
322 for (i
= 1; i
<= lines
; i
++) {
324 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
325 for(j
= 0; j
< vt
->width
; j
++) {
326 c
->attrib
= vt
->default_attrib
;
334 spiceterm_virtual_scroll(spiceTerm
*vt
, int lines
)
336 if (vt
->altbuf
|| lines
== 0) return;
340 int i
= vt
->scroll_height
;
341 if (i
> vt
->total_height
- vt
->height
)
342 i
= vt
->total_height
- vt
->height
;
343 int y1
= vt
->y_base
- i
;
345 y1
+= vt
->total_height
;
346 for(i
= 0; i
< lines
; i
++) {
347 if (vt
->y_displ
== y1
) break;
348 if (--vt
->y_displ
< 0) {
349 vt
->y_displ
= vt
->total_height
- 1;
354 for(i
= 0; i
< lines
; i
++) {
355 if (vt
->y_displ
== vt
->y_base
) break;
356 if (++vt
->y_displ
== vt
->total_height
) {
362 spiceterm_refresh(vt
);
366 spiceterm_respond_esc(spiceTerm
*vt
, const char *esc
)
368 int len
= strlen(esc
);
371 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
372 vt
->ibuf
[vt
->ibuf_count
++] = 27;
373 for (i
= 0; i
< len
; i
++) {
374 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
380 spiceterm_put_lf(spiceTerm
*vt
)
382 if (vt
->cy
+ 1 == vt
->region_bottom
) {
384 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
385 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
389 if (vt
->y_displ
== vt
->y_base
) {
390 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
393 if (vt
->y_displ
== vt
->y_base
) {
394 if (++vt
->y_displ
== vt
->total_height
) {
399 if (++vt
->y_base
== vt
->total_height
) {
403 if (vt
->scroll_height
< vt
->total_height
) {
407 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
408 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
410 for (x
= 0; x
< vt
->width
; x
++) {
412 c
->attrib
= vt
->default_attrib
;
416 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
418 } else if (vt
->cy
< vt
->height
- 1) {
424 spiceterm_csi_m(spiceTerm
*vt
)
428 for (i
= 0; i
< vt
->esc_count
; i
++) {
429 switch (vt
->esc_buf
[i
]) {
430 case 0: /* reset all console attributes to default */
431 vt
->cur_attrib
= vt
->default_attrib
;
434 vt
->cur_attrib
.bold
= 1;
437 vt
->cur_attrib
.uline
= 1;
440 vt
->cur_attrib
.blink
= 1;
443 vt
->cur_attrib
.invers
= 1;
446 vt
->cur_attrib
.unvisible
= 1;
449 vt
->cur_enc
= LAT1_MAP
;
450 // fixme: dispaly controls = 0 ?
451 // fixme: toggle meta = 0 ?
454 vt
->cur_enc
= IBMPC_MAP
;
455 // fixme: dispaly controls = 1 ?
456 // fixme: toggle meta = 0 ?
459 vt
->cur_enc
= IBMPC_MAP
;
460 // fixme: dispaly controls = 1 ?
461 // fixme: toggle meta = 1 ?
464 vt
->cur_attrib
.bold
= 0;
467 vt
->cur_attrib
.uline
= 0;
470 vt
->cur_attrib
.blink
= 0;
473 vt
->cur_attrib
.invers
= 0;
476 vt
->cur_attrib
.unvisible
= 0;
486 /* set foreground color */
487 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
490 /* reset color to default, enable underline */
491 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
492 vt
->cur_attrib
.uline
= 1;
495 /* reset color to default, disable underline */
496 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
497 vt
->cur_attrib
.uline
= 0;
507 /* set background color */
508 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
511 /* reset background color */
512 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
515 fprintf(stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
522 spiceterm_save_cursor(spiceTerm
*vt
)
524 vt
->cx_saved
= vt
->cx
;
525 vt
->cy_saved
= vt
->cy
;
526 vt
->cur_attrib_saved
= vt
->cur_attrib
;
527 vt
->charset_saved
= vt
->charset
;
528 vt
->g0enc_saved
= vt
->g0enc
;
529 vt
->g1enc_saved
= vt
->g1enc
;
530 vt
->cur_enc_saved
= vt
->cur_enc
;
534 spiceterm_restore_cursor(spiceTerm
*vt
)
536 vt
->cx
= vt
->cx_saved
;
537 vt
->cy
= vt
->cy_saved
;
538 vt
->cur_attrib
= vt
->cur_attrib_saved
;
539 vt
->charset
= vt
->charset_saved
;
540 vt
->g0enc
= vt
->g0enc_saved
;
541 vt
->g1enc
= vt
->g1enc_saved
;
542 vt
->cur_enc
= vt
->cur_enc_saved
;
546 spiceterm_set_alternate_buffer(spiceTerm
*vt
, int on_off
)
550 vt
->y_displ
= vt
->y_base
;
554 if (vt
->altbuf
) return;
558 /* alternate buffer & cursor */
560 spiceterm_save_cursor(vt
);
561 /* save screen to altcels */
562 for (y
= 0; y
< vt
->height
; y
++) {
563 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
564 for (x
= 0; x
< vt
->width
; x
++) {
565 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
570 for (y
= 0; y
<= vt
->height
; y
++) {
571 for (x
= 0; x
< vt
->width
; x
++) {
572 // spiceterm_clear_xy(vt, x, y);
578 if (vt
->altbuf
== 0) return;
582 /* restore saved data */
583 for (y
= 0; y
< vt
->height
; y
++) {
584 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
585 for (x
= 0; x
< vt
->width
; x
++) {
586 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
590 spiceterm_restore_cursor(vt
);
593 spiceterm_refresh(vt
);
597 spiceterm_set_mode(spiceTerm
*vt
, int on_off
)
601 for (i
= 0; i
<= vt
->esc_count
; i
++) {
602 if (vt
->esc_ques
) { /* DEC private modes set/reset */
603 switch(vt
->esc_buf
[i
]) {
604 case 10: /* X11 mouse reporting on/off */
605 case 1000: /* SET_VT200_MOUSE */
606 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
607 vt
->report_mouse
= on_off
;
609 case 1049: /* start/end special app mode (smcup/rmcup) */
610 spiceterm_set_alternate_buffer (vt
, on_off
);
612 case 25: /* Cursor on/off */
613 case 9: /* X10 mouse reporting on/off */
614 case 6: /* Origin relative/absolute */
615 case 1: /* Cursor keys in appl mode*/
616 case 5: /* Inverted screen on/off */
617 case 7: /* Autowrap on/off */
618 case 8: /* Autorepeat on/off */
621 } else { /* ANSI modes set/reset */
622 //g_assert_not_reached();
624 /* fixme: implement me */
630 spiceterm_gotoxy(spiceTerm
*vt
, int x
, int y
)
632 /* verify all boundaries */
638 if (x
>= vt
->width
) {
648 if (y
>= vt
->height
) {
656 debug_print_escape_buffer(spiceTerm
*vt
, const char *func
, const char *prefix
,
657 const char *qes
, gunichar2 ch
)
660 if (vt
->esc_count
== 0) {
661 printf("%s:%s ESC[%s%c\n", func
, prefix
, qes
, ch
);
662 } else if (vt
->esc_count
== 1) {
663 printf("%s:%s ESC[%s%d%c\n", func
, prefix
, qes
, vt
->esc_buf
[0], ch
);
666 printf("%s:%s ESC[%s%d", func
, prefix
, qes
, vt
->esc_buf
[0]);
667 for (i
= 1; i
< vt
->esc_count
; i
++) {
668 printf(";%d", vt
->esc_buf
[i
]);
675 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
676 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
677 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
680 spiceterm_putchar(spiceTerm
*vt
, gunichar2 ch
)
684 if (debug
&& !vt
->tty_state
) {
685 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
686 vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
689 switch(vt
->tty_state
) {
691 vt
->tty_state
= ESnormal
;
694 vt
->tty_state
= ESsquare
;
697 vt
->tty_state
= ESnonstd
;
700 vt
->tty_state
= ESpercent
;
703 spiceterm_save_cursor(vt
);
706 spiceterm_restore_cursor(vt
);
709 vt
->tty_state
= ESsetG0
; // SET G0
712 vt
->tty_state
= ESsetG1
; // SET G1
716 if (vt
->cy
== vt
->region_top
)
717 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, 1);
718 else if (vt
->cy
> 0) {
723 /* numeric keypad - ignored */
726 /* appl. keypad - ignored */
729 DPRINTF(1, "got unhandled ESC%c %d", ch
, ch
);
733 case ESnonstd
: /* Operating System Controls */
734 vt
->tty_state
= ESnormal
;
737 case 'P': /* palette escape sequence */
738 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
743 vt
->tty_state
= ESpalette
;
745 case 'R': /* reset palette */
746 // fixme: reset_palette(vc);
753 vt
->osc_textbuf
[0] = 0;
754 vt
->tty_state
= ESosc1
;
757 DPRINTF(1, "got unhandled OSC %c", ch
);
758 vt
->tty_state
= ESnormal
;
763 vt
->tty_state
= ESnormal
;
765 vt
->tty_state
= ESosc2
;
767 DPRINTF(1, "got illegal OSC sequence");
771 if (ch
!= 0x9c && ch
!= 7) {
773 while (vt
->osc_textbuf
[i
]) i
++;
774 vt
->osc_textbuf
[i
++] = ch
;
775 vt
->osc_textbuf
[i
] = 0;
777 DPRINTF(1, "OSC:%c:%s", vt
->osc_cmd
, vt
->osc_textbuf
);
778 vt
->tty_state
= ESnormal
;
782 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
783 || (ch
>= 'a' && ch
<= 'f')) {
784 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
785 if (vt
->esc_count
== 7) {
786 // fixme: this does not work - please test
788 rfbColourMap *cmap =&vt->screen->colourMap;
790 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
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++];
795 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
796 cmap->data.bytes[i] += vt->esc_buf[j];
800 vt
->tty_state
= ESnormal
;
803 vt
->tty_state
= ESnormal
;
806 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
812 vt
->tty_state
= ESgetpars
;
815 vt
->tty_state
= ESidquery
;
819 if ((vt
->esc_ques
= (ch
== '?'))) {
823 if (ch
>= '0' && ch
<= '9') {
825 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
826 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
829 } else if (ch
== ';') {
833 if (vt
->esc_has_par
) {
836 vt
->tty_state
= ESgotpars
;
840 vt
->tty_state
= ESnormal
;
842 char *qes
= vt
->esc_ques
? "?" : "";
845 debug_print_escape_buffer(vt
, __func__
, "", qes
, ch
);
850 spiceterm_set_mode(vt
, 1);
853 spiceterm_set_mode(vt
, 0);
856 if (!vt
->esc_count
) {
857 vt
->esc_count
++; // default parameter 0
862 /* report cursor position */
863 /* TODO: send ESC[row;colR */
867 if (vt
->esc_buf
[0] == 0) {
870 vt
->cy
-= vt
->esc_buf
[0];
877 /* move cursor down */
878 if (vt
->esc_buf
[0] == 0) {
881 vt
->cy
+= vt
->esc_buf
[0];
882 if (vt
->cy
>= vt
->height
) {
883 vt
->cy
= vt
->height
- 1;
888 /* move cursor right */
889 if (vt
->esc_buf
[0] == 0) {
892 vt
->cx
+= vt
->esc_buf
[0];
893 if (vt
->cx
>= vt
->width
) {
894 vt
->cx
= vt
->width
- 1;
898 /* move cursor left */
899 if (vt
->esc_buf
[0] == 0) {
902 vt
->cx
-= vt
->esc_buf
[0];
909 /* move cursor to column */
910 spiceterm_gotoxy(vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
913 /* move cursor to row */
914 spiceterm_gotoxy(vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
918 /* move cursor to row, column */
919 spiceterm_gotoxy(vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
922 switch (vt
->esc_buf
[0]) {
924 /* clear to end of screen */
925 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
926 for (x
= 0; x
< vt
->width
; x
++) {
927 if (y
== vt
->cy
&& x
< vt
->cx
) {
930 spiceterm_clear_xy (vt
, x
, y
);
935 /* clear from beginning of screen */
936 for (y
= 0; y
<= vt
->cy
; y
++) {
937 for (x
= 0; x
< vt
->width
; x
++) {
938 if (y
== vt
->cy
&& x
> vt
->cx
) {
941 spiceterm_clear_xy(vt
, x
, y
);
946 /* clear entire screen */
947 spiceterm_clear_screen(vt
);
952 switch (vt
->esc_buf
[0]) {
955 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
956 spiceterm_clear_xy(vt
, x
, vt
->cy
);
960 /* clear from beginning of line */
961 for (x
= 0; x
<= vt
->cx
; x
++) {
962 spiceterm_clear_xy(vt
, x
, vt
->cy
);
966 /* clear entire line */
967 for(x
= 0; x
< vt
->width
; x
++) {
968 spiceterm_clear_xy(vt
, x
, vt
->cy
);
977 if (c
> vt
->height
- vt
->cy
)
978 c
= vt
->height
- vt
->cy
;
982 spiceterm_scroll_down(vt
, vt
->cy
, vt
->region_bottom
, c
);
988 if (c
> vt
->height
- vt
->cy
)
989 c
= vt
->height
- vt
->cy
;
993 spiceterm_scroll_up(vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
999 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, c
);
1005 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
1008 /* delete c character */
1011 if (c
> vt
->width
- vt
->cx
)
1012 c
= vt
->width
- vt
->cx
;
1016 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
1017 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1018 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
1019 TextCell
*src
= dst
+ c
;
1021 spiceterm_update_xy(vt
, x
+ c
, vt
->cy
);
1023 src
->attrib
= vt
->default_attrib
;
1024 spiceterm_update_xy(vt
, x
, vt
->cy
);
1028 /* save cursor position */
1029 spiceterm_save_cursor(vt
);
1032 /* restore cursor position */
1033 spiceterm_restore_cursor(vt
);
1036 /* erase c characters */
1040 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1042 for(i
= 0; i
< c
; i
++) {
1043 spiceterm_clear_xy(vt
, vt
->cx
+ i
, vt
->cy
);
1047 /* insert c character */
1049 if (c
> (vt
->width
- vt
->cx
)) {
1050 c
= vt
->width
- vt
->cx
;
1054 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1055 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1056 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1057 TextCell
*dst
= src
+ c
;
1059 spiceterm_update_xy (vt
, x
+ c
, vt
->cy
);
1061 src
->attrib
= vt
->cur_attrib
;
1062 spiceterm_update_xy(vt
, x
, vt
->cy
);
1068 if (!vt
->esc_buf
[0])
1070 if (!vt
->esc_buf
[1])
1071 vt
->esc_buf
[1] = vt
->height
;
1072 /* Minimum allowed region is 2 lines */
1073 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1074 vt
->esc_buf
[1] <= vt
->height
) {
1075 vt
->region_top
= vt
->esc_buf
[0] - 1;
1076 vt
->region_bottom
= vt
->esc_buf
[1];
1078 vt
->cy
= vt
->region_top
;
1079 DPRINTF(1, "set region %d %d", vt
->region_top
, vt
->region_bottom
);
1085 debug_print_escape_buffer(vt
, __func__
, " unhandled escape", qes
, ch
);
1091 case ESsetG0
: // Set G0
1092 vt
->tty_state
= ESnormal
;
1095 vt
->g0enc
= GRAF_MAP
;
1097 vt
->g0enc
= LAT1_MAP
;
1099 vt
->g0enc
= IBMPC_MAP
;
1101 vt
->g0enc
= USER_MAP
;
1103 if (vt
->charset
== 0)
1104 vt
->cur_enc
= vt
->g0enc
;
1107 case ESsetG1
: // Set G1
1108 vt
->tty_state
= ESnormal
;
1111 vt
->g1enc
= GRAF_MAP
;
1113 vt
->g1enc
= LAT1_MAP
;
1115 vt
->g1enc
= IBMPC_MAP
;
1117 vt
->g1enc
= USER_MAP
;
1119 if (vt
->charset
== 1)
1120 vt
->cur_enc
= vt
->g1enc
;
1123 case ESidquery
: // vt100 query id
1124 vt
->tty_state
= ESnormal
;
1127 DPRINTF(1, "ESC[>c Query term ID");
1128 spiceterm_respond_esc(vt
, TERMIDCODE
);
1132 vt
->tty_state
= ESnormal
;
1134 case '@': /* defined in ISO 2022 */
1137 case 'G': /* prelim official escape code */
1138 case '8': /* retained for compatibility */
1143 default: // ESnormal
1144 vt
->tty_state
= ESnormal
;
1149 case 7: /* alert aka. bell */
1151 //rfbSendBell(vt->screen);
1153 case 8: /* backspace */
1157 case 9: /* tabspace */
1158 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1160 spiceterm_put_lf(vt
);
1162 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1168 spiceterm_put_lf(vt
);
1170 case 13: /* carriage return */
1174 /* SI (shift in), select character set 1 */
1176 vt
->cur_enc
= vt
->g1enc
;
1177 /* fixme: display controls = 1 */
1180 /* SO (shift out), select character set 0 */
1182 vt
->cur_enc
= vt
->g0enc
;
1183 /* fixme: display controls = 0 */
1186 vt
->tty_state
= ESesc
;
1188 case 127: /* delete */
1191 case 128+27: /* csi */
1192 vt
->tty_state
= ESsquare
;
1195 if (vt
->cx
>= vt
->width
) {
1198 spiceterm_put_lf(vt
);
1201 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1202 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1203 c
->attrib
= vt
->cur_attrib
;
1205 spiceterm_update_xy(vt
, vt
->cx
, vt
->cy
);
1214 spiceterm_puts(spiceTerm
*vt
, const char *buf
, int len
)
1218 spiceterm_show_cursor(vt
, 0);
1221 unsigned char c
= *buf
;
1225 if (vt
->tty_state
!= ESnormal
) {
1226 // never translate escape sequence
1228 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1230 if(c
& 0x80) { // utf8 multi-byte sequence
1232 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1233 // inside UTF8 sequence
1234 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1236 if (vt
->utf_count
== 0) {
1242 // first char of a UTF8 sequence
1243 if ((c
& 0xe0) == 0xc0) {
1245 vt
->utf_char
= (c
& 0x1f);
1246 } else if ((c
& 0xf0) == 0xe0) {
1248 vt
->utf_char
= (c
& 0x0f);
1249 } else if ((c
& 0xf8) == 0xf0) {
1251 vt
->utf_char
= (c
& 0x07);
1252 } else if ((c
& 0xfc) == 0xf8) {
1254 vt
->utf_char
= (c
& 0x03);
1255 } else if ((c
& 0xfe) == 0xfc) {
1257 vt
->utf_char
= (c
& 0x01);
1270 // never translate controls
1271 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1272 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1278 spiceterm_putchar(vt
, tc
);
1281 spiceterm_show_cursor(vt
, 1);
1288 spiceterm_set_xcut_text(char* str, int len, struct _rfbClientRec* cl)
1290 spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
1292 // seems str is Latin-1 encoded
1293 if (vt->selection) free (vt->selection);
1294 vt->selection = (gunichar2 *)malloc (len*sizeof (gunichar2));
1296 for (i = 0; i < len; i++) {
1297 vt->selection[i] = str[i] & 0xff;
1299 vt->selection_len = len;
1304 spiceterm_update_watch_mask(spiceTerm
*vt
, gboolean writable
)
1306 g_assert(vt
!= NULL
);
1308 int mask
= SPICE_WATCH_EVENT_READ
;
1311 mask
|= SPICE_WATCH_EVENT_WRITE
;
1314 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, mask
);
1318 mouse_report(spiceTerm
*vt
, int butt
, int mrx
, int mry
)
1322 sprintf(buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1325 spiceterm_respond_esc(vt
, buf
);
1327 spiceterm_update_watch_mask(vt
, TRUE
);
1331 spiceterm_respond_unichar2(spiceTerm
*vt
, gunichar2 uc
)
1335 gint len
= g_unichar_to_utf8(uc
, buf
);
1338 if ((vt
->ibuf_count
+ len
) < IBUFSIZE
) {
1340 for (i
= 0; i
< len
; i
++) {
1341 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1344 fprintf(stderr
, "warning: input buffer overflow\n");
1348 if ((vt
->ibuf_count
+ 1) < IBUFSIZE
) {
1349 vt
->ibuf
[vt
->ibuf_count
++] = (char)uc
;
1351 fprintf(stderr
, "warning: input buffer overflow\n");
1357 my_kbd_get_leds(SpiceKbdInstance
*sin
)
1362 #define KBD_MOD_CONTROL_L_FLAG (1<<0)
1363 #define KBD_MOD_CONTROL_R_FLAG (1<<1)
1364 #define KBD_MOD_SHIFT_L_FLAG (1<<2)
1365 #define KBD_MOD_SHIFT_R_FLAG (1<<3)
1367 static int kbd_flags
= 0;
1369 my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1371 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, keyboard_sin
);
1373 char *esc
= NULL
; // used to send special keys
1375 static int e0_mode
= 0;
1377 DPRINTF(1, "enter frag=%02x flags=%08x", frag
, kbd_flags
);
1382 case 0x1d: // press Control_R
1383 kbd_flags
|= KBD_MOD_CONTROL_R_FLAG
;
1385 case 0x9d: // release Control_R
1386 kbd_flags
&= ~KBD_MOD_CONTROL_R_FLAG
;
1388 case 0x47: // press Home
1391 case 0x4f: // press END
1394 case 0x48: // press UP
1397 case 0x50: // press DOWN
1400 case 0x4b: // press LEFT
1403 case 0x4d: // press RIGHT
1406 case 0x52: // press INSERT
1409 case 0x49: // press PAGE_UP
1410 if (kbd_flags
& (KBD_MOD_SHIFT_L_FLAG
|KBD_MOD_SHIFT_R_FLAG
)) {
1411 spiceterm_virtual_scroll (vt
, -vt
->height
/2);
1414 case 0x51: // press PAGE_DOWN
1415 if (kbd_flags
& (KBD_MOD_SHIFT_L_FLAG
|KBD_MOD_SHIFT_R_FLAG
)) {
1416 spiceterm_virtual_scroll (vt
, vt
->height
/2);
1425 case 0x1d: // press Control_L
1426 kbd_flags
|= KBD_MOD_CONTROL_L_FLAG
;
1428 case 0x9d: // release Control_L
1429 kbd_flags
&= ~KBD_MOD_CONTROL_L_FLAG
;
1431 case 0x2a: // press Shift_L
1432 kbd_flags
|= KBD_MOD_SHIFT_L_FLAG
;
1434 case 0xaa: // release Shift_L
1435 kbd_flags
&= ~KBD_MOD_SHIFT_L_FLAG
;
1437 case 0x36: // press Shift_R
1438 kbd_flags
|= KBD_MOD_SHIFT_R_FLAG
;
1440 case 0xb6: // release Shift_R
1441 kbd_flags
&= ~KBD_MOD_SHIFT_R_FLAG
;
1443 case 0x52: // press KP_INSERT
1446 case 0x53: // press KP_Delete
1449 case 0x47: // press KP_Home
1452 case 0x4f: // press KP_END
1455 case 0x48: // press KP_UP
1458 case 0x50: // press KP_DOWN
1461 case 0x4b: // press KP_LEFT
1464 case 0x4d: // press KP_RIGHT
1467 case 0x3b: // press F1
1470 case 0x3c: // press F2
1473 case 0x3d: // press F3
1476 case 0x3e: // press F4
1479 case 0x3f: // press F5
1482 case 0x40: // press F6
1485 case 0x41: // press F7
1488 case 0x42: // press F8
1491 case 0x43: // press F9
1494 case 0x44: // press F10
1497 case 0x57: // press F11
1500 case 0x58: // press F12
1507 DPRINTF(1, "escape=%s", esc
);
1508 spiceterm_respond_esc(vt
, esc
);
1510 if (vt
->y_displ
!= vt
->y_base
) {
1511 vt
->y_displ
= vt
->y_base
;
1512 spiceterm_refresh(vt
);
1515 spiceterm_update_watch_mask(vt
, TRUE
);
1518 DPRINTF(1, "leave frag=%02x flags=%08x", frag
, kbd_flags
);
1523 my_kbd_push_utf8(SpiceKbdInstance
*sin
, uint32_t size
, uint8_t *data
)
1525 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, keyboard_sin
);
1527 DPRINTF(1, " size=%d data[0]=%02x", size
, data
[0]);
1529 if (vt
->ibuf_count
+ size
>= IBUFSIZE
) {
1530 fprintf(stderr
, "input buffer oferflow\n");
1534 if (kbd_flags
& (KBD_MOD_CONTROL_L_FLAG
|KBD_MOD_CONTROL_R_FLAG
)) {
1535 if (size
!= 1) return;
1536 if (data
[0] >= 'a' && data
[0] <= 'z') {
1537 vt
->ibuf
[vt
->ibuf_count
++] = data
[0] - 'a' + 1;
1538 } else if (data
[0] >= 'A' && data
[0] <= 'Z') {
1539 vt
->ibuf
[vt
->ibuf_count
++] = data
[0] - 'A' + 1;
1542 if (size
== 1 && data
[0] == 0x7f) {
1543 /* use an escape sequence for DELETE, else it behaves
1546 spiceterm_respond_esc(vt
, "[3~");
1549 for (i
= 0; i
< size
; i
++) {
1550 vt
->ibuf
[vt
->ibuf_count
++] = data
[i
];
1555 if (vt
->y_displ
!= vt
->y_base
) {
1556 vt
->y_displ
= vt
->y_base
;
1557 spiceterm_refresh(vt
);
1560 spiceterm_update_watch_mask(vt
, TRUE
);
1565 static SpiceKbdInterface my_keyboard_sif
= {
1566 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1567 .base
.description
= "spiceterm keyboard device",
1568 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1569 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1570 .push_scan_freg
= my_kbd_push_key
,
1571 .get_leds
= my_kbd_get_leds
,
1572 .push_utf8
= my_kbd_push_utf8
,
1576 /* vdagent interface - to get mouse/clipboarde support */
1578 #define VDAGENT_WBUF_SIZE (1024*50)
1579 static unsigned char vdagent_write_buffer
[VDAGENT_WBUF_SIZE
];
1580 static int vdagent_write_buffer_pos
= 0;
1581 static int agent_owns_clipboard
[256] = { 0, };
1584 spiceterm_clear_selection(spiceTerm
*vt
)
1586 DPRINTF(1, "mark_active = %d", vt
->mark_active
);
1588 vt
->mark_active
= 0;
1589 if (vt
->selection
) free (vt
->selection
);
1590 vt
->selection
= NULL
;
1592 spiceterm_unselect_all(vt
);
1596 spiceterm_motion_event(spiceTerm
*vt
, uint32_t x
, uint32_t y
, uint32_t buttons
)
1598 DPRINTF(1, "mask=%08x x=%d y=%d", buttons
, x
,y
);
1600 static int last_mask
= 0;
1601 static int sel_start_pos
= 0;
1602 static int sel_end_pos
= 0;
1603 static int button2_released
= 1;
1610 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
1612 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
1614 if (vt
->report_mouse
&& buttons
!= last_mask
) {
1615 last_mask
= buttons
;
1617 mouse_report(vt
, 0, cx
, cy
);
1620 mouse_report (vt
, 1, cx
, cy
);
1623 mouse_report(vt
, 2, cx
, cy
);
1626 mouse_report(vt
, 3, cx
, cy
);
1632 if(button2_released
) {
1634 if (agent_owns_clipboard
[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
]) {
1635 if (vt
->selection
) {
1637 for(i
= 0; i
< vt
->selection_len
; i
++) {
1638 spiceterm_respond_unichar2(vt
, vt
->selection
[i
]);
1640 spiceterm_update_watch_mask(vt
, TRUE
);
1641 if (vt
->y_displ
!= vt
->y_base
) {
1642 vt
->y_displ
= vt
->y_base
;
1643 spiceterm_refresh(vt
);
1647 vdagent_request_clipboard(vt
, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
);
1651 button2_released
= 0;
1653 button2_released
= 1;
1657 int pos
= cy
*vt
->width
+ cx
;
1659 // code borrowed from libvncserver (VNCconsole.c)
1661 if (!vt
->mark_active
) {
1663 spiceterm_unselect_all(vt
);
1665 vt
->mark_active
= 1;
1666 sel_start_pos
= sel_end_pos
= pos
;
1667 spiceterm_toggle_marked_cell(vt
, pos
);
1671 if (pos
!= sel_end_pos
) {
1672 if (pos
> sel_end_pos
) {
1673 cx
= sel_end_pos
; cy
=pos
;
1675 cx
=pos
; cy
=sel_end_pos
;
1678 if (cx
< sel_start_pos
) {
1679 if (cy
< sel_start_pos
) cy
--;
1685 spiceterm_toggle_marked_cell(vt
, cx
);
1693 } else if (vt
->mark_active
) {
1694 vt
->mark_active
= 0;
1696 if (sel_start_pos
> sel_end_pos
) {
1697 int tmp
= sel_start_pos
- 1;
1698 sel_start_pos
= sel_end_pos
;
1702 int len
= sel_end_pos
- sel_start_pos
+ 1;
1704 if (vt
->selection
) free (vt
->selection
);
1705 vt
->selection
= (gunichar2
*)malloc (len
*sizeof(gunichar2
));
1706 vt
->selection_len
= len
;
1708 for (i
= 0; i
< len
; i
++) {
1709 int pos
= sel_start_pos
+ i
;
1710 int x
= pos
% vt
->width
;
1711 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
1712 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
1713 vt
->selection
[i
] = c
->ch
;
1717 DPRINTF(1, "selection length = %d", vt
->selection_len
);
1719 vdagent_grab_clipboard(vt
, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
);
1724 vdagent_reply(spiceTerm
*vt
, uint32_t type
, uint32_t error
)
1728 size
= sizeof(VDAgentReply
);
1730 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1731 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1733 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1734 vdagent_write_buffer_pos
+= msg_size
;
1736 memset(buf
, 0, msg_size
);
1738 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1739 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1740 VDAgentReply
*reply
= (VDAgentReply
*)&msg
[1];
1742 reply
->error
= error
;
1744 hdr
->port
= VDP_CLIENT_PORT
;
1745 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1747 msg
->protocol
= VD_AGENT_PROTOCOL
;
1748 msg
->type
= VD_AGENT_REPLY
;
1752 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1756 dump_message(unsigned char *buf
, int size
)
1760 for (i
= 0; i
< size
; i
++) {
1761 printf("%d %02X\n", i
, buf
[i
]);
1767 static void vdagent_send_capabilities(spiceTerm
*vt
, uint32_t request
)
1769 VDAgentAnnounceCapabilities
*caps
;
1772 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
1773 caps
= calloc(1, size
);
1774 g_assert(caps
!= NULL
);
1776 caps
->request
= request
;
1777 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
1778 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
1779 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
1780 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
1781 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_SELECTION
);
1782 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG
);
1783 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_GUEST_LINEEND_LF
);
1785 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1786 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1788 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1789 vdagent_write_buffer_pos
+= msg_size
;
1791 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1792 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1794 hdr
->port
= VDP_CLIENT_PORT
;
1795 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1796 msg
->protocol
= VD_AGENT_PROTOCOL
;
1797 msg
->type
= VD_AGENT_ANNOUNCE_CAPABILITIES
;
1801 memcpy(buf
+ sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
), (uint8_t *)caps
, size
);
1803 if (0) dump_message(buf
, msg_size
);
1805 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1810 static void vdagent_grab_clipboard(spiceTerm
*vt
, uint8_t selection
)
1814 agent_owns_clipboard
[selection
] = 1;
1818 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1819 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1821 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1822 vdagent_write_buffer_pos
+= msg_size
;
1824 memset(buf
, 0, msg_size
);
1826 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1827 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1828 uint8_t *grab
= (uint8_t *)&msg
[1];
1829 *((uint8_t *)grab
) = selection
;
1830 *((uint32_t *)(grab
+ 4)) = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1832 hdr
->port
= VDP_CLIENT_PORT
;
1833 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1835 msg
->protocol
= VD_AGENT_PROTOCOL
;
1836 msg
->type
= VD_AGENT_CLIPBOARD_GRAB
;
1840 if (0) dump_message(buf
, msg_size
);
1842 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1845 static void vdagent_request_clipboard(spiceTerm
*vt
, uint8_t selection
)
1849 size
= 4 + sizeof(VDAgentClipboardRequest
);
1851 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1852 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1854 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1855 vdagent_write_buffer_pos
+= msg_size
;
1857 memset(buf
, 0, msg_size
);
1859 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1860 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1861 uint8_t *data
= (uint8_t *)&msg
[1];
1862 *((uint32_t *)data
) = 0;
1863 data
[0] = selection
;
1864 ((uint32_t *)data
)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1866 hdr
->port
= VDP_CLIENT_PORT
;
1867 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1869 msg
->protocol
= VD_AGENT_PROTOCOL
;
1870 msg
->type
= VD_AGENT_CLIPBOARD_REQUEST
;
1874 if (0) dump_message(buf
, msg_size
);
1876 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1879 static void vdagent_send_clipboard(spiceTerm
*vt
, uint8_t selection
)
1883 if (selection
!= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
1884 fprintf(stderr
, "clipboard select %d is not supported\n", selection
);
1891 sel_data
= g_utf16_to_utf8(vt
->selection
, vt
->selection_len
, NULL
, &sel_len
, NULL
);
1893 sel_len
= vt
->selection_len
;
1894 sel_data
= g_malloc(sel_len
);
1896 for (i
= 0; i
< sel_len
; i
++) { sel_data
[i
] = (char)vt
->selection
[i
]; }
1897 sel_data
[sel_len
] = 0;
1902 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1903 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1905 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1906 vdagent_write_buffer_pos
+= msg_size
;
1908 memset(buf
, 0, msg_size
);
1910 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1911 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1912 uint8_t *data
= (uint8_t *)&msg
[1];
1913 *((uint8_t *)data
) = selection
;
1915 *((uint32_t *)data
) = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1918 memcpy(data
, sel_data
, sel_len
);
1921 hdr
->port
= VDP_CLIENT_PORT
;
1922 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1924 msg
->protocol
= VD_AGENT_PROTOCOL
;
1925 msg
->type
= VD_AGENT_CLIPBOARD
;
1929 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1933 vmc_write(SpiceCharDeviceInstance
*sin
, const uint8_t *buf
, int len
)
1935 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, vdagent_sin
);
1937 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1938 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1940 //g_assert(hdr->port == VDP_SERVER_PORT);
1941 g_assert(msg
->protocol
== VD_AGENT_PROTOCOL
);
1943 DPRINTF(1, "%d %d %d %d", len
, hdr
->port
, msg
->protocol
, msg
->type
);
1945 switch (msg
->type
) {
1946 case VD_AGENT_MOUSE_STATE
: {
1947 VDAgentMouseState
*info
= (VDAgentMouseState
*)&msg
[1];
1948 spiceterm_motion_event(vt
, info
->x
, info
->y
, info
->buttons
);
1951 case VD_AGENT_ANNOUNCE_CAPABILITIES
: {
1952 VDAgentAnnounceCapabilities
*caps
= (VDAgentAnnounceCapabilities
*)&msg
[1];
1953 DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps
->request
);
1956 int caps_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(hdr
->size
);
1957 for (i
= 0; i
< VD_AGENT_END_CAP
; i
++) {
1958 DPRINTF(1, "CAPABILITIES %d %d", i
, VD_AGENT_HAS_CAPABILITY(caps
->caps
, caps_size
, i
));
1961 vdagent_send_capabilities(vt
, 0);
1964 case VD_AGENT_CLIPBOARD_GRAB
: {
1965 VDAgentClipboardGrab
*grab
= (VDAgentClipboardGrab
*)&msg
[1];
1966 uint8_t selection
= *((uint8_t *)grab
);
1967 DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection
);
1968 agent_owns_clipboard
[selection
] = 0;
1969 spiceterm_clear_selection(vt
);
1972 case VD_AGENT_CLIPBOARD_REQUEST
: {
1973 uint8_t *req
= (uint8_t *)&msg
[1];
1974 uint8_t selection
= *((uint8_t *)req
);
1975 uint32_t type
= *((uint32_t *)(req
+ 4));
1977 DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection
, type
);
1979 vdagent_send_clipboard(vt
, selection
);
1983 case VD_AGENT_CLIPBOARD
: {
1984 uint8_t *data
= (uint8_t *)&msg
[1];
1985 uint8_t selection
= data
[0];
1986 uint32_t type
= *(uint32_t *)(data
+ 4);
1987 int size
= msg
->size
- 8;
1988 DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection
, type
, size
);
1990 if (type
== VD_AGENT_CLIPBOARD_UTF8_TEXT
) {
1992 for (i
= 0; i
< size
; i
++) {
1993 if ((vt
->ibuf_count
+ 1) < IBUFSIZE
) {
1994 vt
->ibuf
[vt
->ibuf_count
++] = *(char *)(data
+ 8 + i
);
1996 fprintf(stderr
, "warning: input buffer overflow\n");
1999 spiceterm_update_watch_mask(vt
, TRUE
);
2003 case VD_AGENT_CLIPBOARD_RELEASE
: {
2004 uint8_t *data
= (uint8_t *)&msg
[1];
2005 uint8_t selection
= data
[0];
2007 DPRINTF(1, "VD_AGENT_CLIPBOARD_RELEASE %d", selection
);
2011 case VD_AGENT_MONITORS_CONFIG
: {
2012 VDAgentMonitorsConfig
*list
= (VDAgentMonitorsConfig
*)&msg
[1];
2013 g_assert(list
->num_of_monitors
> 0);
2014 DPRINTF(1, "VD_AGENT_MONITORS_CONFIG %d %d %d", list
->num_of_monitors
,
2015 list
->monitors
[0].width
, list
->monitors
[0].height
);
2017 spiceterm_resize(vt
, list
->monitors
[0].width
, list
->monitors
[0].height
);
2019 vdagent_reply(vt
, VD_AGENT_MONITORS_CONFIG
, VD_AGENT_SUCCESS
);
2023 DPRINTF(1, "got uknown vdagent message type %d\n", msg
->type
);
2030 vmc_read(SpiceCharDeviceInstance
*sin
, uint8_t *buf
, int len
)
2032 DPRINTF(1, "%d %d", len
, vdagent_write_buffer_pos
);
2035 if (!vdagent_write_buffer_pos
) {
2039 int size
= (len
>= vdagent_write_buffer_pos
) ? vdagent_write_buffer_pos
: len
;
2040 memcpy(buf
, vdagent_write_buffer
, size
);
2041 if (size
< vdagent_write_buffer_pos
) {
2042 memmove(vdagent_write_buffer
, vdagent_write_buffer
+ size
,
2043 vdagent_write_buffer_pos
- size
);
2045 vdagent_write_buffer_pos
-= size
;
2047 DPRINTF(1, "RET %d %d", size
, vdagent_write_buffer_pos
);
2052 vmc_state(SpiceCharDeviceInstance
*sin
, int connected
)
2057 static SpiceCharDeviceInterface my_vdagent_sif
= {
2058 .base
.type
= SPICE_INTERFACE_CHAR_DEVICE
,
2059 .base
.description
= "spice virtual channel char device",
2060 .base
.major_version
= SPICE_INTERFACE_CHAR_DEVICE_MAJOR
,
2061 .base
.minor_version
= SPICE_INTERFACE_CHAR_DEVICE_MINOR
,
2068 init_spiceterm(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
2072 g_assert(vt
!= NULL
);
2073 g_assert(vt
->screen
!= NULL
);
2075 vt
->width
= width
/ 8;
2076 vt
->height
= height
/ 16;
2078 vt
->total_height
= vt
->height
* 20;
2079 vt
->scroll_height
= 0;
2084 vt
->region_bottom
= vt
->height
;
2086 vt
->g0enc
= LAT1_MAP
;
2087 vt
->g1enc
= GRAF_MAP
;
2088 vt
->cur_enc
= vt
->g0enc
;
2091 /* default text attributes */
2092 vt
->default_attrib
.bold
= 0;
2093 vt
->default_attrib
.uline
= 0;
2094 vt
->default_attrib
.blink
= 0;
2095 vt
->default_attrib
.invers
= 0;
2096 vt
->default_attrib
.unvisible
= 0;
2097 vt
->default_attrib
.fgcol
= 7;
2098 vt
->default_attrib
.bgcol
= 0;
2100 vt
->cur_attrib
= vt
->default_attrib
;
2110 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
2112 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
2113 vt
->cells
[i
].ch
= ' ';
2114 vt
->cells
[i
].attrib
= vt
->default_attrib
;
2118 g_free(vt
->altcells
);
2121 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
2125 spiceterm_resize(spiceTerm
*vt
, uint32_t width
, uint32_t height
)
2127 width
= (width
/8)*8;
2128 height
= (height
/16)*16;
2130 if (vt
->screen
->width
== width
&& vt
->screen
->height
== height
) {
2134 DPRINTF(0, "width=%u height=%u", width
, height
);
2136 spice_screen_resize(vt
->screen
, width
, height
);
2138 init_spiceterm(vt
, width
, height
);
2140 struct winsize dimensions
;
2141 dimensions
.ws_col
= vt
->width
;
2142 dimensions
.ws_row
= vt
->height
;
2144 ioctl(vt
->pty
, TIOCSWINSZ
, &dimensions
);
2148 create_spiceterm(int argc
, char** argv
, uint32_t maxx
, uint32_t maxy
, guint timeout
)
2150 SpiceCoreInterface
*core
= basic_event_loop_init();
2151 SpiceScreen
*spice_screen
= spice_screen_new(core
, maxx
, maxy
, timeout
);
2153 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
2155 spice_screen
->image_cache
= g_hash_table_new(g_int_hash
, g_int_equal
);
2157 spiceTerm
*vt
= (spiceTerm
*)calloc (sizeof(spiceTerm
), 1);
2159 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
2160 spice_server_add_interface(spice_screen
->server
, &vt
->keyboard_sin
.base
);
2162 vt
->vdagent_sin
.base
.sif
= &my_vdagent_sif
.base
;
2163 vt
->vdagent_sin
.subtype
= "vdagent";
2164 spice_server_add_interface(spice_screen
->server
, &vt
->vdagent_sin
.base
);
2165 vt
->screen
= spice_screen
;
2167 init_spiceterm(vt
, maxx
, maxy
);
2173 master_error_callback(GIOChannel
*channel
, GIOCondition condition
,
2176 //spiceTerm *vt = (spiceTerm *)data;
2178 DPRINTF(1, "condition %d", condition
);
2186 master_watch(int master
, int event
, void *opaque
)
2188 spiceTerm
*vt
= (spiceTerm
*)opaque
;
2191 // fixme: if (!vt->mark_active) {
2193 if (event
== SPICE_WATCH_EVENT_READ
) {
2195 while ((c
= read(master
, buffer
, 1024)) == -1) {
2196 if (errno
!= EAGAIN
) break;
2199 perror("master pipe read error"); // fixme
2201 spiceterm_puts (vt
, buffer
, c
);
2203 if (vt
->ibuf_count
> 0) {
2204 DPRINTF(1, "write input %x %d", vt
->ibuf
[0], vt
->ibuf_count
);
2205 if ((c
= write (master
, vt
->ibuf
, vt
->ibuf_count
)) >= 0) {
2206 if (c
== vt
->ibuf_count
) {
2209 // not all data written
2210 memmove(vt
->ibuf
, vt
->ibuf
+ c
, vt
->ibuf_count
- c
);
2211 vt
->ibuf_count
-= c
;
2213 // nothing written -ignore and try later
2216 perror("master pipe write error");
2219 if (vt
->ibuf_count
== 0) {
2220 spiceterm_update_watch_mask(vt
, FALSE
);
2226 main (int argc
, char** argv
)
2229 char **cmdargv
= NULL
;
2230 char *command
= "/bin/bash"; // execute normal shell as default
2234 struct winsize dimensions
;
2236 g_thread_init(NULL
);
2238 for (i
= 1; i
< argc
; i
++) {
2239 if (!strcmp (argv
[i
], "-c")) {
2240 command
= argv
[i
+1];
2241 cmdargv
= &argv
[i
+1];
2248 if (0) print_usage(NULL
); // fixme:
2250 spiceTerm
*vt
= create_spiceterm (argc
, argv
, 744, 400, 10);
2252 setlocale(LC_ALL
, ""); // set from environment
2254 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
2256 // fixme: ist there a standard way to detect utf8 mode ?
2257 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
2261 dimensions
.ws_col
= vt
->width
;
2262 dimensions
.ws_row
= vt
->height
;
2264 setenv("TERM", TERM
, 1);
2266 DPRINTF(1, "execute %s", command
);
2268 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
2271 // install default signal handlers
2272 signal (SIGQUIT
, SIG_DFL
);
2273 signal (SIGTERM
, SIG_DFL
);
2274 signal (SIGINT
, SIG_DFL
);
2277 execvp (command
, cmdargv
);
2279 execlp (command
, command
, NULL
);
2281 perror ("Error: exec failed\n");
2282 exit (-1); // should not be reached
2283 } else if (pid
== -1) {
2284 perror ("Error: fork failed\n");
2290 /* watch for errors - we need to use glib directly because spice
2291 * does not have SPICE_WATCH_EVENT for this */
2292 GIOChannel
*channel
= g_io_channel_unix_new(master
);
2293 g_io_channel_set_flags(channel
, G_IO_FLAG_NONBLOCK
, NULL
);
2294 g_io_channel_set_encoding(channel
, NULL
, NULL
);
2295 g_io_add_watch(channel
, G_IO_ERR
|G_IO_HUP
, master_error_callback
, vt
);
2297 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
2298 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
2301 basic_event_loop_mainloop();
2305 waitpid(pid
, &status
, 0);