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)
{
}
}
+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_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->attrib.selected = c->attrib.selected ? 0 : 1;
+ spice_screen_draw_char(vt->screen, x, y, c->ch, c->attrib);
+ }
+}
+
static void
spiceterm_show_cursor (spiceTerm *vt, int show)
{
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 */
}
}
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) {
if (ch == 'c') {
DPRINTF(1, "ESC[>c Query term ID");
spiceterm_respond_esc (vt, TERMIDCODE);
- }
+ }
break;
case ESpercent:
vt->tty_state = ESnormal;
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;
+
+ 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;
+ sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
+ (char)('!' + mry));
- int i,j;
- rfbScreenInfoPtr s=vt->screen;
+ spiceterm_respond_esc(vt, buf);
- char *b = s->frameBuffer+y*s->width+x;
+ spiceterm_update_watch_mask(vt, TRUE);
+}
- 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);
+static void
+spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
+{
+ 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 ((vt->ibuf_count + 1) < IBUFSIZE) {
+ vt->ibuf[vt->ibuf_count++] = (char)uc;
+ } else {
+ fprintf(stderr, "warning: input buffer overflow\n");
+ }
}
- }
-*/
}
-/* fixme:
-
-void
-spiceterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
+static void
+spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
{
+ DPRINTF(1, "mask=%08x x=%d y=%d", buttons, x ,y);
- 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;
+ static int last_mask = 0;
+ static int sel_start_pos = 0;
+ static int sel_end_pos = 0;
+ static int button2_released = 1;
- if (vt->report_mouse && buttonMask != last_mask) {
- last_mask = buttonMask;
- if (buttonMask & 1) {
- mouse_report (vt, 0, cx, cy);
- }
- if (buttonMask & 2) {
- mouse_report (vt, 1, cx, cy);
- }
- if (buttonMask & 4) {
- mouse_report (vt, 2, cx, cy);
- }
- if (!buttonMask) {
- mouse_report (vt, 3, cx, cy);
+ int i;
+ int cx = x/8;
+ int cy = y/16;
+
+ if (cx < 0) cx = 0;
+ if (cx >= vt->width) cx = vt->width - 1;
+ if (cy < 0) cy = 0;
+ if (cy >= vt->height) cy = vt->height - 1;
+
+ if (vt->report_mouse && buttons != last_mask) {
+ last_mask = buttons;
+ if (buttons & 2) {
+ mouse_report(vt, 0, cx, cy);
+ }
+ if (buttons & 4) {
+ mouse_report (vt, 1, cx, cy);
+ }
+ if (buttons & 8) {
+ mouse_report (vt, 2, cx, cy);
+ }
+ if (!buttons) {
+ mouse_report (vt, 3, cx, cy);
+ }
}
- }
- if (buttonMask & 2) {
- if(button2_released && vt->selection) {
- int i;
- for(i = 0; i < vt->selection_len; i++) {
- if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
- if (vt->utf8) {
- vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
- } else {
- vt->ibuf[vt->ibuf_count++] = vt->selection[i];
- }
- }
- }
- if (vt->y_displ != vt->y_base) {
- vt->y_displ = vt->y_base;
- spiceterm_refresh (vt);
- }
+ if (buttons & 4) {
+ if(button2_released && vt->selection) {
+ int i;
+ for(i = 0; i < vt->selection_len; i++) {
+ spiceterm_respond_unichar2(vt, vt->selection[i]);
+ }
+ spiceterm_update_watch_mask(vt, TRUE);
+ if (vt->y_displ != vt->y_base) {
+ vt->y_displ = vt->y_base;
+ spiceterm_refresh(vt);
+ }
+ }
+ button2_released = 0;
+ } else {
+ button2_released = 1;
}
- button2_released = 0;
- } else {
- button2_released = 1;
- }
- if (buttonMask & 1) {
- int pos = cy*vt->width + cx;
+ if (buttons & 2) {
+ int pos = cy*vt->width + cx;
- // code borrowed from libvncserver (VNCconsole.c)
+ // code borrowed from libvncserver (VNCconsole.c)
- if (!vt->mark_active) {
+ if (!vt->mark_active) {
- vt->mark_active = 1;
- sel_start_pos = sel_end_pos = pos;
- spiceterm_toggle_marked_cell (vt, pos);
+ if (sel_start_pos != sel_end_pos) {
+ while (sel_start_pos <= sel_end_pos) {
+ spiceterm_toggle_marked_cell(vt, sel_start_pos++);
+ }
+ }
- } else {
+ vt->mark_active = 1;
+ sel_start_pos = sel_end_pos = pos;
+ spiceterm_toggle_marked_cell(vt, pos);
- if (pos != sel_end_pos) {
+ } else {
- if (pos > sel_end_pos) {
- cx = sel_end_pos; cy=pos;
- } else {
- cx=pos; cy=sel_end_pos;
- }
+ if (pos != sel_end_pos) {
- if (cx < sel_start_pos) {
- if (cy < sel_start_pos) cy--;
- } else {
- cx++;
- }
+ if (pos > sel_end_pos) {
+ cx = sel_end_pos; cy=pos;
+ } else {
+ cx=pos; cy=sel_end_pos;
+ }
- while (cx <= cy) {
- spiceterm_toggle_marked_cell (vt, cx);
- cx++;
- }
+ if (cx < sel_start_pos) {
+ if (cy < sel_start_pos) cy--;
+ } else {
+ cx++;
+ }
- sel_end_pos = pos;
- }
- }
+ while (cx <= cy) {
+ spiceterm_toggle_marked_cell(vt, cx);
+ cx++;
+ }
- } else if (vt->mark_active) {
- vt->mark_active = 0;
+ sel_end_pos = pos;
+ }
+ }
- if (sel_start_pos > sel_end_pos) {
- int tmp = sel_start_pos - 1;
- sel_start_pos = sel_end_pos;
- sel_end_pos = tmp;
- }
+ } else if (vt->mark_active) {
+ vt->mark_active = 0;
- 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);
+ if (sel_start_pos > sel_end_pos) {
+ int tmp = sel_start_pos - 1;
+ sel_start_pos = sel_end_pos;
+ sel_end_pos = tmp;
+ }
- while (sel_start_pos <= sel_end_pos) {
- spiceterm_toggle_marked_cell (vt, sel_start_pos++);
- }
+ 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;
- rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
+ 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++;
+ }
+
+ DPRINTF(1, "selection length = %d", vt->selection_len);
+ // fixme: tell client we have something seletced
+ //rfbGotXCutText (vt->screen, sel_latin1, len);
+ }
}
-*/
static void
my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
static int shift = 0;
char *esc = NULL;
- guint uc = 0;
+ gunichar2 uc = 0;
DPRINTF(1, "flags=%d keySym=%08x", flags, keySym);
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);
}
}
}
}
}
- vt->screen->core->watch_update_mask(vt->screen->mwatch,
- SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE);
+ spiceterm_update_watch_mask(vt, TRUE);
}
static uint8_t
}
static 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,
.get_leds = my_kbd_get_leds,
};
-spiceTerm *
-create_spiceterm(int argc, char** argv, int maxx, int maxy)
+/* vdagent interface - to get mouse/clipboarde support */
+static int
+vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
+{
+ spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, vdagent_sin);
+
+ VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+ VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+
+ //g_assert(hdr->port == VDP_SERVER_PORT);
+ g_assert(msg->protocol == VD_AGENT_PROTOCOL);
+
+ DPRINTF(1, "%d %d %d %d", len, hdr->port, msg->protocol, msg->type);
+
+ if (msg->type == VD_AGENT_MOUSE_STATE) {
+ VDAgentMouseState *info = (VDAgentMouseState *)&msg[1];
+ spiceterm_motion_event(vt, info->x, info->y, info->buttons);
+ } else if (msg->type == VD_AGENT_ANNOUNCE_CAPABILITIES) {
+ /* ignore for now */
+ } else if (msg->type == VD_AGENT_MONITORS_CONFIG) {
+ /* ignore for now */
+ } else {
+ DPRINTF(0, "got uknown vdagent message type %d\n", msg->type);
+ }
+
+ return len;
+}
+
+static int
+vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
+{
+ DPRINTF(1, "%d", len);
+
+ return 0;
+}
+
+static void
+vmc_state(SpiceCharDeviceInstance *sin, int connected)
+{
+ /* IGNORE */
+}
+
+static SpiceCharDeviceInterface my_vdagent_sif = {
+ .base.type = SPICE_INTERFACE_CHAR_DEVICE,
+ .base.description = "spice virtual channel char device",
+ .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
+ .state = vmc_state,
+ .write = vmc_write,
+ .read = vmc_read,
+};
+
+static spiceTerm *
+create_spiceterm(int argc, char** argv, int maxx, int maxy, guint timeout)
{
int i;
SpiceScreen *spice_screen;
SpiceCoreInterface *core = basic_event_loop_init();
- spice_screen = spice_screen_new(core);
+ spice_screen = spice_screen_new(core, timeout);
//spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1);
vt->keyboard_sin.base.sif = &my_keyboard_sif.base;
spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base);
+ vt->vdagent_sin.base.sif = &my_vdagent_sif.base;
+ vt->vdagent_sin.subtype = "vdagent";
+ spice_server_add_interface(spice_screen->server, &vt->vdagent_sin.base);
+
// screen->setXCutText = spiceterm_set_xcut_text;
// screen->ptrAddEvent = spiceterm_pointer_event;
// screen->newClientHook = new_client;
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, "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);
}
}
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
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);
}
-
+ /* 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);
basic_event_loop_mainloop();
- //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
-
- /*
- if (vt->ibuf_count > 0) {
- printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
- write (master, vt->ibuf, vt->ibuf_count);
- vt->ibuf_count = 0;
- last_time = time (NULL);
- }
- */
-
kill (pid, 9);
int status;
waitpid(pid, &status, 0);