From patchwork Tue Jun 10 12:25:10 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 357906 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id C843A140085 for ; Tue, 10 Jun 2014 22:26:10 +1000 (EST) Received: from localhost ([::1]:39109 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WuL7w-00086g-PM for incoming@patchwork.ozlabs.org; Tue, 10 Jun 2014 08:26:08 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35803) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WuL7N-0007Sr-4A for qemu-devel@nongnu.org; Tue, 10 Jun 2014 08:25:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WuL7G-00021S-Kk for qemu-devel@nongnu.org; Tue, 10 Jun 2014 08:25:33 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44694) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WuL7G-00021D-BF for qemu-devel@nongnu.org; Tue, 10 Jun 2014 08:25:26 -0400 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s5ACPPfN015185 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 10 Jun 2014 08:25:25 -0400 Received: from nilsson.home.kraxel.org (ovpn-116-60.ams2.redhat.com [10.36.116.60]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s5ACPNiJ001253; Tue, 10 Jun 2014 08:25:24 -0400 Received: by nilsson.home.kraxel.org (Postfix, from userid 500) id 90825803DF; Tue, 10 Jun 2014 14:25:23 +0200 (CEST) From: Gerd Hoffmann To: qemu-devel@nongnu.org Date: Tue, 10 Jun 2014 14:25:10 +0200 Message-Id: <1402403110-18136-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Gerd Hoffmann , Anthony Liguori Subject: [Qemu-devel] [PATCH] spice: add mouse cursor support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org So you'll have a mouse pointer when running non-qxl gfx cards with mouse pointer support (virtio-gpu, IIRC vmware too). Signed-off-by: Gerd Hoffmann --- hw/display/qxl.c | 6 ++- include/ui/spice-display.h | 14 +++++ ui/spice-display.c | 129 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 12 deletions(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 7fb83e4..736fd3c 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -710,7 +710,11 @@ static void interface_release_resource(QXLInstance *sin, if (ext.group_id == MEMSLOT_GROUP_HOST) { /* host group -> vga mode update request */ - qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id); + QXLCommandExt *cmdext = (void *)(ext.info->id); + SimpleSpiceUpdate *update; + g_assert(cmdext->cmd.type == QXL_CMD_DRAW); + update = container_of(cmdext, SimpleSpiceUpdate, ext); + qemu_spice_destroy_update(&qxl->ssd, update); return; } diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index a46bc80..4252ab8 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -69,6 +69,7 @@ QXLCookie *qxl_cookie_new(int type, uint64_t io); typedef struct SimpleSpiceDisplay SimpleSpiceDisplay; typedef struct SimpleSpiceUpdate SimpleSpiceUpdate; +typedef struct SimpleSpiceCursor SimpleSpiceCursor; struct SimpleSpiceDisplay { DisplaySurface *ds; @@ -92,6 +93,13 @@ struct SimpleSpiceDisplay { */ QemuMutex lock; QTAILQ_HEAD(, SimpleSpiceUpdate) updates; + + /* cursor (without qxl): displaychangelistener -> spice server */ + SimpleSpiceCursor *ptr_define; + SimpleSpiceCursor *ptr_move; + uint16_t ptr_x, ptr_y; + + /* cursor (with qxl): qxl local renderer -> displaychangelistener */ QEMUCursor *cursor; int mouse_x, mouse_y; }; @@ -104,6 +112,12 @@ struct SimpleSpiceUpdate { QTAILQ_ENTRY(SimpleSpiceUpdate) next; }; +struct SimpleSpiceCursor { + QXLCursorCmd cmd; + QXLCommandExt ext; + QXLCursor cursor; +}; + int qemu_spice_rect_is_empty(const QXLRect* r); void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); diff --git a/ui/spice-display.c b/ui/spice-display.c index ce6b220..03040b1 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -153,7 +153,7 @@ static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd, drawable->bbox = *rect; drawable->clip.type = SPICE_CLIP_TYPE_NONE; drawable->effect = QXL_EFFECT_OPAQUE; - drawable->release_info.id = (uintptr_t)update; + drawable->release_info.id = (uintptr_t)(&update->ext); drawable->type = QXL_DRAW_COPY; drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = -1; @@ -264,6 +264,49 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) memset(&ssd->dirty, 0, sizeof(ssd->dirty)); } +static SimpleSpiceCursor* +qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd, + QEMUCursor *c) +{ + size_t size = c ? c->width * c->height * 4 : 0; + SimpleSpiceCursor *update; + QXLCursorCmd *ccmd; + QXLCursor *cursor; + QXLCommand *cmd; + + update = g_malloc0(sizeof(*update) + size); + ccmd = &update->cmd; + cursor = &update->cursor; + cmd = &update->ext.cmd; + + if (c) { + ccmd->type = QXL_CURSOR_SET; + ccmd->u.set.position.x = ssd->ptr_x; + ccmd->u.set.position.y = ssd->ptr_y; + ccmd->u.set.visible = true; + ccmd->u.set.shape = (uintptr_t)cursor; + cursor->header.unique = ssd->unique++; + cursor->header.type = SPICE_CURSOR_TYPE_ALPHA; + cursor->header.width = c->width; + cursor->header.height = c->height; + cursor->header.hot_spot_x = c->hot_x; + cursor->header.hot_spot_y = c->hot_y; + cursor->data_size = size; + cursor->chunk.data_size = size; + memcpy(cursor->chunk.data, c->data, size); + } else { + ccmd->type = QXL_CURSOR_MOVE; + ccmd->u.position.x = ssd->ptr_x; + ccmd->u.position.y = ssd->ptr_y; + } + ccmd->release_info.id = (uintptr_t)(&update->ext); + + cmd->type = QXL_CMD_CURSOR; + cmd->data = (uintptr_t)ccmd; + + return update; +} + /* * Called from spice server thread context (via interface_release_resource) * We do *not* hold the global qemu mutex here, so extra care is needed @@ -483,20 +526,50 @@ static int interface_req_cmd_notification(QXLInstance *sin) } static void interface_release_resource(QXLInstance *sin, - struct QXLReleaseInfoExt ext) + struct QXLReleaseInfoExt rext) { SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); - uintptr_t id; + SimpleSpiceUpdate *update; + SimpleSpiceCursor *cursor; + QXLCommandExt *ext; dprint(2, "%s/%d:\n", __func__, ssd->qxl.id); - id = ext.info->id; - qemu_spice_destroy_update(ssd, (void*)id); + ext = (void *)(rext.info->id); + switch (ext->cmd.type) { + case QXL_CMD_DRAW: + update = container_of(ext, SimpleSpiceUpdate, ext); + qemu_spice_destroy_update(ssd, update); + break; + case QXL_CMD_CURSOR: + cursor = container_of(ext, SimpleSpiceCursor, ext); + g_free(cursor); + break; + default: + g_assert_not_reached(); + } } static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) { - dprint(3, "%s:\n", __FUNCTION__); - return false; + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + int ret; + + dprint(3, "%s/%d:\n", __func__, ssd->qxl.id); + + qemu_mutex_lock(&ssd->lock); + if (ssd->ptr_define) { + *ext = ssd->ptr_define->ext; + ssd->ptr_define = NULL; + ret = true; + } else if (ssd->ptr_move) { + *ext = ssd->ptr_move->ext; + ssd->ptr_move = NULL; + ret = true; + } else { + ret = false; + } + qemu_mutex_unlock(&ssd->lock); + return ret; } static int interface_req_cursor_notification(QXLInstance *sin) @@ -617,11 +690,45 @@ static void display_refresh(DisplayChangeListener *dcl) qemu_spice_display_refresh(ssd); } +static void display_mouse_set(DisplayChangeListener *dcl, + int x, int y, int on) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + qemu_mutex_lock(&ssd->lock); + ssd->ptr_x = x; + ssd->ptr_y = x; + if (ssd->ptr_move) { + g_free(ssd->ptr_move); + } + ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL); + qemu_mutex_unlock(&ssd->lock); +} + +static void display_mouse_define(DisplayChangeListener *dcl, + QEMUCursor *c) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + qemu_mutex_lock(&ssd->lock); + if (ssd->ptr_move) { + g_free(ssd->ptr_move); + ssd->ptr_move = NULL; + } + if (ssd->ptr_define) { + g_free(ssd->ptr_define); + } + ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c); + qemu_mutex_unlock(&ssd->lock); +} + static const DisplayChangeListenerOps display_listener_ops = { - .dpy_name = "spice", - .dpy_gfx_update = display_update, - .dpy_gfx_switch = display_switch, - .dpy_refresh = display_refresh, + .dpy_name = "spice", + .dpy_gfx_update = display_update, + .dpy_gfx_switch = display_switch, + .dpy_refresh = display_refresh, + .dpy_mouse_set = display_mouse_set, + .dpy_cursor_define = display_mouse_define, }; static void qemu_spice_display_init_one(QemuConsole *con)