Merge remote-tracking branch 'kraxel/vnc.2' into staging

* kraxel/vnc.2:
  vnc: lift modifier keys on client disconnect.
  vnc: implement shared flag handling.
  vnc: fix ctrl key in vnc terminal emulation
  Fix vnc memory corruption with width = 1400
This commit is contained in:
Anthony Liguori 2012-02-15 18:40:53 -06:00
commit cf5cfe0471
3 changed files with 161 additions and 3 deletions

View File

@ -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 adaptive encodings allows to restore the original static behavior of encodings
like Tight. 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 @end table
ETEXI ETEXI

135
ui/vnc.c
View File

@ -46,6 +46,30 @@ static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl; static DisplayChangeListener *dcl;
static int vnc_cursor_define(VncState *vs); 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, static char *addr_to_string(const char *format,
struct sockaddr_storage *sa, struct sockaddr_storage *sa,
@ -997,6 +1021,7 @@ static void vnc_disconnect_start(VncState *vs)
{ {
if (vs->csock == -1) if (vs->csock == -1)
return; return;
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock); closesocket(vs->csock);
vs->csock = -1; vs->csock = -1;
@ -1027,6 +1052,7 @@ static void vnc_disconnect_finish(VncState *vs)
vnc_sasl_client_cleanup(vs); vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
audio_del(vs); audio_del(vs);
vnc_release_modifiers(vs);
QTAILQ_REMOVE(&vs->vd->clients, vs, next); 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 else
kbd_put_keycode(keycode | SCANCODE_UP); kbd_put_keycode(keycode | SCANCODE_UP);
} else { } else {
bool numlock = vs->modifiers_state[0x45];
bool control = (vs->modifiers_state[0x1d] ||
vs->modifiers_state[0x9d]);
/* QEMU console emulation */ /* QEMU console emulation */
if (down) { if (down) {
int numlock = vs->modifiers_state[0x45];
switch (keycode) { switch (keycode) {
case 0x2a: /* Left Shift */ case 0x2a: /* Left Shift */
case 0x36: /* Right Shift */ case 0x36: /* Right Shift */
@ -1642,13 +1670,40 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
break; break;
default: default:
kbd_put_keysym(sym); if (control) {
kbd_put_keysym(sym & 0x1f);
} else {
kbd_put_keysym(sym);
}
break; 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) static void key_event(VncState *vs, int down, uint32_t sym)
{ {
int keycode; 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) static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
{ {
char buf[1024]; char buf[1024];
VncShareMode mode;
int size; 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_width = ds_get_width(vs->ds);
vs->client_height = ds_get_height(vs->ds); vs->client_height = ds_get_height(vs->ds);
vnc_write_u16(vs, vs->client_width); vnc_write_u16(vs, vs->client_width);
@ -2445,7 +2559,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
guest_ptr = guest_row; guest_ptr = guest_row;
server_ptr = server_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) { x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) if (!test_and_clear_bit((x / 16), vd->guest.dirty[y]))
continue; continue;
@ -2556,6 +2670,7 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
vnc_client_cache_addr(vs); vnc_client_cache_addr(vs);
vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
vs->vd = vd; vs->vd = vd;
vs->ds = vd->ds; vs->ds = vd->ds;
@ -2749,6 +2864,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
if (!(vs->display = strdup(display))) if (!(vs->display = strdup(display)))
return -1; return -1;
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display; options = display;
while ((options = strchr(options, ','))) { while ((options = strchr(options, ','))) {
@ -2804,6 +2920,19 @@ int vnc_display_open(DisplayState *ds, const char *display)
vs->lossy = true; vs->lossy = true;
} else if (strncmp(options, "non-adapative", 13) == 0) { } else if (strncmp(options, "non-adapative", 13) == 0) {
vs->non_adaptive = true; 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;
}
} }
} }

View File

@ -122,9 +122,24 @@ struct VncSurface
DisplaySurface *ds; 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 struct VncDisplay
{ {
QTAILQ_HEAD(, VncState) clients; QTAILQ_HEAD(, VncState) clients;
int num_exclusive;
VncSharePolicy share_policy;
QEMUTimer *timer; QEMUTimer *timer;
int timer_interval; int timer_interval;
int lsock; int lsock;
@ -250,6 +265,7 @@ struct VncState
int last_y; int last_y;
int client_width; int client_width;
int client_height; int client_height;
VncShareMode share_mode;
uint32_t vnc_encoding; uint32_t vnc_encoding;