diff options
Diffstat (limited to 'ui')
-rw-r--r-- | ui/vnc.c | 135 | ||||
-rw-r--r-- | ui/vnc.h | 16 |
2 files changed, 148 insertions, 3 deletions
@@ -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; + } } } @@ -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; |