mirror of
git://git.proxmox.com/git/spiceterm.git
synced 2024-12-31 17:17:55 +03:00
move spice input/vdagent related code to input.c
This commit is contained in:
parent
06a072752e
commit
f424be980a
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
||||
PROGRAMS=spiceterm
|
||||
|
||||
HEADERS=translations.h event_loop.h glyphs.h spiceterm.h
|
||||
SOURCES=screen.c event_loop.c spiceterm.c
|
||||
SOURCES=screen.c event_loop.c input.c spiceterm.c
|
||||
|
||||
#export G_MESSAGES_DEBUG=all
|
||||
#export SPICE_DEBUG=1
|
||||
|
645
input.c
Normal file
645
input.c
Normal file
@ -0,0 +1,645 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2013 Proxmox Server Solutions GmbH
|
||||
|
||||
Copyright: spiceterm is under GNU GPL, the GNU General Public License.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Author: Dietmar Maurer <dietmar@proxmox.com>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spiceterm.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <spice.h>
|
||||
#include <spice/enums.h>
|
||||
#include <spice/macros.h>
|
||||
#include <spice/qxl_dev.h>
|
||||
|
||||
#include "event_loop.h"
|
||||
|
||||
static int debug = 0;
|
||||
|
||||
#define DPRINTF(x, format, ...) { \
|
||||
if (x <= debug) { \
|
||||
printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
my_kbd_get_leds(SpiceKbdInstance *sin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KBD_MOD_CONTROL_L_FLAG (1<<0)
|
||||
#define KBD_MOD_CONTROL_R_FLAG (1<<1)
|
||||
#define KBD_MOD_SHIFT_L_FLAG (1<<2)
|
||||
#define KBD_MOD_SHIFT_R_FLAG (1<<3)
|
||||
|
||||
static int kbd_flags = 0;
|
||||
|
||||
static void
|
||||
my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
|
||||
{
|
||||
spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
|
||||
|
||||
char *esc = NULL; // used to send special keys
|
||||
|
||||
static int e0_mode = 0;
|
||||
|
||||
DPRINTF(1, "enter frag=%02x flags=%08x", frag, kbd_flags);
|
||||
|
||||
if (e0_mode) {
|
||||
e0_mode = 0;
|
||||
switch (frag) {
|
||||
case 0x1d: // press Control_R
|
||||
kbd_flags |= KBD_MOD_CONTROL_R_FLAG;
|
||||
break;
|
||||
case 0x9d: // release Control_R
|
||||
kbd_flags &= ~KBD_MOD_CONTROL_R_FLAG;
|
||||
break;
|
||||
case 0x47: // press Home
|
||||
esc = "OH";
|
||||
break;
|
||||
case 0x4f: // press END
|
||||
esc = "OF";
|
||||
break;
|
||||
case 0x48: // press UP
|
||||
esc = "OA";
|
||||
break;
|
||||
case 0x50: // press DOWN
|
||||
esc = "OB";
|
||||
break;
|
||||
case 0x4b: // press LEFT
|
||||
esc = "OD";
|
||||
break;
|
||||
case 0x4d: // press RIGHT
|
||||
esc = "OC";
|
||||
break;
|
||||
case 0x52: // press INSERT
|
||||
esc = "[2~";
|
||||
break;
|
||||
case 0x49: // press PAGE_UP
|
||||
if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
|
||||
spiceterm_virtual_scroll(vt, -vt->height/2);
|
||||
}
|
||||
break;
|
||||
case 0x51: // press PAGE_DOWN
|
||||
if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
|
||||
spiceterm_virtual_scroll(vt, vt->height/2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (frag) {
|
||||
case 0xe0:
|
||||
e0_mode = 1;
|
||||
break;
|
||||
case 0x1d: // press Control_L
|
||||
kbd_flags |= KBD_MOD_CONTROL_L_FLAG;
|
||||
break;
|
||||
case 0x9d: // release Control_L
|
||||
kbd_flags &= ~KBD_MOD_CONTROL_L_FLAG;
|
||||
break;
|
||||
case 0x2a: // press Shift_L
|
||||
kbd_flags |= KBD_MOD_SHIFT_L_FLAG;
|
||||
break;
|
||||
case 0xaa: // release Shift_L
|
||||
kbd_flags &= ~KBD_MOD_SHIFT_L_FLAG;
|
||||
break;
|
||||
case 0x36: // press Shift_R
|
||||
kbd_flags |= KBD_MOD_SHIFT_R_FLAG;
|
||||
break;
|
||||
case 0xb6: // release Shift_R
|
||||
kbd_flags &= ~KBD_MOD_SHIFT_R_FLAG;
|
||||
break;
|
||||
case 0x52: // press KP_INSERT
|
||||
esc = "[2~";
|
||||
break;
|
||||
case 0x53: // press KP_Delete
|
||||
esc = "[3~";
|
||||
break;
|
||||
case 0x47: // press KP_Home
|
||||
esc = "OH";
|
||||
break;
|
||||
case 0x4f: // press KP_END
|
||||
esc = "OF";
|
||||
break;
|
||||
case 0x48: // press KP_UP
|
||||
esc = "OA";
|
||||
break;
|
||||
case 0x50: // press KP_DOWN
|
||||
esc = "OB";
|
||||
break;
|
||||
case 0x4b: // press KP_LEFT
|
||||
esc = "OD";
|
||||
break;
|
||||
case 0x4d: // press KP_RIGHT
|
||||
esc = "OC";
|
||||
break;
|
||||
case 0x3b: // press F1
|
||||
esc = "OP";
|
||||
break;
|
||||
case 0x3c: // press F2
|
||||
esc = "OQ";
|
||||
break;
|
||||
case 0x3d: // press F3
|
||||
esc = "OR";
|
||||
break;
|
||||
case 0x3e: // press F4
|
||||
esc = "OS";
|
||||
break;
|
||||
case 0x3f: // press F5
|
||||
esc = "[15~";
|
||||
break;
|
||||
case 0x40: // press F6
|
||||
esc = "[17~";
|
||||
break;
|
||||
case 0x41: // press F7
|
||||
esc = "[18~";
|
||||
break;
|
||||
case 0x42: // press F8
|
||||
esc = "[19~";
|
||||
break;
|
||||
case 0x43: // press F9
|
||||
esc = "[20~";
|
||||
break;
|
||||
case 0x44: // press F10
|
||||
esc = "[21~";
|
||||
break;
|
||||
case 0x57: // press F11
|
||||
esc = "[23~";
|
||||
break;
|
||||
case 0x58: // press F12
|
||||
esc = "[24~";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (esc) {
|
||||
DPRINTF(1, "escape=%s", esc);
|
||||
spiceterm_respond_esc(vt, esc);
|
||||
|
||||
if (vt->y_displ != vt->y_base) {
|
||||
vt->y_displ = vt->y_base;
|
||||
spiceterm_refresh(vt);
|
||||
}
|
||||
|
||||
spiceterm_update_watch_mask(vt, TRUE);
|
||||
}
|
||||
|
||||
DPRINTF(1, "leave frag=%02x flags=%08x", frag, kbd_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
my_kbd_push_utf8(SpiceKbdInstance *sin, uint32_t size, uint8_t *data)
|
||||
{
|
||||
spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
|
||||
|
||||
DPRINTF(1, " size=%d data[0]=%02x", size, data[0]);
|
||||
|
||||
if (kbd_flags & (KBD_MOD_CONTROL_L_FLAG|KBD_MOD_CONTROL_R_FLAG)) {
|
||||
if (size != 1) return;
|
||||
if (data[0] >= 'a' && data[0] <= 'z') {
|
||||
uint8_t buf[1] = { data[0] - 'a' + 1 };
|
||||
spiceterm_respond_data(vt, 1, buf);
|
||||
|
||||
} else if (data[0] >= 'A' && data[0] <= 'Z') {
|
||||
uint8_t buf[1] = { data[0] - 'A' + 1 };
|
||||
spiceterm_respond_data(vt, 1, buf);
|
||||
}
|
||||
} else {
|
||||
if (size == 1 && data[0] == 0x7f) {
|
||||
/* use an escape sequence for DELETE, else it behaves like BACKSPACE */
|
||||
spiceterm_respond_esc(vt, "[3~");
|
||||
} else {
|
||||
spiceterm_respond_data(vt, size, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (vt->y_displ != vt->y_base) {
|
||||
vt->y_displ = vt->y_base;
|
||||
spiceterm_refresh(vt);
|
||||
}
|
||||
|
||||
spiceterm_update_watch_mask(vt, TRUE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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_scan_freg = my_kbd_push_key,
|
||||
.get_leds = my_kbd_get_leds,
|
||||
.push_utf8 = my_kbd_push_utf8,
|
||||
};
|
||||
|
||||
|
||||
/* vdagent interface - to get mouse/clipboard 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
|
||||
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
|
||||
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_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);
|
||||
|
||||
if (0) dump_message(buf, msg_size);
|
||||
|
||||
spice_server_char_device_wakeup(&vt->vdagent_sin);
|
||||
|
||||
free(caps);
|
||||
}
|
||||
|
||||
gboolean
|
||||
vdagent_owns_clipboard(spiceTerm *vt)
|
||||
{
|
||||
return !!agent_owns_clipboard[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY];
|
||||
}
|
||||
|
||||
void
|
||||
vdagent_grab_clipboard(spiceTerm *vt)
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
vdagent_request_clipboard(spiceTerm *vt)
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
|
||||
|
||||
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;
|
||||
spiceterm_clear_selection(vt);
|
||||
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) {
|
||||
spiceterm_respond_data(vt, size, data + 8);
|
||||
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(1, "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(1, "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(1, "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,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
spice_screen->image_cache = g_hash_table_new(g_int_hash, g_int_equal);
|
||||
|
||||
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;
|
||||
}
|
673
spiceterm.c
673
spiceterm.c
@ -78,12 +78,6 @@ static int debug = 0;
|
||||
unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
|
||||
8,12,10,14, 9,13,11,15 };
|
||||
|
||||
|
||||
static void spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height);
|
||||
|
||||
static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection);
|
||||
static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection);
|
||||
|
||||
static void
|
||||
print_usage(const char *msg)
|
||||
{
|
||||
@ -184,7 +178,7 @@ spiceterm_show_cursor(spiceTerm *vt, int show)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
spiceterm_refresh(spiceTerm *vt)
|
||||
{
|
||||
int x, y, y1;
|
||||
@ -330,7 +324,7 @@ spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
spiceterm_virtual_scroll(spiceTerm *vt, int lines)
|
||||
{
|
||||
if (vt->altbuf || lines == 0) return;
|
||||
@ -362,7 +356,7 @@ spiceterm_virtual_scroll(spiceTerm *vt, int lines)
|
||||
spiceterm_refresh(vt);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
spiceterm_respond_esc(spiceTerm *vt, const char *esc)
|
||||
{
|
||||
int len = strlen(esc);
|
||||
@ -373,6 +367,24 @@ spiceterm_respond_esc(spiceTerm *vt, const char *esc)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,24 +1295,7 @@ spiceterm_puts(spiceTerm *vt, const char *buf, int len)
|
||||
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
|
||||
spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable)
|
||||
{
|
||||
g_assert(vt != NULL);
|
||||
@ -1335,252 +1330,15 @@ spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
|
||||
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");
|
||||
}
|
||||
spiceterm_respond_data(vt, len, (uint8_t *)buf);
|
||||
}
|
||||
} else {
|
||||
if ((vt->ibuf_count + 1) < IBUFSIZE) {
|
||||
vt->ibuf[vt->ibuf_count++] = (char)uc;
|
||||
} else {
|
||||
fprintf(stderr, "warning: input buffer overflow\n");
|
||||
}
|
||||
uint8_t buf[1] = { (uint8_t)uc };
|
||||
spiceterm_respond_data(vt, 1, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
my_kbd_get_leds(SpiceKbdInstance *sin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KBD_MOD_CONTROL_L_FLAG (1<<0)
|
||||
#define KBD_MOD_CONTROL_R_FLAG (1<<1)
|
||||
#define KBD_MOD_SHIFT_L_FLAG (1<<2)
|
||||
#define KBD_MOD_SHIFT_R_FLAG (1<<3)
|
||||
|
||||
static int kbd_flags = 0;
|
||||
static void
|
||||
my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
|
||||
{
|
||||
spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
|
||||
|
||||
char *esc = NULL; // used to send special keys
|
||||
|
||||
static int e0_mode = 0;
|
||||
|
||||
DPRINTF(1, "enter frag=%02x flags=%08x", frag, kbd_flags);
|
||||
|
||||
if (e0_mode) {
|
||||
e0_mode = 0;
|
||||
switch (frag) {
|
||||
case 0x1d: // press Control_R
|
||||
kbd_flags |= KBD_MOD_CONTROL_R_FLAG;
|
||||
break;
|
||||
case 0x9d: // release Control_R
|
||||
kbd_flags &= ~KBD_MOD_CONTROL_R_FLAG;
|
||||
break;
|
||||
case 0x47: // press Home
|
||||
esc = "OH";
|
||||
break;
|
||||
case 0x4f: // press END
|
||||
esc = "OF";
|
||||
break;
|
||||
case 0x48: // press UP
|
||||
esc = "OA";
|
||||
break;
|
||||
case 0x50: // press DOWN
|
||||
esc = "OB";
|
||||
break;
|
||||
case 0x4b: // press LEFT
|
||||
esc = "OD";
|
||||
break;
|
||||
case 0x4d: // press RIGHT
|
||||
esc = "OC";
|
||||
break;
|
||||
case 0x52: // press INSERT
|
||||
esc = "[2~";
|
||||
break;
|
||||
case 0x49: // press PAGE_UP
|
||||
if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
|
||||
spiceterm_virtual_scroll (vt, -vt->height/2);
|
||||
}
|
||||
break;
|
||||
case 0x51: // press PAGE_DOWN
|
||||
if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
|
||||
spiceterm_virtual_scroll (vt, vt->height/2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (frag) {
|
||||
case 0xe0:
|
||||
e0_mode = 1;
|
||||
break;
|
||||
case 0x1d: // press Control_L
|
||||
kbd_flags |= KBD_MOD_CONTROL_L_FLAG;
|
||||
break;
|
||||
case 0x9d: // release Control_L
|
||||
kbd_flags &= ~KBD_MOD_CONTROL_L_FLAG;
|
||||
break;
|
||||
case 0x2a: // press Shift_L
|
||||
kbd_flags |= KBD_MOD_SHIFT_L_FLAG;
|
||||
break;
|
||||
case 0xaa: // release Shift_L
|
||||
kbd_flags &= ~KBD_MOD_SHIFT_L_FLAG;
|
||||
break;
|
||||
case 0x36: // press Shift_R
|
||||
kbd_flags |= KBD_MOD_SHIFT_R_FLAG;
|
||||
break;
|
||||
case 0xb6: // release Shift_R
|
||||
kbd_flags &= ~KBD_MOD_SHIFT_R_FLAG;
|
||||
break;
|
||||
case 0x52: // press KP_INSERT
|
||||
esc = "[2~";
|
||||
break;
|
||||
case 0x53: // press KP_Delete
|
||||
esc = "[3~";
|
||||
break;
|
||||
case 0x47: // press KP_Home
|
||||
esc = "OH";
|
||||
break;
|
||||
case 0x4f: // press KP_END
|
||||
esc = "OF";
|
||||
break;
|
||||
case 0x48: // press KP_UP
|
||||
esc = "OA";
|
||||
break;
|
||||
case 0x50: // press KP_DOWN
|
||||
esc = "OB";
|
||||
break;
|
||||
case 0x4b: // press KP_LEFT
|
||||
esc = "OD";
|
||||
break;
|
||||
case 0x4d: // press KP_RIGHT
|
||||
esc = "OC";
|
||||
break;
|
||||
case 0x3b: // press F1
|
||||
esc = "OP";
|
||||
break;
|
||||
case 0x3c: // press F2
|
||||
esc = "OQ";
|
||||
break;
|
||||
case 0x3d: // press F3
|
||||
esc = "OR";
|
||||
break;
|
||||
case 0x3e: // press F4
|
||||
esc = "OS";
|
||||
break;
|
||||
case 0x3f: // press F5
|
||||
esc = "[15~";
|
||||
break;
|
||||
case 0x40: // press F6
|
||||
esc = "[17~";
|
||||
break;
|
||||
case 0x41: // press F7
|
||||
esc = "[18~";
|
||||
break;
|
||||
case 0x42: // press F8
|
||||
esc = "[19~";
|
||||
break;
|
||||
case 0x43: // press F9
|
||||
esc = "[20~";
|
||||
break;
|
||||
case 0x44: // press F10
|
||||
esc = "[21~";
|
||||
break;
|
||||
case 0x57: // press F11
|
||||
esc = "[23~";
|
||||
break;
|
||||
case 0x58: // press F12
|
||||
esc = "[24~";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (esc) {
|
||||
DPRINTF(1, "escape=%s", esc);
|
||||
spiceterm_respond_esc(vt, esc);
|
||||
|
||||
if (vt->y_displ != vt->y_base) {
|
||||
vt->y_displ = vt->y_base;
|
||||
spiceterm_refresh(vt);
|
||||
}
|
||||
|
||||
spiceterm_update_watch_mask(vt, TRUE);
|
||||
}
|
||||
|
||||
DPRINTF(1, "leave frag=%02x flags=%08x", frag, kbd_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
my_kbd_push_utf8(SpiceKbdInstance *sin, uint32_t size, uint8_t *data)
|
||||
{
|
||||
spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
|
||||
|
||||
DPRINTF(1, " size=%d data[0]=%02x", size, data[0]);
|
||||
|
||||
if (vt->ibuf_count + size >= IBUFSIZE) {
|
||||
fprintf(stderr, "input buffer oferflow\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (kbd_flags & (KBD_MOD_CONTROL_L_FLAG|KBD_MOD_CONTROL_R_FLAG)) {
|
||||
if (size != 1) return;
|
||||
if (data[0] >= 'a' && data[0] <= 'z') {
|
||||
vt->ibuf[vt->ibuf_count++] = data[0] - 'a' + 1;
|
||||
} else if (data[0] >= 'A' && data[0] <= 'Z') {
|
||||
vt->ibuf[vt->ibuf_count++] = data[0] - 'A' + 1;
|
||||
}
|
||||
} else {
|
||||
if (size == 1 && data[0] == 0x7f) {
|
||||
/* use an escape sequence for DELETE, else it behaves
|
||||
* like BACKSPACE
|
||||
*/
|
||||
spiceterm_respond_esc(vt, "[3~");
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
vt->ibuf[vt->ibuf_count++] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vt->y_displ != vt->y_base) {
|
||||
vt->y_displ = vt->y_base;
|
||||
spiceterm_refresh(vt);
|
||||
}
|
||||
|
||||
spiceterm_update_watch_mask(vt, TRUE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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_scan_freg = my_kbd_push_key,
|
||||
.get_leds = my_kbd_get_leds,
|
||||
.push_utf8 = my_kbd_push_utf8,
|
||||
};
|
||||
|
||||
|
||||
/* 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
|
||||
void
|
||||
spiceterm_clear_selection(spiceTerm *vt)
|
||||
{
|
||||
DPRINTF(1, "mark_active = %d", vt->mark_active);
|
||||
@ -1592,7 +1350,7 @@ spiceterm_clear_selection(spiceTerm *vt)
|
||||
spiceterm_unselect_all(vt);
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
@ -1631,7 +1389,7 @@ spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
|
||||
|
||||
if(button2_released) {
|
||||
|
||||
if (agent_owns_clipboard[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY]) {
|
||||
if (vdagent_owns_clipboard(vt)) {
|
||||
if (vt->selection) {
|
||||
int i;
|
||||
for(i = 0; i < vt->selection_len; i++) {
|
||||
@ -1644,7 +1402,7 @@ spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vdagent_request_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY);
|
||||
vdagent_request_clipboard(vt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1716,355 +1474,11 @@ spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
|
||||
|
||||
DPRINTF(1, "selection length = %d", vt->selection_len);
|
||||
|
||||
vdagent_grab_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY);
|
||||
vdagent_grab_clipboard(vt);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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_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);
|
||||
|
||||
if (0) dump_message(buf, msg_size);
|
||||
|
||||
spice_server_char_device_wakeup(&vt->vdagent_sin);
|
||||
|
||||
free(caps);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
spiceterm_clear_selection(vt);
|
||||
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(1, "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(1, "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(1, "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
|
||||
void
|
||||
init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
|
||||
{
|
||||
int i;
|
||||
@ -2121,7 +1535,7 @@ init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
|
||||
vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
|
||||
{
|
||||
width = (width/8)*8;
|
||||
@ -2144,31 +1558,6 @@ spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t 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);
|
||||
|
||||
spice_screen->image_cache = g_hash_table_new(g_int_hash, g_int_equal);
|
||||
|
||||
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)
|
||||
|
22
spiceterm.h
22
spiceterm.h
@ -138,3 +138,25 @@ typedef struct spiceTerm {
|
||||
unsigned int report_mouse:1;
|
||||
|
||||
} spiceTerm;
|
||||
|
||||
void init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height);
|
||||
void spiceterm_refresh(spiceTerm *vt);
|
||||
|
||||
void spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height);
|
||||
void spiceterm_virtual_scroll(spiceTerm *vt, int lines);
|
||||
void spiceterm_clear_selection(spiceTerm *vt);
|
||||
void spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y,
|
||||
uint32_t buttons);
|
||||
|
||||
void spiceterm_respond_esc(spiceTerm *vt, const char *esc);
|
||||
void spiceterm_respond_data(spiceTerm *vt, int len, uint8_t *data);
|
||||
void spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable);
|
||||
|
||||
spiceTerm *create_spiceterm(int argc, char** argv, uint32_t maxx,
|
||||
uint32_t maxy, guint timeout);
|
||||
|
||||
gboolean vdagent_owns_clipboard(spiceTerm *vt);
|
||||
void vdagent_request_clipboard(spiceTerm *vt);
|
||||
void vdagent_grab_clipboard(spiceTerm *vt);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user