#include <sys/wait.h>
#include <signal.h>
#include <locale.h>
+#include <getopt.h>
#include "spiceterm.h"
#define TERMIDCODE "[?1;2c" // vt100 ID
-#define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
- fprintf(stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
- print_usage(NULL); \
- exit(1); \
-}
-
/* these colours are from linux kernel drivers/char/vt.c */
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");
-}
-
-/* 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;
}
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 (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; }
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) {
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)
+void
+spiceterm_refresh(spiceTerm *vt)
{
int x, y, y1;
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);
}
static void
-spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines)
+spiceterm_clear_screen(spiceTerm *vt)
+{
+ int x, y;
+
+ for (y = 0; y <= vt->height; y++) {
+ int y1 = (vt->y_base + y) % vt->total_height;
+ TextCell *c = &vt->cells[y1 * vt->width];
+ for (x = 0; x < 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++;
+ }
+ }
+
+ spice_screen_clear(vt->screen, 0, 0, vt->screen->primary_width,
+ vt->screen->primary_height);
+}
+
+void
+spiceterm_unselect_all(spiceTerm *vt)
+{
+ int x, y, y1;
+
+ y1 = vt->y_displ;
+ for(y = 0; y < vt->total_height; y++) {
+ TextCell *c = vt->cells + y1 * vt->width;
+ for(x = 0; x < vt->width; x++) {
+ if (c->attrib.selected) {
+ c->attrib.selected = 0;
+ if (y < vt->height) {
+ draw_char_at(vt, x, y, c->ch, c->attrib);
+ }
+ }
+ c++;
+ }
+ if (++y1 == vt->total_height)
+ y1 = 0;
+ }
+}
+
+static void
+spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines)
{
if ((top + lines) >= bottom) {
lines = bottom - top -1;
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++) {
}
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;
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++) {
}
}
-static void
-spiceterm_virtual_scroll (spiceTerm *vt, int lines)
+void
+spiceterm_virtual_scroll(spiceTerm *vt, int lines)
{
if (vt->altbuf || lines == 0) return;
}
}
- spiceterm_refresh (vt);
+ spiceterm_refresh(vt);
}
-static void
-spiceterm_respond_esc (spiceTerm *vt, const char *esc)
+void
+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)) {
for (i = 0; i < len; i++) {
vt->ibuf[vt->ibuf_count++] = esc[i];
}
+ } else {
+ fprintf(stderr, "input buffer oferflow\n");
+ return;
+ }
+}
+
+void
+spiceterm_respond_data(spiceTerm *vt, int len, uint8_t *data)
+{
+ int i;
+
+ if (vt->ibuf_count < (IBUFSIZE - len)) {
+ for (i = 0; i < len; i++) {
+ vt->ibuf[vt->ibuf_count++] = data[i];
+ }
+ } else {
+ fprintf(stderr, "input buffer oferflow\n");
+ return;
}
}
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) {
}
static void
-spiceterm_csi_m (spiceTerm *vt)
+spiceterm_csi_m(spiceTerm *vt)
{
int i;
}
static void
-spiceterm_save_cursor (spiceTerm *vt)
+spiceterm_save_cursor(spiceTerm *vt)
{
vt->cx_saved = vt->cx;
vt->cy_saved = vt->cy;
}
static void
-spiceterm_restore_cursor (spiceTerm *vt)
+spiceterm_restore_cursor(spiceTerm *vt)
{
vt->cx = vt->cx_saved;
vt->cy = vt->cy_saved;
}
static void
-spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
+spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off)
{
int x, y;
/* 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;
/* 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);
}
}
}
}
- 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;
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 */
vt->cy = y;
}
-static void
+static void
debug_print_escape_buffer(spiceTerm *vt, const char *func, const char *prefix,
const char *qes, gunichar2 ch)
{
ESpalette, ESidquery, ESosc1, ESosc2};
static void
-spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
+spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
{
int x, y, i, c;
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
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--;
}
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 */
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]) {
if (y == vt->cy && x > vt->cx) {
break;
}
- spiceterm_clear_xy (vt, x, y);
+ 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);
- }
- }
+ spiceterm_clear_screen(vt);
break;
}
break;
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;
}
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 */
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 */
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 */
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 '@':
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;
if (ch == 'c') {
DPRINTF(1, "ESC[>c Query term ID");
- spiceterm_respond_esc (vt, TERMIDCODE);
- }
+ spiceterm_respond_esc(vt, TERMIDCODE);
+ }
break;
case ESpercent:
vt->tty_state = ESnormal;
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));
}
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;
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;
}
}
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;
}
}
- 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 *vt =(spiceTerm *)cl->screen->screenData;
-
- // seems str is Latin-1 encoded
- if (vt->selection) free (vt->selection);
- vt->selection = (gunichar2 *)malloc (len*sizeof (gunichar2));
- int i;
- for (i = 0; i < len; i++) {
- vt->selection[i] = str[i] & 0xff;
- }
- 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);
- sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
- (char)('!' + mry));
+ int mask = SPICE_WATCH_EVENT_READ;
- spiceterm_respond_esc (vt, buf);
+ if (writable) {
+ mask |= SPICE_WATCH_EVENT_WRITE;
+ }
+
+ 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;
-
- 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 libvncserver (VNCconsole.c)
-
- if (!vt->mark_active) {
-
- vt->mark_active = 1;
- sel_start_pos = sel_end_pos = pos;
- spiceterm_toggle_marked_cell (vt, pos);
-
+ if (len > 0) {
+ spiceterm_respond_data(vt, len, (uint8_t *)buf);
+ }
} else {
+ uint8_t buf[1] = { (uint8_t)uc };
+ spiceterm_respond_data(vt, 1, buf);
+ }
+}
- if (pos != sel_end_pos) {
-
- if (pos > sel_end_pos) {
- cx = sel_end_pos; cy=pos;
- } else {
- cx=pos; cy=sel_end_pos;
- }
+void
+spiceterm_clear_selection(spiceTerm *vt)
+{
+ DPRINTF(1, "mark_active = %d", vt->mark_active);
- if (cx < sel_start_pos) {
- if (cy < sel_start_pos) cy--;
- } else {
- cx++;
- }
+ vt->mark_active = 0;
+ if (vt->selection) free (vt->selection);
+ vt->selection = NULL;
+
+ spiceterm_unselect_all(vt);
+}
+
+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);
- while (cx <= cy) {
- spiceterm_toggle_marked_cell (vt, cx);
- cx++;
- }
+ static int last_mask = 0;
+ static int sel_start_pos = 0;
+ static int sel_end_pos = 0;
+ static int button2_released = 1;
- sel_end_pos = pos;
- }
+ 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);
+ }
}
- } 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 (buttons & 4) {
- int len = sel_end_pos - sel_start_pos + 1;
+ if(button2_released) {
- 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);
+ if (vdagent_owns_clipboard(vt)) {
+ 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);
+ }
+ }
- while (sel_start_pos <= sel_end_pos) {
- spiceterm_toggle_marked_cell (vt, sel_start_pos++);
+ button2_released = 0;
+ } else {
+ button2_released = 1;
}
- }
+ if (buttons & 2) {
+ int pos = cy*vt->width + cx;
- rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
+ // code borrowed from libvncserver (VNCconsole.c)
-}
-*/
+ if (!vt->mark_active) {
-static void
-my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
-{
- // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
+ spiceterm_unselect_all(vt);
- /* we no not need this */
+ vt->mark_active = 1;
+ sel_start_pos = sel_end_pos = pos;
+ spiceterm_toggle_marked_cell(vt, pos);
- return;
-}
+ } else {
-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;
-
- DPRINTF(1, "flags=%d keySym=%08x", flags, keySym);
-
- if (flags & 1) {
- if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
- shift = 1;
- } if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
- control = 1;
- } else if (vt->ibuf_count < (IBUFSIZE - 32)) {
-
- if (control) {
- if(keySym >= 'a' && keySym <= 'z')
- uc = keySym - 'a' + 1;
- else if (keySym >= 'A' && keySym <= 'Z')
- uc = keySym - 'A' + 1;
- else
- uc = 0;
+ if (pos != sel_end_pos) {
+ if (pos > sel_end_pos) {
+ cx = sel_end_pos; cy=pos;
+ } else {
+ cx=pos; cy=sel_end_pos;
+ }
- } else {
- switch (keySym) {
- case GDK_KEY_Escape:
- uc = 27; break;
- case GDK_KEY_Return:
- uc = '\r'; break;
- case GDK_KEY_BackSpace:
- uc = 8; break;
- case GDK_KEY_Tab:
- uc = '\t'; break;
- case GDK_KEY_Delete: /* kdch1 */
- case GDK_KEY_KP_Delete:
- esc = "[3~";break;
- case GDK_KEY_Home: /* khome */
- case GDK_KEY_KP_Home:
- esc = "OH";break;
- case GDK_KEY_End:
- case GDK_KEY_KP_End: /* kend */
- esc = "OF";break;
- case GDK_KEY_Insert: /* kich1 */
- case GDK_KEY_KP_Insert:
- esc = "[2~";break;
- case GDK_KEY_Up:
- case GDK_KEY_KP_Up: /* kcuu1 */
- esc = "OA";break;
- case GDK_KEY_Down: /* kcud1 */
- case GDK_KEY_KP_Down:
- esc = "OB";break;
- case GDK_KEY_Right:
- case GDK_KEY_KP_Right: /* kcuf1 */
- esc = "OC";break;
- case GDK_KEY_Left:
- case GDK_KEY_KP_Left: /* kcub1 */
- esc = "OD";break;
- case GDK_KEY_Page_Up:
- if (shift) {
- spiceterm_virtual_scroll (vt, -vt->height/2);
- goto ret;
- }
- esc = "[5~";break;
- case GDK_KEY_Page_Down:
- if (shift) {
- spiceterm_virtual_scroll (vt, vt->height/2);
- goto ret;
- }
- esc = "[6~";break;
- case GDK_KEY_F1:
- esc = "OP";break;
- case GDK_KEY_F2:
- esc = "OQ";break;
- case GDK_KEY_F3:
- esc = "OR";break;
- case GDK_KEY_F4:
- esc = "OS";break;
- case GDK_KEY_F5:
- esc = "[15~";break;
- case GDK_KEY_F6:
- esc = "[17~";break;
- case GDK_KEY_F7:
- esc = "[18~";break;
- case GDK_KEY_F8:
- esc = "[19~";break;
- case GDK_KEY_F9:
- esc = "[20~";break;
- case GDK_KEY_F10:
- esc = "[21~";break;
- case GDK_KEY_F11:
- esc = "[23~";break;
- case GDK_KEY_F12:
- esc = "[24~";break;
- default:
- if (keySym < 0x100) {
- uc = keySym;
- }
- break;
+ if (cx < sel_start_pos) {
+ if (cy < sel_start_pos) cy--;
+ } else {
+ cx++;
}
- }
- DPRINTF(1, "escape=%s unicode=%08x\n", esc, uc);
+ while (cx <= cy) {
+ spiceterm_toggle_marked_cell(vt, cx);
+ cx++;
+ }
- if (vt->y_displ != vt->y_base) {
- vt->y_displ = vt->y_base;
- spiceterm_refresh (vt);
+ sel_end_pos = pos;
}
+ }
- 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;
- }
- }
+ } 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;
-ret:
+ if (vt->selection) free (vt->selection);
+ vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2));
+ vt->selection_len = len;
- if (flags & 2) { // UP
- if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
- shift = 0;
- } else if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
- control = 0;
+ 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++;
}
- }
- vt->screen->core->watch_update_mask(vt->screen->mwatch,
- SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE);
-}
+ DPRINTF(1, "selection length = %d", vt->selection_len);
-static uint8_t
-my_kbd_get_leds(SpiceKbdInstance *sin)
-{
- return 0;
+ vdagent_grab_clipboard(vt);
+ }
}
-static SpiceKbdInterface my_keyboard_sif = {
- .base.type = SPICE_INTERFACE_KEYBOARD ,
- .base.description = "spiceterm keyboard device",
- .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
- .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
- .push_keyval = my_kbd_push_keyval,
- .push_scan_freg = my_kbd_push_key,
- .get_leds = my_kbd_get_leds,
-};
-
-spiceTerm *
-create_spiceterm(int argc, char** argv, int maxx, int maxy)
+void
+init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
{
int i;
- SpiceScreen *spice_screen;
-
- SpiceCoreInterface *core = basic_event_loop_init();
- spice_screen = spice_screen_new(core);
- //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);
-
- // screen->setXCutText = spiceterm_set_xcut_text;
- // screen->ptrAddEvent = spiceterm_pointer_event;
- // screen->newClientHook = new_client;
- // screen->desktopName = "SPICE Command Terminal";
-
- vt->maxx = spice_screen->width;
- vt->maxy = spice_screen->height;
+ g_assert(vt != NULL);
+ g_assert(vt->screen != NULL);
- vt->width = vt->maxx / 8;
- vt->height = vt->maxy / 16;
+ vt->width = width / 8;
+ vt->height = height / 16;
vt->total_height = vt->height * 20;
vt->scroll_height = 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);
+}
- vt->screen = spice_screen;
+void
+spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
+{
+ width = (width/8)*8;
+ height = (height/16)*16;
+
+ if (vt->screen->width == width && vt->screen->height == height) {
+ return;
+ }
+
+ DPRINTF(0, "width=%u height=%u", width, height);
+
+ spice_screen_resize(vt->screen, width, height);
+
+ init_spiceterm(vt, width, height);
- return vt;
+ struct winsize dimensions;
+ dimensions.ws_col = vt->width;
+ dimensions.ws_row = vt->height;
+
+ ioctl(vt->pty, TIOCSWINSZ, &dimensions);
}
-static gboolean
-master_error_callback(GIOChannel *channel, GIOCondition condition,
+static gboolean
+master_error_callback(GIOChannel *channel, GIOCondition condition,
gpointer data)
{
//spiceTerm *vt = (spiceTerm *)data;
return FALSE;
}
-static void
+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, "write input %x %d", vt->ibuf[0], vt->ibuf_count);
- write (master, vt->ibuf, vt->ibuf_count);
- vt->ibuf_count = 0; // fixme: what if not all data written
+ 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);
}
}
+static void
+spiceterm_print_usage(const char *msg)
+{
+ if (msg) {
+ fprintf(stderr, "ERROR: %s\n", msg);
+ }
+ fprintf(stderr, "USAGE: spiceterm [OPTIONS] [-- command [args]]\n");
+ fprintf(stderr, " --timeout <seconds> Wait this time before aborting (default is 10 seconds)\n");
+ fprintf(stderr, " --authpath <path> Authentication path (PVE AUTH)\n");
+ fprintf(stderr, " --permission <perm> Required permissions (PVE AUTH)\n");
+ fprintf(stderr, " --port <port> Bind to port <port>\n");
+ fprintf(stderr, " --addr <addr> Bind to address <addr>\n");
+ fprintf(stderr, " --sasl Enable SASL based authentication\n");
+ fprintf(stderr, " --noauth Disable authentication\n");
+ fprintf(stderr, " --keymap Spefify keymap (uses kvm keymap files)\n");
+}
+
int
main (int argc, char** argv)
{
- int i;
+ int c;
char **cmdargv = NULL;
char *command = "/bin/bash"; // execute normal shell as default
int pid;
int master;
char ptyname[1024];
struct winsize dimensions;
+ SpiceTermOptions opts = {
+ .timeout = 10,
+ .port = 5900,
+ .addr = NULL,
+ .noauth = FALSE,
+ .sasl = FALSE,
+ };
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;
+ static struct option long_options[] = {
+ { "timeout", required_argument, 0, 't' },
+ { "authpath", required_argument, 0, 'A' },
+ { "permissions", required_argument, 0, 'P' },
+ { "port", required_argument, 0, 'p' },
+ { "addr", required_argument, 0, 'a' },
+ { "keymap", required_argument, 0, 'k' },
+ { "noauth", no_argument, 0, 'n' },
+ { "sasl", no_argument, 0, 's' },
+ { NULL, 0, 0, 0 },
+ };
+
+ while ((c = getopt_long(argc, argv, "nkst:a:p:P:", long_options, NULL)) != -1) {
+
+ switch (c) {
+ case 'n':
+ opts.noauth = TRUE;
+ break;
+ case 's':
+ opts.sasl = TRUE;
+ break;
+ case 'k':
+ opts.keymap = optarg;
break;
+ case 'A':
+ pve_auth_set_path(optarg);
+ break;
+ case 'P':
+ pve_auth_set_permissions(optarg);
+ break;
+ case 'p':
+ opts.port = atoi(optarg);
+ break;
+ case 'a':
+ opts.addr = optarg;
+ break;
+ case 't':
+ opts.timeout = atoi(optarg);
+ break;
+ case '?':
+ spiceterm_print_usage(NULL);
+ exit(-1);
+ break;
+ default:
+ spiceterm_print_usage("getopt returned unknown character code");
+ exit(-1);
}
}
-
- if (0) print_usage(NULL); // fixme:
-
- spiceTerm *vt = create_spiceterm (argc, argv, 745, 400);
+
+ if (optind < argc) {
+ command = argv[optind+1];
+ cmdargv = &argv[optind+1];
+ }
+
+ spiceTerm *vt = spiceterm_create(744, 400, &opts);
setlocale(LC_ALL, ""); // set from environment
dimensions.ws_col = vt->width;
dimensions.ws_row = vt->height;
- setenv ("TERM", TERM, 1);
+ setenv("TERM", TERM, 1);
DPRINTF(1, "execute %s", command);
exit (-1);
}
+ vt->pty = master;
+
/* 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 */,
master_watch, vt);