Loading qemu-options.hx +13 −0 Original line number Diff line number Diff line Loading @@ -1095,6 +1095,19 @@ This can be really helpful to save bandwidth when playing videos. Disabling adaptive encodings allows to restore the original static behavior of encodings like Tight. @item share=[allow-exclusive|force-shared|ignore] Set display sharing policy. 'allow-exclusive' allows clients to ask for exclusive access. As suggested by the rfb spec this is implemented by dropping other connections. Connecting multiple clients in parallel requires all clients asking for a shared session (vncviewer: -shared switch). This is the default. 'force-shared' disables exclusive client access. Useful for shared desktop sessions, where you don't want someone forgetting specify -shared disconnect everybody else. 'ignore' completely ignores the shared flag and allows everybody connect unconditionally. Doesn't conform to the rfb spec but is traditional qemu behavior. @end table ETEXI Loading ui/vnc.c +132 −3 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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 */ Loading Loading @@ -1642,13 +1670,40 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) break; default: 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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, ','))) { Loading Loading @@ -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; } } } Loading ui/vnc.h +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -250,6 +265,7 @@ struct VncState int last_y; int client_width; int client_height; VncShareMode share_mode; uint32_t vnc_encoding; Loading Loading
qemu-options.hx +13 −0 Original line number Diff line number Diff line Loading @@ -1095,6 +1095,19 @@ This can be really helpful to save bandwidth when playing videos. Disabling adaptive encodings allows to restore the original static behavior of encodings like Tight. @item share=[allow-exclusive|force-shared|ignore] Set display sharing policy. 'allow-exclusive' allows clients to ask for exclusive access. As suggested by the rfb spec this is implemented by dropping other connections. Connecting multiple clients in parallel requires all clients asking for a shared session (vncviewer: -shared switch). This is the default. 'force-shared' disables exclusive client access. Useful for shared desktop sessions, where you don't want someone forgetting specify -shared disconnect everybody else. 'ignore' completely ignores the shared flag and allows everybody connect unconditionally. Doesn't conform to the rfb spec but is traditional qemu behavior. @end table ETEXI Loading
ui/vnc.c +132 −3 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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 */ Loading Loading @@ -1642,13 +1670,40 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) break; default: 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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, ','))) { Loading Loading @@ -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; } } } Loading
ui/vnc.h +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -250,6 +265,7 @@ struct VncState int last_y; int client_width; int client_height; VncShareMode share_mode; uint32_t vnc_encoding; Loading