X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=spiceterm.c;h=31f719f16c0e2847b62fba263941d48aecd09bb0;hb=8f53ae816c9d9a1ddc4e3e2bef99f482855805a6;hp=6a09e402bc4eb8818f753dc86a46c938ab692784;hpb=b052e9c7dc3a8fcef287a2a6ebb92811f05509de;p=spiceterm.git diff --git a/spiceterm.c b/spiceterm.c index 6a09e40..31f719f 100644 --- a/spiceterm.c +++ b/spiceterm.c @@ -20,12 +20,12 @@ Author: Dietmar Maurer - Note: most of the code here is copied from vncterm (which is + Note: most of the code here is copied from vncterm (which is also written by me). */ -#define _GNU_SOURCE +#define _GNU_SOURCE #include #include @@ -56,7 +56,7 @@ #include "translations.h" static int debug = 0; - + #define DPRINTF(x, format, ...) { \ if (x <= debug) { \ printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \ @@ -79,41 +79,20 @@ unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15 }; -static void -print_usage (const char *msg) -{ - if (msg) { fprintf(stderr, "ERROR: %s\n", msg); } - fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n"); -} +static void spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height); -/* 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; - } +static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection); +static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection); - return 0; +static void +print_usage(const char *msg) +{ + if (msg) { fprintf(stderr, "ERROR: %s\n", msg); } + fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n"); } -*/ 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,94 +102,123 @@ 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; } + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } - int y1 = (vt->y_base + y) % vt->total_height; - int y2 = y1 - vt->y_displ; - if (y2 < 0) { - y2 += vt->total_height; - } - if (y2 < vt->height) { - TextCell *c = &vt->cells[y1 * vt->width + x]; - draw_char_at (vt, x, y2, c->ch, c->attrib); - } + int y1 = (vt->y_base + y) % vt->total_height; + int y2 = y1 - vt->y_displ; + if (y2 < 0) { + y2 += vt->total_height; + } + if (y2 < vt->height) { + TextCell *c = &vt->cells[y1 * vt->width + x]; + 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; } + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } + + int y1 = (vt->y_base + y) % vt->total_height; + int y2 = y1 - vt->y_displ; + if (y2 < 0) { + y2 += vt->total_height; + } + if (y2 < vt->height) { + TextCell *c = &vt->cells[y1 * vt->width + x]; + c->ch = ' '; + c->attrib = vt->default_attrib; + c->attrib.fgcol = vt->cur_attrib.fgcol; + c->attrib.bgcol = vt->cur_attrib.bgcol; + + 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; - int y1 = (vt->y_base + y) % vt->total_height; - int y2 = y1 - vt->y_displ; - if (y2 < 0) { - y2 += vt->total_height; - } - if (y2 < vt->height) { TextCell *c = &vt->cells[y1 * vt->width + x]; - c->ch = ' '; - c->attrib = vt->default_attrib; - c->attrib.fgcol = vt->cur_attrib.fgcol; - c->attrib.bgcol = vt->cur_attrib.bgcol; + c->attrib.selected = c->attrib.selected ? 0 : 1; - draw_char_at (vt, x, y, c->ch, c->attrib); - } + 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) { - x = vt->width - 1; - } + int x = vt->cx; + if (x >= vt->width) { + x = vt->width - 1; + } - int y1 = (vt->y_base + vt->cy) % vt->total_height; - int y = y1 - vt->y_displ; - if (y < 0) { - y += vt->total_height; - } + int y1 = (vt->y_base + vt->cy) % vt->total_height; + int y = y1 - vt->y_displ; + if (y < 0) { + y += vt->total_height; + } - if (y < vt->height) { + if (y < vt->height) { - TextCell *c = &vt->cells[y1 * vt->width + x]; + TextCell *c = &vt->cells[y1 * vt->width + x]; - if (show) { - TextAttributes attrib = vt->default_attrib; - attrib.invers = !(attrib.invers); /* invert fg and bg */ - draw_char_at (vt, x, y, c->ch, attrib); - } else { - draw_char_at (vt, x, y, c->ch, c->attrib); + if (show) { + TextAttributes attrib = vt->default_attrib; + attrib.invers = !(attrib.invers); /* invert fg and bg */ + draw_char_at(vt, x, y, c->ch, attrib); + } else { + draw_char_at(vt, x, y, c->ch, c->attrib); + } } - } } static void -spiceterm_refresh (spiceTerm *vt) +spiceterm_refresh(spiceTerm *vt) { - int x, y, y1; + int x, y, y1; - // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol); + y1 = vt->y_displ; + 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); + c++; + } + if (++y1 == vt->total_height) + y1 = 0; + } + + spiceterm_show_cursor(vt, 1); +} + +void +spiceterm_unselect_all(spiceTerm *vt) +{ + int i; - y1 = vt->y_displ; - 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); - c++; + for (i = 0; i < vt->width*vt->total_height; i++) { + if (vt->cells[i].attrib.selected) { + vt->cells[i].attrib.selected = 0; + } } - if (++y1 == vt->total_height) - y1 = 0; - } - //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy); - spiceterm_show_cursor (vt, 1); + 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; @@ -225,7 +233,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++) { @@ -248,7 +256,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; @@ -266,7 +274,7 @@ spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr spice_screen_scroll(vt->screen, 0, y0, vt->screen->primary_width, y2 -h, 0, y1); spice_screen_clear(vt->screen, 0, y2 -h, vt->screen->primary_width, y2); - + if (!moveattr) { return; } @@ -278,7 +286,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++) { @@ -293,323 +301,345 @@ 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; - - if (lines < 0) { - lines = -lines; - int i = vt->scroll_height; - if (i > vt->total_height - vt->height) - i = vt->total_height - vt->height; - int y1 = vt->y_base - i; - if (y1 < 0) - y1 += vt->total_height; - for(i = 0; i < lines; i++) { - if (vt->y_displ == y1) break; - if (--vt->y_displ < 0) { - vt->y_displ = vt->total_height - 1; - } - } - } else { - int i; - for(i = 0; i < lines; i++) { - if (vt->y_displ == vt->y_base) break; - if (++vt->y_displ == vt->total_height) { - vt->y_displ = 0; - } + if (vt->altbuf || lines == 0) return; + + if (lines < 0) { + lines = -lines; + int i = vt->scroll_height; + if (i > vt->total_height - vt->height) + i = vt->total_height - vt->height; + int y1 = vt->y_base - i; + if (y1 < 0) + y1 += vt->total_height; + for(i = 0; i < lines; i++) { + if (vt->y_displ == y1) break; + if (--vt->y_displ < 0) { + vt->y_displ = vt->total_height - 1; + } + } + } else { + int i; + for(i = 0; i < lines; i++) { + if (vt->y_displ == vt->y_base) break; + if (++vt->y_displ == vt->total_height) { + vt->y_displ = 0; + } + } } - } - - 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 i; + int len = strlen(esc); + int i; - if (vt->ibuf_count < (IBUFSIZE - 1 - len)) { - vt->ibuf[vt->ibuf_count++] = 27; - for (i = 0; i < len; i++) { - vt->ibuf[vt->ibuf_count++] = esc[i]; + if (vt->ibuf_count < (IBUFSIZE - 1 - len)) { + vt->ibuf[vt->ibuf_count++] = 27; + for (i = 0; i < len; i++) { + vt->ibuf[vt->ibuf_count++] = esc[i]; + } } - } } static void -spiceterm_put_lf (spiceTerm *vt) +spiceterm_put_lf(spiceTerm *vt) { - if (vt->cy + 1 == vt->region_bottom) { + 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); - return; - } + if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) { + 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); - } + if (vt->y_displ == vt->y_base) { + spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0); + } - if (vt->y_displ == vt->y_base) { - if (++vt->y_displ == vt->total_height) { - vt->y_displ = 0; - } - } + if (vt->y_displ == vt->y_base) { + if (++vt->y_displ == vt->total_height) { + vt->y_displ = 0; + } + } - if (++vt->y_base == vt->total_height) { - vt->y_base = 0; - } + if (++vt->y_base == vt->total_height) { + vt->y_base = 0; + } - if (vt->scroll_height < vt->total_height) { - vt->scroll_height++; - } + if (vt->scroll_height < vt->total_height) { + vt->scroll_height++; + } - int y1 = (vt->y_base + vt->height - 1) % vt->total_height; - TextCell *c = &vt->cells[y1 * vt->width]; - int x; - for (x = 0; x < vt->width; x++) { - c->ch = ' '; - c->attrib = vt->default_attrib; - c++; - } + int y1 = (vt->y_base + vt->height - 1) % vt->total_height; + TextCell *c = &vt->cells[y1 * vt->width]; + int x; + for (x = 0; x < vt->width; x++) { + c->ch = ' '; + c->attrib = vt->default_attrib; + c++; + } - // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ); + // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ); - } else if (vt->cy < vt->height - 1) { - vt->cy += 1; - } + } else if (vt->cy < vt->height - 1) { + vt->cy += 1; + } } - static void -spiceterm_csi_m (spiceTerm *vt) +spiceterm_csi_m(spiceTerm *vt) { - int i; + int i; - for (i = 0; i < vt->esc_count; i++) { - switch (vt->esc_buf[i]) { - case 0: /* reset all console attributes to default */ - vt->cur_attrib = vt->default_attrib; - break; - case 1: - vt->cur_attrib.bold = 1; - break; - case 4: - vt->cur_attrib.uline = 1; - break; - case 5: - vt->cur_attrib.blink = 1; - break; - case 7: - vt->cur_attrib.invers = 1; - break; - case 8: - vt->cur_attrib.unvisible = 1; - break; - case 10: - vt->cur_enc = LAT1_MAP; - // fixme: dispaly controls = 0 ? - // fixme: toggle meta = 0 ? - break; - case 11: - vt->cur_enc = IBMPC_MAP; - // fixme: dispaly controls = 1 ? - // fixme: toggle meta = 0 ? - break; - case 12: - vt->cur_enc = IBMPC_MAP; - // fixme: dispaly controls = 1 ? - // fixme: toggle meta = 1 ? - break; - case 22: - vt->cur_attrib.bold = 0; - break; - case 24: - vt->cur_attrib.uline = 0; - break; - case 25: - vt->cur_attrib.blink = 0; - break; - case 27: - vt->cur_attrib.invers = 0; - break; - case 28: - vt->cur_attrib.unvisible = 0; - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - /* set foreground color */ - vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30]; - break; - case 38: - /* reset color to default, enable underline */ - vt->cur_attrib.fgcol = vt->default_attrib.fgcol; - vt->cur_attrib.uline = 1; - break; - case 39: - /* reset color to default, disable underline */ - vt->cur_attrib.fgcol = vt->default_attrib.fgcol; - vt->cur_attrib.uline = 0; - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - /* set background color */ - vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40]; - break; - case 49: - /* reset background color */ - vt->cur_attrib.bgcol = vt->default_attrib.bgcol; - break; - default: - fprintf(stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]); - //fixme: implement - } - } + for (i = 0; i < vt->esc_count; i++) { + switch (vt->esc_buf[i]) { + case 0: /* reset all console attributes to default */ + vt->cur_attrib = vt->default_attrib; + break; + case 1: + vt->cur_attrib.bold = 1; + break; + case 4: + vt->cur_attrib.uline = 1; + break; + case 5: + vt->cur_attrib.blink = 1; + break; + case 7: + vt->cur_attrib.invers = 1; + break; + case 8: + vt->cur_attrib.unvisible = 1; + break; + case 10: + vt->cur_enc = LAT1_MAP; + // fixme: dispaly controls = 0 ? + // fixme: toggle meta = 0 ? + break; + case 11: + vt->cur_enc = IBMPC_MAP; + // fixme: dispaly controls = 1 ? + // fixme: toggle meta = 0 ? + break; + case 12: + vt->cur_enc = IBMPC_MAP; + // fixme: dispaly controls = 1 ? + // fixme: toggle meta = 1 ? + break; + case 22: + vt->cur_attrib.bold = 0; + break; + case 24: + vt->cur_attrib.uline = 0; + break; + case 25: + vt->cur_attrib.blink = 0; + break; + case 27: + vt->cur_attrib.invers = 0; + break; + case 28: + vt->cur_attrib.unvisible = 0; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + /* set foreground color */ + vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30]; + break; + case 38: + /* reset color to default, enable underline */ + vt->cur_attrib.fgcol = vt->default_attrib.fgcol; + vt->cur_attrib.uline = 1; + break; + case 39: + /* reset color to default, disable underline */ + vt->cur_attrib.fgcol = vt->default_attrib.fgcol; + vt->cur_attrib.uline = 0; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + /* set background color */ + vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40]; + break; + case 49: + /* reset background color */ + vt->cur_attrib.bgcol = vt->default_attrib.bgcol; + break; + default: + fprintf(stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]); + //fixme: implement + } + } } static void -spiceterm_save_cursor (spiceTerm *vt) +spiceterm_save_cursor(spiceTerm *vt) { - vt->cx_saved = vt->cx; - vt->cy_saved = vt->cy; - vt->cur_attrib_saved = vt->cur_attrib; - vt->charset_saved = vt->charset; - vt->g0enc_saved = vt->g0enc; - vt->g1enc_saved = vt->g1enc; - vt->cur_enc_saved = vt->cur_enc; + vt->cx_saved = vt->cx; + vt->cy_saved = vt->cy; + vt->cur_attrib_saved = vt->cur_attrib; + vt->charset_saved = vt->charset; + vt->g0enc_saved = vt->g0enc; + vt->g1enc_saved = vt->g1enc; + vt->cur_enc_saved = vt->cur_enc; } static void -spiceterm_restore_cursor (spiceTerm *vt) +spiceterm_restore_cursor(spiceTerm *vt) { - vt->cx = vt->cx_saved; - vt->cy = vt->cy_saved; - vt->cur_attrib = vt->cur_attrib_saved; - vt->charset = vt->charset_saved; - vt->g0enc = vt->g0enc_saved; - vt->g1enc = vt->g1enc_saved; - vt->cur_enc = vt->cur_enc_saved; + vt->cx = vt->cx_saved; + vt->cy = vt->cy_saved; + vt->cur_attrib = vt->cur_attrib_saved; + vt->charset = vt->charset_saved; + vt->g0enc = vt->g0enc_saved; + vt->g1enc = vt->g1enc_saved; + vt->cur_enc = vt->cur_enc_saved; } static void -spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off) +spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off) { - int x, y; + int x, y; - vt->y_displ = vt->y_base; + vt->y_displ = vt->y_base; - if (on_off) { + if (on_off) { - if (vt->altbuf) return; + if (vt->altbuf) return; - vt->altbuf = 1; + vt->altbuf = 1; - /* alternate buffer & cursor */ + /* alternate buffer & cursor */ - spiceterm_save_cursor (vt); - /* save screen to altcels */ - for (y = 0; y < vt->height; y++) { - int y1 = (vt->y_base + y) % vt->total_height; - for (x = 0; x < vt->width; x++) { - vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x]; - } - } + spiceterm_save_cursor(vt); + /* save screen to altcels */ + for (y = 0; y < vt->height; y++) { + int y1 = (vt->y_base + y) % vt->total_height; + for (x = 0; x < vt->width; x++) { + vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x]; + } + } - /* clear screen */ - for (y = 0; y <= vt->height; y++) { - for (x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, y); - } - } + /* clear screen */ + for (y = 0; y <= vt->height; y++) { + for (x = 0; x < vt->width; x++) { + spiceterm_clear_xy(vt, x, y); + } + } - } else { + } else { - if (vt->altbuf == 0) return; + if (vt->altbuf == 0) return; - vt->altbuf = 0; + vt->altbuf = 0; - /* restore saved data */ - for (y = 0; y < vt->height; y++) { - int y1 = (vt->y_base + y) % vt->total_height; - for (x = 0; x < vt->width; x++) { - vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x]; - } - } + /* restore saved data */ + for (y = 0; y < vt->height; y++) { + int y1 = (vt->y_base + y) % vt->total_height; + for (x = 0; x < vt->width; x++) { + vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x]; + } + } - 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; + int i; - for (i = 0; i <= vt->esc_count; i++) { - if (vt->esc_ques) { /* DEC private modes set/reset */ - switch(vt->esc_buf[i]) { - case 10: /* X11 mouse reporting on/off */ - case 1000: - 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 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 */ - /* fixme: implement me */ + for (i = 0; i <= vt->esc_count; i++) { + if (vt->esc_ques) { /* DEC private modes set/reset */ + switch(vt->esc_buf[i]) { + case 10: /* X11 mouse reporting on/off */ + 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 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 */ + /* verify all boundaries */ - if (x < 0) { - x = 0; - } + if (x < 0) { + x = 0; + } - if (x >= vt->width) { - x = vt->width - 1; - } + if (x >= vt->width) { + x = vt->width - 1; + } - vt->cx = x; + vt->cx = x; - if (y < 0) { - y = 0; - } + if (y < 0) { + y = 0; + } - if (y >= vt->height) { - y = vt->height - 1; - } + if (y >= vt->height) { + y = vt->height - 1; + } - vt->cy = 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, @@ -617,641 +647,619 @@ enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, ESpalette, ESidquery, ESosc1, ESosc2}; static void -spiceterm_putchar (spiceTerm *vt, gunichar2 ch) +spiceterm_putchar(spiceTerm *vt, gunichar2 ch) { - int x, y, i, c; - - if (!vt->tty_state) { - DPRINTF(1, "%s: CHAR:%2d: %4x '%c' (cur_enc %d) %d %d", __func__, - vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy); - } + int x, y, i, c; - switch(vt->tty_state) { - case ESesc: - vt->tty_state = ESnormal; - switch (ch) { - case '[': - vt->tty_state = ESsquare; - break; - case ']': - vt->tty_state = ESnonstd; - break; - case '%': - vt->tty_state = ESpercent; - break; - case '7': - spiceterm_save_cursor (vt); - break; - case '8': - spiceterm_restore_cursor (vt); - break; - case '(': - vt->tty_state = ESsetG0; // SET G0 - break; - case ')': - vt->tty_state = ESsetG1; // SET G1 - break; - case 'M': - /* cursor up (ri) */ - if (vt->cy == vt->region_top) - spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1); - else if (vt->cy > 0) { - vt->cy--; - } - break; - case '>': - /* numeric keypad - ignored */ - break; - case '=': - /* appl. keypad - ignored */ - break; - default: - DPRINTF(1, "%s: got unhandled ESC%c %d", __func__, ch, ch); - break; - } - break; - case ESnonstd: /* Operating System Controls */ - vt->tty_state = ESnormal; - - switch (ch) { - case 'P': /* palette escape sequence */ - for(i = 0; i < MAX_ESC_PARAMS; i++) { - vt->esc_buf[i] = 0; - } - - vt->esc_count = 0; - vt->tty_state = ESpalette; - break; - case 'R': /* reset palette */ - // fixme: reset_palette(vc); - break; - case '0': - case '1': - case '2': - case '4': - vt->osc_cmd = ch; - vt->osc_textbuf[0] = 0; - vt->tty_state = ESosc1; - break; - default: - DPRINTF(1, "%s: got unhandled OSC %c", __func__, ch); - vt->tty_state = ESnormal; - break; - } - break; - case ESosc1: - vt->tty_state = ESnormal; - if (ch == ';') { - vt->tty_state = ESosc2; - } else { - DPRINTF(1, "%s: got illegal OSC sequence", __func__); - } - break; - case ESosc2: - if (ch != 0x9c && ch != 7) { - int i = 0; - while (vt->osc_textbuf[i]) i++; - vt->osc_textbuf[i++] = ch; - vt->osc_textbuf[i] = 0; - } else { - DPRINTF(1, "%s: OSC:%c:%s", __func__, vt->osc_cmd, vt->osc_textbuf); - vt->tty_state = ESnormal; - } - break; - case ESpalette: - if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') - || (ch >= 'a' && ch <= 'f')) { - vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0'); - if (vt->esc_count == 7) { - // fixme: this does not work - please test - /* - rfbColourMap *cmap =&vt->screen->colourMap; - - int i = color_table[vt->esc_buf[0]] * 3, j = 1; - cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; - cmap->data.bytes[i++] += vt->esc_buf[j++]; - cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; - cmap->data.bytes[i++] += vt->esc_buf[j++]; - cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; - cmap->data.bytes[i] += vt->esc_buf[j]; - */ - //set_palette(vc); ? - - vt->tty_state = ESnormal; - } - } else - vt->tty_state = ESnormal; - break; - case ESsquare: - for(i = 0; i < MAX_ESC_PARAMS; i++) { - vt->esc_buf[i] = 0; + if (debug && !vt->tty_state) { + DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d", + vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy); } - vt->esc_count = 0; - vt->esc_has_par = 0; - vt->tty_state = ESgetpars; + switch(vt->tty_state) { + case ESesc: + vt->tty_state = ESnormal; + switch (ch) { + case '[': + vt->tty_state = ESsquare; + break; + case ']': + vt->tty_state = ESnonstd; + break; + case '%': + vt->tty_state = ESpercent; + break; + case '7': + spiceterm_save_cursor(vt); + break; + case '8': + spiceterm_restore_cursor(vt); + break; + case '(': + vt->tty_state = ESsetG0; // SET G0 + break; + case ')': + vt->tty_state = ESsetG1; // SET G1 + break; + case 'M': + /* cursor up (ri) */ + if (vt->cy == vt->region_top) + spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1); + else if (vt->cy > 0) { + vt->cy--; + } + break; + case '>': + /* numeric keypad - ignored */ + break; + case '=': + /* appl. keypad - ignored */ + break; + default: + DPRINTF(1, "got unhandled ESC%c %d", ch, ch); + break; + } + break; + case ESnonstd: /* Operating System Controls */ + vt->tty_state = ESnormal; + + switch (ch) { + case 'P': /* palette escape sequence */ + for(i = 0; i < MAX_ESC_PARAMS; i++) { + vt->esc_buf[i] = 0; + } - if (ch == '>') { - vt->tty_state = ESidquery; - break; - } + vt->esc_count = 0; + vt->tty_state = ESpalette; + break; + case 'R': /* reset palette */ + // fixme: reset_palette(vc); + break; + case '0': + case '1': + case '2': + case '4': + vt->osc_cmd = ch; + vt->osc_textbuf[0] = 0; + vt->tty_state = ESosc1; + break; + default: + DPRINTF(1, "got unhandled OSC %c", ch); + vt->tty_state = ESnormal; + break; + } + break; + case ESosc1: + vt->tty_state = ESnormal; + if (ch == ';') { + vt->tty_state = ESosc2; + } else { + DPRINTF(1, "got illegal OSC sequence"); + } + break; + case ESosc2: + if (ch != 0x9c && ch != 7) { + int i = 0; + while (vt->osc_textbuf[i]) i++; + vt->osc_textbuf[i++] = ch; + vt->osc_textbuf[i] = 0; + } else { + DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf); + vt->tty_state = ESnormal; + } + break; + case ESpalette: + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') + || (ch >= 'a' && ch <= 'f')) { + vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0'); + if (vt->esc_count == 7) { + // fixme: this does not work - please test + /* + rfbColourMap *cmap =&vt->screen->colourMap; + + int i = color_table[vt->esc_buf[0]] * 3, j = 1; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i++] += vt->esc_buf[j++]; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i++] += vt->esc_buf[j++]; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i] += vt->esc_buf[j]; + */ + //set_palette(vc); ? + + vt->tty_state = ESnormal; + } + } else + vt->tty_state = ESnormal; + break; + case ESsquare: + for(i = 0; i < MAX_ESC_PARAMS; i++) { + vt->esc_buf[i] = 0; + } - if ((vt->esc_ques = (ch == '?'))) { - break; - } - case ESgetpars: - if (ch >= '0' && ch <= '9') { - vt->esc_has_par = 1; - if (vt->esc_count < MAX_ESC_PARAMS) { - vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0'; - } - break; - } else if (ch == ';') { - vt->esc_count++; - break; - } else { - if (vt->esc_has_par) { - vt->esc_count++; - } - vt->tty_state = ESgotpars; - } - case ESgotpars: + vt->esc_count = 0; + vt->esc_has_par = 0; + vt->tty_state = ESgetpars; - vt->tty_state = ESnormal; + if (ch == '>') { + vt->tty_state = ESidquery; + break; + } - char *qes = vt->esc_ques ? "?" : ""; - - if (debug) { - 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); + if ((vt->esc_ques = (ch == '?'))) { + break; + } + case ESgetpars: + if (ch >= '0' && ch <= '9') { + vt->esc_has_par = 1; + if (vt->esc_count < MAX_ESC_PARAMS) { + vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0'; + } + break; + } else if (ch == ';') { + vt->esc_count++; + break; } 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]); + if (vt->esc_has_par) { + vt->esc_count++; } - printf("%c\n", ch); + vt->tty_state = ESgotpars; } - } + case ESgotpars: - switch (ch) { - case 'h': - spiceterm_set_mode (vt, 1); - break; - case 'l': - spiceterm_set_mode (vt, 0); - break; - case 'm': - if (!vt->esc_count) { - vt->esc_count++; // default parameter 0 - } - spiceterm_csi_m (vt); - break; - case 'n': - /* report cursor position */ - /* TODO: send ESC[row;colR */ - break; - case 'A': - /* move cursor up */ - if (vt->esc_buf[0] == 0) { - vt->esc_buf[0] = 1; - } - vt->cy -= vt->esc_buf[0]; - if (vt->cy < 0) { - vt->cy = 0; - } - break; - case 'B': - case 'e': - /* move cursor down */ - if (vt->esc_buf[0] == 0) { - vt->esc_buf[0] = 1; - } - vt->cy += vt->esc_buf[0]; - if (vt->cy >= vt->height) { - vt->cy = vt->height - 1; - } - break; - case 'C': - case 'a': - /* move cursor right */ - if (vt->esc_buf[0] == 0) { - vt->esc_buf[0] = 1; - } - vt->cx += vt->esc_buf[0]; - if (vt->cx >= vt->width) { - vt->cx = vt->width - 1; - } - break; - case 'D': - /* move cursor left */ - if (vt->esc_buf[0] == 0) { - vt->esc_buf[0] = 1; - } - vt->cx -= vt->esc_buf[0]; - if (vt->cx < 0) { - vt->cx = 0; - } - break; - case 'G': - case '`': - /* move cursor to column */ - 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); - break; - case 'f': - case 'H': - /* move cursor to row, column */ - spiceterm_gotoxy (vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); - break; - case 'J': - switch (vt->esc_buf[0]) { - case 0: - /* clear to end of screen */ - for (y = vt->cy; y < vt->height; y++) { - for (x = 0; x < vt->width; x++) { - if (y == vt->cy && x < vt->cx) { - continue; - } - spiceterm_clear_xy (vt, x, y); - } - } - break; - case 1: - /* clear from beginning of screen */ - for (y = 0; y <= vt->cy; y++) { - for (x = 0; x < vt->width; x++) { - if (y == vt->cy && x > vt->cx) { - break; - } - spiceterm_clear_xy (vt, x, y); - } - } - break; - case 2: - /* clear entire screen */ - for (y = 0; y <= vt->height; y++) { - for (x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, y); - } - } - break; - } - break; - case 'K': - switch (vt->esc_buf[0]) { - case 0: - /* clear to eol */ - for(x = vt->cx; x < vt->width; x++) { - 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); - } - break; - case 2: - /* clear entire line */ - for(x = 0; x < vt->width; x++) { - spiceterm_clear_xy (vt, x, vt->cy); - } - break; - } - break; - case 'L': - /* insert line */ - c = vt->esc_buf[0]; - - if (c > vt->height - vt->cy) - c = vt->height - vt->cy; - else if (!c) - c = 1; - - spiceterm_scroll_down (vt, vt->cy, vt->region_bottom, c); - break; - case 'M': - /* delete line */ - c = vt->esc_buf[0]; - - if (c > vt->height - vt->cy) - c = vt->height - vt->cy; - else if (!c) - 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); - 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); - break; - case 'P': - /* delete c character */ - c = vt->esc_buf[0]; - - if (c > vt->width - vt->cx) - c = vt->width - vt->cx; - else if (!c) - c = 1; - - for (x = vt->cx; x < vt->width - c; x++) { - int y1 = (vt->y_base + vt->cy) % vt->total_height; - TextCell *dst = &vt->cells[y1 * vt->width + x]; - TextCell *src = dst + c; - *dst = *src; - spiceterm_update_xy (vt, x + c, vt->cy); - src->ch = ' '; - src->attrib = vt->default_attrib; - spiceterm_update_xy (vt, x, vt->cy); - } - break; - case 's': - /* save cursor position */ - spiceterm_save_cursor (vt); - break; - case 'u': - /* restore cursor position */ - spiceterm_restore_cursor (vt); - break; - case 'X': - /* erase c characters */ - c = vt->esc_buf[0]; - if (!c) c = 1; - - 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); - } - break; - case '@': - /* insert c character */ - c = vt->esc_buf[0]; - if (c > (vt->width - vt->cx)) { - c = vt->width - vt->cx; - } - if (!c) c = 1; - - for (x = vt->width - c; x >= vt->cx; x--) { - int y1 = (vt->y_base + vt->cy) % vt->total_height; - TextCell *src = &vt->cells[y1 * vt->width + x]; - TextCell *dst = src + c; - *dst = *src; - spiceterm_update_xy (vt, x + c, vt->cy); - src->ch = ' '; - src->attrib = vt->cur_attrib; - spiceterm_update_xy (vt, x, vt->cy); - } - - break; - case 'r': - /* set region */ - if (!vt->esc_buf[0]) - vt->esc_buf[0]++; - if (!vt->esc_buf[1]) - vt->esc_buf[1] = vt->height; - /* Minimum allowed region is 2 lines */ - if (vt->esc_buf[0] < vt->esc_buf[1] && - vt->esc_buf[1] <= vt->height) { - vt->region_top = vt->esc_buf[0] - 1; - 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); - } - - break; - default: - if (debug) { - 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); - } - } - break; - } - vt->esc_ques = 0; - break; - case ESsetG0: // Set G0 - vt->tty_state = ESnormal; - - if (ch == '0') - vt->g0enc = GRAF_MAP; - else if (ch == 'B') - vt->g0enc = LAT1_MAP; - else if (ch == 'U') - vt->g0enc = IBMPC_MAP; - else if (ch == 'K') - vt->g0enc = USER_MAP; - - if (vt->charset == 0) - vt->cur_enc = vt->g0enc; - - break; - case ESsetG1: // Set G1 - vt->tty_state = ESnormal; - - if (ch == '0') - vt->g1enc = GRAF_MAP; - else if (ch == 'B') - vt->g1enc = LAT1_MAP; - else if (ch == 'U') - vt->g1enc = IBMPC_MAP; - else if (ch == 'K') - vt->g1enc = USER_MAP; - - if (vt->charset == 1) - vt->cur_enc = vt->g1enc; - - break; - case ESidquery: // vt100 query id - vt->tty_state = ESnormal; - - if (ch == 'c') { - DPRINTF(1, "%s: ESC[>c Query term ID", __func__); - spiceterm_respond_esc (vt, TERMIDCODE); - } - break; - case ESpercent: - vt->tty_state = ESnormal; - switch (ch) { - case '@': /* defined in ISO 2022 */ - vt->utf8 = 0; - break; - case 'G': /* prelim official escape code */ - case '8': /* retained for compatibility */ - vt->utf8 = 1; - break; - } - break; - default: // ESnormal - vt->tty_state = ESnormal; - - switch(ch) { - case 0: - break; - case 7: /* alert aka. bell */ - // fixme: - //rfbSendBell(vt->screen); - break; - case 8: /* backspace */ - if (vt->cx > 0) - vt->cx--; - break; - case 9: /* tabspace */ - if (vt->cx + (8 - (vt->cx % 8)) > vt->width) { - vt->cx = 0; - spiceterm_put_lf (vt); - } else { - vt->cx = vt->cx + (8 - (vt->cx % 8)); - } - break; - case 10: /* LF,*/ - case 11: /* VT */ - case 12: /* FF */ - spiceterm_put_lf (vt); - break; - case 13: /* carriage return */ - vt->cx = 0; - break; - case 14: - /* SI (shift in), select character set 1 */ - vt->charset = 1; - vt->cur_enc = vt->g1enc; - /* fixme: display controls = 1 */ - break; - case 15: - /* SO (shift out), select character set 0 */ - vt->charset = 0; - vt->cur_enc = vt->g0enc; - /* fixme: display controls = 0 */ - break; - case 27: /* esc */ - vt->tty_state = ESesc; - break; - case 127: /* delete */ - /* ignore */ - break; - case 128+27: /* csi */ - vt->tty_state = ESsquare; - break; - default: - if (vt->cx >= vt->width) { - /* line wrap */ - vt->cx = 0; - 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); - vt->cx++; - break; + vt->tty_state = ESnormal; + + char *qes = vt->esc_ques ? "?" : ""; + + if (debug) { + debug_print_escape_buffer(vt, __func__, "", qes, ch); + } + + switch (ch) { + case 'h': + spiceterm_set_mode(vt, 1); + break; + case 'l': + spiceterm_set_mode(vt, 0); + break; + case 'm': + if (!vt->esc_count) { + vt->esc_count++; // default parameter 0 + } + spiceterm_csi_m(vt); + break; + case 'n': + /* report cursor position */ + /* TODO: send ESC[row;colR */ + break; + case 'A': + /* move cursor up */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cy -= vt->esc_buf[0]; + if (vt->cy < 0) { + vt->cy = 0; + } + break; + case 'B': + case 'e': + /* move cursor down */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cy += vt->esc_buf[0]; + if (vt->cy >= vt->height) { + vt->cy = vt->height - 1; + } + break; + case 'C': + case 'a': + /* move cursor right */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cx += vt->esc_buf[0]; + if (vt->cx >= vt->width) { + vt->cx = vt->width - 1; + } + break; + case 'D': + /* move cursor left */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cx -= vt->esc_buf[0]; + if (vt->cx < 0) { + vt->cx = 0; + } + break; + case 'G': + case '`': + /* move cursor to column */ + 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); + break; + case 'f': + case 'H': + /* move cursor to row, column */ + spiceterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); + break; + case 'J': + switch (vt->esc_buf[0]) { + case 0: + /* clear to end of screen */ + for (y = vt->cy; y < vt->height; y++) { + for (x = 0; x < vt->width; x++) { + if (y == vt->cy && x < vt->cx) { + continue; + } + spiceterm_clear_xy (vt, x, y); + } + } + break; + case 1: + /* clear from beginning of screen */ + for (y = 0; y <= vt->cy; y++) { + for (x = 0; x < vt->width; x++) { + if (y == vt->cy && x > vt->cx) { + break; + } + spiceterm_clear_xy(vt, x, y); + } + } + break; + case 2: + /* clear entire screen */ + for (y = 0; y <= vt->height; y++) { + for (x = 0; x < vt->width; x++) { + spiceterm_clear_xy(vt, x, y); + } + } + break; + } + break; + case 'K': + switch (vt->esc_buf[0]) { + case 0: + /* clear to eol */ + for(x = vt->cx; x < vt->width; x++) { + 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); + } + break; + case 2: + /* clear entire line */ + for(x = 0; x < vt->width; x++) { + spiceterm_clear_xy(vt, x, vt->cy); + } + break; + } + break; + case 'L': + /* insert line */ + c = vt->esc_buf[0]; + + if (c > vt->height - vt->cy) + c = vt->height - vt->cy; + else if (!c) + c = 1; + + spiceterm_scroll_down(vt, vt->cy, vt->region_bottom, c); + break; + case 'M': + /* delete line */ + c = vt->esc_buf[0]; + + if (c > vt->height - vt->cy) + c = vt->height - vt->cy; + else if (!c) + 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); + 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); + break; + case 'P': + /* delete c character */ + c = vt->esc_buf[0]; + + if (c > vt->width - vt->cx) + c = vt->width - vt->cx; + else if (!c) + c = 1; + + for (x = vt->cx; x < vt->width - c; x++) { + int y1 = (vt->y_base + vt->cy) % vt->total_height; + TextCell *dst = &vt->cells[y1 * vt->width + x]; + TextCell *src = dst + c; + *dst = *src; + spiceterm_update_xy(vt, x + c, vt->cy); + src->ch = ' '; + src->attrib = vt->default_attrib; + spiceterm_update_xy(vt, x, vt->cy); + } + break; + case 's': + /* save cursor position */ + spiceterm_save_cursor(vt); + break; + case 'u': + /* restore cursor position */ + spiceterm_restore_cursor(vt); + break; + case 'X': + /* erase c characters */ + c = vt->esc_buf[0]; + if (!c) c = 1; + + 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); + } + break; + case '@': + /* insert c character */ + c = vt->esc_buf[0]; + if (c > (vt->width - vt->cx)) { + c = vt->width - vt->cx; + } + if (!c) c = 1; + + for (x = vt->width - c; x >= vt->cx; x--) { + int y1 = (vt->y_base + vt->cy) % vt->total_height; + TextCell *src = &vt->cells[y1 * vt->width + x]; + TextCell *dst = src + c; + *dst = *src; + spiceterm_update_xy (vt, x + c, vt->cy); + src->ch = ' '; + src->attrib = vt->cur_attrib; + spiceterm_update_xy(vt, x, vt->cy); + } + + break; + case 'r': + /* set region */ + if (!vt->esc_buf[0]) + vt->esc_buf[0]++; + if (!vt->esc_buf[1]) + vt->esc_buf[1] = vt->height; + /* Minimum allowed region is 2 lines */ + if (vt->esc_buf[0] < vt->esc_buf[1] && + vt->esc_buf[1] <= vt->height) { + vt->region_top = vt->esc_buf[0] - 1; + vt->region_bottom = vt->esc_buf[1]; + vt->cx = 0; + vt->cy = vt->region_top; + DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom); + } + + break; + default: + if (debug) { + debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch); + } + break; + } + vt->esc_ques = 0; + break; + case ESsetG0: // Set G0 + vt->tty_state = ESnormal; + + if (ch == '0') + vt->g0enc = GRAF_MAP; + else if (ch == 'B') + vt->g0enc = LAT1_MAP; + else if (ch == 'U') + vt->g0enc = IBMPC_MAP; + else if (ch == 'K') + vt->g0enc = USER_MAP; + + if (vt->charset == 0) + vt->cur_enc = vt->g0enc; + + break; + case ESsetG1: // Set G1 + vt->tty_state = ESnormal; + + if (ch == '0') + vt->g1enc = GRAF_MAP; + else if (ch == 'B') + vt->g1enc = LAT1_MAP; + else if (ch == 'U') + vt->g1enc = IBMPC_MAP; + else if (ch == 'K') + vt->g1enc = USER_MAP; + + if (vt->charset == 1) + vt->cur_enc = vt->g1enc; + + break; + case ESidquery: // vt100 query id + vt->tty_state = ESnormal; + + if (ch == 'c') { + DPRINTF(1, "ESC[>c Query term ID"); + spiceterm_respond_esc(vt, TERMIDCODE); + } + break; + case ESpercent: + vt->tty_state = ESnormal; + switch (ch) { + case '@': /* defined in ISO 2022 */ + vt->utf8 = 0; + break; + case 'G': /* prelim official escape code */ + case '8': /* retained for compatibility */ + vt->utf8 = 1; + break; + } + break; + default: // ESnormal + vt->tty_state = ESnormal; + + switch(ch) { + case 0: + break; + case 7: /* alert aka. bell */ + // fixme: + //rfbSendBell(vt->screen); + break; + case 8: /* backspace */ + if (vt->cx > 0) + vt->cx--; + break; + case 9: /* tabspace */ + if (vt->cx + (8 - (vt->cx % 8)) > vt->width) { + vt->cx = 0; + spiceterm_put_lf(vt); + } else { + vt->cx = vt->cx + (8 - (vt->cx % 8)); + } + break; + case 10: /* LF,*/ + case 11: /* VT */ + case 12: /* FF */ + spiceterm_put_lf(vt); + break; + case 13: /* carriage return */ + vt->cx = 0; + break; + case 14: + /* SI (shift in), select character set 1 */ + vt->charset = 1; + vt->cur_enc = vt->g1enc; + /* fixme: display controls = 1 */ + break; + case 15: + /* SO (shift out), select character set 0 */ + vt->charset = 0; + vt->cur_enc = vt->g0enc; + /* fixme: display controls = 0 */ + break; + case 27: /* esc */ + vt->tty_state = ESesc; + break; + case 127: /* delete */ + /* ignore */ + break; + case 128+27: /* csi */ + vt->tty_state = ESsquare; + break; + default: + if (vt->cx >= vt->width) { + /* line wrap */ + vt->cx = 0; + 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); + vt->cx++; + break; + } + break; } - break; - } } 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; - len--; - buf++; - - if (vt->tty_state != ESnormal) { - // never translate escape sequence - tc = c; - } else if (vt->utf8 && !vt->cur_enc) { - - if(c & 0x80) { // utf8 multi-byte sequence - - if (vt->utf_count > 0 && (c & 0xc0) == 0x80) { - // inside UTF8 sequence - vt->utf_char = (vt->utf_char << 6) | (c & 0x3f); - vt->utf_count--; - if (vt->utf_count == 0) { - tc = vt->utf_char; - } else { - continue; - } - } else { - // first char of a UTF8 sequence - if ((c & 0xe0) == 0xc0) { - vt->utf_count = 1; - vt->utf_char = (c & 0x1f); - } else if ((c & 0xf0) == 0xe0) { - vt->utf_count = 2; - vt->utf_char = (c & 0x0f); - } else if ((c & 0xf8) == 0xf0) { - vt->utf_count = 3; - vt->utf_char = (c & 0x07); - } else if ((c & 0xfc) == 0xf8) { - vt->utf_count = 4; - vt->utf_char = (c & 0x03); - } else if ((c & 0xfe) == 0xfc) { - vt->utf_count = 5; - vt->utf_char = (c & 0x01); - } else - vt->utf_count = 0; - - continue; - } - } else { - // utf8 single byte - tc = c; - vt->utf_count = 0; - } - - } else { - // never translate controls - if (c >= 32 && c != 127 && c != (128+27)) { - tc = translations[vt->cur_enc][c & 0x0ff]; - } else { - tc = c; - } - } - - spiceterm_putchar (vt, tc); + unsigned char c = *buf; + len--; + buf++; + + if (vt->tty_state != ESnormal) { + // never translate escape sequence + tc = c; + } else if (vt->utf8 && !vt->cur_enc) { + + if(c & 0x80) { // utf8 multi-byte sequence + + if (vt->utf_count > 0 && (c & 0xc0) == 0x80) { + // inside UTF8 sequence + vt->utf_char = (vt->utf_char << 6) | (c & 0x3f); + vt->utf_count--; + if (vt->utf_count == 0) { + tc = vt->utf_char; + } else { + continue; + } + } else { + // first char of a UTF8 sequence + if ((c & 0xe0) == 0xc0) { + vt->utf_count = 1; + vt->utf_char = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + vt->utf_count = 2; + vt->utf_char = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + vt->utf_count = 3; + vt->utf_char = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + vt->utf_count = 4; + vt->utf_char = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + vt->utf_count = 5; + vt->utf_char = (c & 0x01); + } else + vt->utf_count = 0; + + continue; + } + } else { + // utf8 single byte + tc = c; + vt->utf_count = 0; + } + + } else { + // never translate controls + if (c >= 32 && c != 127 && c != (128+27)) { + tc = translations[vt->cur_enc][c & 0x0ff]; + } else { + tc = c; + } + } + + 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; @@ -1265,177 +1273,62 @@ 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) { - - 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; - - 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 && 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); - } - } - - 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); - } - } - button2_released = 0; - } else { - button2_released = 1; - } - - if (buttonMask & 1) { - int pos = cy*vt->width + cx; - - // code borrowed from libspiceserver (SPICEonsole.c) - - if (!vt->mark_active) { - - vt->mark_active = 1; - sel_start_pos = sel_end_pos = pos; - spiceterm_toggle_marked_cell (vt, pos); - + if (vt->utf8) { + gchar buf[10]; + gint len = g_unichar_to_utf8(uc, buf); + + 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 (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++; - } - - while (cx <= cy) { - spiceterm_toggle_marked_cell (vt, cx); - cx++; - } - - sel_end_pos = pos; - } - } - - } 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; - } - - 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); - - while (sel_start_pos <= sel_end_pos) { - spiceterm_toggle_marked_cell (vt, sel_start_pos++); + if ((vt->ibuf_count + 1) < IBUFSIZE) { + vt->ibuf[vt->ibuf_count++] = (char)uc; + } else { + fprintf(stderr, "warning: input buffer overflow\n"); + } } - - } - - rfbDefaultPtrAddEvent (buttonMask, x, y, cl); - } -*/ -static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) +static void +my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) { // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin); @@ -1444,16 +1337,17 @@ static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) return; } -static void my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) +static void +my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags) { spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin); static int control = 0; 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,29 +1442,17 @@ static void 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; - spiceterm_refresh (vt); + spiceterm_refresh(vt); } - + 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,17 +1468,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 my_kbd_get_leds(SpiceKbdInstance *sin) +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, @@ -1605,183 +1487,719 @@ 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 */ + +#define VDAGENT_WBUF_SIZE (1024*50) +static unsigned char vdagent_write_buffer[VDAGENT_WBUF_SIZE]; +static int vdagent_write_buffer_pos = 0; +static int agent_owns_clipboard[256] = { 0, }; + +static void +spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons) { - int i; + DPRINTF(1, "mask=%08x x=%d y=%d", buttons, x ,y); + + static int last_mask = 0; + static int sel_start_pos = 0; + static int sel_end_pos = 0; + static int button2_released = 1; + + 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 (buttons & 4) { + + if(button2_released) { + + if (agent_owns_clipboard[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY]) { + if (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); + } + } + } else { + vdagent_request_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY); + } + } - SpiceScreen *spice_screen; + button2_released = 0; + } else { + button2_released = 1; + } - SpiceCoreInterface *core = basic_event_loop_init(); - spice_screen = spice_screen_new(core); - //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); + if (buttons & 2) { + int pos = cy*vt->width + cx; - spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1); + // code borrowed from libvncserver (VNCconsole.c) - vt->keyboard_sin.base.sif = &my_keyboard_sif.base; - spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base); + if (!vt->mark_active) { - // screen->setXCutText = spiceterm_set_xcut_text; - // screen->ptrAddEvent = spiceterm_pointer_event; - // screen->newClientHook = new_client; - // screen->desktopName = "SPICE Command Terminal"; + spiceterm_unselect_all(vt); - vt->maxx = spice_screen->width; - vt->maxy = spice_screen->height; + vt->mark_active = 1; + sel_start_pos = sel_end_pos = pos; + spiceterm_toggle_marked_cell(vt, pos); - vt->width = vt->maxx / 8; - vt->height = vt->maxy / 16; + } else { - vt->total_height = vt->height * 20; - vt->scroll_height = 0; - vt->y_base = 0; - vt->y_displ = 0; + if (pos != sel_end_pos) { + if (pos > sel_end_pos) { + cx = sel_end_pos; cy=pos; + } else { + cx=pos; cy=sel_end_pos; + } - vt->region_top = 0; - vt->region_bottom = vt->height; + if (cx < sel_start_pos) { + if (cy < sel_start_pos) cy--; + } else { + cx++; + } - vt->g0enc = LAT1_MAP; - vt->g1enc = GRAF_MAP; - vt->cur_enc = vt->g0enc; - vt->charset = 0; + while (cx <= cy) { + spiceterm_toggle_marked_cell(vt, cx); + cx++; + } - /* default text attributes */ - vt->default_attrib.bold = 0; - vt->default_attrib.uline = 0; - vt->default_attrib.blink = 0; - vt->default_attrib.invers = 0; - vt->default_attrib.unvisible = 0; - vt->default_attrib.fgcol = 7; - vt->default_attrib.bgcol = 0; + sel_end_pos = pos; + } + } - vt->cur_attrib = vt->default_attrib; + } else if (vt->mark_active) { + vt->mark_active = 0; - vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height); + if (sel_start_pos > sel_end_pos) { + int tmp = sel_start_pos - 1; + sel_start_pos = sel_end_pos; + sel_end_pos = tmp; + } - for (i = 0; i < vt->width*vt->total_height; i++) { - vt->cells[i].ch = ' '; - vt->cells[i].attrib = vt->default_attrib; - } + int len = sel_end_pos - sel_start_pos + 1; - vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height); + if (vt->selection) free (vt->selection); + vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2)); + vt->selection_len = len; - vt->screen = spice_screen; + 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++; + } - return vt; + DPRINTF(1, "selection length = %d", vt->selection_len); + + vdagent_grab_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY); + } } -static void master_watch(int master, int event, void *opaque) +static void +vdagent_reply(spiceTerm *vt, uint32_t type, uint32_t error) +{ + uint32_t size; + + size = sizeof(VDAgentReply); + + int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size; + g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE); + + unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos; + vdagent_write_buffer_pos += msg_size; + + memset(buf, 0, msg_size); + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + VDAgentReply *reply = (VDAgentReply *)&msg[1]; + reply->type = type; + reply->error = error; + + hdr->port = VDP_CLIENT_PORT; + hdr->size = sizeof(VDAgentMessage) + size; + + msg->protocol = VD_AGENT_PROTOCOL; + msg->type = VD_AGENT_REPLY; + msg->opaque = 0; + msg->size = size; + + spice_server_char_device_wakeup(&vt->vdagent_sin); +} + +static void vdagent_send_capabilities(spiceTerm *vt, uint32_t request) +{ + VDAgentAnnounceCapabilities *caps; + uint32_t size; + + size = sizeof(*caps) + VD_AGENT_CAPS_BYTES; + caps = calloc(1, size); + g_assert(caps != NULL); + + caps->request = request; + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF); + + int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size; + g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE); + + unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos; + vdagent_write_buffer_pos += msg_size; + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + + hdr->port = VDP_CLIENT_PORT; + hdr->size = sizeof(VDAgentMessage) + size; + msg->protocol = VD_AGENT_PROTOCOL; + msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES; + msg->opaque = 0; + msg->size = size; + + memcpy(buf + sizeof(VDIChunkHeader) + sizeof(VDAgentMessage), (uint8_t *)caps, size); + + spice_server_char_device_wakeup(&vt->vdagent_sin); + + free(caps); +} + +static void +dump_message(unsigned char *buf, int size) +{ + int i; + + for (i = 0; i < size; i++) { + printf("%d %02X\n", i, buf[i]); + } + + // exit(0); +} + +static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection) +{ + uint32_t size; + + agent_owns_clipboard[selection] = 1; + + size = 8; + + int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size; + g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE); + + unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos; + vdagent_write_buffer_pos += msg_size; + + memset(buf, 0, msg_size); + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + uint8_t *grab = (uint8_t *)&msg[1]; + *((uint8_t *)grab) = selection; + *((uint32_t *)(grab + 4)) = VD_AGENT_CLIPBOARD_UTF8_TEXT; + + hdr->port = VDP_CLIENT_PORT; + hdr->size = sizeof(VDAgentMessage) + size; + + msg->protocol = VD_AGENT_PROTOCOL; + msg->type = VD_AGENT_CLIPBOARD_GRAB; + msg->opaque = 0; + msg->size = size; + + if (0) dump_message(buf, msg_size); + + spice_server_char_device_wakeup(&vt->vdagent_sin); +} + +static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection) +{ + uint32_t size; + + agent_owns_clipboard[selection] = 1; + + size = 4 + sizeof(VDAgentClipboardRequest); + + int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size; + g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE); + + unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos; + vdagent_write_buffer_pos += msg_size; + + memset(buf, 0, msg_size); + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + uint8_t *data = (uint8_t *)&msg[1]; + *((uint32_t *)data) = 0; + data[0] = selection; + ((uint32_t *)data)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT; + + hdr->port = VDP_CLIENT_PORT; + hdr->size = sizeof(VDAgentMessage) + size; + + msg->protocol = VD_AGENT_PROTOCOL; + msg->type = VD_AGENT_CLIPBOARD_REQUEST; + msg->opaque = 0; + msg->size = size; + + if (0) dump_message(buf, msg_size); + + spice_server_char_device_wakeup(&vt->vdagent_sin); +} + +static void vdagent_send_clipboard(spiceTerm *vt, uint8_t selection) +{ + uint32_t size; + + if (selection != VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) { + fprintf(stderr, "clipboard select %d is not supported\n", selection); + return; + } + + gchar *sel_data; + glong sel_len; + if (vt->utf8) { + sel_data = g_utf16_to_utf8(vt->selection, vt->selection_len, NULL, &sel_len, NULL); + } else { + sel_len = vt->selection_len; + sel_data = g_malloc(sel_len); + int i; + for (i = 0; i < sel_len; i++) { sel_data[i] = (char)vt->selection[i]; } + sel_data[sel_len] = 0; + } + + size = 8 + sel_len; + + int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size; + g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE); + + unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos; + vdagent_write_buffer_pos += msg_size; + + memset(buf, 0, msg_size); + + VDIChunkHeader *hdr = (VDIChunkHeader *)buf; + VDAgentMessage *msg = (VDAgentMessage *)&hdr[1]; + uint8_t *data = (uint8_t *)&msg[1]; + *((uint8_t *)data) = selection; + data += 4; + *((uint32_t *)data) = VD_AGENT_CLIPBOARD_UTF8_TEXT; + data += 4; + + memcpy(data, sel_data, sel_len); + g_free(sel_data); + + hdr->port = VDP_CLIENT_PORT; + hdr->size = sizeof(VDAgentMessage) + size; + + msg->protocol = VD_AGENT_PROTOCOL; + msg->type = VD_AGENT_CLIPBOARD; + msg->opaque = 0; + msg->size = size; + + spice_server_char_device_wakeup(&vt->vdagent_sin); +} + +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); + + switch (msg->type) { + case VD_AGENT_MOUSE_STATE: { + VDAgentMouseState *info = (VDAgentMouseState *)&msg[1]; + spiceterm_motion_event(vt, info->x, info->y, info->buttons); + break; + } + case VD_AGENT_ANNOUNCE_CAPABILITIES: { + VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)&msg[1]; + DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps->request); + int i; + + int caps_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(hdr->size); + for (i = 0; i < VD_AGENT_END_CAP; i++) { + DPRINTF(1, "CAPABILITIES %d %d", i, VD_AGENT_HAS_CAPABILITY(caps->caps, caps_size, i)); + } + + vdagent_send_capabilities(vt, 0); + break; + } + case VD_AGENT_CLIPBOARD_GRAB: { + VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1]; + uint8_t selection = *((uint8_t *)grab); + DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection); + agent_owns_clipboard[selection] = 0; + break; + } + case VD_AGENT_CLIPBOARD_REQUEST: { + uint8_t *req = (uint8_t *)&msg[1]; + uint8_t selection = *((uint8_t *)req); + uint32_t type = *((uint32_t *)(req + 4)); + + DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection, type); + + vdagent_send_clipboard(vt, selection); + + break; + } + case VD_AGENT_CLIPBOARD: { + uint8_t *data = (uint8_t *)&msg[1]; + uint8_t selection = data[0]; + uint32_t type = *(uint32_t *)(data + 4); + int size = msg->size - 8; + DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection, type, size); + + if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) { + int i; + for (i = 0; i < size; i++) { + if ((vt->ibuf_count + 1) < IBUFSIZE) { + vt->ibuf[vt->ibuf_count++] = *(char *)(data + 8 + i); + } else { + fprintf(stderr, "warning: input buffer overflow\n"); + } + } + spiceterm_update_watch_mask(vt, TRUE); + } + break; + } + case VD_AGENT_CLIPBOARD_RELEASE: { + uint8_t *data = (uint8_t *)&msg[1]; + uint8_t selection = data[0]; + + DPRINTF(0, "VD_AGENT_CLIPBOARD_RELEASE %d", selection); + + break; + } + case VD_AGENT_MONITORS_CONFIG: { + VDAgentMonitorsConfig *list = (VDAgentMonitorsConfig *)&msg[1]; + g_assert(list->num_of_monitors > 0); + DPRINTF(0, "VD_AGENT_MONITORS_CONFIG %d %d %d", list->num_of_monitors, + list->monitors[0].width, list->monitors[0].height); + + spiceterm_resize(vt, list->monitors[0].width, list->monitors[0].height); + + vdagent_reply(vt, VD_AGENT_MONITORS_CONFIG, VD_AGENT_SUCCESS); + break; + } + default: + 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 %d", len, vdagent_write_buffer_pos); + g_assert(len >= 8); + + if (!vdagent_write_buffer_pos) { + return 0; + } + + int size = (len >= vdagent_write_buffer_pos) ? vdagent_write_buffer_pos : len; + memcpy(buf, vdagent_write_buffer, size); + if (size < vdagent_write_buffer_pos) { + memmove(vdagent_write_buffer, vdagent_write_buffer + size, + vdagent_write_buffer_pos - size); + } + vdagent_write_buffer_pos -= size; + + DPRINTF(1, "RET %d %d", size, vdagent_write_buffer_pos); + return size; +} + +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 void +init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height) +{ + int i; + + g_assert(vt != NULL); + g_assert(vt->screen != NULL); + + vt->width = width / 8; + vt->height = height / 16; + + vt->total_height = vt->height * 20; + vt->scroll_height = 0; + vt->y_base = 0; + vt->y_displ = 0; + + vt->region_top = 0; + vt->region_bottom = vt->height; + + vt->g0enc = LAT1_MAP; + vt->g1enc = GRAF_MAP; + vt->cur_enc = vt->g0enc; + vt->charset = 0; + + /* default text attributes */ + vt->default_attrib.bold = 0; + vt->default_attrib.uline = 0; + vt->default_attrib.blink = 0; + vt->default_attrib.invers = 0; + vt->default_attrib.unvisible = 0; + vt->default_attrib.fgcol = 7; + vt->default_attrib.bgcol = 0; + + vt->cur_attrib = vt->default_attrib; + + if (vt->cells) { + vt->cx = 0; + vt->cy = 0; + vt->cx_saved = 0; + vt->cy_saved = 0; + g_free(vt->cells); + } + + vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height); + + for (i = 0; i < vt->width*vt->total_height; i++) { + vt->cells[i].ch = ' '; + vt->cells[i].attrib = vt->default_attrib; + } + + if (vt->altcells) { + g_free(vt->altcells); + } + + vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height); +} + +static void +spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height) +{ + DPRINTF(0, "width=%u height=%u", width, height); + + if (vt->screen->width == width && vt->screen->height == height) { + return; + } + + spice_screen_resize(vt->screen, width, height); + + init_spiceterm(vt, width, height); + + struct winsize dimensions; + dimensions.ws_col = vt->width; + dimensions.ws_row = vt->height; + + ioctl(vt->pty, TIOCSWINSZ, &dimensions); +} + +static spiceTerm * +create_spiceterm(int argc, char** argv, uint32_t maxx, uint32_t maxy, guint timeout) +{ + SpiceCoreInterface *core = basic_event_loop_init(); + SpiceScreen *spice_screen = spice_screen_new(core, maxx, maxy, timeout); + + //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF); + + 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); + vt->screen = spice_screen; + + init_spiceterm(vt, maxx, maxy); + + return vt; +} + +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); } } int main (int argc, char** argv) { - int i; - char **cmdargv = NULL; - char *command = "/bin/bash"; // execute normal shell as default - int pid; - int master; - char ptyname[1024]; - struct winsize dimensions; - - g_thread_init(NULL); - - for (i = 1; i < argc; i++) { - if (!strcmp (argv[i], "-c")) { - command = argv[i+1]; - cmdargv = &argv[i+1]; - argc = i; - argv[i] = NULL; - break; + int i; + char **cmdargv = NULL; + char *command = "/bin/bash"; // execute normal shell as default + int pid; + int master; + char ptyname[1024]; + struct winsize dimensions; + + g_thread_init(NULL); + + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-c")) { + command = argv[i+1]; + cmdargv = &argv[i+1]; + argc = i; + argv[i] = NULL; + break; + } } - } - if (0) print_usage(NULL); // fixme: + 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 + setlocale(LC_ALL, ""); // set from environment - char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE + char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE - // fixme: ist there a standard way to detect utf8 mode ? - if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) { - vt->utf8 = 1; - } + // fixme: ist there a standard way to detect utf8 mode ? + if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) { + vt->utf8 = 1; + } - dimensions.ws_col = vt->width; - dimensions.ws_row = vt->height; + 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) { + pid = forkpty (&master, ptyname, NULL, &dimensions); + if(!pid) { - // install default signal handlers - signal (SIGQUIT, SIG_DFL); - signal (SIGTERM, SIG_DFL); - signal (SIGINT, SIG_DFL); + // install default signal handlers + signal (SIGQUIT, SIG_DFL); + signal (SIGTERM, SIG_DFL); + signal (SIGINT, SIG_DFL); - if (cmdargv) { - execvp (command, cmdargv); - } else { - execlp (command, command, NULL); + if (cmdargv) { + execvp (command, cmdargv); + } else { + execlp (command, command, NULL); + } + perror ("Error: exec failed\n"); + exit (-1); // should not be reached + } else if (pid == -1) { + perror ("Error: fork failed\n"); + exit (-1); } - perror ("Error: exec failed\n"); - exit (-1); // should not be reached - } else if (pid == -1) { - perror ("Error: fork failed\n"); - exit (-1); - } - - vt->screen->mwatch = vt->screen->core->watch_add( - master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */, - master_watch, vt); + vt->pty = master; - basic_event_loop_mainloop(); + /* 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); - //rfbProcessEvents (vt->screen, 40000); /* 40 ms */ + vt->screen->mwatch = vt->screen->core->watch_add( + master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */, + master_watch, vt); - /* - 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); - } - */ + basic_event_loop_mainloop(); - kill (pid, 9); - int status; - waitpid(pid, &status, 0); + kill (pid, 9); + int status; + waitpid(pid, &status, 0); - exit (0); + exit (0); }