5
0
mirror of git://git.proxmox.com/git/spiceterm.git synced 2024-12-22 13:34:06 +03:00
spiceterm/spiceterm.c

1752 lines
47 KiB
C
Raw Normal View History

/*
2013-09-11 14:08:08 +04:00
Copyright (C) 2013 Proxmox Server Solutions GmbH
2013-09-11 14:08:08 +04:00
Copyright: spiceterm is under GNU GPL, the GNU General Public License.
2013-09-11 14:08:08 +04:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
2013-09-11 14:08:08 +04:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2013-09-11 14:08:08 +04:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
2013-09-11 14:08:08 +04:00
Author: Dietmar Maurer <dietmar@proxmox.com>
2013-09-12 09:16:03 +04:00
Note: most of the code here is copied from vncterm (which is
2013-09-11 14:08:08 +04:00
also written by me).
*/
2013-09-12 09:16:03 +04:00
#define _GNU_SOURCE
2013-09-10 15:05:05 +04:00
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
2013-08-21 09:43:55 +04:00
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pty.h> /* for openpty and forkpty */
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <locale.h>
#include <getopt.h>
#include "spiceterm.h"
2013-08-21 14:58:20 +04:00
#include <glib.h>
#include <spice.h>
#include <spice/enums.h>
#include <spice/macros.h>
#include <spice/qxl_dev.h>
#include "event_loop.h"
2013-09-11 09:44:47 +04:00
#include "translations.h"
2013-09-11 14:56:28 +04:00
static int debug = 0;
2013-09-12 09:16:03 +04:00
2013-09-11 14:56:28 +04:00
#define DPRINTF(x, format, ...) { \
if (x <= debug) { \
printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
} \
}
#define TERM "xterm"
#define TERMIDCODE "[?1;2c" // vt100 ID
/* 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
2013-09-13 12:56:03 +04:00
draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
{
2013-08-21 09:43:55 +04:00
if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) {
return;
}
spice_screen_draw_char(vt->screen, x, y, ch, attrib);
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_update_xy(spiceTerm *vt, int x, int y)
{
2013-09-12 09:16:03 +04:00
if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
2013-09-12 09:16:03 +04:00
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];
2013-09-13 12:56:03 +04:00
draw_char_at(vt, x, y2, c->ch, c->attrib);
2013-09-12 09:16:03 +04:00
}
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(spiceTerm *vt, int x, int y)
{
2013-09-12 09:16:03 +04:00
if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
2013-09-12 09:16:03 +04:00
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;
2013-09-13 12:56:03 +04:00
draw_char_at(vt, x, y, c->ch, c->attrib);
2013-09-12 09:16:03 +04:00
}
}
2013-09-13 11:53:24 +04:00
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; }
2013-09-13 12:47:02 +04:00
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);
2013-09-13 11:53:24 +04:00
}
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_show_cursor(spiceTerm *vt, int show)
{
2013-09-12 09:16:03 +04:00
int x = vt->cx;
if (x >= vt->width) {
x = vt->width - 1;
}
2013-09-12 09:16:03 +04:00
int y1 = (vt->y_base + vt->cy) % vt->total_height;
int y = y1 - vt->y_displ;
if (y < 0) {
y += vt->total_height;
}
2013-09-12 09:16:03 +04:00
if (y < vt->height) {
2013-09-12 09:16:03 +04:00
TextCell *c = &vt->cells[y1 * vt->width + x];
2013-09-12 09:16:03 +04:00
if (show) {
TextAttributes attrib = vt->default_attrib;
attrib.invers = !(attrib.invers); /* invert fg and bg */
2013-09-13 12:56:03 +04:00
draw_char_at(vt, x, y, c->ch, attrib);
2013-09-12 09:16:03 +04:00
} else {
2013-09-13 12:56:03 +04:00
draw_char_at(vt, x, y, c->ch, c->attrib);
2013-09-12 09:16:03 +04:00
}
}
}
void
2013-09-13 12:47:02 +04:00
spiceterm_refresh(spiceTerm *vt)
{
2013-09-12 09:16:03 +04:00
int x, y, y1;
2013-09-12 09:16:03 +04:00
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++) {
2013-09-13 12:47:02 +04:00
draw_char_at(vt, x, y, c->ch, c->attrib);
2013-09-12 09:16:03 +04:00
c++;
}
if (++y1 == vt->total_height)
y1 = 0;
}
2013-09-13 12:47:02 +04:00
spiceterm_show_cursor(vt, 1);
}
2013-10-09 13:32:12 +04:00
static void
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);
}
2013-09-13 12:47:02 +04:00
void
spiceterm_unselect_all(spiceTerm *vt)
{
2013-10-09 11:30:56 +04:00
int x, y, y1;
2013-09-13 12:47:02 +04:00
2013-10-09 11:30:56 +04:00
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++;
2013-09-13 12:47:02 +04:00
}
2013-10-09 11:30:56 +04:00
if (++y1 == vt->total_height)
y1 = 0;
2013-09-13 12:47:02 +04:00
}
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines)
{
2013-08-21 16:30:16 +04:00
if ((top + lines) >= bottom) {
lines = bottom - top -1;
}
2013-08-21 16:30:16 +04:00
if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
return;
}
2013-08-21 16:30:16 +04:00
int i;
for(i = bottom - top - lines - 1; i >= 0; i--) {
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;
2013-09-13 12:56:03 +04:00
memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
2013-08-21 16:30:16 +04:00
}
2013-08-21 16:30:16 +04:00
for (i = 0; i < lines; i++) {
int j;
TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
for(j = 0; j < vt->width; j++) {
c->attrib = vt->default_attrib;
c->ch = ' ';
c++;
}
}
2013-08-21 16:30:16 +04:00
int h = lines * 16;
int y0 = top*16;
int y1 = y0 + h;
int y2 = bottom*16;
spice_screen_scroll(vt->screen, 0, y1, vt->screen->primary_width, y2, 0, y0);
spice_screen_clear(vt->screen, 0, y0, vt->screen->primary_width, y1);
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
{
2013-08-21 16:30:16 +04:00
if ((top + lines) >= bottom) {
lines = bottom - top - 1;
}
2013-08-21 16:30:16 +04:00
if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
return;
}
2013-08-21 16:30:16 +04:00
int h = lines * 16;
int y0 = top*16;
int y1 = (top + lines)*16;
int y2 = bottom*16;
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);
2013-09-12 09:16:03 +04:00
2013-08-21 16:30:16 +04:00
if (!moveattr) {
return;
}
2013-08-21 16:30:16 +04:00
// move attributes
2013-08-21 16:30:16 +04:00
int i;
for(i = 0; i < (bottom - top - lines); i++) {
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;
2013-09-13 12:56:03 +04:00
memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
2013-08-21 16:30:16 +04:00
}
2013-08-21 16:30:16 +04:00
for (i = 1; i <= lines; i++) {
int j;
TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
for(j = 0; j < vt->width; j++) {
c->attrib = vt->default_attrib;
c->ch = ' ';
c++;
}
}
}
void
2013-09-13 12:56:03 +04:00
spiceterm_virtual_scroll(spiceTerm *vt, int lines)
{
2013-09-12 09:16:03 +04:00
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;
}
}
}
2013-09-13 12:56:03 +04:00
spiceterm_refresh(vt);
}
2013-09-12 09:16:03 +04:00
void
2013-09-13 12:56:03 +04:00
spiceterm_respond_esc(spiceTerm *vt, const char *esc)
{
2013-09-13 12:56:03 +04:00
int len = strlen(esc);
2013-09-12 09:16:03 +04:00
int i;
2013-09-12 09:16:03 +04:00
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];
}
} 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
2013-09-13 12:56:03 +04:00
spiceterm_put_lf(spiceTerm *vt)
{
2013-09-12 09:16:03 +04:00
if (vt->cy + 1 == vt->region_bottom) {
2013-09-12 09:16:03 +04:00
if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
2013-09-13 12:56:03 +04:00
spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1);
2013-09-12 09:16:03 +04:00
return;
}
2013-09-12 09:16:03 +04:00
if (vt->y_displ == vt->y_base) {
2013-09-13 12:56:03 +04:00
spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0);
2013-09-12 09:16:03 +04:00
}
2013-09-12 09:16:03 +04:00
if (vt->y_displ == vt->y_base) {
if (++vt->y_displ == vt->total_height) {
vt->y_displ = 0;
}
}
2013-09-12 09:16:03 +04:00
if (++vt->y_base == vt->total_height) {
vt->y_base = 0;
}
2013-09-12 09:16:03 +04:00
if (vt->scroll_height < vt->total_height) {
vt->scroll_height++;
}
2013-09-12 09:16:03 +04:00
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++;
}
2013-09-12 09:16:03 +04:00
// fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
2013-09-12 09:16:03 +04:00
} else if (vt->cy < vt->height - 1) {
vt->cy += 1;
}
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_csi_m(spiceTerm *vt)
{
2013-09-12 09:16:03 +04:00
int i;
2013-09-12 09:16:03 +04:00
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
2013-09-13 12:56:03 +04:00
spiceterm_save_cursor(spiceTerm *vt)
{
2013-09-12 09:16:03 +04:00
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
2013-09-13 12:56:03 +04:00
spiceterm_restore_cursor(spiceTerm *vt)
{
2013-09-12 09:16:03 +04:00
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
2013-09-13 12:56:03 +04:00
spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off)
{
2013-09-12 09:16:03 +04:00
int x, y;
2013-09-12 09:16:03 +04:00
vt->y_displ = vt->y_base;
2013-09-12 09:16:03 +04:00
if (on_off) {
2013-09-12 09:16:03 +04:00
if (vt->altbuf) return;
2013-09-12 09:16:03 +04:00
vt->altbuf = 1;
2013-09-12 09:16:03 +04:00
/* alternate buffer & cursor */
2013-09-13 12:56:03 +04:00
spiceterm_save_cursor(vt);
2013-09-12 09:16:03 +04:00
/* 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];
}
}
2013-09-12 09:16:03 +04:00
/* clear screen */
for (y = 0; y <= vt->height; y++) {
for (x = 0; x < vt->width; x++) {
// spiceterm_clear_xy(vt, x, y);
2013-09-12 09:16:03 +04:00
}
}
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
} else {
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
if (vt->altbuf == 0) return;
2013-09-12 09:16:03 +04:00
vt->altbuf = 0;
2013-09-12 09:16:03 +04:00
/* 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];
}
}
2013-09-13 12:56:03 +04:00
spiceterm_restore_cursor(vt);
}
2013-09-13 12:56:03 +04:00
spiceterm_refresh(vt);
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_set_mode(spiceTerm *vt, int on_off)
{
2013-09-12 09:16:03 +04:00
int i;
2013-09-12 09:16:03 +04:00
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 */
2013-09-12 17:36:37 +04:00
case 1000: /* SET_VT200_MOUSE */
case 1002: /* xterm SET_BTN_EVENT_MOUSE */
2013-09-12 09:16:03 +04:00
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 */
2013-09-12 17:36:37 +04:00
case 9: /* X10 mouse reporting on/off */
2013-09-12 09:16:03 +04:00
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;
2013-09-12 17:36:37 +04:00
}
2013-09-12 09:16:03 +04:00
} else { /* ANSI modes set/reset */
2013-09-12 17:36:37 +04:00
//g_assert_not_reached();
2013-09-12 09:16:03 +04:00
/* fixme: implement me */
}
}
}
static void
2013-09-13 12:56:03 +04:00
spiceterm_gotoxy(spiceTerm *vt, int x, int y)
{
2013-09-12 09:16:03 +04:00
/* verify all boundaries */
2013-09-12 09:16:03 +04:00
if (x < 0) {
x = 0;
}
2013-09-12 09:16:03 +04:00
if (x >= vt->width) {
x = vt->width - 1;
}
2013-09-12 09:16:03 +04:00
vt->cx = x;
2013-09-12 09:16:03 +04:00
if (y < 0) {
y = 0;
}
2013-09-12 09:16:03 +04:00
if (y >= vt->height) {
y = vt->height - 1;
}
2013-09-12 09:16:03 +04:00
vt->cy = y;
}
2013-09-13 12:56:03 +04:00
static void
2013-09-12 11:26:44 +04:00
debug_print_escape_buffer(spiceTerm *vt, const char *func, const char *prefix,
const char *qes, gunichar2 ch)
{
if (debug >=1 ) {
if (vt->esc_count == 0) {
printf("%s:%s ESC[%s%c\n", func, prefix, qes, ch);
} else if (vt->esc_count == 1) {
printf("%s:%s ESC[%s%d%c\n", func, prefix, qes, vt->esc_buf[0], ch);
} else {
int i;
printf("%s:%s ESC[%s%d", func, prefix, qes, vt->esc_buf[0]);
for (i = 1; i < vt->esc_count; i++) {
printf(";%d", vt->esc_buf[i]);
}
printf("%c\n", ch);
}
}
}
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
ESpalette, ESidquery, ESosc1, ESosc2};
static void
2013-09-13 12:56:03 +04:00
spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
{
2013-09-12 09:16:03 +04:00
int x, y, i, c;
2013-09-12 09:16:03 +04:00
if (debug && !vt->tty_state) {
2013-09-12 11:26:44 +04:00
DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
2013-09-12 09:16:03 +04:00
vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
}
2013-09-12 09:16:03 +04:00
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':
2013-09-13 12:56:03 +04:00
spiceterm_save_cursor(vt);
2013-09-12 09:16:03 +04:00
break;
case '8':
2013-09-13 12:56:03 +04:00
spiceterm_restore_cursor(vt);
2013-09-12 09:16:03 +04:00
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)
2013-09-13 12:56:03 +04:00
spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1);
2013-09-12 09:16:03 +04:00
else if (vt->cy > 0) {
vt->cy--;
}
break;
case '>':
/* numeric keypad - ignored */
break;
case '=':
/* appl. keypad - ignored */
break;
default:
2013-09-12 11:26:44 +04:00
DPRINTF(1, "got unhandled ESC%c %d", ch, ch);
2013-09-12 09:16:03 +04:00
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;
}
2013-09-12 09:16:03 +04:00
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:
2013-09-12 11:26:44 +04:00
DPRINTF(1, "got unhandled OSC %c", ch);
2013-09-12 09:16:03 +04:00
vt->tty_state = ESnormal;
break;
}
break;
case ESosc1:
vt->tty_state = ESnormal;
if (ch == ';') {
vt->tty_state = ESosc2;
} else {
2013-09-12 11:26:44 +04:00
DPRINTF(1, "got illegal OSC sequence");
2013-09-12 09:16:03 +04:00
}
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 {
2013-09-12 11:26:44 +04:00
DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf);
2013-09-12 09:16:03 +04:00
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); ?
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
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;
}
2013-09-12 09:16:03 +04:00
vt->esc_count = 0;
vt->esc_has_par = 0;
vt->tty_state = ESgetpars;
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
if (ch == '>') {
vt->tty_state = ESidquery;
break;
}
2013-09-12 09:16:03 +04:00
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;
2013-09-11 14:56:28 +04:00
} else {
2013-09-12 09:16:03 +04:00
if (vt->esc_has_par) {
vt->esc_count++;
}
vt->tty_state = ESgotpars;
}
case ESgotpars:
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
vt->tty_state = ESnormal;
char *qes = vt->esc_ques ? "?" : "";
if (debug) {
2013-09-12 11:26:44 +04:00
debug_print_escape_buffer(vt, __func__, "", qes, ch);
2013-09-11 14:56:28 +04:00
}
2013-09-12 09:16:03 +04:00
switch (ch) {
case 'h':
2013-09-12 17:36:37 +04:00
spiceterm_set_mode(vt, 1);
2013-09-12 09:16:03 +04:00
break;
case 'l':
2013-09-12 17:36:37 +04:00
spiceterm_set_mode(vt, 0);
2013-09-12 09:16:03 +04:00
break;
case 'm':
if (!vt->esc_count) {
vt->esc_count++; // default parameter 0
}
2013-09-13 12:56:03 +04:00
spiceterm_csi_m(vt);
2013-09-12 09:16:03 +04:00
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 */
2013-09-13 12:56:03 +04:00
spiceterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy);
2013-09-12 09:16:03 +04:00
break;
case 'd':
/* move cursor to row */
2013-09-13 12:56:03 +04:00
spiceterm_gotoxy(vt, vt->cx , vt->esc_buf[0] - 1);
2013-09-12 09:16:03 +04:00
break;
case 'f':
case 'H':
/* move cursor to row, column */
2013-09-13 12:56:03 +04:00
spiceterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1);
2013-09-12 09:16:03 +04:00
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;
}
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(vt, x, y);
2013-09-12 09:16:03 +04:00
}
}
break;
case 2:
/* clear entire screen */
2013-10-09 13:32:12 +04:00
spiceterm_clear_screen(vt);
2013-09-12 09:16:03 +04:00
break;
}
break;
case 'K':
switch (vt->esc_buf[0]) {
case 0:
/* clear to eol */
for(x = vt->cx; x < vt->width; x++) {
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(vt, x, vt->cy);
2013-09-12 09:16:03 +04:00
}
break;
case 1:
/* clear from beginning of line */
for (x = 0; x <= vt->cx; x++) {
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(vt, x, vt->cy);
2013-09-12 09:16:03 +04:00
}
break;
case 2:
/* clear entire line */
for(x = 0; x < vt->width; x++) {
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(vt, x, vt->cy);
2013-09-12 09:16:03 +04:00
}
break;
}
break;
case 'L':
/* insert line */
c = vt->esc_buf[0];
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
if (c > vt->height - vt->cy)
c = vt->height - vt->cy;
else if (!c)
c = 1;
2013-09-13 12:56:03 +04:00
spiceterm_scroll_down(vt, vt->cy, vt->region_bottom, c);
2013-09-12 09:16:03 +04:00
break;
case 'M':
/* delete line */
c = vt->esc_buf[0];
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
if (c > vt->height - vt->cy)
c = vt->height - vt->cy;
else if (!c)
c = 1;
2013-09-13 12:56:03 +04:00
spiceterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1);
2013-09-12 09:16:03 +04:00
break;
case 'T':
/* scroll down */
c = vt->esc_buf[0];
if (!c) c = 1;
2013-09-13 12:56:03 +04:00
spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, c);
2013-09-12 09:16:03 +04:00
break;
case 'S':
/* scroll up */
c = vt->esc_buf[0];
if (!c) c = 1;
2013-09-13 12:56:03 +04:00
spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, c, 1);
2013-09-12 09:16:03 +04:00
break;
case 'P':
/* delete c character */
c = vt->esc_buf[0];
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
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;
2013-09-13 12:56:03 +04:00
spiceterm_update_xy(vt, x + c, vt->cy);
2013-09-12 09:16:03 +04:00
src->ch = ' ';
src->attrib = vt->default_attrib;
2013-09-13 12:56:03 +04:00
spiceterm_update_xy(vt, x, vt->cy);
2013-09-12 09:16:03 +04:00
}
break;
case 's':
/* save cursor position */
2013-09-13 12:56:03 +04:00
spiceterm_save_cursor(vt);
2013-09-12 09:16:03 +04:00
break;
case 'u':
/* restore cursor position */
2013-09-13 12:56:03 +04:00
spiceterm_restore_cursor(vt);
2013-09-12 09:16:03 +04:00
break;
case 'X':
/* erase c characters */
c = vt->esc_buf[0];
if (!c) c = 1;
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
2013-09-12 09:16:03 +04:00
for(i = 0; i < c; i++) {
2013-09-13 12:56:03 +04:00
spiceterm_clear_xy(vt, vt->cx + i, vt->cy);
2013-09-12 09:16:03 +04:00
}
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;
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
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;
2013-09-13 12:56:03 +04:00
spiceterm_update_xy(vt, x, vt->cy);
2013-09-12 09:16:03 +04:00
}
2013-09-12 09:16:03 +04:00
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;
2013-09-12 11:26:44 +04:00
DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom);
2013-09-12 09:16:03 +04:00
}
2013-09-12 09:16:03 +04:00
break;
default:
if (debug) {
2013-09-12 11:26:44 +04:00
debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch);
2013-09-12 09:16:03 +04:00
}
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;
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
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') {
2013-09-12 11:26:44 +04:00
DPRINTF(1, "ESC[>c Query term ID");
2013-09-13 12:56:03 +04:00
spiceterm_respond_esc(vt, TERMIDCODE);
2013-09-12 17:36:37 +04:00
}
2013-09-12 09:16:03 +04:00
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;
2013-09-13 12:56:03 +04:00
spiceterm_put_lf(vt);
2013-09-12 09:16:03 +04:00
} else {
vt->cx = vt->cx + (8 - (vt->cx % 8));
}
break;
case 10: /* LF,*/
case 11: /* VT */
case 12: /* FF */
2013-09-13 12:56:03 +04:00
spiceterm_put_lf(vt);
2013-09-12 09:16:03 +04:00
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;
2013-09-13 12:56:03 +04:00
spiceterm_put_lf(vt);
2013-09-12 09:16:03 +04:00
}
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;
2013-09-13 12:56:03 +04:00
spiceterm_update_xy(vt, vt->cx, vt->cy);
2013-09-12 09:16:03 +04:00
vt->cx++;
break;
}
break;
}
}
static int
2013-09-13 12:56:03 +04:00
spiceterm_puts(spiceTerm *vt, const char *buf, int len)
{
2013-09-11 15:15:53 +04:00
gunichar2 tc;
2013-09-13 12:56:03 +04:00
spiceterm_show_cursor(vt, 0);
while (len) {
2013-09-12 09:16:03 +04:00
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;
}
2013-09-12 09:16:03 +04:00
} else {
// never translate controls
if (c >= 32 && c != 127 && c != (128+27)) {
tc = translations[vt->cur_enc][c & 0x0ff];
} else {
tc = c;
}
}
2013-09-12 09:17:50 +04:00
2013-09-13 12:56:03 +04:00
spiceterm_putchar(vt, tc);
}
2013-09-13 12:56:03 +04:00
spiceterm_show_cursor(vt, 1);
2013-09-12 09:17:50 +04:00
return len;
}
void
2013-09-13 09:38:07 +04:00
spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable)
{
g_assert(vt != NULL);
int mask = SPICE_WATCH_EVENT_READ;
if (writable) {
mask |= SPICE_WATCH_EVENT_WRITE;
}
vt->screen->core->watch_update_mask(vt->screen->mwatch, mask);
}
static void
2013-09-12 17:36:37 +04:00
mouse_report(spiceTerm *vt, int butt, int mrx, int mry)
{
2013-09-12 17:36:37 +04:00
char buf[8];
2013-09-13 12:56:03 +04:00
sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
2013-09-12 17:36:37 +04:00
(char)('!' + mry));
2013-09-12 17:36:37 +04:00
spiceterm_respond_esc(vt, buf);
2013-09-13 09:38:07 +04:00
spiceterm_update_watch_mask(vt, TRUE);
}
2013-09-13 11:53:24 +04:00
static void
spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
{
2013-09-13 11:53:24 +04:00
if (vt->utf8) {
gchar buf[10];
gint len = g_unichar_to_utf8(uc, buf);
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);
}
}
void
2013-10-08 17:08:04 +04:00
spiceterm_clear_selection(spiceTerm *vt)
{
DPRINTF(1, "mark_active = %d", vt->mark_active);
vt->mark_active = 0;
if (vt->selection) free (vt->selection);
vt->selection = NULL;
spiceterm_unselect_all(vt);
}
void
2013-09-16 14:23:49 +04:00
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);
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 (vdagent_owns_clipboard(vt)) {
2013-09-16 14:23:49 +04:00
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);
2013-09-16 14:23:49 +04:00
}
}
button2_released = 0;
} else {
button2_released = 1;
}
if (buttons & 2) {
int pos = cy*vt->width + cx;
// code borrowed from libvncserver (VNCconsole.c)
if (!vt->mark_active) {
spiceterm_unselect_all(vt);
vt->mark_active = 1;
sel_start_pos = sel_end_pos = pos;
spiceterm_toggle_marked_cell(vt, pos);
} 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;
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);
vdagent_grab_clipboard(vt);
2013-09-13 16:18:46 +04:00
}
2013-09-12 16:22:05 +04:00
}
void
2013-09-17 14:57:23 +04:00
init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
{
2013-09-12 09:16:03 +04:00
int i;
2013-09-17 14:57:23 +04:00
g_assert(vt != NULL);
g_assert(vt->screen != NULL);
2013-09-17 14:57:23 +04:00
vt->width = width / 8;
vt->height = height / 16;
2013-09-12 09:16:03 +04:00
vt->total_height = vt->height * 20;
vt->scroll_height = 0;
vt->y_base = 0;
vt->y_displ = 0;
2013-09-12 09:16:03 +04:00
vt->region_top = 0;
vt->region_bottom = vt->height;
2013-09-12 09:16:03 +04:00
vt->g0enc = LAT1_MAP;
vt->g1enc = GRAF_MAP;
vt->cur_enc = vt->g0enc;
vt->charset = 0;
2013-09-12 09:16:03 +04:00
/* 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;
2013-09-12 09:16:03 +04:00
vt->cur_attrib = vt->default_attrib;
2013-09-17 14:57:23 +04:00
if (vt->cells) {
vt->cx = 0;
vt->cy = 0;
vt->cx_saved = 0;
vt->cy_saved = 0;
g_free(vt->cells);
}
2013-09-12 09:16:03 +04:00
vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
2013-09-12 09:17:50 +04:00
2013-09-12 09:16:03 +04:00
for (i = 0; i < vt->width*vt->total_height; i++) {
vt->cells[i].ch = ' ';
vt->cells[i].attrib = vt->default_attrib;
}
2013-09-17 14:57:23 +04:00
if (vt->altcells) {
g_free(vt->altcells);
}
2013-09-12 09:16:03 +04:00
vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
2013-09-17 14:57:23 +04:00
}
void
2013-09-17 14:57:23 +04:00
spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
{
2013-10-08 14:14:10 +04:00
width = (width/8)*8;
height = (height/16)*16;
2013-09-17 14:57:23 +04:00
if (vt->screen->width == width && vt->screen->height == height) {
return;
}
2013-10-08 14:14:10 +04:00
DPRINTF(0, "width=%u height=%u", width, height);
2013-09-17 14:57:23 +04:00
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);
}
2013-09-13 12:56:03 +04:00
static gboolean
master_error_callback(GIOChannel *channel, GIOCondition condition,
gpointer data)
{
//spiceTerm *vt = (spiceTerm *)data;
DPRINTF(1, "condition %d", condition);
exit(0);
return FALSE;
}
2013-09-13 12:56:03 +04:00
static void
2013-09-12 09:17:50 +04:00
master_watch(int master, int event, void *opaque)
{
2013-09-11 09:20:40 +04:00
spiceTerm *vt = (spiceTerm *)opaque;
2013-09-13 09:52:54 +04:00
int c;
// fixme: if (!vt->mark_active) {
if (event == SPICE_WATCH_EVENT_READ) {
char buffer[1024];
while ((c = read(master, buffer, 1024)) == -1) {
if (errno != EAGAIN) break;
}
if (c == -1) {
2013-09-13 09:52:54 +04:00
perror("master pipe read error"); // fixme
}
2013-09-11 09:20:40 +04:00
spiceterm_puts (vt, buffer, c);
} else {
if (vt->ibuf_count > 0) {
2013-09-12 11:26:44 +04:00
DPRINTF(1, "write input %x %d", vt->ibuf[0], vt->ibuf_count);
2013-09-13 09:52:54 +04:00
if ((c = write (master, vt->ibuf, vt->ibuf_count)) >= 0) {
if (c == vt->ibuf_count) {
2013-09-13 12:56:03 +04:00
vt->ibuf_count = 0;
2013-09-13 09:52:54 +04:00
} else if (c > 0) {
// not all data written
memmove(vt->ibuf, vt->ibuf + c, vt->ibuf_count - c);
2013-09-13 12:56:03 +04:00
vt->ibuf_count -= c;
2013-09-13 09:52:54 +04:00
} else {
// nothing written -ignore and try later
}
} else {
perror("master pipe write error");
}
}
if (vt->ibuf_count == 0) {
spiceterm_update_watch_mask(vt, FALSE);
}
}
}
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");
2013-10-29 12:39:30 +04:00
fprintf(stderr, " --keymap Spefify keymap (uses kvm keymap files)\n");
}
int
main (int argc, char** argv)
{
int c;
2013-09-12 09:16:03 +04:00
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,
};
2013-09-12 09:16:03 +04:00
g_thread_init(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' },
2013-10-29 12:39:30 +04:00
{ "keymap", required_argument, 0, 'k' },
{ "noauth", no_argument, 0, 'n' },
{ "sasl", no_argument, 0, 's' },
{ NULL, 0, 0, 0 },
};
2013-10-29 12:39:30 +04:00
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;
2013-10-29 12:39:30 +04:00
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);
2013-09-12 09:16:03 +04:00
break;
case '?':
spiceterm_print_usage(NULL);
exit(-1);
break;
default:
spiceterm_print_usage("getopt returned unknown character code");
exit(-1);
2013-09-12 09:16:03 +04:00
}
}
if (optind < argc) {
command = argv[optind+1];
cmdargv = &argv[optind+1];
}
spiceTerm *vt = spiceterm_create(744, 400, &opts);
2013-10-29 14:56:07 +04:00
if (!vt)
exit(-1);
2013-09-12 09:16:03 +04:00
setlocale(LC_ALL, ""); // set from environment
2013-09-12 09:16:03 +04:00
char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
2013-09-12 09:16:03 +04:00
// fixme: ist there a standard way to detect utf8 mode ?
if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) {
vt->utf8 = 1;
}
2013-09-12 09:16:03 +04:00
dimensions.ws_col = vt->width;
dimensions.ws_row = vt->height;
2013-09-12 17:36:37 +04:00
setenv("TERM", TERM, 1);
2013-09-12 11:26:44 +04:00
DPRINTF(1, "execute %s", command);
2013-09-12 09:16:03 +04:00
pid = forkpty (&master, ptyname, NULL, &dimensions);
if(!pid) {
2013-09-12 09:16:03 +04:00
// install default signal handlers
signal (SIGQUIT, SIG_DFL);
signal (SIGTERM, SIG_DFL);
signal (SIGINT, SIG_DFL);
2013-09-12 09:16:03 +04:00
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);
}
2013-09-17 14:57:23 +04:00
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);
2013-09-13 12:56:03 +04:00
2013-09-12 09:16:03 +04:00
vt->screen->mwatch = vt->screen->core->watch_add(
master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */,
master_watch, vt);
2013-09-12 09:16:03 +04:00
basic_event_loop_mainloop();
2013-09-12 09:16:03 +04:00
kill (pid, 9);
int status;
waitpid(pid, &status, 0);
2013-09-12 09:16:03 +04:00
exit (0);
}