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 vdagent_grab_clipboard(spiceTerm
*vt
, uint8_t selection
);
83 static void vdagent_request_clipboard(spiceTerm
*vt
, uint8_t selection
);
86 print_usage(const char *msg
)
88 if (msg
) { fprintf(stderr
, "ERROR: %s\n", msg
); }
89 fprintf(stderr
, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
93 draw_char_at(spiceTerm
*vt
, int x
, int y
, gunichar2 ch
, TextAttributes attrib
)
95 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) {
99 spice_screen_draw_char(vt
->screen
, x
, y
, ch
, attrib
);
103 spiceterm_update_xy(spiceTerm
*vt
, int x
, int y
)
105 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
107 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
108 int y2
= y1
- vt
->y_displ
;
110 y2
+= vt
->total_height
;
112 if (y2
< vt
->height
) {
113 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
114 draw_char_at(vt
, x
, y2
, c
->ch
, c
->attrib
);
119 spiceterm_clear_xy(spiceTerm
*vt
, int x
, int y
)
121 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
123 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
124 int y2
= y1
- vt
->y_displ
;
126 y2
+= vt
->total_height
;
128 if (y2
< vt
->height
) {
129 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
131 c
->attrib
= vt
->default_attrib
;
132 c
->attrib
.fgcol
= vt
->cur_attrib
.fgcol
;
133 c
->attrib
.bgcol
= vt
->cur_attrib
.bgcol
;
135 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
140 spiceterm_toggle_marked_cell(spiceTerm
*vt
, int pos
)
142 int x
= (pos
%vt
->width
);
143 int y
= (pos
/vt
->width
);
145 if (x
< 0 || y
< 0 || x
>= vt
->width
|| y
>= vt
->height
) { return; }
147 int y1
= (vt
->y_displ
+ y
) % vt
->total_height
;
149 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
150 c
->attrib
.selected
= c
->attrib
.selected
? 0 : 1;
152 if (y
< vt
->height
) {
153 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
158 spiceterm_show_cursor(spiceTerm
*vt
, int show
)
161 if (x
>= vt
->width
) {
165 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
166 int y
= y1
- vt
->y_displ
;
168 y
+= vt
->total_height
;
171 if (y
< vt
->height
) {
173 TextCell
*c
= &vt
->cells
[y1
* vt
->width
+ x
];
176 TextAttributes attrib
= vt
->default_attrib
;
177 attrib
.invers
= !(attrib
.invers
); /* invert fg and bg */
178 draw_char_at(vt
, x
, y
, c
->ch
, attrib
);
180 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
186 spiceterm_refresh(spiceTerm
*vt
)
191 for(y
= 0; y
< vt
->height
; y
++) {
192 TextCell
*c
= vt
->cells
+ y1
* vt
->width
;
193 for(x
= 0; x
< vt
->width
; x
++) {
194 draw_char_at(vt
, x
, y
, c
->ch
, c
->attrib
);
197 if (++y1
== vt
->total_height
)
201 spiceterm_show_cursor(vt
, 1);
205 spiceterm_unselect_all(spiceTerm
*vt
)
209 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
210 if (vt
->cells
[i
].attrib
.selected
) {
211 vt
->cells
[i
].attrib
.selected
= 0;
215 spiceterm_refresh(vt
);
219 spiceterm_scroll_down(spiceTerm
*vt
, int top
, int bottom
, int lines
)
221 if ((top
+ lines
) >= bottom
) {
222 lines
= bottom
- top
-1;
225 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
230 for(i
= bottom
- top
- lines
- 1; i
>= 0; i
--) {
231 int src
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
232 int dst
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
234 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
237 for (i
= 0; i
< lines
; i
++) {
239 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
240 for(j
= 0; j
< vt
->width
; j
++) {
241 c
->attrib
= vt
->default_attrib
;
252 spice_screen_scroll(vt
->screen
, 0, y1
, vt
->screen
->primary_width
, y2
, 0, y0
);
253 spice_screen_clear(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y1
);
257 spiceterm_scroll_up(spiceTerm
*vt
, int top
, int bottom
, int lines
, int moveattr
)
259 if ((top
+ lines
) >= bottom
) {
260 lines
= bottom
- top
- 1;
263 if (top
< 0 || bottom
> vt
->height
|| top
>= bottom
|| lines
< 1) {
270 int y1
= (top
+ lines
)*16;
273 spice_screen_scroll(vt
->screen
, 0, y0
, vt
->screen
->primary_width
, y2
-h
, 0, y1
);
274 spice_screen_clear(vt
->screen
, 0, y2
-h
, vt
->screen
->primary_width
, y2
);
283 for(i
= 0; i
< (bottom
- top
- lines
); i
++) {
284 int dst
= ((vt
->y_base
+ top
+ i
) % vt
->total_height
)*vt
->width
;
285 int src
= ((vt
->y_base
+ top
+ lines
+ i
) % vt
->total_height
)*vt
->width
;
287 memmove(vt
->cells
+ dst
, vt
->cells
+ src
, vt
->width
*sizeof(TextCell
));
290 for (i
= 1; i
<= lines
; i
++) {
292 TextCell
*c
= vt
->cells
+ ((vt
->y_base
+ bottom
- i
) % vt
->total_height
)*vt
->width
;
293 for(j
= 0; j
< vt
->width
; j
++) {
294 c
->attrib
= vt
->default_attrib
;
302 spiceterm_virtual_scroll(spiceTerm
*vt
, int lines
)
304 if (vt
->altbuf
|| lines
== 0) return;
308 int i
= vt
->scroll_height
;
309 if (i
> vt
->total_height
- vt
->height
)
310 i
= vt
->total_height
- vt
->height
;
311 int y1
= vt
->y_base
- i
;
313 y1
+= vt
->total_height
;
314 for(i
= 0; i
< lines
; i
++) {
315 if (vt
->y_displ
== y1
) break;
316 if (--vt
->y_displ
< 0) {
317 vt
->y_displ
= vt
->total_height
- 1;
322 for(i
= 0; i
< lines
; i
++) {
323 if (vt
->y_displ
== vt
->y_base
) break;
324 if (++vt
->y_displ
== vt
->total_height
) {
330 spiceterm_refresh(vt
);
334 spiceterm_respond_esc(spiceTerm
*vt
, const char *esc
)
336 int len
= strlen(esc
);
339 if (vt
->ibuf_count
< (IBUFSIZE
- 1 - len
)) {
340 vt
->ibuf
[vt
->ibuf_count
++] = 27;
341 for (i
= 0; i
< len
; i
++) {
342 vt
->ibuf
[vt
->ibuf_count
++] = esc
[i
];
348 spiceterm_put_lf(spiceTerm
*vt
)
350 if (vt
->cy
+ 1 == vt
->region_bottom
) {
352 if (vt
->altbuf
|| vt
->region_top
!= 0 || vt
->region_bottom
!= vt
->height
) {
353 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 1);
357 if (vt
->y_displ
== vt
->y_base
) {
358 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, 1, 0);
361 if (vt
->y_displ
== vt
->y_base
) {
362 if (++vt
->y_displ
== vt
->total_height
) {
367 if (++vt
->y_base
== vt
->total_height
) {
371 if (vt
->scroll_height
< vt
->total_height
) {
375 int y1
= (vt
->y_base
+ vt
->height
- 1) % vt
->total_height
;
376 TextCell
*c
= &vt
->cells
[y1
* vt
->width
];
378 for (x
= 0; x
< vt
->width
; x
++) {
380 c
->attrib
= vt
->default_attrib
;
384 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
386 } else if (vt
->cy
< vt
->height
- 1) {
392 spiceterm_csi_m(spiceTerm
*vt
)
396 for (i
= 0; i
< vt
->esc_count
; i
++) {
397 switch (vt
->esc_buf
[i
]) {
398 case 0: /* reset all console attributes to default */
399 vt
->cur_attrib
= vt
->default_attrib
;
402 vt
->cur_attrib
.bold
= 1;
405 vt
->cur_attrib
.uline
= 1;
408 vt
->cur_attrib
.blink
= 1;
411 vt
->cur_attrib
.invers
= 1;
414 vt
->cur_attrib
.unvisible
= 1;
417 vt
->cur_enc
= LAT1_MAP
;
418 // fixme: dispaly controls = 0 ?
419 // fixme: toggle meta = 0 ?
422 vt
->cur_enc
= IBMPC_MAP
;
423 // fixme: dispaly controls = 1 ?
424 // fixme: toggle meta = 0 ?
427 vt
->cur_enc
= IBMPC_MAP
;
428 // fixme: dispaly controls = 1 ?
429 // fixme: toggle meta = 1 ?
432 vt
->cur_attrib
.bold
= 0;
435 vt
->cur_attrib
.uline
= 0;
438 vt
->cur_attrib
.blink
= 0;
441 vt
->cur_attrib
.invers
= 0;
444 vt
->cur_attrib
.unvisible
= 0;
454 /* set foreground color */
455 vt
->cur_attrib
.fgcol
= color_table
[vt
->esc_buf
[i
] - 30];
458 /* reset color to default, enable underline */
459 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
460 vt
->cur_attrib
.uline
= 1;
463 /* reset color to default, disable underline */
464 vt
->cur_attrib
.fgcol
= vt
->default_attrib
.fgcol
;
465 vt
->cur_attrib
.uline
= 0;
475 /* set background color */
476 vt
->cur_attrib
.bgcol
= color_table
[vt
->esc_buf
[i
] - 40];
479 /* reset background color */
480 vt
->cur_attrib
.bgcol
= vt
->default_attrib
.bgcol
;
483 fprintf(stderr
, "unhandled ESC[%d m code\n",vt
->esc_buf
[i
]);
490 spiceterm_save_cursor(spiceTerm
*vt
)
492 vt
->cx_saved
= vt
->cx
;
493 vt
->cy_saved
= vt
->cy
;
494 vt
->cur_attrib_saved
= vt
->cur_attrib
;
495 vt
->charset_saved
= vt
->charset
;
496 vt
->g0enc_saved
= vt
->g0enc
;
497 vt
->g1enc_saved
= vt
->g1enc
;
498 vt
->cur_enc_saved
= vt
->cur_enc
;
502 spiceterm_restore_cursor(spiceTerm
*vt
)
504 vt
->cx
= vt
->cx_saved
;
505 vt
->cy
= vt
->cy_saved
;
506 vt
->cur_attrib
= vt
->cur_attrib_saved
;
507 vt
->charset
= vt
->charset_saved
;
508 vt
->g0enc
= vt
->g0enc_saved
;
509 vt
->g1enc
= vt
->g1enc_saved
;
510 vt
->cur_enc
= vt
->cur_enc_saved
;
514 spiceterm_set_alternate_buffer(spiceTerm
*vt
, int on_off
)
518 vt
->y_displ
= vt
->y_base
;
522 if (vt
->altbuf
) return;
526 /* alternate buffer & cursor */
528 spiceterm_save_cursor(vt
);
529 /* save screen to altcels */
530 for (y
= 0; y
< vt
->height
; y
++) {
531 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
532 for (x
= 0; x
< vt
->width
; x
++) {
533 vt
->altcells
[y
*vt
->width
+ x
] = vt
->cells
[y1
*vt
->width
+ x
];
538 for (y
= 0; y
<= vt
->height
; y
++) {
539 for (x
= 0; x
< vt
->width
; x
++) {
540 spiceterm_clear_xy(vt
, x
, y
);
546 if (vt
->altbuf
== 0) return;
550 /* restore saved data */
551 for (y
= 0; y
< vt
->height
; y
++) {
552 int y1
= (vt
->y_base
+ y
) % vt
->total_height
;
553 for (x
= 0; x
< vt
->width
; x
++) {
554 vt
->cells
[y1
*vt
->width
+ x
] = vt
->altcells
[y
*vt
->width
+ x
];
558 spiceterm_restore_cursor(vt
);
561 spiceterm_refresh(vt
);
565 spiceterm_set_mode(spiceTerm
*vt
, int on_off
)
569 for (i
= 0; i
<= vt
->esc_count
; i
++) {
570 if (vt
->esc_ques
) { /* DEC private modes set/reset */
571 switch(vt
->esc_buf
[i
]) {
572 case 10: /* X11 mouse reporting on/off */
573 case 1000: /* SET_VT200_MOUSE */
574 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
575 vt
->report_mouse
= on_off
;
577 case 1049: /* start/end special app mode (smcup/rmcup) */
578 spiceterm_set_alternate_buffer (vt
, on_off
);
580 case 25: /* Cursor on/off */
581 case 9: /* X10 mouse reporting on/off */
582 case 6: /* Origin relative/absolute */
583 case 1: /* Cursor keys in appl mode*/
584 case 5: /* Inverted screen on/off */
585 case 7: /* Autowrap on/off */
586 case 8: /* Autorepeat on/off */
589 } else { /* ANSI modes set/reset */
590 //g_assert_not_reached();
592 /* fixme: implement me */
598 spiceterm_gotoxy(spiceTerm
*vt
, int x
, int y
)
600 /* verify all boundaries */
606 if (x
>= vt
->width
) {
616 if (y
>= vt
->height
) {
624 debug_print_escape_buffer(spiceTerm
*vt
, const char *func
, const char *prefix
,
625 const char *qes
, gunichar2 ch
)
628 if (vt
->esc_count
== 0) {
629 printf("%s:%s ESC[%s%c\n", func
, prefix
, qes
, ch
);
630 } else if (vt
->esc_count
== 1) {
631 printf("%s:%s ESC[%s%d%c\n", func
, prefix
, qes
, vt
->esc_buf
[0], ch
);
634 printf("%s:%s ESC[%s%d", func
, prefix
, qes
, vt
->esc_buf
[0]);
635 for (i
= 1; i
< vt
->esc_count
; i
++) {
636 printf(";%d", vt
->esc_buf
[i
]);
643 enum { ESnormal
, ESesc
, ESsquare
, ESgetpars
, ESgotpars
, ESfunckey
,
644 EShash
, ESsetG0
, ESsetG1
, ESpercent
, ESignore
, ESnonstd
,
645 ESpalette
, ESidquery
, ESosc1
, ESosc2
};
648 spiceterm_putchar(spiceTerm
*vt
, gunichar2 ch
)
652 if (debug
&& !vt
->tty_state
) {
653 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
654 vt
->tty_state
, ch
, ch
, vt
->cur_enc
, vt
->cx
, vt
->cy
);
657 switch(vt
->tty_state
) {
659 vt
->tty_state
= ESnormal
;
662 vt
->tty_state
= ESsquare
;
665 vt
->tty_state
= ESnonstd
;
668 vt
->tty_state
= ESpercent
;
671 spiceterm_save_cursor(vt
);
674 spiceterm_restore_cursor(vt
);
677 vt
->tty_state
= ESsetG0
; // SET G0
680 vt
->tty_state
= ESsetG1
; // SET G1
684 if (vt
->cy
== vt
->region_top
)
685 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, 1);
686 else if (vt
->cy
> 0) {
691 /* numeric keypad - ignored */
694 /* appl. keypad - ignored */
697 DPRINTF(1, "got unhandled ESC%c %d", ch
, ch
);
701 case ESnonstd
: /* Operating System Controls */
702 vt
->tty_state
= ESnormal
;
705 case 'P': /* palette escape sequence */
706 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
711 vt
->tty_state
= ESpalette
;
713 case 'R': /* reset palette */
714 // fixme: reset_palette(vc);
721 vt
->osc_textbuf
[0] = 0;
722 vt
->tty_state
= ESosc1
;
725 DPRINTF(1, "got unhandled OSC %c", ch
);
726 vt
->tty_state
= ESnormal
;
731 vt
->tty_state
= ESnormal
;
733 vt
->tty_state
= ESosc2
;
735 DPRINTF(1, "got illegal OSC sequence");
739 if (ch
!= 0x9c && ch
!= 7) {
741 while (vt
->osc_textbuf
[i
]) i
++;
742 vt
->osc_textbuf
[i
++] = ch
;
743 vt
->osc_textbuf
[i
] = 0;
745 DPRINTF(1, "OSC:%c:%s", vt
->osc_cmd
, vt
->osc_textbuf
);
746 vt
->tty_state
= ESnormal
;
750 if ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F')
751 || (ch
>= 'a' && ch
<= 'f')) {
752 vt
->esc_buf
[vt
->esc_count
++] = (ch
> '9' ? (ch
& 0xDF) - 'A' + 10 : ch
- '0');
753 if (vt
->esc_count
== 7) {
754 // fixme: this does not work - please test
756 rfbColourMap *cmap =&vt->screen->colourMap;
758 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
759 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
760 cmap->data.bytes[i++] += vt->esc_buf[j++];
761 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
762 cmap->data.bytes[i++] += vt->esc_buf[j++];
763 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
764 cmap->data.bytes[i] += vt->esc_buf[j];
768 vt
->tty_state
= ESnormal
;
771 vt
->tty_state
= ESnormal
;
774 for(i
= 0; i
< MAX_ESC_PARAMS
; i
++) {
780 vt
->tty_state
= ESgetpars
;
783 vt
->tty_state
= ESidquery
;
787 if ((vt
->esc_ques
= (ch
== '?'))) {
791 if (ch
>= '0' && ch
<= '9') {
793 if (vt
->esc_count
< MAX_ESC_PARAMS
) {
794 vt
->esc_buf
[vt
->esc_count
] = vt
->esc_buf
[vt
->esc_count
] * 10 + ch
- '0';
797 } else if (ch
== ';') {
801 if (vt
->esc_has_par
) {
804 vt
->tty_state
= ESgotpars
;
808 vt
->tty_state
= ESnormal
;
810 char *qes
= vt
->esc_ques
? "?" : "";
813 debug_print_escape_buffer(vt
, __func__
, "", qes
, ch
);
818 spiceterm_set_mode(vt
, 1);
821 spiceterm_set_mode(vt
, 0);
824 if (!vt
->esc_count
) {
825 vt
->esc_count
++; // default parameter 0
830 /* report cursor position */
831 /* TODO: send ESC[row;colR */
835 if (vt
->esc_buf
[0] == 0) {
838 vt
->cy
-= vt
->esc_buf
[0];
845 /* move cursor down */
846 if (vt
->esc_buf
[0] == 0) {
849 vt
->cy
+= vt
->esc_buf
[0];
850 if (vt
->cy
>= vt
->height
) {
851 vt
->cy
= vt
->height
- 1;
856 /* move cursor right */
857 if (vt
->esc_buf
[0] == 0) {
860 vt
->cx
+= vt
->esc_buf
[0];
861 if (vt
->cx
>= vt
->width
) {
862 vt
->cx
= vt
->width
- 1;
866 /* move cursor left */
867 if (vt
->esc_buf
[0] == 0) {
870 vt
->cx
-= vt
->esc_buf
[0];
877 /* move cursor to column */
878 spiceterm_gotoxy(vt
, vt
->esc_buf
[0] - 1, vt
->cy
);
881 /* move cursor to row */
882 spiceterm_gotoxy(vt
, vt
->cx
, vt
->esc_buf
[0] - 1);
886 /* move cursor to row, column */
887 spiceterm_gotoxy(vt
, vt
->esc_buf
[1] - 1, vt
->esc_buf
[0] - 1);
890 switch (vt
->esc_buf
[0]) {
892 /* clear to end of screen */
893 for (y
= vt
->cy
; y
< vt
->height
; y
++) {
894 for (x
= 0; x
< vt
->width
; x
++) {
895 if (y
== vt
->cy
&& x
< vt
->cx
) {
898 spiceterm_clear_xy (vt
, x
, y
);
903 /* clear from beginning of screen */
904 for (y
= 0; y
<= vt
->cy
; y
++) {
905 for (x
= 0; x
< vt
->width
; x
++) {
906 if (y
== vt
->cy
&& x
> vt
->cx
) {
909 spiceterm_clear_xy(vt
, x
, y
);
914 /* clear entire screen */
915 for (y
= 0; y
<= vt
->height
; y
++) {
916 for (x
= 0; x
< vt
->width
; x
++) {
917 spiceterm_clear_xy(vt
, x
, y
);
924 switch (vt
->esc_buf
[0]) {
927 for(x
= vt
->cx
; x
< vt
->width
; x
++) {
928 spiceterm_clear_xy(vt
, x
, vt
->cy
);
932 /* clear from beginning of line */
933 for (x
= 0; x
<= vt
->cx
; x
++) {
934 spiceterm_clear_xy(vt
, x
, vt
->cy
);
938 /* clear entire line */
939 for(x
= 0; x
< vt
->width
; x
++) {
940 spiceterm_clear_xy(vt
, x
, vt
->cy
);
949 if (c
> vt
->height
- vt
->cy
)
950 c
= vt
->height
- vt
->cy
;
954 spiceterm_scroll_down(vt
, vt
->cy
, vt
->region_bottom
, c
);
960 if (c
> vt
->height
- vt
->cy
)
961 c
= vt
->height
- vt
->cy
;
965 spiceterm_scroll_up(vt
, vt
->cy
, vt
->region_bottom
, c
, 1);
971 spiceterm_scroll_down(vt
, vt
->region_top
, vt
->region_bottom
, c
);
977 spiceterm_scroll_up(vt
, vt
->region_top
, vt
->region_bottom
, c
, 1);
980 /* delete c character */
983 if (c
> vt
->width
- vt
->cx
)
984 c
= vt
->width
- vt
->cx
;
988 for (x
= vt
->cx
; x
< vt
->width
- c
; x
++) {
989 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
990 TextCell
*dst
= &vt
->cells
[y1
* vt
->width
+ x
];
991 TextCell
*src
= dst
+ c
;
993 spiceterm_update_xy(vt
, x
+ c
, vt
->cy
);
995 src
->attrib
= vt
->default_attrib
;
996 spiceterm_update_xy(vt
, x
, vt
->cy
);
1000 /* save cursor position */
1001 spiceterm_save_cursor(vt
);
1004 /* restore cursor position */
1005 spiceterm_restore_cursor(vt
);
1008 /* erase c characters */
1012 if (c
> (vt
->width
- vt
->cx
)) c
= vt
->width
- vt
->cx
;
1014 for(i
= 0; i
< c
; i
++) {
1015 spiceterm_clear_xy(vt
, vt
->cx
+ i
, vt
->cy
);
1019 /* insert c character */
1021 if (c
> (vt
->width
- vt
->cx
)) {
1022 c
= vt
->width
- vt
->cx
;
1026 for (x
= vt
->width
- c
; x
>= vt
->cx
; x
--) {
1027 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1028 TextCell
*src
= &vt
->cells
[y1
* vt
->width
+ x
];
1029 TextCell
*dst
= src
+ c
;
1031 spiceterm_update_xy (vt
, x
+ c
, vt
->cy
);
1033 src
->attrib
= vt
->cur_attrib
;
1034 spiceterm_update_xy(vt
, x
, vt
->cy
);
1040 if (!vt
->esc_buf
[0])
1042 if (!vt
->esc_buf
[1])
1043 vt
->esc_buf
[1] = vt
->height
;
1044 /* Minimum allowed region is 2 lines */
1045 if (vt
->esc_buf
[0] < vt
->esc_buf
[1] &&
1046 vt
->esc_buf
[1] <= vt
->height
) {
1047 vt
->region_top
= vt
->esc_buf
[0] - 1;
1048 vt
->region_bottom
= vt
->esc_buf
[1];
1050 vt
->cy
= vt
->region_top
;
1051 DPRINTF(1, "set region %d %d", vt
->region_top
, vt
->region_bottom
);
1057 debug_print_escape_buffer(vt
, __func__
, " unhandled escape", qes
, ch
);
1063 case ESsetG0
: // Set G0
1064 vt
->tty_state
= ESnormal
;
1067 vt
->g0enc
= GRAF_MAP
;
1069 vt
->g0enc
= LAT1_MAP
;
1071 vt
->g0enc
= IBMPC_MAP
;
1073 vt
->g0enc
= USER_MAP
;
1075 if (vt
->charset
== 0)
1076 vt
->cur_enc
= vt
->g0enc
;
1079 case ESsetG1
: // Set G1
1080 vt
->tty_state
= ESnormal
;
1083 vt
->g1enc
= GRAF_MAP
;
1085 vt
->g1enc
= LAT1_MAP
;
1087 vt
->g1enc
= IBMPC_MAP
;
1089 vt
->g1enc
= USER_MAP
;
1091 if (vt
->charset
== 1)
1092 vt
->cur_enc
= vt
->g1enc
;
1095 case ESidquery
: // vt100 query id
1096 vt
->tty_state
= ESnormal
;
1099 DPRINTF(1, "ESC[>c Query term ID");
1100 spiceterm_respond_esc(vt
, TERMIDCODE
);
1104 vt
->tty_state
= ESnormal
;
1106 case '@': /* defined in ISO 2022 */
1109 case 'G': /* prelim official escape code */
1110 case '8': /* retained for compatibility */
1115 default: // ESnormal
1116 vt
->tty_state
= ESnormal
;
1121 case 7: /* alert aka. bell */
1123 //rfbSendBell(vt->screen);
1125 case 8: /* backspace */
1129 case 9: /* tabspace */
1130 if (vt
->cx
+ (8 - (vt
->cx
% 8)) > vt
->width
) {
1132 spiceterm_put_lf(vt
);
1134 vt
->cx
= vt
->cx
+ (8 - (vt
->cx
% 8));
1140 spiceterm_put_lf(vt
);
1142 case 13: /* carriage return */
1146 /* SI (shift in), select character set 1 */
1148 vt
->cur_enc
= vt
->g1enc
;
1149 /* fixme: display controls = 1 */
1152 /* SO (shift out), select character set 0 */
1154 vt
->cur_enc
= vt
->g0enc
;
1155 /* fixme: display controls = 0 */
1158 vt
->tty_state
= ESesc
;
1160 case 127: /* delete */
1163 case 128+27: /* csi */
1164 vt
->tty_state
= ESsquare
;
1167 if (vt
->cx
>= vt
->width
) {
1170 spiceterm_put_lf(vt
);
1173 int y1
= (vt
->y_base
+ vt
->cy
) % vt
->total_height
;
1174 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ vt
->cx
];
1175 c
->attrib
= vt
->cur_attrib
;
1177 spiceterm_update_xy(vt
, vt
->cx
, vt
->cy
);
1186 spiceterm_puts(spiceTerm
*vt
, const char *buf
, int len
)
1190 spiceterm_show_cursor(vt
, 0);
1193 unsigned char c
= *buf
;
1197 if (vt
->tty_state
!= ESnormal
) {
1198 // never translate escape sequence
1200 } else if (vt
->utf8
&& !vt
->cur_enc
) {
1202 if(c
& 0x80) { // utf8 multi-byte sequence
1204 if (vt
->utf_count
> 0 && (c
& 0xc0) == 0x80) {
1205 // inside UTF8 sequence
1206 vt
->utf_char
= (vt
->utf_char
<< 6) | (c
& 0x3f);
1208 if (vt
->utf_count
== 0) {
1214 // first char of a UTF8 sequence
1215 if ((c
& 0xe0) == 0xc0) {
1217 vt
->utf_char
= (c
& 0x1f);
1218 } else if ((c
& 0xf0) == 0xe0) {
1220 vt
->utf_char
= (c
& 0x0f);
1221 } else if ((c
& 0xf8) == 0xf0) {
1223 vt
->utf_char
= (c
& 0x07);
1224 } else if ((c
& 0xfc) == 0xf8) {
1226 vt
->utf_char
= (c
& 0x03);
1227 } else if ((c
& 0xfe) == 0xfc) {
1229 vt
->utf_char
= (c
& 0x01);
1242 // never translate controls
1243 if (c
>= 32 && c
!= 127 && c
!= (128+27)) {
1244 tc
= translations
[vt
->cur_enc
][c
& 0x0ff];
1250 spiceterm_putchar(vt
, tc
);
1253 spiceterm_show_cursor(vt
, 1);
1260 spiceterm_set_xcut_text(char* str, int len, struct _rfbClientRec* cl)
1262 spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
1264 // seems str is Latin-1 encoded
1265 if (vt->selection) free (vt->selection);
1266 vt->selection = (gunichar2 *)malloc (len*sizeof (gunichar2));
1268 for (i = 0; i < len; i++) {
1269 vt->selection[i] = str[i] & 0xff;
1271 vt->selection_len = len;
1276 spiceterm_update_watch_mask(spiceTerm
*vt
, gboolean writable
)
1278 g_assert(vt
!= NULL
);
1280 int mask
= SPICE_WATCH_EVENT_READ
;
1283 mask
|= SPICE_WATCH_EVENT_WRITE
;
1286 vt
->screen
->core
->watch_update_mask(vt
->screen
->mwatch
, mask
);
1290 mouse_report(spiceTerm
*vt
, int butt
, int mrx
, int mry
)
1294 sprintf(buf
, "[M%c%c%c", (char)(' ' + butt
), (char)('!' + mrx
),
1297 spiceterm_respond_esc(vt
, buf
);
1299 spiceterm_update_watch_mask(vt
, TRUE
);
1303 spiceterm_respond_unichar2(spiceTerm
*vt
, gunichar2 uc
)
1307 gint len
= g_unichar_to_utf8(uc
, buf
);
1310 if ((vt
->ibuf_count
+ len
) < IBUFSIZE
) {
1312 for (i
= 0; i
< len
; i
++) {
1313 vt
->ibuf
[vt
->ibuf_count
++] = buf
[i
];
1316 fprintf(stderr
, "warning: input buffer overflow\n");
1320 if ((vt
->ibuf_count
+ 1) < IBUFSIZE
) {
1321 vt
->ibuf
[vt
->ibuf_count
++] = (char)uc
;
1323 fprintf(stderr
, "warning: input buffer overflow\n");
1329 my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
1331 // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
1333 /* we no not need this */
1339 my_kbd_push_keyval(SpiceKbdInstance
*sin
, uint32_t keySym
, int flags
)
1341 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, keyboard_sin
);
1342 static int control
= 0;
1343 static int shift
= 0;
1348 DPRINTF(1, "flags=%d keySym=%08x", flags
, keySym
);
1351 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1353 } if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1355 } else if (vt
->ibuf_count
< (IBUFSIZE
- 32)) {
1358 if(keySym
>= 'a' && keySym
<= 'z')
1359 uc
= keySym
- 'a' + 1;
1360 else if (keySym
>= 'A' && keySym
<= 'Z')
1361 uc
= keySym
- 'A' + 1;
1367 case GDK_KEY_Escape
:
1369 case GDK_KEY_Return
:
1371 case GDK_KEY_BackSpace
:
1375 case GDK_KEY_Delete
: /* kdch1 */
1376 case GDK_KEY_KP_Delete
:
1378 case GDK_KEY_Home
: /* khome */
1379 case GDK_KEY_KP_Home
:
1382 case GDK_KEY_KP_End
: /* kend */
1384 case GDK_KEY_Insert
: /* kich1 */
1385 case GDK_KEY_KP_Insert
:
1388 case GDK_KEY_KP_Up
: /* kcuu1 */
1390 case GDK_KEY_Down
: /* kcud1 */
1391 case GDK_KEY_KP_Down
:
1394 case GDK_KEY_KP_Right
: /* kcuf1 */
1397 case GDK_KEY_KP_Left
: /* kcub1 */
1399 case GDK_KEY_Page_Up
:
1401 spiceterm_virtual_scroll (vt
, -vt
->height
/2);
1405 case GDK_KEY_Page_Down
:
1407 spiceterm_virtual_scroll (vt
, vt
->height
/2);
1436 if (keySym
< 0x100) {
1443 DPRINTF(1, "escape=%s unicode=%08x\n", esc
, uc
);
1445 if (vt
->y_displ
!= vt
->y_base
) {
1446 vt
->y_displ
= vt
->y_base
;
1447 spiceterm_refresh (vt
);
1451 spiceterm_respond_esc(vt
, esc
);
1452 } else if (uc
> 0) {
1453 spiceterm_respond_unichar2(vt
, uc
);
1461 if (flags
& 2) { // UP
1462 if (keySym
== GDK_KEY_Shift_L
|| keySym
== GDK_KEY_Shift_R
) {
1464 } else if (keySym
== GDK_KEY_Control_L
|| keySym
== GDK_KEY_Control_R
) {
1469 spiceterm_update_watch_mask(vt
, TRUE
);
1473 my_kbd_get_leds(SpiceKbdInstance
*sin
)
1478 static SpiceKbdInterface my_keyboard_sif
= {
1479 .base
.type
= SPICE_INTERFACE_KEYBOARD
,
1480 .base
.description
= "spiceterm keyboard device",
1481 .base
.major_version
= SPICE_INTERFACE_KEYBOARD_MAJOR
,
1482 .base
.minor_version
= SPICE_INTERFACE_KEYBOARD_MINOR
,
1483 .push_keyval
= my_kbd_push_keyval
,
1484 .push_scan_freg
= my_kbd_push_key
,
1485 .get_leds
= my_kbd_get_leds
,
1489 /* vdagent interface - to get mouse/clipboarde support */
1491 #define VDAGENT_WBUF_SIZE (1024*50)
1492 static unsigned char vdagent_write_buffer
[VDAGENT_WBUF_SIZE
];
1493 static int vdagent_write_buffer_pos
= 0;
1494 static int agent_owns_clipboard
[256] = { 0, };
1497 spiceterm_motion_event(spiceTerm
*vt
, uint32_t x
, uint32_t y
, uint32_t buttons
)
1499 DPRINTF(1, "mask=%08x x=%d y=%d", buttons
, x
,y
);
1501 static int last_mask
= 0;
1502 static int sel_start_pos
= 0;
1503 static int sel_end_pos
= 0;
1504 static int button2_released
= 1;
1511 if (cx
>= vt
->width
) cx
= vt
->width
- 1;
1513 if (cy
>= vt
->height
) cy
= vt
->height
- 1;
1515 if (vt
->report_mouse
&& buttons
!= last_mask
) {
1516 last_mask
= buttons
;
1518 mouse_report(vt
, 0, cx
, cy
);
1521 mouse_report (vt
, 1, cx
, cy
);
1524 mouse_report(vt
, 2, cx
, cy
);
1527 mouse_report(vt
, 3, cx
, cy
);
1533 if(button2_released
) {
1535 if (agent_owns_clipboard
[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
]) {
1536 if (vt
->selection
) {
1538 for(i
= 0; i
< vt
->selection_len
; i
++) {
1539 spiceterm_respond_unichar2(vt
, vt
->selection
[i
]);
1541 spiceterm_update_watch_mask(vt
, TRUE
);
1542 if (vt
->y_displ
!= vt
->y_base
) {
1543 vt
->y_displ
= vt
->y_base
;
1544 spiceterm_refresh(vt
);
1548 vdagent_request_clipboard(vt
, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
);
1552 button2_released
= 0;
1554 button2_released
= 1;
1558 int pos
= cy
*vt
->width
+ cx
;
1560 // code borrowed from libvncserver (VNCconsole.c)
1562 if (!vt
->mark_active
) {
1564 spiceterm_unselect_all(vt
);
1566 vt
->mark_active
= 1;
1567 sel_start_pos
= sel_end_pos
= pos
;
1568 spiceterm_toggle_marked_cell(vt
, pos
);
1572 if (pos
!= sel_end_pos
) {
1573 if (pos
> sel_end_pos
) {
1574 cx
= sel_end_pos
; cy
=pos
;
1576 cx
=pos
; cy
=sel_end_pos
;
1579 if (cx
< sel_start_pos
) {
1580 if (cy
< sel_start_pos
) cy
--;
1586 spiceterm_toggle_marked_cell(vt
, cx
);
1594 } else if (vt
->mark_active
) {
1595 vt
->mark_active
= 0;
1597 if (sel_start_pos
> sel_end_pos
) {
1598 int tmp
= sel_start_pos
- 1;
1599 sel_start_pos
= sel_end_pos
;
1603 int len
= sel_end_pos
- sel_start_pos
+ 1;
1605 if (vt
->selection
) free (vt
->selection
);
1606 vt
->selection
= (gunichar2
*)malloc (len
*sizeof(gunichar2
));
1607 vt
->selection_len
= len
;
1609 for (i
= 0; i
< len
; i
++) {
1610 int pos
= sel_start_pos
+ i
;
1611 int x
= pos
% vt
->width
;
1612 int y1
= ((pos
/ vt
->width
) + vt
->y_displ
) % vt
->total_height
;
1613 TextCell
*c
= &vt
->cells
[y1
*vt
->width
+ x
];
1614 vt
->selection
[i
] = c
->ch
;
1618 DPRINTF(1, "selection length = %d", vt
->selection_len
);
1620 vdagent_grab_clipboard(vt
, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
);
1624 static void vdagent_send_capabilities(spiceTerm
*vt
, uint32_t request
)
1626 VDAgentAnnounceCapabilities
*caps
;
1629 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
1630 caps
= calloc(1, size
);
1631 g_assert(caps
!= NULL
);
1633 caps
->request
= request
;
1634 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
1635 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
1636 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
1637 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
1638 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_SELECTION
);
1639 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG
);
1640 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_GUEST_LINEEND_LF
);
1642 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1643 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1645 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1646 vdagent_write_buffer_pos
+= msg_size
;
1648 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1649 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1651 hdr
->port
= VDP_CLIENT_PORT
;
1652 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1653 msg
->protocol
= VD_AGENT_PROTOCOL
;
1654 msg
->type
= VD_AGENT_ANNOUNCE_CAPABILITIES
;
1658 memcpy(buf
+ sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
), (uint8_t *)caps
, size
);
1660 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1666 dump_message(unsigned char *buf
, int size
)
1670 for (i
= 0; i
< size
; i
++) {
1671 printf("%d %02X\n", i
, buf
[i
]);
1677 static void vdagent_grab_clipboard(spiceTerm
*vt
, uint8_t selection
)
1681 agent_owns_clipboard
[selection
] = 1;
1685 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1686 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1688 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1689 vdagent_write_buffer_pos
+= msg_size
;
1691 memset(buf
, 0, msg_size
);
1693 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1694 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1695 uint8_t *grab
= (uint8_t *)&msg
[1];
1696 *((uint8_t *)grab
) = selection
;
1697 *((uint32_t *)(grab
+ 4)) = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1699 hdr
->port
= VDP_CLIENT_PORT
;
1700 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1702 msg
->protocol
= VD_AGENT_PROTOCOL
;
1703 msg
->type
= VD_AGENT_CLIPBOARD_GRAB
;
1707 if (0) dump_message(buf
, msg_size
);
1709 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1712 static void vdagent_request_clipboard(spiceTerm
*vt
, uint8_t selection
)
1716 agent_owns_clipboard
[selection
] = 1;
1718 size
= 4 + sizeof(VDAgentClipboardRequest
);
1720 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1721 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1723 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1724 vdagent_write_buffer_pos
+= msg_size
;
1726 memset(buf
, 0, msg_size
);
1728 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1729 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1730 uint8_t *data
= (uint8_t *)&msg
[1];
1731 *((uint32_t *)data
) = 0;
1732 data
[0] = selection
;
1733 ((uint32_t *)data
)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1735 hdr
->port
= VDP_CLIENT_PORT
;
1736 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1738 msg
->protocol
= VD_AGENT_PROTOCOL
;
1739 msg
->type
= VD_AGENT_CLIPBOARD_REQUEST
;
1743 if (0) dump_message(buf
, msg_size
);
1745 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1748 static void vdagent_send_clipboard(spiceTerm
*vt
, uint8_t selection
)
1752 if (selection
!= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
1753 fprintf(stderr
, "clipboard select %d is not supported\n", selection
);
1760 sel_data
= g_utf16_to_utf8(vt
->selection
, vt
->selection_len
, NULL
, &sel_len
, NULL
);
1762 sel_len
= vt
->selection_len
;
1763 sel_data
= g_malloc(sel_len
);
1765 for (i
= 0; i
< sel_len
; i
++) { sel_data
[i
] = (char)vt
->selection
[i
]; }
1766 sel_data
[sel_len
] = 0;
1771 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
1772 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
1774 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
1775 vdagent_write_buffer_pos
+= msg_size
;
1777 memset(buf
, 0, msg_size
);
1779 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1780 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1781 uint8_t *data
= (uint8_t *)&msg
[1];
1782 *((uint8_t *)data
) = selection
;
1784 *((uint32_t *)data
) = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
1787 memcpy(data
, sel_data
, sel_len
);
1790 hdr
->port
= VDP_CLIENT_PORT
;
1791 hdr
->size
= sizeof(VDAgentMessage
) + size
;
1793 msg
->protocol
= VD_AGENT_PROTOCOL
;
1794 msg
->type
= VD_AGENT_CLIPBOARD
;
1798 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
1802 vmc_write(SpiceCharDeviceInstance
*sin
, const uint8_t *buf
, int len
)
1804 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, vdagent_sin
);
1806 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
1807 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
1809 //g_assert(hdr->port == VDP_SERVER_PORT);
1810 g_assert(msg
->protocol
== VD_AGENT_PROTOCOL
);
1812 DPRINTF(1, "%d %d %d %d", len
, hdr
->port
, msg
->protocol
, msg
->type
);
1814 switch (msg
->type
) {
1815 case VD_AGENT_MOUSE_STATE
: {
1816 VDAgentMouseState
*info
= (VDAgentMouseState
*)&msg
[1];
1817 spiceterm_motion_event(vt
, info
->x
, info
->y
, info
->buttons
);
1820 case VD_AGENT_ANNOUNCE_CAPABILITIES
: {
1821 VDAgentAnnounceCapabilities
*caps
= (VDAgentAnnounceCapabilities
*)&msg
[1];
1822 DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps
->request
);
1825 int caps_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(hdr
->size
);
1826 for (i
= 0; i
< VD_AGENT_END_CAP
; i
++) {
1827 DPRINTF(1, "CAPABILITIES %d %d", i
, VD_AGENT_HAS_CAPABILITY(caps
->caps
, caps_size
, i
));
1830 vdagent_send_capabilities(vt
, 0);
1833 case VD_AGENT_CLIPBOARD_GRAB
: {
1834 VDAgentClipboardGrab
*grab
= (VDAgentClipboardGrab
*)&msg
[1];
1835 uint8_t selection
= *((uint8_t *)grab
);
1836 DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection
);
1837 agent_owns_clipboard
[selection
] = 0;
1840 case VD_AGENT_CLIPBOARD_REQUEST
: {
1841 uint8_t *req
= (uint8_t *)&msg
[1];
1842 uint8_t selection
= *((uint8_t *)req
);
1843 uint32_t type
= *((uint32_t *)(req
+ 4));
1845 DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection
, type
);
1847 vdagent_send_clipboard(vt
, selection
);
1851 case VD_AGENT_CLIPBOARD
: {
1852 uint8_t *data
= (uint8_t *)&msg
[1];
1853 uint8_t selection
= data
[0];
1854 uint32_t type
= *(uint32_t *)(data
+ 4);
1855 int size
= msg
->size
- 8;
1856 DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection
, type
, size
);
1858 if (type
== VD_AGENT_CLIPBOARD_UTF8_TEXT
) {
1860 for (i
= 0; i
< size
; i
++) {
1861 if ((vt
->ibuf_count
+ 1) < IBUFSIZE
) {
1862 vt
->ibuf
[vt
->ibuf_count
++] = *(char *)(data
+ 8 + i
);
1864 fprintf(stderr
, "warning: input buffer overflow\n");
1867 spiceterm_update_watch_mask(vt
, TRUE
);
1871 case VD_AGENT_CLIPBOARD_RELEASE
: {
1872 uint8_t *data
= (uint8_t *)&msg
[1];
1873 uint8_t selection
= data
[0];
1875 DPRINTF(0, "VD_AGENT_CLIPBOARD_RELEASE %d", selection
);
1879 case VD_AGENT_MONITORS_CONFIG
:
1880 /* ignore for now */
1883 DPRINTF(0, "got uknown vdagent message type %d\n", msg
->type
);
1890 vmc_read(SpiceCharDeviceInstance
*sin
, uint8_t *buf
, int len
)
1892 DPRINTF(0, "%d %d", len
, vdagent_write_buffer_pos
);
1895 if (!vdagent_write_buffer_pos
) {
1899 int size
= (len
>= vdagent_write_buffer_pos
) ? vdagent_write_buffer_pos
: len
;
1900 memcpy(buf
, vdagent_write_buffer
, size
);
1901 if (size
< vdagent_write_buffer_pos
) {
1902 DPRINTF(0, "MOVE %d", size
);
1903 memmove(vdagent_write_buffer
, vdagent_write_buffer
+ size
,
1904 vdagent_write_buffer_pos
- size
);
1906 vdagent_write_buffer_pos
-= size
;
1908 DPRINTF(0, "RET %d %d", size
, vdagent_write_buffer_pos
);
1913 vmc_state(SpiceCharDeviceInstance
*sin
, int connected
)
1918 static SpiceCharDeviceInterface my_vdagent_sif
= {
1919 .base
.type
= SPICE_INTERFACE_CHAR_DEVICE
,
1920 .base
.description
= "spice virtual channel char device",
1921 .base
.major_version
= SPICE_INTERFACE_CHAR_DEVICE_MAJOR
,
1922 .base
.minor_version
= SPICE_INTERFACE_CHAR_DEVICE_MINOR
,
1929 create_spiceterm(int argc
, char** argv
, int maxx
, int maxy
, guint timeout
)
1933 SpiceScreen
*spice_screen
;
1935 SpiceCoreInterface
*core
= basic_event_loop_init();
1936 spice_screen
= spice_screen_new(core
, timeout
);
1937 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
1939 spiceTerm
*vt
= (spiceTerm
*)calloc (sizeof(spiceTerm
), 1);
1941 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
1942 spice_server_add_interface(spice_screen
->server
, &vt
->keyboard_sin
.base
);
1944 vt
->vdagent_sin
.base
.sif
= &my_vdagent_sif
.base
;
1945 vt
->vdagent_sin
.subtype
= "vdagent";
1946 spice_server_add_interface(spice_screen
->server
, &vt
->vdagent_sin
.base
);
1948 // screen->setXCutText = spiceterm_set_xcut_text;
1949 // screen->ptrAddEvent = spiceterm_pointer_event;
1950 // screen->newClientHook = new_client;
1951 // screen->desktopName = "SPICE Command Terminal";
1953 vt
->maxx
= spice_screen
->width
;
1954 vt
->maxy
= spice_screen
->height
;
1956 vt
->width
= vt
->maxx
/ 8;
1957 vt
->height
= vt
->maxy
/ 16;
1959 vt
->total_height
= vt
->height
* 20;
1960 vt
->scroll_height
= 0;
1965 vt
->region_bottom
= vt
->height
;
1967 vt
->g0enc
= LAT1_MAP
;
1968 vt
->g1enc
= GRAF_MAP
;
1969 vt
->cur_enc
= vt
->g0enc
;
1972 /* default text attributes */
1973 vt
->default_attrib
.bold
= 0;
1974 vt
->default_attrib
.uline
= 0;
1975 vt
->default_attrib
.blink
= 0;
1976 vt
->default_attrib
.invers
= 0;
1977 vt
->default_attrib
.unvisible
= 0;
1978 vt
->default_attrib
.fgcol
= 7;
1979 vt
->default_attrib
.bgcol
= 0;
1981 vt
->cur_attrib
= vt
->default_attrib
;
1983 vt
->cells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->total_height
);
1985 for (i
= 0; i
< vt
->width
*vt
->total_height
; i
++) {
1986 vt
->cells
[i
].ch
= ' ';
1987 vt
->cells
[i
].attrib
= vt
->default_attrib
;
1990 vt
->altcells
= (TextCell
*)calloc (sizeof (TextCell
), vt
->width
*vt
->height
);
1992 vt
->screen
= spice_screen
;
1998 master_error_callback(GIOChannel
*channel
, GIOCondition condition
,
2001 //spiceTerm *vt = (spiceTerm *)data;
2003 DPRINTF(1, "condition %d", condition
);
2011 master_watch(int master
, int event
, void *opaque
)
2013 spiceTerm
*vt
= (spiceTerm
*)opaque
;
2016 // fixme: if (!vt->mark_active) {
2018 if (event
== SPICE_WATCH_EVENT_READ
) {
2020 while ((c
= read(master
, buffer
, 1024)) == -1) {
2021 if (errno
!= EAGAIN
) break;
2024 perror("master pipe read error"); // fixme
2026 spiceterm_puts (vt
, buffer
, c
);
2028 if (vt
->ibuf_count
> 0) {
2029 DPRINTF(1, "write input %x %d", vt
->ibuf
[0], vt
->ibuf_count
);
2030 if ((c
= write (master
, vt
->ibuf
, vt
->ibuf_count
)) >= 0) {
2031 if (c
== vt
->ibuf_count
) {
2034 // not all data written
2035 memmove(vt
->ibuf
, vt
->ibuf
+ c
, vt
->ibuf_count
- c
);
2036 vt
->ibuf_count
-= c
;
2038 // nothing written -ignore and try later
2041 perror("master pipe write error");
2044 if (vt
->ibuf_count
== 0) {
2045 spiceterm_update_watch_mask(vt
, FALSE
);
2051 main (int argc
, char** argv
)
2054 char **cmdargv
= NULL
;
2055 char *command
= "/bin/bash"; // execute normal shell as default
2059 struct winsize dimensions
;
2061 g_thread_init(NULL
);
2063 for (i
= 1; i
< argc
; i
++) {
2064 if (!strcmp (argv
[i
], "-c")) {
2065 command
= argv
[i
+1];
2066 cmdargv
= &argv
[i
+1];
2073 if (0) print_usage(NULL
); // fixme:
2075 spiceTerm
*vt
= create_spiceterm (argc
, argv
, 745, 400, 10);
2077 setlocale(LC_ALL
, ""); // set from environment
2079 char *ctype
= setlocale (LC_CTYPE
, NULL
); // query LC_CTYPE
2081 // fixme: ist there a standard way to detect utf8 mode ?
2082 if (strcasestr (ctype
, ".utf-8")||strcasestr (ctype
, ".utf8")) {
2086 dimensions
.ws_col
= vt
->width
;
2087 dimensions
.ws_row
= vt
->height
;
2089 setenv("TERM", TERM
, 1);
2091 DPRINTF(1, "execute %s", command
);
2093 pid
= forkpty (&master
, ptyname
, NULL
, &dimensions
);
2096 // install default signal handlers
2097 signal (SIGQUIT
, SIG_DFL
);
2098 signal (SIGTERM
, SIG_DFL
);
2099 signal (SIGINT
, SIG_DFL
);
2102 execvp (command
, cmdargv
);
2104 execlp (command
, command
, NULL
);
2106 perror ("Error: exec failed\n");
2107 exit (-1); // should not be reached
2108 } else if (pid
== -1) {
2109 perror ("Error: fork failed\n");
2113 /* watch for errors - we need to use glib directly because spice
2114 * does not have SPICE_WATCH_EVENT for this */
2115 GIOChannel
*channel
= g_io_channel_unix_new(master
);
2116 g_io_channel_set_flags(channel
, G_IO_FLAG_NONBLOCK
, NULL
);
2117 g_io_channel_set_encoding(channel
, NULL
, NULL
);
2118 g_io_add_watch(channel
, G_IO_ERR
|G_IO_HUP
, master_error_callback
, vt
);
2120 vt
->screen
->mwatch
= vt
->screen
->core
->watch_add(
2121 master
, SPICE_WATCH_EVENT_READ
/* |SPICE_WATCH_EVENT_WRITE */,
2124 basic_event_loop_mainloop();
2128 waitpid(pid
, &status
, 0);