@@ -406,6 +406,7 @@ cfi_debug="false"
seccomp="auto"
glusterfs="auto"
gtk="auto"
+wayland="auto"
tls_priority="NORMAL"
gnutls="auto"
nettle="auto"
@@ -1383,6 +1384,10 @@ for opt do
;;
--enable-gtk) gtk="enabled"
;;
+ --disable-wayland) wayland="disabled"
+ ;;
+ --enable-wayland) wayland="enabled"
+ ;;
--tls-priority=*) tls_priority="$optarg"
;;
--disable-gnutls) gnutls="disabled"
@@ -1868,6 +1873,7 @@ disabled with --disable-FEATURE, default is enabled if 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" = no; then
-Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \
-Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf -Dnvmm=$nvmm \
-Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \
- -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \
+ -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dwayland=$wayland -Dsdl_image=$sdl_image \
-Dlibusb=$libusb -Dsmartcard=$smartcard -Dusb_redir=$usb_redir -Dvte=$vte \
-Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \
-Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f -Dvirtiofsd=$virtiofsd \
@@ -927,6 +927,37 @@ if gtkx11.found()
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
kwargs: static_kwargs)
endif
+
+wayland = not_found
+if not get_option('wayland').auto()
+ wlclientdep = dependency('wayland-client', version: '>= 1.18.90',
+ method: 'pkg-config',
+ required: get_option('wayland'),
+ kwargs: static_kwargs)
+ wlprotocolsdep = dependency('wayland-protocols', version: '>= 1.14.91',
+ method: 'pkg-config',
+ required: get_option('wayland'),
+ kwargs: static_kwargs)
+
+ if not wlprotocolsdep.found()
+ wlproto_dir = subproject('wayland-protocols').get_variable('wayland_protocols_srcdir')
+ else
+ wlproto_dir = wlprotocolsdep.get_pkgconfig_variable('pkgdatadir')
+ endif
+
+ wayland = declare_dependency(dependencies: [wlclientdep, wlprotocolsdep])
+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 = not_found
png = not_found
jpeg = not_found
@@ -1256,6 +1287,7 @@ if glusterfs.found()
config_host_data.set('CONFIG_GLUSTERFS_IOCB_HAS_STAT', glusterfs_iocb_has_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 += {'SDL support': sdl.found()}
summary_info += {'SDL image support': sdl_image.found()}
# TODO: add back version
summary_info += {'GTK support': gtk.found()}
+summary_info += {'Wayland support': wayland.found()}
summary_info += {'pixman': pixman.found()}
# TODO: add back version
summary_info += {'VTE support': vte.found()}
@@ -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',
@@ -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' },
@@ -64,6 +64,58 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found()
ui_modules += {'egl-headless' : egl_headless_ss}
endif
+wayland_scanner = find_program('wayland-scanner')
+proto_sources = [
+ ['xdg-shell', 'stable', ],
+ ['fullscreen-shell', 'unstable', 'v1', ],
+ ['linux-dmabuf', 'unstable', 'v1', ],
+]
+wayland_headers = []
+wayland_proto_sources = []
+
+if wayland.found()
+ foreach p: proto_sources
+ proto_name = p.get(0)
+ proto_stability = p.get(1)
+
+ if proto_stability == 'stable'
+ output_base = proto_name
+ input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
+ else
+ proto_version = p.get(2)
+ output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability, proto_version)
+ input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
+ endif
+
+ wayland_headers += custom_target('@0@ client header'.format(output_base),
+ input: input,
+ output: '@0@-client-protocol.h'.format(output_base),
+ command: [
+ wayland_scanner,
+ 'client-header',
+ '@INPUT@', '@OUTPUT@',
+ ], build_by_default: true
+ )
+
+ wayland_proto_sources += custom_target('@0@ source'.format(output_base),
+ 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 = ss.source_set()
+ wayland_ss.add(when: wayland, if_true: files('wayland.c', 'xdg-shell-protocol.c', 'fullscreen-shell-unstable-v1-protocol.c','linux-dmabuf-unstable-v1-protocol.c'))
+ #wayland_ss.add(when: wayland, if_true: files('wayland.c'), [wayland_proto_sources])
+ ui_modules += {'wayland' : wayland_ss}
+endif
+
if gtk.found()
softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c'))
new file mode 100644
@@ -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 later.
+ * 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 <linux/input.h>
+#include <wayland-client.h>
+#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 = data;
+
+ xdg_surface_ack_configure(surface, serial);
+ window->wait_for_configure = false;
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ 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 = {
+ 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 = data;
+
+ if (callback) {
+ assert(window->callback == callback);
+ wl_callback_destroy(callback);
+ window->callback = NULL;
+ }
+ graphic_hw_gl_block(window->dcl.con, false);
+ graphic_hw_gl_flushed(window->dcl.con);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ wayland_window_redraw
+};
+
+static void wayland_buffer_release(void *data, struct wl_buffer *buf)
+{
+ wayland_buffer *buffer = data;
+ QemuDmaBuf *dmabuf = buffer->dmabuf;
+
+ dmabuf->fence_fd = -1;
+ graphic_hw_gl_flushed(buffer->con);
+ buffer->busy = false;
+ wl_buffer_destroy(buf);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ wayland_buffer_release
+};
+
+static wayland_buffer *window_next_buffer(wayland_window *window)
+{
+ int i;
+
+ for (i = 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 = container_of(dcl, wayland_window, dcl);
+ wayland_display *display = window->display;
+ wayland_buffer *buffer = window_next_buffer(window);
+ struct zwp_linux_buffer_params_v1 *params;
+
+ if (!buffer) {
+ error_report("Can't find free buffer\n");
+ exit(1);
+ }
+ params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
+ zwp_linux_buffer_params_v1_add(params, dmabuf->fd, 0, 0, dmabuf->stride,
+ 0, 0);
+ buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params,
+ dmabuf->width,
+ dmabuf->height,
+ dmabuf->fourcc,
+ 0);
+ zwp_linux_buffer_params_v1_destroy(params);
+ buffer->dmabuf = dmabuf;
+ buffer->con = window->dcl.con;
+ window->new_buffer = buffer;
+ window->width = dmabuf->width;
+ window->height = dmabuf->height;
+ dmabuf->fence_fd = 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 = container_of(dcl, wayland_window, dcl);
+ struct wl_region *region;
+
+ graphic_hw_gl_block(window->new_buffer->con, true);
+ region = 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 = 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 = 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 = {
+ 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 = {
+ xdg_wm_base_ping,
+};
+
+static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ wayland_input *input = data;
+
+ if (!surface) {
+ return;
+ }
+ input->focus = 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 *surface)
+{
+ wayland_input *input = data;
+
+ if (!surface || !input->focus) {
+ return;
+ }
+ input->focus = 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 = data;
+ wayland_window *window = input->focus;
+ int x, y;
+
+ if (!window) {
+ return;
+ }
+ x = wl_fixed_to_int(sx);
+ y = 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 = x;
+ window->last_y = y;
+ window->last_set = true;
+}
+
+static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time,
+ uint32_t button, uint32_t state)
+{
+ wayland_input *input = data;
+ wayland_window *window = input->focus;
+ InputButton btn;
+
+ if (!window) {
+ return;
+ }
+ if (button == BTN_LEFT) {
+ btn = INPUT_BUTTON_LEFT;
+ } else if (button == BTN_MIDDLE) {
+ btn = INPUT_BUTTON_MIDDLE;
+ } else if (button == BTN_RIGHT) {
+ btn = INPUT_BUTTON_RIGHT;
+ } else if (button == BTN_SIDE) {
+ btn = INPUT_BUTTON_SIDE;
+ } else {
+ return;
+ }
+ qemu_input_queue_btn(window->dcl.con, btn,
+ state == 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 value)
+{
+ wayland_input *input = data;
+ wayland_window *window = input->focus;
+ InputButton btn;
+ int delta_y = 0;
+
+ if (!window) {
+ return;
+ }
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ delta_y = wl_fixed_to_int(value) / 10;
+ }
+ if (delta_y > 0) {
+ btn = INPUT_BUTTON_WHEEL_DOWN;
+ } else {
+ btn = 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 = {
+ 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 *keyboard,
+ 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 *surface,
+ struct wl_array *keys)
+{
+}
+
+static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+{
+ wayland_input *input = data;
+ wayland_window *window = input->focus;
+ int keycode = key + 8, qcode;
+
+ if (!window || !input->keycode_map ||
+ keycode > input->keycode_maplen) {
+ return;
+ }
+ qcode = input->keycode_map[keycode];
+ qkbd_state_key_event(window->kbd, qcode, state);
+}
+
+static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ 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 = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+ input->pointer = 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 = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+ input->keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(input->keyboard, &keyboard_listener, input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+ wl_keyboard_destroy(input->keyboard);
+ input->keyboard = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface,
+ uint32_t version)
+{
+ wayland_display *display = data;
+ wayland_input *input;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ display->compositor = wl_registry_bind(registry,
+ id, &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "xdg_wm_base") == 0) {
+ display->wm_base = 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") == 0) {
+ input = g_new0(wayland_input, 1);
+ input->keycode_maplen = qemu_input_map_xorgevdev_to_qcode_len;
+ input->keycode_map = qemu_input_map_xorgevdev_to_qcode;
+ input->seat = 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") == 0) {
+ display->fshell = wl_registry_bind(registry,
+ id, &zwp_fullscreen_shell_v1_interface,
+ 1);
+ } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
+ display->dmabuf = 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 = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static const DisplayChangeListenerOps wayland_ops = {
+ .dpy_name = "wayland",
+ .dpy_refresh = wayland_refresh,
+ .dpy_gl_ctx_create = wayland_create_context,
+ .dpy_gl_ctx_destroy = wayland_destroy_context,
+ .dpy_gl_ctx_make_current = wayland_make_context_current,
+
+ .dpy_gl_scanout_disable = wayland_scanout_disable,
+ .dpy_gl_scanout_texture = wayland_scanout_texture,
+ .dpy_gl_scanout_dmabuf = wayland_scanout_dmabuf,
+ .dpy_gl_release_dmabuf = wayland_release_dmabuf,
+ .dpy_gl_update = wayland_scanout_flush,
+};
+
+static wayland_display *wayland_create_display(void)
+{
+ wayland_display *display;
+
+ display = g_new0(wayland_display, 1);
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ display->registry = wl_display_get_registry(display->display);
+ wl_registry_add_listener(display->registry,
+ ®istry_listener, display);
+ wl_display_roundtrip(display->display);
+ if (display->dmabuf == 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 = g_new0(wayland_window, 1);
+ window->display = display;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ wl_surface_set_user_data(window->surface, window);
+
+ if (display->wm_base) {
+ window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
+ window->surface);
+ assert(window->xdg_surface);
+ xdg_surface_add_listener(window->xdg_surface,
+ &xdg_surface_listener, window);
+ window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
+ 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 = 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) != 0)
+ wl_display_dispatch_pending(display);
+ wl_display_flush(display);
+}
+
+static void wayland_dispatch_handler(void *opaque)
+{
+ wayland_display *display = 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 compositors \
+ that do single buffer/frontbuffer rendering\n");
+
+ display = wayland_create_display();
+ for (idx = 0;; idx++) {
+ con = qemu_console_lookup_by_index(idx);
+ if (!con || !qemu_console_is_graphic(con)) {
+ break;
+ }
+
+ window = wayland_create_window(display);
+ window->dcl.con = con;
+ window->dcl.ops = &wayland_ops;
+ window->kbd = 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 = 1;
+}
+
+static QemuDisplay qemu_display_wayland = {
+ .type = DISPLAY_TYPE_WAYLAND,
+ .early_init = early_wayland_init,
+ .init = wayland_init,
+};
+
+static void register_wayland(void)
+{
+ qemu_display_register(&qemu_display_wayland);
+}
+
+type_init(register_wayland);
Cc: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> --- 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