From nobody Thu May 2 02:10:03 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1631572543504138.08453363537728; Mon, 13 Sep 2021 15:35:43 -0700 (PDT) Received: from localhost ([::1]:53636 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mPuYA-00082x-I8 for importer@patchew.org; Mon, 13 Sep 2021 18:35:42 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:39264) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mPuWf-0005R1-O9 for qemu-devel@nongnu.org; Mon, 13 Sep 2021 18:34:09 -0400 Received: from mga01.intel.com ([192.55.52.88]:34064) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mPuWc-000845-Ak for qemu-devel@nongnu.org; Mon, 13 Sep 2021 18:34:08 -0400 Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Sep 2021 15:33:53 -0700 Received: from vkasired-desk2.fm.intel.com ([10.105.128.127]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Sep 2021 15:33:52 -0700 X-IronPort-AV: E=McAfee;i="6200,9189,10106"; a="244139499" X-IronPort-AV: E=Sophos;i="5.85,290,1624345200"; d="scan'208";a="244139499" X-IronPort-AV: E=Sophos;i="5.85,290,1624345200"; d="scan'208";a="432749428" From: Vivek Kasireddy To: qemu-devel@nongnu.org Subject: [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE Date: Mon, 13 Sep 2021 15:20:35 -0700 Message-Id: <20210913222036.3193732-2-vivek.kasireddy@intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210913222036.3193732-1-vivek.kasireddy@intel.com> References: <20210913222036.3193732-1-vivek.kasireddy@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=192.55.52.88; envelope-from=vivek.kasireddy@intel.com; helo=mga01.intel.com X-Spam_score_int: -68 X-Spam_score: -6.9 X-Spam_bar: ------ X-Spam_report: (-6.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vivek Kasireddy , Gerd Hoffmann Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1631572545366100005 Content-Type: text/plain; charset="utf-8" To support this feature, we add a new queue named rel_fenceq to enqueue finished set_scanout_blob cmds. Cc: Gerd Hoffmann Signed-off-by: Vivek Kasireddy --- hw/display/virtio-gpu-base.c | 3 ++ hw/display/virtio-gpu-udmabuf.c | 4 +- hw/display/virtio-gpu.c | 54 ++++++++++++++++++++- include/hw/virtio/virtio-gpu.h | 6 +++ include/standard-headers/linux/virtio_gpu.h | 2 + 5 files changed, 66 insertions(+), 3 deletions(-) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index c8da4806e0..0218a6d3d2 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -211,6 +211,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64= _t features, if (virtio_gpu_blob_enabled(g->conf)) { features |=3D (1 << VIRTIO_GPU_F_RESOURCE_BLOB); } + if (virtio_gpu_relfence_enabled(g->conf)) { + features |=3D (1 << VIRTIO_GPU_F_RELEASE_FENCE); + } =20 return features; } diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabu= f.c index c6f7f58784..adb3fd4e0c 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -157,7 +157,7 @@ void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_r= esource *res) } } =20 -static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) +void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) { struct virtio_gpu_scanout *scanout; =20 @@ -216,7 +216,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, new_primary->buf.height); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); =20 - if (old_primary) { + if (old_primary && !virtio_gpu_relfence_enabled(g->parent_obj.conf)) { virtio_gpu_free_dmabuf(g, old_primary); } =20 diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 182e0868b0..115c7033aa 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -771,6 +771,11 @@ static void virtio_gpu_set_scanout_blob(VirtIOGPU *g, =20 virtio_gpu_do_set_scanout(g, ss.scanout_id, &fb, res, &ss.r, &cmd->error); + + if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) { + cmd->finished =3D true; + cmd->error =3D VIRTIO_GPU_RESP_OK_NODATA; + } } =20 int virtio_gpu_create_mapping_iov(VirtIOGPU *g, @@ -1038,12 +1043,43 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) fprintf(stderr, "inflight: %3d (+)\r", g->inflight); } } else { - g_free(cmd); + if (cmd->cmd_hdr.type =3D=3D VIRTIO_GPU_CMD_SET_SCANOUT_BLOB && + cmd->error =3D=3D VIRTIO_GPU_RESP_OK_NODATA) { + QTAILQ_INSERT_TAIL(&g->rel_fenceq, cmd, next); + } else { + g_free(cmd); + } } } g->processing_cmdq =3D false; } =20 +static void virtio_gpu_process_rel_fenceq(VirtIOGPU *g) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_set_scanout_blob ss; + struct virtio_gpu_ctrl_command *cmd, *tmp; + VGPUDMABuf *dmabuf, *temp; + + QTAILQ_FOREACH_SAFE(dmabuf, &g->dmabuf.bufs, next, temp) { + if (dmabuf->buf.fence_fd < 0) { + QTAILQ_FOREACH_SAFE(cmd, &g->rel_fenceq, next, tmp) { + VIRTIO_GPU_FILL_CMD(ss); + virtio_gpu_bswap_32(&ss, sizeof(ss)); + res =3D virtio_gpu_find_check_resource(g, ss.resource_id, = true, + __func__, &cmd->error= ); + if (res && dmabuf->buf.fd =3D=3D res->dmabuf_fd) { + virtio_gpu_ctrl_response_nodata(g, cmd, + VIRTIO_GPU_RESP_OK_NOD= ATA); + QTAILQ_REMOVE(&g->rel_fenceq, cmd, next); + g_free(cmd); + } + } + virtio_gpu_free_dmabuf(g, dmabuf); + } + } +} + static void virtio_gpu_process_fenceq(VirtIOGPU *g) { struct virtio_gpu_ctrl_command *cmd, *tmp; @@ -1064,6 +1100,12 @@ static void virtio_gpu_handle_gl_flushed(VirtIOGPUBa= se *b) { VirtIOGPU *g =3D container_of(b, VirtIOGPU, parent_obj); =20 + if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) { + virtio_gpu_process_rel_fenceq(g); + } + if (g->parent_obj.renderer_blocked) { + return; + } virtio_gpu_process_fenceq(g); virtio_gpu_process_cmdq(g); } @@ -1323,6 +1365,13 @@ void virtio_gpu_device_realize(DeviceState *qdev, Er= ror **errp) } } =20 + if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) { + if (!virtio_gpu_blob_enabled(g->parent_obj.conf)) { + error_setg(errp, "cannot enable relfence without blob resource= s"); + return; + } + } + if (!virtio_gpu_base_device_realize(qdev, virtio_gpu_handle_ctrl_cb, virtio_gpu_handle_cursor_cb, @@ -1337,6 +1386,7 @@ void virtio_gpu_device_realize(DeviceState *qdev, Err= or **errp) QTAILQ_INIT(&g->reslist); QTAILQ_INIT(&g->cmdq); QTAILQ_INIT(&g->fenceq); + QTAILQ_INIT(&g->rel_fenceq); } =20 void virtio_gpu_reset(VirtIODevice *vdev) @@ -1418,6 +1468,8 @@ static Property virtio_gpu_properties[] =3D { 256 * MiB), DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_BLOB_ENABLED, false), + DEFINE_PROP_BIT("relfence", VirtIOGPU, parent_obj.conf.flags, + VIRTIO_GPU_FLAG_RELFENCE_ENABLED, false), DEFINE_PROP_END_OF_LIST(), }; =20 diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 24c6628944..60dd2b8985 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -90,6 +90,7 @@ enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_EDID_ENABLED, VIRTIO_GPU_FLAG_DMABUF_ENABLED, VIRTIO_GPU_FLAG_BLOB_ENABLED, + VIRTIO_GPU_FLAG_RELFENCE_ENABLED, }; =20 #define virtio_gpu_virgl_enabled(_cfg) \ @@ -102,6 +103,8 @@ enum virtio_gpu_base_conf_flags { (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED)) #define virtio_gpu_blob_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED)) +#define virtio_gpu_relfence_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RELFENCE_ENABLED)) =20 struct virtio_gpu_base_conf { uint32_t max_outputs; @@ -170,6 +173,7 @@ struct VirtIOGPU { QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; + QTAILQ_HEAD(, virtio_gpu_ctrl_command) rel_fenceq; =20 uint64_t hostmem; =20 @@ -274,6 +278,8 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, struct virtio_gpu_framebuffer *fb); +void virtio_gpu_free_dmabuf(VirtIOGPU *g, + VGPUDMABuf *dmabuf); =20 /* virtio-gpu-3d.c */ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard= -headers/linux/virtio_gpu.h index 1357e4774e..4331f999c4 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -60,6 +60,8 @@ */ #define VIRTIO_GPU_F_RESOURCE_BLOB 3 =20 +#define VIRTIO_GPU_F_RELEASE_FENCE 4 + enum virtio_gpu_ctrl_type { VIRTIO_GPU_UNDEFINED =3D 0, =20 --=20 2.30.2 From nobody Thu May 2 02:10:03 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1631572551211889.539434456753; Mon, 13 Sep 2021 15:35:51 -0700 (PDT) Received: from localhost ([::1]:53824 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mPuYI-0008Aa-5h for importer@patchew.org; Mon, 13 Sep 2021 18:35:50 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:39262) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mPuWe-0005Pq-3r for qemu-devel@nongnu.org; Mon, 13 Sep 2021 18:34:08 -0400 Received: from mga01.intel.com ([192.55.52.88]:34057) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mPuWa-0007zA-Sz for qemu-devel@nongnu.org; Mon, 13 Sep 2021 18:34:07 -0400 Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Sep 2021 15:33:53 -0700 Received: from vkasired-desk2.fm.intel.com ([10.105.128.127]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Sep 2021 15:33:52 -0700 X-IronPort-AV: E=McAfee;i="6200,9189,10106"; a="244139501" X-IronPort-AV: E=Sophos;i="5.85,290,1624345200"; d="scan'208";a="244139501" X-IronPort-AV: E=Sophos;i="5.85,290,1624345200"; d="scan'208";a="432749437" From: Vivek Kasireddy To: qemu-devel@nongnu.org Subject: [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Date: Mon, 13 Sep 2021 15:20:36 -0700 Message-Id: <20210913222036.3193732-3-vivek.kasireddy@intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210913222036.3193732-1-vivek.kasireddy@intel.com> References: <20210913222036.3193732-1-vivek.kasireddy@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=192.55.52.88; envelope-from=vivek.kasireddy@intel.com; helo=mga01.intel.com X-Spam_score_int: -68 X-Spam_score: -6.9 X-Spam_bar: ------ X-Spam_report: (-6.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vivek Kasireddy , Gerd Hoffmann Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1631572551978100003 Content-Type: text/plain; charset="utf-8" Cc: Gerd Hoffmann Signed-off-by: Vivek Kasireddy --- configure | 8 +- meson.build | 33 +++ meson_options.txt | 2 + qapi/ui.json | 3 + ui/meson.build | 52 ++++ ui/wayland.c | 628 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 ui/wayland.c diff --git a/configure b/configure index da2501489f..02339681dc 100755 --- a/configure +++ b/configure @@ -406,6 +406,7 @@ cfi_debug=3D"false" seccomp=3D"auto" glusterfs=3D"auto" gtk=3D"auto" +wayland=3D"auto" tls_priority=3D"NORMAL" gnutls=3D"auto" nettle=3D"auto" @@ -1383,6 +1384,10 @@ for opt do ;; --enable-gtk) gtk=3D"enabled" ;; + --disable-wayland) wayland=3D"disabled" + ;; + --enable-wayland) wayland=3D"enabled" + ;; --tls-priority=3D*) tls_priority=3D"$optarg" ;; --disable-gnutls) gnutls=3D"disabled" @@ -1868,6 +1873,7 @@ disabled with --disable-FEATURE, default is enabled i= f available sdl SDL UI sdl-image SDL Image support for icons gtk gtk UI + wayland Wayland UI vte vte support for the gtk UI curses curses UI iconv font glyph conversion support @@ -5191,7 +5197,7 @@ if test "$skip_meson" =3D no; then -Dmalloc=3D$malloc -Dmalloc_trim=3D$malloc_trim -Dsparse=3D$sparse= \ -Dkvm=3D$kvm -Dhax=3D$hax -Dwhpx=3D$whpx -Dhvf=3D$hvf -Dnvmm=3D$nv= mm \ -Dxen=3D$xen -Dxen_pci_passthrough=3D$xen_pci_passthrough -Dtcg=3D= $tcg \ - -Dcocoa=3D$cocoa -Dgtk=3D$gtk -Dmpath=3D$mpath -Dsdl=3D$sdl -Dsdl_= image=3D$sdl_image \ + -Dcocoa=3D$cocoa -Dgtk=3D$gtk -Dmpath=3D$mpath -Dsdl=3D$sdl -Dwayl= and=3D$wayland -Dsdl_image=3D$sdl_image \ -Dlibusb=3D$libusb -Dsmartcard=3D$smartcard -Dusb_redir=3D$usb_red= ir -Dvte=3D$vte \ -Dvnc=3D$vnc -Dvnc_sasl=3D$vnc_sasl -Dvnc_jpeg=3D$vnc_jpeg -Dvnc_p= ng=3D$vnc_png \ -Dgettext=3D$gettext -Dxkbcommon=3D$xkbcommon -Du2f=3D$u2f -Dvirti= ofsd=3D$virtiofsd \ diff --git a/meson.build b/meson.build index 7d7d14a4bc..cda10b7d0f 100644 --- a/meson.build +++ b/meson.build @@ -927,6 +927,37 @@ if gtkx11.found() x11 =3D dependency('x11', method: 'pkg-config', required: gtkx11.found(), kwargs: static_kwargs) endif + +wayland =3D not_found +if not get_option('wayland').auto() + wlclientdep =3D dependency('wayland-client', version: '>=3D 1.18.90', + method: 'pkg-config', + required: get_option('wayland'), + kwargs: static_kwargs) + wlprotocolsdep =3D dependency('wayland-protocols', version: '>=3D 1.14.9= 1', + method: 'pkg-config', + required: get_option('wayland'), + kwargs: static_kwargs) + + if not wlprotocolsdep.found() + wlproto_dir =3D subproject('wayland-protocols').get_variable('wayland_= protocols_srcdir') + else + wlproto_dir =3D wlprotocolsdep.get_pkgconfig_variable('pkgdatadir') + endif + + wayland =3D declare_dependency(dependencies: [wlclientdep, wlprotocolsde= p]) +endif + +if wayland.found() and get_option('sdl').enabled() + error('Wayland and SDL cannot be enabled at the same time') +endif +if wayland.found() and get_option('gtk').enabled() + error('Wayland and GTK+ cannot be enabled at the same time') +endif +if wayland.found() and get_option('cocoa').enabled() + error('Wayland and Cocoa cannot be enabled at the same time') +endif + vnc =3D not_found png =3D not_found jpeg =3D not_found @@ -1256,6 +1287,7 @@ if glusterfs.found() config_host_data.set('CONFIG_GLUSTERFS_IOCB_HAS_STAT', glusterfs_iocb_ha= s_stat) endif config_host_data.set('CONFIG_GTK', gtk.found()) +config_host_data.set('CONFIG_WAYLAND', wayland.found()) config_host_data.set('CONFIG_VTE', vte.found()) config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) @@ -3052,6 +3084,7 @@ summary_info +=3D {'SDL support': sdl.found()} summary_info +=3D {'SDL image support': sdl_image.found()} # TODO: add back version summary_info +=3D {'GTK support': gtk.found()} +summary_info +=3D {'Wayland support': wayland.found()} summary_info +=3D {'pixman': pixman.found()} # TODO: add back version summary_info +=3D {'VTE support': vte.found()} diff --git a/meson_options.txt b/meson_options.txt index a9a9b8f4c6..6c0e27e83b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -104,6 +104,8 @@ option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') option('gtk', type : 'feature', value : 'auto', description: 'GTK+ user interface') +option('wayland', type : 'feature', value : 'auto', + description: 'Wayland user interface') option('sdl', type : 'feature', value : 'auto', description: 'SDL user interface') option('sdl_image', type : 'feature', value : 'auto', diff --git a/qapi/ui.json b/qapi/ui.json index b2cf7a6759..8da0baa0bd 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1112,6 +1112,8 @@ # DRI device. Graphical display need to be paired with # VNC or Spice. (Since 3.1) # +# @wayland: The Wayland user interface. +# # @curses: Display video output via curses. For graphics device # models which support a text mode, QEMU can display this # output using a curses/ncurses interface. Nothing is @@ -1135,6 +1137,7 @@ { 'name': 'none' }, { 'name': 'gtk', 'if': 'CONFIG_GTK' }, { 'name': 'sdl', 'if': 'CONFIG_SDL' }, + { 'name': 'wayland', 'if': 'CONFIG_WAYLAND' }, { 'name': 'egl-headless', 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, { 'name': 'curses', 'if': 'CONFIG_CURSES' }, diff --git a/ui/meson.build b/ui/meson.build index a73beb0e54..86fc324c82 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -64,6 +64,58 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found() ui_modules +=3D {'egl-headless' : egl_headless_ss} endif =20 +wayland_scanner =3D find_program('wayland-scanner') +proto_sources =3D [ + ['xdg-shell', 'stable', ], + ['fullscreen-shell', 'unstable', 'v1', ], + ['linux-dmabuf', 'unstable', 'v1', ], +] +wayland_headers =3D [] +wayland_proto_sources =3D [] + +if wayland.found() + foreach p: proto_sources + proto_name =3D p.get(0) + proto_stability =3D p.get(1) + + if proto_stability =3D=3D 'stable' + output_base =3D proto_name + input =3D files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(pro= to_stability, proto_name, output_base))) + else + proto_version =3D p.get(2) + output_base =3D '@0@-@1@-@2@'.format(proto_name, proto_stability, pr= oto_version) + input =3D files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(pro= to_stability, proto_name, output_base))) + endif + + wayland_headers +=3D custom_target('@0@ client header'.format(output_b= ase), + input: input, + output: '@0@-client-protocol.h'.format(output_base), + command: [ + wayland_scanner, + 'client-header', + '@INPUT@', '@OUTPUT@', + ], build_by_default: true + ) + + wayland_proto_sources +=3D custom_target('@0@ source'.format(output_ba= se), + input: input, + output: '@0@-protocol.c'.format(output_base), + command: [ + wayland_scanner, + 'private-code', + '@INPUT@', '@OUTPUT@', + ], build_by_default: true + ) + endforeach +endif + +if wayland.found() + wayland_ss =3D ss.source_set() + wayland_ss.add(when: wayland, if_true: files('wayland.c', 'xdg-shell-pro= tocol.c', 'fullscreen-shell-unstable-v1-protocol.c','linux-dmabuf-unstable-= v1-protocol.c')) + #wayland_ss.add(when: wayland, if_true: files('wayland.c'), [wayland_pro= to_sources]) + ui_modules +=3D {'wayland' : wayland_ss} +endif + if gtk.found() softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) =20 diff --git a/ui/wayland.c b/ui/wayland.c new file mode 100644 index 0000000000..9e011bd701 --- /dev/null +++ b/ui/wayland.c @@ -0,0 +1,628 @@ +/* + * Wayland UI -- A simple Qemu UI backend to share Guest Compositor buffers + * with Host Wayland compositors directly. + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + * Mostly (boilerplate) based on: + * https://cgit.freedesktop.org/wayland/weston/tree/clients/simple-dmabuf-= egl.c + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "sysemu/sysemu.h" +#include "ui/console.h" +#include "ui/input.h" +#include "ui/kbd-state.h" +#include "keymaps.h" +#include +#include +#include "xdg-shell-client-protocol.h" +#include "fullscreen-shell-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +#define MAX_BUFFERS 4 + +typedef struct wayland_display { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct xdg_wm_base *wm_base; + struct zwp_fullscreen_shell_v1 *fshell; + struct zwp_linux_dmabuf_v1 *dmabuf; +} wayland_display; + +typedef struct wayland_buffer { + QemuConsole *con; + QemuDmaBuf *dmabuf; + struct wl_buffer *buffer; + bool busy; +} wayland_buffer; + +typedef struct wayland_window { + wayland_display *display; + DisplayChangeListener dcl; + QKbdState *kbd; + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_callback *callback; + wayland_buffer buffers[MAX_BUFFERS]; + wayland_buffer *new_buffer; + int width, height; + bool wait_for_configure; + int last_x, last_y; + bool last_set; +} wayland_window; + +typedef struct wayland_input { + wayland_window *focus; + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_keyboard *keyboard; + const uint16_t *keycode_map; + size_t keycode_maplen; +} wayland_input; + +static const struct wl_callback_listener frame_listener; + +static void xdg_surface_handle_configure(void *data, + struct xdg_surface *surface, + uint32_t serial) +{ + wayland_window *window =3D data; + + xdg_surface_ack_configure(surface, serial); + window->wait_for_configure =3D false; +} + +static const struct xdg_surface_listener xdg_surface_listener =3D { + xdg_surface_handle_configure, +}; + +static void xdg_toplevel_handle_configure(void *data, + struct xdg_toplevel *toplevel, + int32_t width, int32_t height, + struct wl_array *states) +{ +} + +static void xdg_toplevel_handle_close(void *data, + struct xdg_toplevel *xdg_toplevel) +{ +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener =3D { + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, +}; + +static void wayland_refresh(DisplayChangeListener *dcl) +{ + graphic_hw_update(dcl->con); +} + +static QEMUGLContext wayland_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + return NULL; +} + +static void wayland_destroy_context(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ +} + +static int wayland_make_context_current(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ + return 0; +} + +static void wayland_scanout_disable(DisplayChangeListener *dcl) +{ +} + +static void wayland_scanout_texture(DisplayChangeListener *dcl, + uint32_t backing_id, + bool backing_y_0_top, + uint32_t backing_width, + uint32_t backing_height, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ +} + +static void wayland_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ +} + +static void wayland_window_redraw(void *data, struct wl_callback *callback, + uint32_t time) +{ + wayland_window *window =3D data; + + if (callback) { + assert(window->callback =3D=3D callback); + wl_callback_destroy(callback); + window->callback =3D NULL; + } + graphic_hw_gl_block(window->dcl.con, false); + graphic_hw_gl_flushed(window->dcl.con); +} + +static const struct wl_callback_listener frame_listener =3D { + wayland_window_redraw +}; + +static void wayland_buffer_release(void *data, struct wl_buffer *buf) +{ + wayland_buffer *buffer =3D data; + QemuDmaBuf *dmabuf =3D buffer->dmabuf; + + dmabuf->fence_fd =3D -1; + graphic_hw_gl_flushed(buffer->con); + buffer->busy =3D false; + wl_buffer_destroy(buf); +} + +static const struct wl_buffer_listener buffer_listener =3D { + wayland_buffer_release +}; + +static wayland_buffer *window_next_buffer(wayland_window *window) +{ + int i; + + for (i =3D 0; i < MAX_BUFFERS; i++) { + if (!window->buffers[i].busy) { + return &window->buffers[i]; + } + } + return NULL; +} + +static void wayland_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + wayland_window *window =3D container_of(dcl, wayland_window, dcl); + wayland_display *display =3D window->display; + wayland_buffer *buffer =3D window_next_buffer(window); + struct zwp_linux_buffer_params_v1 *params; + + if (!buffer) { + error_report("Can't find free buffer\n"); + exit(1); + } + params =3D zwp_linux_dmabuf_v1_create_params(display->dmabuf); + zwp_linux_buffer_params_v1_add(params, dmabuf->fd, 0, 0, dmabuf->strid= e, + 0, 0); + buffer->buffer =3D zwp_linux_buffer_params_v1_create_immed(params, + dmabuf->width, + dmabuf->heigh= t, + dmabuf->fourc= c, + 0); + zwp_linux_buffer_params_v1_destroy(params); + buffer->dmabuf =3D dmabuf; + buffer->con =3D window->dcl.con; + window->new_buffer =3D buffer; + window->width =3D dmabuf->width; + window->height =3D dmabuf->height; + dmabuf->fence_fd =3D 1; + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); +} + +static void wayland_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + wayland_window *window =3D container_of(dcl, wayland_window, dcl); + struct wl_region *region; + + graphic_hw_gl_block(window->new_buffer->con, true); + region =3D wl_compositor_create_region(window->display->compositor); + wl_region_add(region, 0, 0, window->width, window->height); + wl_surface_set_opaque_region(window->surface, region); + wl_region_destroy(region); + + window->callback =3D wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_attach(window->surface, window->new_buffer->buffer, 0, 0); + wl_surface_damage(window->surface, 0, 0, window->width, window->height= ); + wl_surface_commit(window->surface); + wl_display_flush(window->display->display); + window->new_buffer->busy =3D true; +} + +static void dmabuf_modifier(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, + uint32_t modifier_lo) +{ +} + +static void dmabuf_format(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format) +{ +} + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener =3D { + dmabuf_format, + dmabuf_modifier +}; + +static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, + uint32_t serial) +{ + xdg_wm_base_pong(shell, serial); +} + +static const struct xdg_wm_base_listener wm_base_listener =3D { + xdg_wm_base_ping, +}; + +static void pointer_handle_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surfa= ce, + wl_fixed_t sx, wl_fixed_t sy) +{ + wayland_input *input =3D data; + + if (!surface) { + return; + } + input->focus =3D wl_surface_get_user_data(surface); + wl_pointer_set_cursor(pointer, serial, NULL, 0, 0); +} + +static void pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surfa= ce) +{ + wayland_input *input =3D data; + + if (!surface || !input->focus) { + return; + } + input->focus =3D NULL; +} + +static void pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t= sy) +{ + wayland_input *input =3D data; + wayland_window *window =3D input->focus; + int x, y; + + if (!window) { + return; + } + x =3D wl_fixed_to_int(sx); + y =3D wl_fixed_to_int(sy); + if (qemu_input_is_absolute()) { + qemu_input_queue_abs(window->dcl.con, INPUT_AXIS_X, + x, 0, window->width); + qemu_input_queue_abs(window->dcl.con, INPUT_AXIS_Y, + y, 0, window->height); + qemu_input_event_sync(); + } else if (window->last_set) { + qemu_input_queue_rel(window->dcl.con, INPUT_AXIS_X, + x - window->last_x); + qemu_input_queue_rel(window->dcl.con, INPUT_AXIS_Y, + y - window->last_y); + qemu_input_event_sync(); + } + window->last_x =3D x; + window->last_y =3D y; + window->last_set =3D true; +} + +static void pointer_handle_button(void *data, struct wl_pointer *wl_pointe= r, + uint32_t serial, uint32_t time, + uint32_t button, uint32_t state) +{ + wayland_input *input =3D data; + wayland_window *window =3D input->focus; + InputButton btn; + + if (!window) { + return; + } + if (button =3D=3D BTN_LEFT) { + btn =3D INPUT_BUTTON_LEFT; + } else if (button =3D=3D BTN_MIDDLE) { + btn =3D INPUT_BUTTON_MIDDLE; + } else if (button =3D=3D BTN_RIGHT) { + btn =3D INPUT_BUTTON_RIGHT; + } else if (button =3D=3D BTN_SIDE) { + btn =3D INPUT_BUTTON_SIDE; + } else { + return; + } + qemu_input_queue_btn(window->dcl.con, btn, + state =3D=3D WL_POINTER_BUTTON_STATE_PRESSED); + qemu_input_event_sync(); +} + +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t v= alue) +{ + wayland_input *input =3D data; + wayland_window *window =3D input->focus; + InputButton btn; + int delta_y =3D 0; + + if (!window) { + return; + } + if (axis =3D=3D WL_POINTER_AXIS_VERTICAL_SCROLL) { + delta_y =3D wl_fixed_to_int(value) / 10; + } + if (delta_y > 0) { + btn =3D INPUT_BUTTON_WHEEL_DOWN; + } else { + btn =3D INPUT_BUTTON_WHEEL_UP; + } + + qemu_input_queue_btn(window->dcl.con, btn, true); + qemu_input_event_sync(); + qemu_input_queue_btn(window->dcl.con, btn, false); + qemu_input_event_sync(); +} + +static const struct wl_pointer_listener pointer_listener =3D { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboar= d, + uint32_t format, int fd, uint32_t size) +{ + close(fd); +} + +static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surf= ace, + struct wl_array *keys) +{ +} + +static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surf= ace) +{ +} + +static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t time, uint32_t k= ey, + uint32_t state) +{ + wayland_input *input =3D data; + wayland_window *window =3D input->focus; + int keycode =3D key + 8, qcode; + + if (!window || !input->keycode_map || + keycode > input->keycode_maplen) { + return; + } + qcode =3D input->keycode_map[keycode]; + qkbd_state_key_event(window->kbd, qcode, state); +} + +static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyb= oard, + uint32_t serial, uint32_t mods_depre= ssed, + uint32_t mods_latched, uint32_t mods= _locked, + uint32_t group) +{ +} + +static const struct wl_keyboard_listener keyboard_listener =3D { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *seat, + enum wl_seat_capability caps) +{ + wayland_input *input =3D data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) { + input->pointer =3D wl_seat_get_pointer(seat); + wl_pointer_add_listener(input->pointer, &pointer_listener, input); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { + wl_pointer_destroy(input->pointer); + input->pointer =3D NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) { + input->keyboard =3D wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(input->keyboard, &keyboard_listener, inpu= t); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) { + wl_keyboard_destroy(input->keyboard); + input->keyboard =3D NULL; + } +} + +static const struct wl_seat_listener seat_listener =3D { + seat_handle_capabilities, +}; + +static void registry_handle_global(void *data, struct wl_registry *registr= y, + uint32_t id, const char *interface, + uint32_t version) +{ + wayland_display *display =3D data; + wayland_input *input; + + if (strcmp(interface, "wl_compositor") =3D=3D 0) { + display->compositor =3D wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "xdg_wm_base") =3D=3D 0) { + display->wm_base =3D wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(display->wm_base, &wm_base_listener, display); + } else if (strcmp(interface, "wl_seat") =3D=3D 0) { + input =3D g_new0(wayland_input, 1); + input->keycode_maplen =3D qemu_input_map_xorgevdev_to_qcode_len; + input->keycode_map =3D qemu_input_map_xorgevdev_to_qcode; + input->seat =3D wl_registry_bind(registry, id, + &wl_seat_interface, 1); + wl_seat_add_listener(input->seat, &seat_listener, input); + } else if (strcmp(interface, "zwp_fullscreen_shell_v1") =3D=3D 0) { + display->fshell =3D wl_registry_bind(registry, + id, &zwp_fullscreen_shell_v1_interface, + 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") =3D=3D 0) { + display->dmabuf =3D wl_registry_bind(registry, + id, &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(display->dmabuf, &dmabuf_listener, + display); + } +} + +static void registry_handle_global_remove(void *data, + struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener =3D { + registry_handle_global, + registry_handle_global_remove +}; + +static const DisplayChangeListenerOps wayland_ops =3D { + .dpy_name =3D "wayland", + .dpy_refresh =3D wayland_refresh, + .dpy_gl_ctx_create =3D wayland_create_context, + .dpy_gl_ctx_destroy =3D wayland_destroy_context, + .dpy_gl_ctx_make_current =3D wayland_make_context_current, + + .dpy_gl_scanout_disable =3D wayland_scanout_disable, + .dpy_gl_scanout_texture =3D wayland_scanout_texture, + .dpy_gl_scanout_dmabuf =3D wayland_scanout_dmabuf, + .dpy_gl_release_dmabuf =3D wayland_release_dmabuf, + .dpy_gl_update =3D wayland_scanout_flush, +}; + +static wayland_display *wayland_create_display(void) +{ + wayland_display *display; + + display =3D g_new0(wayland_display, 1); + display->display =3D wl_display_connect(NULL); + assert(display->display); + + display->registry =3D wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + wl_display_roundtrip(display->display); + if (display->dmabuf =3D=3D NULL) { + error_report("No zwp_linux_dmabuf global\n"); + exit(1); + } + return display; +} + +static wayland_window *wayland_create_window(wayland_display *display) +{ + wayland_window *window; + + window =3D g_new0(wayland_window, 1); + window->display =3D display; + window->surface =3D wl_compositor_create_surface(display->compositor); + wl_surface_set_user_data(window->surface, window); + + if (display->wm_base) { + window->xdg_surface =3D xdg_wm_base_get_xdg_surface(display->wm_ba= se, + window->surface); + assert(window->xdg_surface); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + window->xdg_toplevel =3D xdg_surface_get_toplevel(window->xdg_surf= ace); + assert(window->xdg_toplevel); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + xdg_toplevel_set_title(window->xdg_toplevel, "qemu-wayland"); + window->wait_for_configure =3D true; + wl_surface_commit(window->surface); + } else if (display->fshell) { + zwp_fullscreen_shell_v1_present_surface(display->fshell, + window->surface, + ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT, + NULL); + } else { + assert(0); + } + wl_display_roundtrip(display->display); + return window; +} + +static void wayland_prepare_for_events(struct wl_display *display) +{ + while (wl_display_prepare_read(display) !=3D 0) + wl_display_dispatch_pending(display); + wl_display_flush(display); +} + +static void wayland_dispatch_handler(void *opaque) +{ + wayland_display *display =3D opaque; + + wl_display_read_events(display->display); + wl_display_dispatch_pending(display->display); + wayland_prepare_for_events(display->display); +} + +static void wayland_init(DisplayState *ds, DisplayOptions *opts) +{ + QemuConsole *con; + wayland_display *display; + wayland_window *window; + int idx; + + warn_report("Wayland UI backend should not be used with Guest composit= ors \ + that do single buffer/frontbuffer rendering\n"); + + display =3D wayland_create_display(); + for (idx =3D 0;; idx++) { + con =3D qemu_console_lookup_by_index(idx); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + + window =3D wayland_create_window(display); + window->dcl.con =3D con; + window->dcl.ops =3D &wayland_ops; + window->kbd =3D qkbd_state_init(con); + register_displaychangelistener(&window->dcl); + } + wayland_prepare_for_events(display->display); + qemu_set_fd_handler(wl_display_get_fd(display->display), + wayland_dispatch_handler, NULL, display); +} + +static void early_wayland_init(DisplayOptions *opts) +{ + display_opengl =3D 1; +} + +static QemuDisplay qemu_display_wayland =3D { + .type =3D DISPLAY_TYPE_WAYLAND, + .early_init =3D early_wayland_init, + .init =3D wayland_init, +}; + +static void register_wayland(void) +{ + qemu_display_register(&qemu_display_wayland); +} + +type_init(register_wayland); --=20 2.30.2