X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=spiceterm.c;h=f4a5163a0d913f47e82fea1e99e8c2394233a303;hb=ea7edec46fe293ffe863a9d3bd8c545b4f45ff12;hp=6e5007d6e81d0137736d0f08ab272da3f6c56df3;hpb=6afaad3af87e36a8f807754222adeda8ebedfa15;p=spiceterm.git diff --git a/spiceterm.c b/spiceterm.c index 6e5007d..f4a5163 100644 --- a/spiceterm.c +++ b/spiceterm.c @@ -86,34 +86,8 @@ print_usage(const char *msg) fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n"); } -/* Convert UCS2 to UTF8 sequence, trailing zero */ -/* -static int -ucs2_to_utf8 (gunichar2 c, char *out) -{ - if (c < 0x80) { - out[0] = c; // 0******* - out[1] = 0; - return 1; - } else if (c < 0x800) { - out[0] = 0xc0 | (c >> 6); // 110***** 10****** - out[1] = 0x80 | (c & 0x3f); - out[2] = 0; - return 2; - } else { - out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10****** - out[1] = 0x80 | ((c >> 6) & 0x3f); - out[2] = 0x80 | (c & 0x3f); - out[3] = 0; - return 3; - } - - return 0; -} -*/ - static void -draw_char_at (spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib) +draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib) { if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; @@ -123,7 +97,7 @@ draw_char_at (spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib) } static void -spiceterm_update_xy (spiceTerm *vt, int x, int y) +spiceterm_update_xy(spiceTerm *vt, int x, int y) { if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } @@ -134,12 +108,12 @@ spiceterm_update_xy (spiceTerm *vt, int x, int y) } if (y2 < vt->height) { TextCell *c = &vt->cells[y1 * vt->width + x]; - draw_char_at (vt, x, y2, c->ch, c->attrib); + draw_char_at(vt, x, y2, c->ch, c->attrib); } } static void -spiceterm_clear_xy (spiceTerm *vt, int x, int y) +spiceterm_clear_xy(spiceTerm *vt, int x, int y) { if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } @@ -155,12 +129,30 @@ spiceterm_clear_xy (spiceTerm *vt, int x, int y) c->attrib.fgcol = vt->cur_attrib.fgcol; c->attrib.bgcol = vt->cur_attrib.bgcol; - draw_char_at (vt, x, y, c->ch, c->attrib); + draw_char_at(vt, x, y, c->ch, c->attrib); + } +} + +void +spiceterm_toggle_marked_cell(spiceTerm *vt, int pos) +{ + int x = (pos%vt->width); + int y = (pos/vt->width); + + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } + + int y1 = (vt->y_displ + y) % vt->total_height; + + TextCell *c = &vt->cells[y1 * vt->width + x]; + c->attrib.selected = c->attrib.selected ? 0 : 1; + + if (y < vt->height) { + draw_char_at(vt, x, y, c->ch, c->attrib); } } static void -spiceterm_show_cursor (spiceTerm *vt, int show) +spiceterm_show_cursor(spiceTerm *vt, int show) { int x = vt->cx; if (x >= vt->width) { @@ -180,15 +172,15 @@ spiceterm_show_cursor (spiceTerm *vt, int show) if (show) { TextAttributes attrib = vt->default_attrib; attrib.invers = !(attrib.invers); /* invert fg and bg */ - draw_char_at (vt, x, y, c->ch, attrib); + draw_char_at(vt, x, y, c->ch, attrib); } else { - draw_char_at (vt, x, y, c->ch, c->attrib); + draw_char_at(vt, x, y, c->ch, c->attrib); } } } static void -spiceterm_refresh (spiceTerm *vt) +spiceterm_refresh(spiceTerm *vt) { int x, y, y1; @@ -196,18 +188,32 @@ spiceterm_refresh (spiceTerm *vt) for(y = 0; y < vt->height; y++) { TextCell *c = vt->cells + y1 * vt->width; for(x = 0; x < vt->width; x++) { - draw_char_at (vt, x, y, c->ch, c->attrib); + draw_char_at(vt, x, y, c->ch, c->attrib); c++; } if (++y1 == vt->total_height) y1 = 0; } - spiceterm_show_cursor (vt, 1); + spiceterm_show_cursor(vt, 1); +} + +void +spiceterm_unselect_all(spiceTerm *vt) +{ + int i; + + for (i = 0; i < vt->width*vt->total_height; i++) { + if (vt->cells[i].attrib.selected) { + vt->cells[i].attrib.selected = 0; + } + } + + spiceterm_refresh(vt); } static void -spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines) +spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines) { if ((top + lines) >= bottom) { lines = bottom - top -1; @@ -222,7 +228,7 @@ spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines) int src = ((vt->y_base + top + i) % vt->total_height)*vt->width; int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width; - memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell)); + memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell)); } for (i = 0; i < lines; i++) { @@ -245,7 +251,7 @@ spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines) } static void -spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr) +spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr) { if ((top + lines) >= bottom) { lines = bottom - top - 1; @@ -275,7 +281,7 @@ spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width; int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width; - memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell)); + memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell)); } for (i = 1; i <= lines; i++) { @@ -290,7 +296,7 @@ spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr } static void -spiceterm_virtual_scroll (spiceTerm *vt, int lines) +spiceterm_virtual_scroll(spiceTerm *vt, int lines) { if (vt->altbuf || lines == 0) return; @@ -318,13 +324,13 @@ spiceterm_virtual_scroll (spiceTerm *vt, int lines) } } - spiceterm_refresh (vt); + spiceterm_refresh(vt); } static void -spiceterm_respond_esc (spiceTerm *vt, const char *esc) +spiceterm_respond_esc(spiceTerm *vt, const char *esc) { - int len = strlen (esc); + int len = strlen(esc); int i; if (vt->ibuf_count < (IBUFSIZE - 1 - len)) { @@ -336,17 +342,17 @@ spiceterm_respond_esc (spiceTerm *vt, const char *esc) } static void -spiceterm_put_lf (spiceTerm *vt) +spiceterm_put_lf(spiceTerm *vt) { if (vt->cy + 1 == vt->region_bottom) { if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) { - spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 1); + spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1); return; } if (vt->y_displ == vt->y_base) { - spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0); + spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0); } if (vt->y_displ == vt->y_base) { @@ -380,7 +386,7 @@ spiceterm_put_lf (spiceTerm *vt) } static void -spiceterm_csi_m (spiceTerm *vt) +spiceterm_csi_m(spiceTerm *vt) { int i; @@ -478,7 +484,7 @@ spiceterm_csi_m (spiceTerm *vt) } static void -spiceterm_save_cursor (spiceTerm *vt) +spiceterm_save_cursor(spiceTerm *vt) { vt->cx_saved = vt->cx; vt->cy_saved = vt->cy; @@ -490,7 +496,7 @@ spiceterm_save_cursor (spiceTerm *vt) } static void -spiceterm_restore_cursor (spiceTerm *vt) +spiceterm_restore_cursor(spiceTerm *vt) { vt->cx = vt->cx_saved; vt->cy = vt->cy_saved; @@ -502,7 +508,7 @@ spiceterm_restore_cursor (spiceTerm *vt) } static void -spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off) +spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off) { int x, y; @@ -516,7 +522,7 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off) /* alternate buffer & cursor */ - spiceterm_save_cursor (vt); + spiceterm_save_cursor(vt); /* save screen to altcels */ for (y = 0; y < vt->height; y++) { int y1 = (vt->y_base + y) % vt->total_height; @@ -528,7 +534,7 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off) /* clear screen */ for (y = 0; y <= vt->height; y++) { for (x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, y); + spiceterm_clear_xy(vt, x, y); } } @@ -546,14 +552,14 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off) } } - spiceterm_restore_cursor (vt); + spiceterm_restore_cursor(vt); } - spiceterm_refresh (vt); + spiceterm_refresh(vt); } static void -spiceterm_set_mode (spiceTerm *vt, int on_off) +spiceterm_set_mode(spiceTerm *vt, int on_off) { int i; @@ -561,29 +567,32 @@ spiceterm_set_mode (spiceTerm *vt, int on_off) if (vt->esc_ques) { /* DEC private modes set/reset */ switch(vt->esc_buf[i]) { case 10: /* X11 mouse reporting on/off */ - case 1000: + case 1000: /* SET_VT200_MOUSE */ + case 1002: /* xterm SET_BTN_EVENT_MOUSE */ vt->report_mouse = on_off; break; case 1049: /* start/end special app mode (smcup/rmcup) */ spiceterm_set_alternate_buffer (vt, on_off); break; case 25: /* Cursor on/off */ - case 9: /* X10 mouse reporting on/off */ + case 9: /* X10 mouse reporting on/off */ case 6: /* Origin relative/absolute */ case 1: /* Cursor keys in appl mode*/ case 5: /* Inverted screen on/off */ case 7: /* Autowrap on/off */ case 8: /* Autorepeat on/off */ break; - } + } } else { /* ANSI modes set/reset */ + //g_assert_not_reached(); + /* fixme: implement me */ } } } static void -spiceterm_gotoxy (spiceTerm *vt, int x, int y) +spiceterm_gotoxy(spiceTerm *vt, int x, int y) { /* verify all boundaries */ @@ -608,17 +617,37 @@ spiceterm_gotoxy (spiceTerm *vt, int x, int y) vt->cy = y; } +static void +debug_print_escape_buffer(spiceTerm *vt, const char *func, const char *prefix, + const char *qes, gunichar2 ch) +{ + if (debug >=1 ) { + if (vt->esc_count == 0) { + printf("%s:%s ESC[%s%c\n", func, prefix, qes, ch); + } else if (vt->esc_count == 1) { + printf("%s:%s ESC[%s%d%c\n", func, prefix, qes, vt->esc_buf[0], ch); + } else { + int i; + printf("%s:%s ESC[%s%d", func, prefix, qes, vt->esc_buf[0]); + for (i = 1; i < vt->esc_count; i++) { + printf(";%d", vt->esc_buf[i]); + } + printf("%c\n", ch); + } + } +} + enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, ESpalette, ESidquery, ESosc1, ESosc2}; static void -spiceterm_putchar (spiceTerm *vt, gunichar2 ch) +spiceterm_putchar(spiceTerm *vt, gunichar2 ch) { int x, y, i, c; if (debug && !vt->tty_state) { - DPRINTF(1, "%s: CHAR:%2d: %4x '%c' (cur_enc %d) %d %d", __func__, + DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d", vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy); } @@ -636,10 +665,10 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) vt->tty_state = ESpercent; break; case '7': - spiceterm_save_cursor (vt); + spiceterm_save_cursor(vt); break; case '8': - spiceterm_restore_cursor (vt); + spiceterm_restore_cursor(vt); break; case '(': vt->tty_state = ESsetG0; // SET G0 @@ -650,7 +679,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) case 'M': /* cursor up (ri) */ if (vt->cy == vt->region_top) - spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1); + spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1); else if (vt->cy > 0) { vt->cy--; } @@ -662,7 +691,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) /* appl. keypad - ignored */ break; default: - DPRINTF(1, "%s: got unhandled ESC%c %d", __func__, ch, ch); + DPRINTF(1, "got unhandled ESC%c %d", ch, ch); break; } break; @@ -690,7 +719,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) vt->tty_state = ESosc1; break; default: - DPRINTF(1, "%s: got unhandled OSC %c", __func__, ch); + DPRINTF(1, "got unhandled OSC %c", ch); vt->tty_state = ESnormal; break; } @@ -700,7 +729,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) if (ch == ';') { vt->tty_state = ESosc2; } else { - DPRINTF(1, "%s: got illegal OSC sequence", __func__); + DPRINTF(1, "got illegal OSC sequence"); } break; case ESosc2: @@ -710,7 +739,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) vt->osc_textbuf[i++] = ch; vt->osc_textbuf[i] = 0; } else { - DPRINTF(1, "%s: OSC:%c:%s", __func__, vt->osc_cmd, vt->osc_textbuf); + DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf); vt->tty_state = ESnormal; } break; @@ -778,33 +807,21 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) char *qes = vt->esc_ques ? "?" : ""; if (debug) { - //fixme: - if (vt->esc_count == 0) { - DPRINTF(1, "%s: ESC[%s%c", __func__, qes, ch); - } else if (vt->esc_count == 1) { - DPRINTF(1, "%s: ESC[%s%d%c\n", __func__, qes, vt->esc_buf[0], ch); - } else { - int i; - printf("ESC[%s%d", qes, vt->esc_buf[0]); - for (i = 1; i < vt->esc_count; i++) { - printf(";%d", vt->esc_buf[i]); - } - printf("%c\n", ch); - } + debug_print_escape_buffer(vt, __func__, "", qes, ch); } switch (ch) { case 'h': - spiceterm_set_mode (vt, 1); + spiceterm_set_mode(vt, 1); break; case 'l': - spiceterm_set_mode (vt, 0); + spiceterm_set_mode(vt, 0); break; case 'm': if (!vt->esc_count) { vt->esc_count++; // default parameter 0 } - spiceterm_csi_m (vt); + spiceterm_csi_m(vt); break; case 'n': /* report cursor position */ @@ -855,16 +872,16 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) case 'G': case '`': /* move cursor to column */ - spiceterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy); + spiceterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy); break; case 'd': /* move cursor to row */ - spiceterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1); + spiceterm_gotoxy(vt, vt->cx , vt->esc_buf[0] - 1); break; case 'f': case 'H': /* move cursor to row, column */ - spiceterm_gotoxy (vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); + spiceterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); break; case 'J': switch (vt->esc_buf[0]) { @@ -886,7 +903,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) if (y == vt->cy && x > vt->cx) { break; } - spiceterm_clear_xy (vt, x, y); + spiceterm_clear_xy(vt, x, y); } } break; @@ -894,7 +911,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) /* clear entire screen */ for (y = 0; y <= vt->height; y++) { for (x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, y); + spiceterm_clear_xy(vt, x, y); } } break; @@ -905,19 +922,19 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) case 0: /* clear to eol */ for(x = vt->cx; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, vt->cy); + spiceterm_clear_xy(vt, x, vt->cy); } break; case 1: /* clear from beginning of line */ for (x = 0; x <= vt->cx; x++) { - spiceterm_clear_xy (vt, x, vt->cy); + spiceterm_clear_xy(vt, x, vt->cy); } break; case 2: /* clear entire line */ for(x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, vt->cy); + spiceterm_clear_xy(vt, x, vt->cy); } break; } @@ -931,7 +948,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) else if (!c) c = 1; - spiceterm_scroll_down (vt, vt->cy, vt->region_bottom, c); + spiceterm_scroll_down(vt, vt->cy, vt->region_bottom, c); break; case 'M': /* delete line */ @@ -942,19 +959,19 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) else if (!c) c = 1; - spiceterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1); + spiceterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1); break; case 'T': /* scroll down */ c = vt->esc_buf[0]; if (!c) c = 1; - spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, c); + spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, c); break; case 'S': /* scroll up */ c = vt->esc_buf[0]; if (!c) c = 1; - spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1); + spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, c, 1); break; case 'P': /* delete c character */ @@ -970,19 +987,19 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) TextCell *dst = &vt->cells[y1 * vt->width + x]; TextCell *src = dst + c; *dst = *src; - spiceterm_update_xy (vt, x + c, vt->cy); + spiceterm_update_xy(vt, x + c, vt->cy); src->ch = ' '; src->attrib = vt->default_attrib; - spiceterm_update_xy (vt, x, vt->cy); + spiceterm_update_xy(vt, x, vt->cy); } break; case 's': /* save cursor position */ - spiceterm_save_cursor (vt); + spiceterm_save_cursor(vt); break; case 'u': /* restore cursor position */ - spiceterm_restore_cursor (vt); + spiceterm_restore_cursor(vt); break; case 'X': /* erase c characters */ @@ -992,7 +1009,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) if (c > (vt->width - vt->cx)) c = vt->width - vt->cx; for(i = 0; i < c; i++) { - spiceterm_clear_xy (vt, vt->cx + i, vt->cy); + spiceterm_clear_xy(vt, vt->cx + i, vt->cy); } break; case '@': @@ -1011,7 +1028,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) spiceterm_update_xy (vt, x + c, vt->cy); src->ch = ' '; src->attrib = vt->cur_attrib; - spiceterm_update_xy (vt, x, vt->cy); + spiceterm_update_xy(vt, x, vt->cy); } break; @@ -1028,25 +1045,13 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) vt->region_bottom = vt->esc_buf[1]; vt->cx = 0; vt->cy = vt->region_top; - DPRINTF(1, "%s: set region %d %d", __func__, vt->region_top, vt->region_bottom); + DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom); } break; default: if (debug) { - // fixme - if (vt->esc_count == 0) { - DPRINTF(1, "%s: unhandled escape ESC[%s%c", __func__, qes, ch); - } else if (vt->esc_count == 1) { - DPRINTF(1, "%s: unhandled escape ESC[%s%d%c\n", __func__, qes, vt->esc_buf[0], ch); - } else { - int i; - printf("unhandled escape ESC[%s%d", qes, vt->esc_buf[0]); - for (i = 1; i < vt->esc_count; i++) { - printf(";%d", vt->esc_buf[i]); - } - printf("%c\n", ch); - } + debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch); } break; } @@ -1088,9 +1093,9 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) vt->tty_state = ESnormal; if (ch == 'c') { - DPRINTF(1, "%s: ESC[>c Query term ID", __func__); - spiceterm_respond_esc (vt, TERMIDCODE); - } + DPRINTF(1, "ESC[>c Query term ID"); + spiceterm_respond_esc(vt, TERMIDCODE); + } break; case ESpercent: vt->tty_state = ESnormal; @@ -1121,7 +1126,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) case 9: /* tabspace */ if (vt->cx + (8 - (vt->cx % 8)) > vt->width) { vt->cx = 0; - spiceterm_put_lf (vt); + spiceterm_put_lf(vt); } else { vt->cx = vt->cx + (8 - (vt->cx % 8)); } @@ -1129,7 +1134,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) case 10: /* LF,*/ case 11: /* VT */ case 12: /* FF */ - spiceterm_put_lf (vt); + spiceterm_put_lf(vt); break; case 13: /* carriage return */ vt->cx = 0; @@ -1159,14 +1164,14 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) if (vt->cx >= vt->width) { /* line wrap */ vt->cx = 0; - spiceterm_put_lf (vt); + spiceterm_put_lf(vt); } int y1 = (vt->y_base + vt->cy) % vt->total_height; TextCell *c = &vt->cells[y1*vt->width + vt->cx]; c->attrib = vt->cur_attrib; c->ch = ch; - spiceterm_update_xy (vt, vt->cx, vt->cy); + spiceterm_update_xy(vt, vt->cx, vt->cy); vt->cx++; break; } @@ -1175,11 +1180,11 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch) } static int -spiceterm_puts (spiceTerm *vt, const char *buf, int len) +spiceterm_puts(spiceTerm *vt, const char *buf, int len) { gunichar2 tc; - spiceterm_show_cursor (vt, 0); + spiceterm_show_cursor(vt, 0); while (len) { unsigned char c = *buf; @@ -1239,17 +1244,17 @@ spiceterm_puts (spiceTerm *vt, const char *buf, int len) } } - spiceterm_putchar (vt, tc); + spiceterm_putchar(vt, tc); } - spiceterm_show_cursor (vt, 1); + spiceterm_show_cursor(vt, 1); return len; } /* fixme: void -spiceterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl) +spiceterm_set_xcut_text(char* str, int len, struct _rfbClientRec* cl) { spiceTerm *vt =(spiceTerm *)cl->screen->screenData; @@ -1263,177 +1268,181 @@ spiceterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl) vt->selection_len = len; } */ -/* + static void -mouse_report (spiceTerm *vt, int butt, int mrx, int mry) +spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable) { - char buf[8]; + g_assert(vt != NULL); + + int mask = SPICE_WATCH_EVENT_READ; - sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), - (char)('!' + mry)); + if (writable) { + mask |= SPICE_WATCH_EVENT_WRITE; + } - spiceterm_respond_esc (vt, buf); + vt->screen->core->watch_update_mask(vt->screen->mwatch, mask); } -*/ -void -spiceterm_toggle_marked_cell (spiceTerm *vt, int pos) +static void +mouse_report(spiceTerm *vt, int butt, int mrx, int mry) { + char buf[8]; -/* fixme: - int x= (pos%vt->width)*8; - int y= (pos/vt->width)*16; - - int i,j; - rfbScreenInfoPtr s=vt->screen; + sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), + (char)('!' + mry)); - char *b = s->frameBuffer+y*s->width+x; + spiceterm_respond_esc(vt, buf); - for (j=0; j < 16; j++) { - for(i=0; i < 8; i++) { - b[j*s->width+i] ^= 0x0f; - rfbMarkRectAsModified (s, x, y, x+8, y+16); - } - } -*/ + spiceterm_update_watch_mask(vt, TRUE); } -/* fixme: - -void -spiceterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl) +static void +spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc) { + if (vt->utf8) { + gchar buf[10]; + gint len = g_unichar_to_utf8(uc, buf); - spiceTerm *vt =(spiceTerm *)cl->screen->screenData; - static int button2_released = 1; - static int last_mask = 0; - static int sel_start_pos = 0; - static int sel_end_pos = 0; - int i; + if (len > 0) { + if ((vt->ibuf_count + len) < IBUFSIZE) { + int i; + for (i = 0; i < len; i++) { + vt->ibuf[vt->ibuf_count++] = buf[i]; + } + } else { + fprintf(stderr, "warning: input buffer overflow\n"); + } + } + } else { + if ((vt->ibuf_count + 1) < IBUFSIZE) { + vt->ibuf[vt->ibuf_count++] = (char)uc; + } else { + fprintf(stderr, "warning: input buffer overflow\n"); + } + } +} - int cx = x/8; - int cy = y/16; +static void +spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons) +{ + DPRINTF(1, "mask=%08x x=%d y=%d", buttons, x ,y); - if (cx < 0) cx = 0; - if (cx >= vt->width) cx = vt->width - 1; - if (cy < 0) cy = 0; - if (cy >= vt->height) cy = vt->height - 1; + static int last_mask = 0; + static int sel_start_pos = 0; + static int sel_end_pos = 0; + static int button2_released = 1; - if (vt->report_mouse && buttonMask != last_mask) { - last_mask = buttonMask; - if (buttonMask & 1) { - mouse_report (vt, 0, cx, cy); - } - if (buttonMask & 2) { - mouse_report (vt, 1, cx, cy); - } - if (buttonMask & 4) { - mouse_report (vt, 2, cx, cy); - } - if (!buttonMask) { - mouse_report (vt, 3, cx, cy); + int i; + int cx = x/8; + int cy = y/16; + + if (cx < 0) cx = 0; + if (cx >= vt->width) cx = vt->width - 1; + if (cy < 0) cy = 0; + if (cy >= vt->height) cy = vt->height - 1; + + if (vt->report_mouse && buttons != last_mask) { + last_mask = buttons; + if (buttons & 2) { + mouse_report(vt, 0, cx, cy); + } + if (buttons & 4) { + mouse_report (vt, 1, cx, cy); + } + if (buttons & 8) { + mouse_report(vt, 2, cx, cy); + } + if(!buttons) { + mouse_report(vt, 3, cx, cy); + } } - } - if (buttonMask & 2) { - if(button2_released && vt->selection) { - int i; - for(i = 0; i < vt->selection_len; i++) { - if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide - if (vt->utf8) { - vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]); - } else { - vt->ibuf[vt->ibuf_count++] = vt->selection[i]; - } - } - } - if (vt->y_displ != vt->y_base) { - vt->y_displ = vt->y_base; - spiceterm_refresh (vt); - } + if (buttons & 4) { + if(button2_released && vt->selection) { + int i; + for(i = 0; i < vt->selection_len; i++) { + spiceterm_respond_unichar2(vt, vt->selection[i]); + } + spiceterm_update_watch_mask(vt, TRUE); + if (vt->y_displ != vt->y_base) { + vt->y_displ = vt->y_base; + spiceterm_refresh(vt); + } + } + button2_released = 0; + } else { + button2_released = 1; } - button2_released = 0; - } else { - button2_released = 1; - } - if (buttonMask & 1) { - int pos = cy*vt->width + cx; + if (buttons & 2) { + int pos = cy*vt->width + cx; - // code borrowed from libvncserver (VNCconsole.c) + // code borrowed from libvncserver (VNCconsole.c) - if (!vt->mark_active) { + if (!vt->mark_active) { - vt->mark_active = 1; - sel_start_pos = sel_end_pos = pos; - spiceterm_toggle_marked_cell (vt, pos); + spiceterm_unselect_all(vt); - } else { + vt->mark_active = 1; + sel_start_pos = sel_end_pos = pos; + spiceterm_toggle_marked_cell(vt, pos); - if (pos != sel_end_pos) { + } else { - if (pos > sel_end_pos) { - cx = sel_end_pos; cy=pos; - } else { - cx=pos; cy=sel_end_pos; - } + if (pos != sel_end_pos) { + if (pos > sel_end_pos) { + cx = sel_end_pos; cy=pos; + } else { + cx=pos; cy=sel_end_pos; + } - if (cx < sel_start_pos) { - if (cy < sel_start_pos) cy--; - } else { - cx++; - } + if (cx < sel_start_pos) { + if (cy < sel_start_pos) cy--; + } else { + cx++; + } - while (cx <= cy) { - spiceterm_toggle_marked_cell (vt, cx); - cx++; - } + while (cx <= cy) { + spiceterm_toggle_marked_cell(vt, cx); + cx++; + } - sel_end_pos = pos; - } - } + sel_end_pos = pos; + } + } - } else if (vt->mark_active) { - vt->mark_active = 0; + } else if (vt->mark_active) { + vt->mark_active = 0; - if (sel_start_pos > sel_end_pos) { - int tmp = sel_start_pos - 1; - sel_start_pos = sel_end_pos; - sel_end_pos = tmp; - } + if (sel_start_pos > sel_end_pos) { + int tmp = sel_start_pos - 1; + sel_start_pos = sel_end_pos; + sel_end_pos = tmp; + } - int len = sel_end_pos - sel_start_pos + 1; - - if (vt->selection) free (vt->selection); - vt->selection = (gunichar2 *)malloc (len*sizeof (gunichar2)); - vt->selection_len = len; - char *sel_latin1 = (char *)malloc (len + 1); - - for (i = 0; i < len; i++) { - int pos = sel_start_pos + i; - int x = pos % vt->width; - int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height; - TextCell *c = &vt->cells[y1*vt->width + x]; - vt->selection[i] = c->ch; - sel_latin1[i] = (char)c->ch; - c++; - } - sel_latin1[len] = 0; - rfbGotXCutText (vt->screen, sel_latin1, len); - free (sel_latin1); + int len = sel_end_pos - sel_start_pos + 1; - while (sel_start_pos <= sel_end_pos) { - spiceterm_toggle_marked_cell (vt, sel_start_pos++); - } + if (vt->selection) free (vt->selection); + vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2)); + vt->selection_len = len; - } + for (i = 0; i < len; i++) { + int pos = sel_start_pos + i; + int x = pos % vt->width; + int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height; + TextCell *c = &vt->cells[y1*vt->width + x]; + vt->selection[i] = c->ch; + c++; + } - rfbDefaultPtrAddEvent (buttonMask, x, y, cl); + DPRINTF(1, "selection length = %d", vt->selection_len); + // fixme: tell client we have something seletced + //rfbGotXCutText (vt->screen, sel_latin1, len); + } } -*/ -static void +static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) { // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin); @@ -1443,7 +1452,7 @@ my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) return; } -static void +static void my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) { spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin); @@ -1451,9 +1460,9 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) static int shift = 0; char *esc = NULL; - guint uc = 0; + gunichar2 uc = 0; - DPRINTF(1, "%s: flags=%d keySym=%08x", __func__, flags, keySym); + DPRINTF(1, "flags=%d keySym=%08x", flags, keySym); if (flags & 1) { if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) { @@ -1548,7 +1557,7 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) } } - DPRINTF(1, "%s: escape=%s unicode=%08x\n", __func__, esc, uc); + DPRINTF(1, "escape=%s unicode=%08x\n", esc, uc); if (vt->y_displ != vt->y_base) { vt->y_displ = vt->y_base; @@ -1558,19 +1567,7 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) if (esc) { spiceterm_respond_esc(vt, esc); } else if (uc > 0) { - if (vt->utf8) { - gchar buf[10]; - gint len = g_unichar_to_utf8(uc, buf); - - if (len > 0) { - int i; - for (i = 0; i < len; i++) { - vt->ibuf[vt->ibuf_count++] = buf[i]; - } - } - } else { - vt->ibuf[vt->ibuf_count++] = (char)uc; - } + spiceterm_respond_unichar2(vt, uc); } } } @@ -1586,18 +1583,17 @@ ret: } } - vt->screen->core->watch_update_mask(vt->screen->mwatch, - SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE); + spiceterm_update_watch_mask(vt, TRUE); } -static uint8_t +static uint8_t my_kbd_get_leds(SpiceKbdInstance *sin) { return 0; } static SpiceKbdInterface my_keyboard_sif = { - .base.type = SPICE_INTERFACE_KEYBOARD , + .base.type = SPICE_INTERFACE_KEYBOARD, .base.description = "spiceterm keyboard device", .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, @@ -1606,24 +1602,78 @@ static SpiceKbdInterface my_keyboard_sif = { .get_leds = my_kbd_get_leds, }; -spiceTerm * -create_spiceterm(int argc, char** argv, int maxx, int maxy) +/* vdagent interface - to get mouse/clipboarde support */ +static int +vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) +{ + spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, vdagent_sin); + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + + //g_assert(hdr->port == VDP_SERVER_PORT); + g_assert(msg->protocol == VD_AGENT_PROTOCOL); + + DPRINTF(1, "%d %d %d %d", len, hdr->port, msg->protocol, msg->type); + + if (msg->type == VD_AGENT_MOUSE_STATE) { + VDAgentMouseState *info = (VDAgentMouseState *)&msg[1]; + spiceterm_motion_event(vt, info->x, info->y, info->buttons); + } else if (msg->type == VD_AGENT_ANNOUNCE_CAPABILITIES) { + /* ignore for now */ + } else if (msg->type == VD_AGENT_MONITORS_CONFIG) { + /* ignore for now */ + } else { + DPRINTF(0, "got uknown vdagent message type %d\n", msg->type); + } + + return len; +} + +static int +vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) +{ + DPRINTF(1, "%d", len); + + return 0; +} + +static void +vmc_state(SpiceCharDeviceInstance *sin, int connected) +{ + /* IGNORE */ +} + +static SpiceCharDeviceInterface my_vdagent_sif = { + .base.type = SPICE_INTERFACE_CHAR_DEVICE, + .base.description = "spice virtual channel char device", + .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, + .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, + .state = vmc_state, + .write = vmc_write, + .read = vmc_read, +}; + +static spiceTerm * +create_spiceterm(int argc, char** argv, int maxx, int maxy, guint timeout) { int i; SpiceScreen *spice_screen; SpiceCoreInterface *core = basic_event_loop_init(); - spice_screen = spice_screen_new(core); + spice_screen = spice_screen_new(core, timeout); //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF); - spice_screen_add_display_interface(spice_screen); - spice_screen_add_agent_interface(spice_screen->server); spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1); vt->keyboard_sin.base.sif = &my_keyboard_sif.base; spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base); + vt->vdagent_sin.base.sif = &my_vdagent_sif.base; + vt->vdagent_sin.subtype = "vdagent"; + spice_server_add_interface(spice_screen->server, &vt->vdagent_sin.base); + // screen->setXCutText = spiceterm_set_xcut_text; // screen->ptrAddEvent = spiceterm_pointer_event; // screen->newClientHook = new_client; @@ -1673,30 +1723,56 @@ create_spiceterm(int argc, char** argv, int maxx, int maxy) return vt; } -static void +static gboolean +master_error_callback(GIOChannel *channel, GIOCondition condition, + gpointer data) +{ + //spiceTerm *vt = (spiceTerm *)data; + + DPRINTF(1, "condition %d", condition); + + exit(0); + + return FALSE; +} + +static void master_watch(int master, int event, void *opaque) { spiceTerm *vt = (spiceTerm *)opaque; + int c; // fixme: if (!vt->mark_active) { if (event == SPICE_WATCH_EVENT_READ) { char buffer[1024]; - int c; while ((c = read(master, buffer, 1024)) == -1) { if (errno != EAGAIN) break; } if (c == -1) { - g_error("got read error"); // fixme + perror("master pipe read error"); // fixme } spiceterm_puts (vt, buffer, c); } else { if (vt->ibuf_count > 0) { - DPRINTF(1, "%s: write input %x %d", __func__, vt->ibuf[0], vt->ibuf_count); - write (master, vt->ibuf, vt->ibuf_count); - vt->ibuf_count = 0; // fixme: what if not all data written + DPRINTF(1, "write input %x %d", vt->ibuf[0], vt->ibuf_count); + if ((c = write (master, vt->ibuf, vt->ibuf_count)) >= 0) { + if (c == vt->ibuf_count) { + vt->ibuf_count = 0; + } else if (c > 0) { + // not all data written + memmove(vt->ibuf, vt->ibuf + c, vt->ibuf_count - c); + vt->ibuf_count -= c; + } else { + // nothing written -ignore and try later + } + } else { + perror("master pipe write error"); + } + } + if (vt->ibuf_count == 0) { + spiceterm_update_watch_mask(vt, FALSE); } - vt->screen->core->watch_update_mask(vt->screen->mwatch, SPICE_WATCH_EVENT_READ); } } @@ -1725,7 +1801,7 @@ main (int argc, char** argv) if (0) print_usage(NULL); // fixme: - spiceTerm *vt = create_spiceterm (argc, argv, 745, 400); + spiceTerm *vt = create_spiceterm (argc, argv, 745, 400, 10); setlocale(LC_ALL, ""); // set from environment @@ -1739,9 +1815,9 @@ main (int argc, char** argv) dimensions.ws_col = vt->width; dimensions.ws_row = vt->height; - setenv ("TERM", TERM, 1); + setenv("TERM", TERM, 1); - DPRINTF(1, "%s: execute %s", __func__, command); + DPRINTF(1, "execute %s", command); pid = forkpty (&master, ptyname, NULL, &dimensions); if(!pid) { @@ -1763,6 +1839,12 @@ main (int argc, char** argv) exit (-1); } + /* watch for errors - we need to use glib directly because spice + * does not have SPICE_WATCH_EVENT for this */ + GIOChannel *channel = g_io_channel_unix_new(master); + g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_add_watch(channel, G_IO_ERR|G_IO_HUP, master_error_callback, vt); vt->screen->mwatch = vt->screen->core->watch_add( master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */, @@ -1770,17 +1852,6 @@ main (int argc, char** argv) basic_event_loop_mainloop(); - //rfbProcessEvents (vt->screen, 40000); /* 40 ms */ - - /* - if (vt->ibuf_count > 0) { - printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count); - write (master, vt->ibuf, vt->ibuf_count); - vt->ibuf_count = 0; - last_time = time (NULL); - } - */ - kill (pid, 9); int status; waitpid(pid, &status, 0);