aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/vnc.c135
-rw-r--r--ui/vnc.h16
2 files changed, 148 insertions, 3 deletions
diff --git a/ui/vnc.c b/ui/vnc.c
index 16b79ec423..02b71bcd2c 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -46,6 +46,30 @@ static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
static int vnc_cursor_define(VncState *vs);
+static void vnc_release_modifiers(VncState *vs);
+
+static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
+{
+#ifdef _VNC_DEBUG
+ static const char *mn[] = {
+ [0] = "undefined",
+ [VNC_SHARE_MODE_CONNECTING] = "connecting",
+ [VNC_SHARE_MODE_SHARED] = "shared",
+ [VNC_SHARE_MODE_EXCLUSIVE] = "exclusive",
+ [VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
+ };
+ fprintf(stderr, "%s/%d: %s -> %s\n", __func__,
+ vs->csock, mn[vs->share_mode], mn[mode]);
+#endif
+
+ if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) {
+ vs->vd->num_exclusive--;
+ }
+ vs->share_mode = mode;
+ if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) {
+ vs->vd->num_exclusive++;
+ }
+}
static char *addr_to_string(const char *format,
struct sockaddr_storage *sa,
@@ -997,6 +1021,7 @@ static void vnc_disconnect_start(VncState *vs)
{
if (vs->csock == -1)
return;
+ vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock);
vs->csock = -1;
@@ -1027,6 +1052,7 @@ static void vnc_disconnect_finish(VncState *vs)
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
audio_del(vs);
+ vnc_release_modifiers(vs);
QTAILQ_REMOVE(&vs->vd->clients, vs, next);
@@ -1552,9 +1578,11 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
else
kbd_put_keycode(keycode | SCANCODE_UP);
} else {
+ bool numlock = vs->modifiers_state[0x45];
+ bool control = (vs->modifiers_state[0x1d] ||
+ vs->modifiers_state[0x9d]);
/* QEMU console emulation */
if (down) {
- int numlock = vs->modifiers_state[0x45];
switch (keycode) {
case 0x2a: /* Left Shift */
case 0x36: /* Right Shift */
@@ -1642,13 +1670,40 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
break;
default:
- kbd_put_keysym(sym);
+ if (control) {
+ kbd_put_keysym(sym & 0x1f);
+ } else {
+ kbd_put_keysym(sym);
+ }
break;
}
}
}
}
+static void vnc_release_modifiers(VncState *vs)
+{
+ static const int keycodes[] = {
+ /* shift, control, alt keys, both left & right */
+ 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8,
+ };
+ int i, keycode;
+
+ if (!is_graphic_console()) {
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(keycodes); i++) {
+ keycode = keycodes[i];
+ if (!vs->modifiers_state[keycode]) {
+ continue;
+ }
+ if (keycode & SCANCODE_GREY) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ }
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ }
+}
+
static void key_event(VncState *vs, int down, uint32_t sym)
{
int keycode;
@@ -2048,8 +2103,67 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
{
char buf[1024];
+ VncShareMode mode;
int size;
+ mode = data[0] ? VNC_SHARE_MODE_SHARED : VNC_SHARE_MODE_EXCLUSIVE;
+ switch (vs->vd->share_policy) {
+ case VNC_SHARE_POLICY_IGNORE:
+ /*
+ * Ignore the shared flag. Nothing to do here.
+ *
+ * Doesn't conform to the rfb spec but is traditional qemu
+ * behavior, thus left here as option for compatibility
+ * reasons.
+ */
+ break;
+ case VNC_SHARE_POLICY_ALLOW_EXCLUSIVE:
+ /*
+ * Policy: Allow clients ask for exclusive access.
+ *
+ * Implementation: When a client asks for exclusive access,
+ * disconnect all others. Shared connects are allowed as long
+ * as no exclusive connection exists.
+ *
+ * This is how the rfb spec suggests to handle the shared flag.
+ */
+ if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
+ VncState *client;
+ QTAILQ_FOREACH(client, &vs->vd->clients, next) {
+ if (vs == client) {
+ continue;
+ }
+ if (client->share_mode != VNC_SHARE_MODE_EXCLUSIVE &&
+ client->share_mode != VNC_SHARE_MODE_SHARED) {
+ continue;
+ }
+ vnc_disconnect_start(client);
+ }
+ }
+ if (mode == VNC_SHARE_MODE_SHARED) {
+ if (vs->vd->num_exclusive > 0) {
+ vnc_disconnect_start(vs);
+ return 0;
+ }
+ }
+ break;
+ case VNC_SHARE_POLICY_FORCE_SHARED:
+ /*
+ * Policy: Shared connects only.
+ * Implementation: Disallow clients asking for exclusive access.
+ *
+ * Useful for shared desktop sessions where you don't want
+ * someone forgetting to say -shared when running the vnc
+ * client disconnect everybody else.
+ */
+ if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
+ vnc_disconnect_start(vs);
+ return 0;
+ }
+ break;
+ }
+ vnc_set_share_mode(vs, mode);
+
vs->client_width = ds_get_width(vs->ds);
vs->client_height = ds_get_height(vs->ds);
vnc_write_u16(vs, vs->client_width);
@@ -2445,7 +2559,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
guest_ptr = guest_row;
server_ptr = server_row;
- for (x = 0; x < vd->guest.ds->width;
+ for (x = 0; x + 15 < vd->guest.ds->width;
x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
if (!test_and_clear_bit((x / 16), vd->guest.dirty[y]))
continue;
@@ -2556,6 +2670,7 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
vnc_client_cache_addr(vs);
vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
+ vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
vs->vd = vd;
vs->ds = vd->ds;
@@ -2749,6 +2864,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
if (!(vs->display = strdup(display)))
return -1;
+ vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display;
while ((options = strchr(options, ','))) {
@@ -2804,6 +2920,19 @@ int vnc_display_open(DisplayState *ds, const char *display)
vs->lossy = true;
} else if (strncmp(options, "non-adapative", 13) == 0) {
vs->non_adaptive = true;
+ } else if (strncmp(options, "share=", 6) == 0) {
+ if (strncmp(options+6, "ignore", 6) == 0) {
+ vs->share_policy = VNC_SHARE_POLICY_IGNORE;
+ } else if (strncmp(options+6, "allow-exclusive", 15) == 0) {
+ vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
+ } else if (strncmp(options+6, "force-shared", 12) == 0) {
+ vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
+ } else {
+ fprintf(stderr, "unknown vnc share= option\n");
+ g_free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
}
}
diff --git a/ui/vnc.h b/ui/vnc.h
index 66689f1d60..0bd1fc6d23 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -122,9 +122,24 @@ struct VncSurface
DisplaySurface *ds;
};
+typedef enum VncShareMode {
+ VNC_SHARE_MODE_CONNECTING = 1,
+ VNC_SHARE_MODE_SHARED,
+ VNC_SHARE_MODE_EXCLUSIVE,
+ VNC_SHARE_MODE_DISCONNECTED,
+} VncShareMode;
+
+typedef enum VncSharePolicy {
+ VNC_SHARE_POLICY_IGNORE = 1,
+ VNC_SHARE_POLICY_ALLOW_EXCLUSIVE,
+ VNC_SHARE_POLICY_FORCE_SHARED,
+} VncSharePolicy;
+
struct VncDisplay
{
QTAILQ_HEAD(, VncState) clients;
+ int num_exclusive;
+ VncSharePolicy share_policy;
QEMUTimer *timer;
int timer_interval;
int lsock;
@@ -250,6 +265,7 @@ struct VncState
int last_y;
int client_width;
int client_height;
+ VncShareMode share_mode;
uint32_t vnc_encoding;