mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
wef # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmSa+6McHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5YxfD/46nwpTCTWWdKdTeo5p # OUdrF0rfHFqu4FN3OWHbfRxZCO/AdHL+UZ52owV4+5bJJI5uOnXwYKpkrwKYBrFd # H8Bll7NitzA41rw4AQa0GeaQYCPJ99OOfnhbRI5Aep2NG2DfX5PK4RWnfqYw8LD1 # TiHtRv2lWnX9EyMjnEh93C+n17OfquP5Ew3ozZNQJ0+SiJ3CvsUn6hEqxOA8OdyX # lj6l00CASQA2BxW+zjXjJKvRakCV4gfdvrL9eMf4eu0UopzET7ombBJGPnYVsrDU # /4R7b0JgGM4iOpXFxK4Ng6myP28vPdOEJAU/OJLH+oMRz1caohS+0Ijl2KviUCex # SGpb9plxqI7fI2QQt+1CxAlXADSW7oV1zV0/tLkKl/n5+MF3HJ/5qR3tefLhYu1p # 2LpfbPMKGQ9V3+5Z/UvWx6GQYP1iBRm5THPLn+HSDMSqLmt6yp5cOTwP3KTx1Zlc # JfpBtekT2Cgs54nnCcfnXa6/EPo4uR7cMFzrgXdSacPz/GssMVa1c2mNUYkgYEYU # PeyDWZG2Rt/70y+CFDPBpKWEQVICnf7Ha43oj4BtGTqqUFeuZClMTTtZ5poSg3ir # FcRNJ5zSWg2KhHIQ9TQKxIAwrxxVBY0AiQleNRyDzx+YGAuBBadO6i5eCqqpGgOa # QRVBsP33Pg/QD1JdxN9GSSEh0w== # =cR6x # -----END PGP SIGNATURE----- # gpg: Signature made Tue 27 Jun 2023 05:09:23 PM CEST # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] * tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu: (33 commits) ui/dbus: use shared D3D11 Texture2D when possible virtio-gpu-virgl: use D3D11_SHARE_TEXTURE when available ui: add optional d3d texture pointer to scanout texture ui/egl: query ANGLE d3d device virtio-gpu-virgl: teach it to get the QEMU EGL display ui/dbus: add some GL traces ui/dbus: add GL support on win32 ui: add egl_fb_read_rect() ui/egl: default to GLES on windows ui: add egl-headless support on win32 ui/dbus: use shared memory when possible on win32 virtio-gpu/win32: allocate shareable 2d resources/images console/win32: allocate shareable display surface ui/dbus: introduce "Interfaces" properties tests: make dbus-display-test work on win32 qtest: add qtest_pid() ui/dbus: win32 support scripts: add a XML preprocessor script ui/dbus: compile without gio/gunixfdlist.h ui/egl: fix make_context_current() callback return value ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -3202,6 +3202,7 @@ F: docs/interop/dbus*
|
||||
F: docs/sphinx/dbus*
|
||||
F: docs/sphinx/fakedbusdoc.py
|
||||
F: tests/qtest/dbus*
|
||||
F: scripts/xml-preprocess*
|
||||
|
||||
Seccomp
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
|
||||
+39
-4
@@ -29,7 +29,11 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/dbus.h"
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <gio/gunixfdlist.h>
|
||||
#endif
|
||||
|
||||
#include "ui/dbus.h"
|
||||
#include "ui/dbus-display1.h"
|
||||
|
||||
#define AUDIO_CAP "dbus"
|
||||
@@ -444,7 +448,9 @@ listener_in_vanished_cb(GDBusConnection *connection,
|
||||
static gboolean
|
||||
dbus_audio_register_listener(AudioState *s,
|
||||
GDBusMethodInvocation *invocation,
|
||||
#ifdef G_OS_UNIX
|
||||
GUnixFDList *fd_list,
|
||||
#endif
|
||||
GVariant *arg_listener,
|
||||
bool out)
|
||||
{
|
||||
@@ -471,6 +477,11 @@ dbus_audio_register_listener(AudioState *s,
|
||||
return DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
|
||||
return DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
#else
|
||||
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
|
||||
if (err) {
|
||||
g_dbus_method_invocation_return_error(invocation,
|
||||
@@ -480,6 +491,7 @@ dbus_audio_register_listener(AudioState *s,
|
||||
err->message);
|
||||
return DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
socket = g_socket_new_from_fd(fd, &err);
|
||||
if (err) {
|
||||
@@ -488,15 +500,28 @@ dbus_audio_register_listener(AudioState *s,
|
||||
DBUS_DISPLAY_ERROR_FAILED,
|
||||
"Couldn't make a socket: %s",
|
||||
err->message);
|
||||
#ifdef G_OS_WIN32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
socket_conn = g_socket_connection_factory_create_connection(socket);
|
||||
if (out) {
|
||||
qemu_dbus_display1_audio_complete_register_out_listener(
|
||||
da->iface, invocation, NULL);
|
||||
da->iface, invocation
|
||||
#ifdef G_OS_UNIX
|
||||
, NULL
|
||||
#endif
|
||||
);
|
||||
} else {
|
||||
qemu_dbus_display1_audio_complete_register_in_listener(
|
||||
da->iface, invocation, NULL);
|
||||
da->iface, invocation
|
||||
#ifdef G_OS_UNIX
|
||||
, NULL
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
listener_conn =
|
||||
@@ -574,22 +599,32 @@ dbus_audio_register_listener(AudioState *s,
|
||||
static gboolean
|
||||
dbus_audio_register_out_listener(AudioState *s,
|
||||
GDBusMethodInvocation *invocation,
|
||||
#ifdef G_OS_UNIX
|
||||
GUnixFDList *fd_list,
|
||||
#endif
|
||||
GVariant *arg_listener)
|
||||
{
|
||||
return dbus_audio_register_listener(s, invocation,
|
||||
fd_list, arg_listener, true);
|
||||
#ifdef G_OS_UNIX
|
||||
fd_list,
|
||||
#endif
|
||||
arg_listener, true);
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dbus_audio_register_in_listener(AudioState *s,
|
||||
GDBusMethodInvocation *invocation,
|
||||
#ifdef G_OS_UNIX
|
||||
GUnixFDList *fd_list,
|
||||
#endif
|
||||
GVariant *arg_listener)
|
||||
{
|
||||
return dbus_audio_register_listener(s, invocation,
|
||||
fd_list, arg_listener, false);
|
||||
#ifdef G_OS_UNIX
|
||||
fd_list,
|
||||
#endif
|
||||
arg_listener, false);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -190,7 +190,7 @@ static void qemu_chr_open_stdio(Chardev *chr,
|
||||
}
|
||||
}
|
||||
|
||||
dwMode |= ENABLE_LINE_INPUT;
|
||||
dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
|
||||
if (is_console) {
|
||||
/* set the terminal in raw mode */
|
||||
|
||||
@@ -132,7 +132,8 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
|
||||
void *pdata = NULL;
|
||||
|
||||
res->dmabuf_fd = -1;
|
||||
if (res->iov_cnt == 1) {
|
||||
if (res->iov_cnt == 1 &&
|
||||
res->iov[0].iov_len < 4096) {
|
||||
pdata = res->iov[0].iov_base;
|
||||
} else {
|
||||
virtio_gpu_create_udmabuf(res);
|
||||
|
||||
@@ -18,9 +18,17 @@
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-gpu.h"
|
||||
|
||||
#include "ui/egl-helpers.h"
|
||||
|
||||
#include <virglrenderer.h>
|
||||
|
||||
static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
|
||||
#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
|
||||
static void *
|
||||
virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
|
||||
{
|
||||
return qemu_egl_display;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
|
||||
struct virtio_gpu_ctrl_command *cmd)
|
||||
@@ -145,7 +153,6 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
|
||||
struct virtio_gpu_ctrl_command *cmd)
|
||||
{
|
||||
struct virtio_gpu_set_scanout ss;
|
||||
struct virgl_renderer_resource_info info;
|
||||
int ret;
|
||||
|
||||
VIRTIO_GPU_FILL_CMD(ss);
|
||||
@@ -160,10 +167,20 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
|
||||
}
|
||||
g->parent_obj.enable = 1;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
if (ss.resource_id && ss.r.width && ss.r.height) {
|
||||
struct virgl_renderer_resource_info info;
|
||||
void *d3d_tex2d = NULL;
|
||||
|
||||
#ifdef HAVE_VIRGL_D3D_INFO_EXT
|
||||
struct virgl_renderer_resource_info_ext ext;
|
||||
memset(&ext, 0, sizeof(ext));
|
||||
ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
|
||||
info = ext.base;
|
||||
d3d_tex2d = ext.d3d_tex2d;
|
||||
#else
|
||||
memset(&info, 0, sizeof(info));
|
||||
ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
|
||||
#endif
|
||||
if (ret == -1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: illegal resource specified %d\n",
|
||||
@@ -178,7 +195,8 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
|
||||
g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
|
||||
info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
|
||||
info.width, info.height,
|
||||
ss.r.x, ss.r.y, ss.r.width, ss.r.height);
|
||||
ss.r.x, ss.r.y, ss.r.width, ss.r.height,
|
||||
d3d_tex2d);
|
||||
} else {
|
||||
dpy_gfx_replace_surface(
|
||||
g->parent_obj.scanout[ss.scanout_id].con, NULL);
|
||||
@@ -607,8 +625,21 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
|
||||
int virtio_gpu_virgl_init(VirtIOGPU *g)
|
||||
{
|
||||
int ret;
|
||||
uint32_t flags = 0;
|
||||
|
||||
ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
|
||||
#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
|
||||
if (qemu_egl_display) {
|
||||
virtio_gpu_3d_cbs.version = 4;
|
||||
virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display;
|
||||
}
|
||||
#endif
|
||||
#ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE
|
||||
if (qemu_egl_angle_d3d) {
|
||||
flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs);
|
||||
if (ret != 0) {
|
||||
error_report("virgl could not be initialized: %d", ret);
|
||||
return ret;
|
||||
|
||||
+79
-20
@@ -258,6 +258,16 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat,
|
||||
return height * stride;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
static void
|
||||
win32_pixman_image_destroy(pixman_image_t *image, void *data)
|
||||
{
|
||||
HANDLE handle = data;
|
||||
|
||||
qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
|
||||
struct virtio_gpu_ctrl_command *cmd)
|
||||
{
|
||||
@@ -304,12 +314,27 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
|
||||
|
||||
res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height);
|
||||
if (res->hostmem + g->hostmem < g->conf_max_hostmem) {
|
||||
void *bits = NULL;
|
||||
#ifdef WIN32
|
||||
bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
|
||||
if (!bits) {
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
res->image = pixman_image_create_bits(pformat,
|
||||
c2d.width,
|
||||
c2d.height,
|
||||
NULL, 0);
|
||||
bits, res->hostmem / c2d.height);
|
||||
#ifdef WIN32
|
||||
if (res->image) {
|
||||
pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
end:
|
||||
#endif
|
||||
if (!res->image) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: resource creation failed %d %d %d\n",
|
||||
@@ -438,11 +463,11 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
|
||||
struct virtio_gpu_ctrl_command *cmd)
|
||||
{
|
||||
struct virtio_gpu_simple_resource *res;
|
||||
int h;
|
||||
int h, bpp;
|
||||
uint32_t src_offset, dst_offset, stride;
|
||||
int bpp;
|
||||
pixman_format_code_t format;
|
||||
struct virtio_gpu_transfer_to_host_2d t2d;
|
||||
void *img_data;
|
||||
|
||||
VIRTIO_GPU_FILL_CMD(t2d);
|
||||
virtio_gpu_t2d_bswap(&t2d);
|
||||
@@ -471,23 +496,23 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
|
||||
format = pixman_image_get_format(res->image);
|
||||
bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
|
||||
stride = pixman_image_get_stride(res->image);
|
||||
img_data = pixman_image_get_data(res->image);
|
||||
|
||||
if (t2d.offset || t2d.r.x || t2d.r.y ||
|
||||
t2d.r.width != pixman_image_get_width(res->image)) {
|
||||
void *img_data = pixman_image_get_data(res->image);
|
||||
if (t2d.r.x || t2d.r.width != pixman_image_get_width(res->image)) {
|
||||
for (h = 0; h < t2d.r.height; h++) {
|
||||
src_offset = t2d.offset + stride * h;
|
||||
dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
|
||||
|
||||
iov_to_buf(res->iov, res->iov_cnt, src_offset,
|
||||
(uint8_t *)img_data
|
||||
+ dst_offset, t2d.r.width * bpp);
|
||||
(uint8_t *)img_data + dst_offset,
|
||||
t2d.r.width * bpp);
|
||||
}
|
||||
} else {
|
||||
iov_to_buf(res->iov, res->iov_cnt, 0,
|
||||
pixman_image_get_data(res->image),
|
||||
pixman_image_get_stride(res->image)
|
||||
* pixman_image_get_height(res->image));
|
||||
src_offset = t2d.offset;
|
||||
dst_offset = t2d.r.y * stride + t2d.r.x * bpp;
|
||||
iov_to_buf(res->iov, res->iov_cnt, src_offset,
|
||||
(uint8_t *)img_data + dst_offset,
|
||||
stride * t2d.r.height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,6 +523,8 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
|
||||
struct virtio_gpu_resource_flush rf;
|
||||
struct virtio_gpu_scanout *scanout;
|
||||
pixman_region16_t flush_region;
|
||||
bool within_bounds = false;
|
||||
bool update_submitted = false;
|
||||
int i;
|
||||
|
||||
VIRTIO_GPU_FILL_CMD(rf);
|
||||
@@ -518,13 +545,28 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
|
||||
rf.r.x < scanout->x + scanout->width &&
|
||||
rf.r.x + rf.r.width >= scanout->x &&
|
||||
rf.r.y < scanout->y + scanout->height &&
|
||||
rf.r.y + rf.r.height >= scanout->y &&
|
||||
console_has_gl(scanout->con)) {
|
||||
dpy_gl_update(scanout->con, 0, 0, scanout->width,
|
||||
scanout->height);
|
||||
rf.r.y + rf.r.height >= scanout->y) {
|
||||
within_bounds = true;
|
||||
|
||||
if (console_has_gl(scanout->con)) {
|
||||
dpy_gl_update(scanout->con, 0, 0, scanout->width,
|
||||
scanout->height);
|
||||
update_submitted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
if (update_submitted) {
|
||||
return;
|
||||
}
|
||||
if (!within_bounds) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside scanouts"
|
||||
" bounds for flush %d: %d %d %d %d\n",
|
||||
__func__, rf.resource_id, rf.r.x, rf.r.y,
|
||||
rf.r.width, rf.r.height);
|
||||
cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!res->blob &&
|
||||
@@ -634,8 +676,10 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
if (console_has_gl(scanout->con)) {
|
||||
if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
|
||||
virtio_gpu_update_scanout(g, scanout_id, res, r);
|
||||
return;
|
||||
} else {
|
||||
*error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
data = res->blob;
|
||||
@@ -666,6 +710,9 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
|
||||
*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
|
||||
return;
|
||||
}
|
||||
#ifdef WIN32
|
||||
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
|
||||
#endif
|
||||
|
||||
pixman_image_unref(rect);
|
||||
dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con,
|
||||
@@ -1209,6 +1256,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
|
||||
struct virtio_gpu_simple_resource *res;
|
||||
struct virtio_gpu_scanout *scanout;
|
||||
uint32_t resource_id, pformat;
|
||||
void *bits = NULL;
|
||||
int i;
|
||||
|
||||
g->hostmem = 0;
|
||||
@@ -1233,15 +1281,23 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
|
||||
g_free(res);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
|
||||
#ifdef WIN32
|
||||
bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
|
||||
if (!bits) {
|
||||
g_free(res);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
res->image = pixman_image_create_bits(pformat,
|
||||
res->width, res->height,
|
||||
NULL, 0);
|
||||
bits, res->hostmem / res->height);
|
||||
if (!res->image) {
|
||||
g_free(res);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
|
||||
|
||||
res->addrs = g_new(uint64_t, res->iov_cnt);
|
||||
res->iov = g_new(struct iovec, res->iov_cnt);
|
||||
@@ -1302,6 +1358,9 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
|
||||
if (!scanout->ds) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#ifdef WIN32
|
||||
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
|
||||
#endif
|
||||
|
||||
dpy_gfx_replace_surface(scanout->con, scanout->ds);
|
||||
dpy_gfx_update_full(scanout->con);
|
||||
|
||||
@@ -48,6 +48,9 @@ struct virtio_gpu_simple_resource {
|
||||
unsigned int iov_cnt;
|
||||
uint32_t scanout_bitmask;
|
||||
pixman_image_t *image;
|
||||
#ifdef WIN32
|
||||
HANDLE handle;
|
||||
#endif
|
||||
uint64_t hostmem;
|
||||
|
||||
uint64_t blob_size;
|
||||
|
||||
@@ -263,6 +263,9 @@ EXCEPTION_DISPOSITION
|
||||
win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*,
|
||||
struct _CONTEXT*, void*);
|
||||
|
||||
void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp);
|
||||
void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+28
-2
@@ -5,6 +5,7 @@
|
||||
#include "qom/object.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qapi/qapi-types-ui.h"
|
||||
#include "ui/input.h"
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
# include <epoxy/gl.h>
|
||||
@@ -95,6 +96,20 @@ bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl);
|
||||
void kbd_put_string_console(QemuConsole *s, const char *str, int len);
|
||||
void kbd_put_keysym(int keysym);
|
||||
|
||||
/* Touch devices */
|
||||
typedef struct touch_slot {
|
||||
int x;
|
||||
int y;
|
||||
int tracking_id;
|
||||
} touch_slot;
|
||||
|
||||
void console_handle_touch_event(QemuConsole *con,
|
||||
struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
|
||||
uint64_t num_slot,
|
||||
int width, int height,
|
||||
double x, double y,
|
||||
InputMultiTouchType type,
|
||||
Error **errp);
|
||||
/* consoles */
|
||||
|
||||
#define TYPE_QEMU_CONSOLE "qemu-console"
|
||||
@@ -117,6 +132,7 @@ typedef struct ScanoutTexture {
|
||||
uint32_t y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
void *d3d_tex2d;
|
||||
} ScanoutTexture;
|
||||
|
||||
typedef struct DisplaySurface {
|
||||
@@ -128,6 +144,10 @@ typedef struct DisplaySurface {
|
||||
GLenum gltype;
|
||||
GLuint texture;
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
HANDLE handle;
|
||||
uint32_t handle_offset;
|
||||
#endif
|
||||
} DisplaySurface;
|
||||
|
||||
typedef struct QemuUIInfo {
|
||||
@@ -251,7 +271,8 @@ typedef struct DisplayChangeListenerOps {
|
||||
uint32_t backing_width,
|
||||
uint32_t backing_height,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h);
|
||||
uint32_t w, uint32_t h,
|
||||
void *d3d_tex2d);
|
||||
/* optional (default to true if has dpy_gl_scanout_dmabuf) */
|
||||
bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl);
|
||||
/* optional */
|
||||
@@ -314,6 +335,10 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height,
|
||||
DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image);
|
||||
DisplaySurface *qemu_create_placeholder_surface(int w, int h,
|
||||
const char *msg);
|
||||
#ifdef WIN32
|
||||
void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
|
||||
HANDLE h, uint32_t offset);
|
||||
#endif
|
||||
PixelFormat qemu_default_pixelformat(int bpp);
|
||||
|
||||
DisplaySurface *qemu_create_displaysurface(int width, int height);
|
||||
@@ -355,7 +380,8 @@ void dpy_gl_scanout_disable(QemuConsole *con);
|
||||
void dpy_gl_scanout_texture(QemuConsole *con,
|
||||
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);
|
||||
uint32_t x, uint32_t y, uint32_t w, uint32_t h,
|
||||
void *d3d_tex2d);
|
||||
void dpy_gl_scanout_dmabuf(QemuConsole *con,
|
||||
QemuDmaBuf *dmabuf);
|
||||
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
extern EGLDisplay *qemu_egl_display;
|
||||
extern EGLConfig qemu_egl_config;
|
||||
extern DisplayGLMode qemu_egl_mode;
|
||||
extern bool qemu_egl_angle_d3d;
|
||||
|
||||
typedef struct egl_fb {
|
||||
int width;
|
||||
@@ -31,16 +32,18 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
|
||||
void egl_fb_setup_new_tex(egl_fb *fb, int width, int height);
|
||||
void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip);
|
||||
void egl_fb_read(DisplaySurface *dst, egl_fb *src);
|
||||
void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h);
|
||||
|
||||
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip);
|
||||
void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
|
||||
int x, int y, double scale_x, double scale_y);
|
||||
|
||||
extern EGLContext qemu_egl_rn_ctx;
|
||||
|
||||
#ifdef CONFIG_GBM
|
||||
|
||||
extern int qemu_egl_rn_fd;
|
||||
extern struct gbm_device *qemu_egl_rn_gbm_dev;
|
||||
extern EGLContext qemu_egl_rn_ctx;
|
||||
|
||||
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode);
|
||||
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
|
||||
@@ -62,9 +65,15 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode);
|
||||
#endif
|
||||
|
||||
EGLContext qemu_egl_init_ctx(void);
|
||||
bool qemu_egl_has_dmabuf(void);
|
||||
|
||||
bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp);
|
||||
|
||||
const char *qemu_egl_get_error_string(void);
|
||||
|
||||
#endif /* EGL_HELPERS_H */
|
||||
|
||||
+4
-2
@@ -175,7 +175,8 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
|
||||
uint32_t backing_width,
|
||||
uint32_t backing_height,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h);
|
||||
uint32_t w, uint32_t h,
|
||||
void *d3d_tex2d);
|
||||
void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
|
||||
QemuDmaBuf *dmabuf);
|
||||
void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
|
||||
@@ -211,7 +212,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
|
||||
uint32_t backing_width,
|
||||
uint32_t backing_height,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h);
|
||||
uint32_t w, uint32_t h,
|
||||
void *d3d_tex2d);
|
||||
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
|
||||
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
|
||||
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
|
||||
+2
-1
@@ -90,7 +90,8 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
|
||||
uint32_t backing_width,
|
||||
uint32_t backing_height,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h);
|
||||
uint32_t w, uint32_t h,
|
||||
void *d3d_tex2d);
|
||||
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
|
||||
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
|
||||
|
||||
+8
-2
@@ -838,6 +838,8 @@ if gdbus_codegen.found() and get_option('cfi')
|
||||
gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity'
|
||||
endif
|
||||
|
||||
xml_pp = find_program('scripts/xml-preprocess.py')
|
||||
|
||||
lttng = not_found
|
||||
if 'ust' in get_option('trace_backends')
|
||||
lttng = dependency('lttng-ust', required: true, version: '>= 2.1',
|
||||
@@ -1070,6 +1072,12 @@ if not get_option('virglrenderer').auto() or have_system or have_vhost_user_gpu
|
||||
virgl = dependency('virglrenderer',
|
||||
method: 'pkg-config',
|
||||
required: get_option('virglrenderer'))
|
||||
if virgl.found()
|
||||
config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT',
|
||||
cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d',
|
||||
prefix: '#include <virglrenderer.h>',
|
||||
dependencies: virgl))
|
||||
endif
|
||||
endif
|
||||
blkio = not_found
|
||||
if not get_option('blkio').auto() or have_block
|
||||
@@ -1985,8 +1993,6 @@ dbus_display = get_option('dbus_display') \
|
||||
error_message: '-display dbus requires glib>=2.64') \
|
||||
.require(gdbus_codegen.found(),
|
||||
error_message: gdbus_codegen_error.format('-display dbus')) \
|
||||
.require(targetos != 'windows',
|
||||
error_message: '-display dbus is not available on Windows') \
|
||||
.allowed()
|
||||
|
||||
have_virtfs = get_option('virtfs') \
|
||||
|
||||
+2
-3
@@ -1484,8 +1484,7 @@
|
||||
{ 'name': 'none' },
|
||||
{ 'name': 'gtk', 'if': 'CONFIG_GTK' },
|
||||
{ 'name': 'sdl', 'if': 'CONFIG_SDL' },
|
||||
{ 'name': 'egl-headless',
|
||||
'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
|
||||
{ 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' },
|
||||
{ 'name': 'curses', 'if': 'CONFIG_CURSES' },
|
||||
{ 'name': 'cocoa', 'if': 'CONFIG_COCOA' },
|
||||
{ 'name': 'spice-app', 'if': 'CONFIG_SPICE' },
|
||||
@@ -1525,7 +1524,7 @@
|
||||
'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' },
|
||||
'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' },
|
||||
'egl-headless': { 'type': 'DisplayEGLHeadless',
|
||||
'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
|
||||
'if': 'CONFIG_OPENGL' },
|
||||
'dbus': { 'type': 'DisplayDBus', 'if': 'CONFIG_DBUS_DISPLAY' },
|
||||
'sdl': { 'type': 'DisplaySDL', 'if': 'CONFIG_SDL' }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
if stap.found()
|
||||
install_data('qemu-trace-stap', install_dir: get_option('bindir'))
|
||||
endif
|
||||
|
||||
test('xml-preprocess', files('xml-preprocess-test.py'), suite: ['unit'])
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2023 Red Hat, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""Unit tests for xml-preprocess"""
|
||||
|
||||
import contextlib
|
||||
import importlib
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from io import StringIO
|
||||
|
||||
xmlpp = importlib.import_module("xml-preprocess")
|
||||
|
||||
|
||||
class TestXmlPreprocess(unittest.TestCase):
|
||||
"""Tests for xml-preprocess.Preprocessor"""
|
||||
|
||||
def test_preprocess_xml(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
|
||||
temp_file.write("<root></root>")
|
||||
temp_file_name = temp_file.name
|
||||
result = xmlpp.preprocess_xml(temp_file_name)
|
||||
self.assertEqual(result, "<root></root>")
|
||||
os.remove(temp_file_name)
|
||||
|
||||
def test_save_xml(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
|
||||
temp_file_name = temp_file.name
|
||||
xmlpp.save_xml("<root></root>", temp_file_name)
|
||||
self.assertTrue(os.path.isfile(temp_file_name))
|
||||
os.remove(temp_file_name)
|
||||
|
||||
def test_include(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as inc_file:
|
||||
inc_file.write("<included>Content from included file</included>")
|
||||
inc_file_name = inc_file.name
|
||||
xml_str = f"<?include {inc_file_name} ?>"
|
||||
expected = "<included>Content from included file</included>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
os.remove(inc_file_name)
|
||||
self.assertRaises(FileNotFoundError, xpp.preprocess, xml_str)
|
||||
|
||||
def test_envvar(self):
|
||||
os.environ["TEST_ENV_VAR"] = "TestValue"
|
||||
xml_str = "<root>$(env.TEST_ENV_VAR)</root>"
|
||||
expected = "<root>TestValue</root>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertRaises(KeyError, xpp.preprocess, "$(env.UNKNOWN)")
|
||||
|
||||
def test_sys_var(self):
|
||||
xml_str = "<root>$(sys.ARCH)</root>"
|
||||
expected = f"<root>{platform.architecture()[0]}</root>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertRaises(KeyError, xpp.preprocess, "$(sys.UNKNOWN)")
|
||||
|
||||
def test_cus_var(self):
|
||||
xml_str = "<root>$(var.USER)</root>"
|
||||
expected = "<root></root>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
xml_str = "<?define USER=FOO?><root>$(var.USER)</root>"
|
||||
expected = "<root>FOO</root>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_error_warning(self):
|
||||
xml_str = "<root><?warning \"test warn\"?></root>"
|
||||
expected = "<root></root>"
|
||||
xpp = xmlpp.Preprocessor()
|
||||
out = StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
result = xpp.preprocess(xml_str)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertEqual(out.getvalue(), "[Warning]: test warn\n")
|
||||
self.assertRaises(RuntimeError, xpp.preprocess, "<?error \"test\"?>")
|
||||
|
||||
def test_cmd(self):
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess('<root><?cmd "echo hello world"?></root>')
|
||||
self.assertEqual(result, "<root>hello world</root>")
|
||||
self.assertRaises(
|
||||
subprocess.CalledProcessError,
|
||||
xpp.preprocess, '<?cmd "test-unknown-cmd"?>'
|
||||
)
|
||||
|
||||
def test_foreach(self):
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess(
|
||||
'<root><?foreach x in a;b;c?>$(var.x)<?endforeach?></root>'
|
||||
)
|
||||
self.assertEqual(result, "<root>abc</root>")
|
||||
|
||||
def test_if_elseif(self):
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess('<root><?if True?>ok<?endif?></root>')
|
||||
self.assertEqual(result, "<root>ok</root>")
|
||||
result = xpp.preprocess('<root><?if False?>ok<?endif?></root>')
|
||||
self.assertEqual(result, "<root></root>")
|
||||
result = xpp.preprocess('<root><?if True?>ok<?else?>ko<?endif?></root>')
|
||||
self.assertEqual(result, "<root>ok</root>")
|
||||
result = xpp.preprocess('<root><?if False?>ok<?else?>ko<?endif?></root>')
|
||||
self.assertEqual(result, "<root>ko</root>")
|
||||
result = xpp.preprocess(
|
||||
'<root><?if False?>ok<?elseif True?>ok2<?else?>ko<?endif?></root>'
|
||||
)
|
||||
self.assertEqual(result, "<root>ok2</root>")
|
||||
result = xpp.preprocess(
|
||||
'<root><?if False?>ok<?elseif False?>ok<?else?>ko<?endif?></root>'
|
||||
)
|
||||
self.assertEqual(result, "<root>ko</root>")
|
||||
|
||||
def test_ifdef(self):
|
||||
xpp = xmlpp.Preprocessor()
|
||||
result = xpp.preprocess('<root><?ifdef USER?>ok<?else?>ko<?endif?></root>')
|
||||
self.assertEqual(result, "<root>ko</root>")
|
||||
result = xpp.preprocess(
|
||||
'<?define USER=FOO?><root><?ifdef USER?>ok<?else?>ko<?endif?></root>'
|
||||
)
|
||||
self.assertEqual(result, "<root>ok</root>")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Executable
+293
@@ -0,0 +1,293 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2017-2019 Tony Su
|
||||
# Copyright (c) 2023 Red Hat, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Adapted from https://github.com/peitaosu/XML-Preprocessor
|
||||
#
|
||||
"""This is a XML Preprocessor which can be used to process your XML file before
|
||||
you use it, to process conditional statements, variables, iteration
|
||||
statements, error/warning, execute command, etc.
|
||||
|
||||
## XML Schema
|
||||
|
||||
### Include Files
|
||||
```
|
||||
<?include path/to/file ?>
|
||||
```
|
||||
|
||||
### Variables
|
||||
```
|
||||
$(env.EnvironmentVariable)
|
||||
|
||||
$(sys.SystemVariable)
|
||||
|
||||
$(var.CustomVariable)
|
||||
```
|
||||
|
||||
### Conditional Statements
|
||||
```
|
||||
<?if ?>
|
||||
|
||||
<?ifdef ?>
|
||||
|
||||
<?ifndef ?>
|
||||
|
||||
<?else?>
|
||||
|
||||
<?elseif ?>
|
||||
|
||||
<?endif?>
|
||||
```
|
||||
|
||||
### Iteration Statements
|
||||
```
|
||||
<?foreach VARNAME in 1;2;3?>
|
||||
$(var.VARNAME)
|
||||
<?endforeach?>
|
||||
```
|
||||
|
||||
### Errors and Warnings
|
||||
```
|
||||
<?error "This is error message!" ?>
|
||||
|
||||
<?warning "This is warning message!" ?>
|
||||
```
|
||||
|
||||
### Commands
|
||||
```
|
||||
<? cmd "echo hello world" ?>
|
||||
```
|
||||
"""
|
||||
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Optional
|
||||
from xml.dom import minidom
|
||||
|
||||
|
||||
class Preprocessor():
|
||||
"""This class holds the XML preprocessing state"""
|
||||
|
||||
def __init__(self):
|
||||
self.sys_vars = {
|
||||
"ARCH": platform.architecture()[0],
|
||||
"SOURCE": os.path.abspath(__file__),
|
||||
"CURRENT": os.getcwd(),
|
||||
}
|
||||
self.cus_vars = {}
|
||||
|
||||
def _pp_include(self, xml_str: str) -> str:
|
||||
include_regex = r"(<\?include([\w\s\\/.:_-]+)\s*\?>)"
|
||||
matches = re.findall(include_regex, xml_str)
|
||||
for group_inc, group_xml in matches:
|
||||
inc_file_path = group_xml.strip()
|
||||
with open(inc_file_path, "r", encoding="utf-8") as inc_file:
|
||||
inc_file_content = inc_file.read()
|
||||
xml_str = xml_str.replace(group_inc, inc_file_content)
|
||||
return xml_str
|
||||
|
||||
def _pp_env_var(self, xml_str: str) -> str:
|
||||
envvar_regex = r"(\$\(env\.(\w+)\))"
|
||||
matches = re.findall(envvar_regex, xml_str)
|
||||
for group_env, group_var in matches:
|
||||
xml_str = xml_str.replace(group_env, os.environ[group_var])
|
||||
return xml_str
|
||||
|
||||
def _pp_sys_var(self, xml_str: str) -> str:
|
||||
sysvar_regex = r"(\$\(sys\.(\w+)\))"
|
||||
matches = re.findall(sysvar_regex, xml_str)
|
||||
for group_sys, group_var in matches:
|
||||
xml_str = xml_str.replace(group_sys, self.sys_vars[group_var])
|
||||
return xml_str
|
||||
|
||||
def _pp_cus_var(self, xml_str: str) -> str:
|
||||
define_regex = r"(<\?define\s*(\w+)\s*=\s*([\w\s\"]+)\s*\?>)"
|
||||
matches = re.findall(define_regex, xml_str)
|
||||
for group_def, group_name, group_var in matches:
|
||||
group_name = group_name.strip()
|
||||
group_var = group_var.strip().strip("\"")
|
||||
self.cus_vars[group_name] = group_var
|
||||
xml_str = xml_str.replace(group_def, "")
|
||||
cusvar_regex = r"(\$\(var\.(\w+)\))"
|
||||
matches = re.findall(cusvar_regex, xml_str)
|
||||
for group_cus, group_var in matches:
|
||||
xml_str = xml_str.replace(
|
||||
group_cus,
|
||||
self.cus_vars.get(group_var, "")
|
||||
)
|
||||
return xml_str
|
||||
|
||||
def _pp_foreach(self, xml_str: str) -> str:
|
||||
foreach_regex = r"(<\?foreach\s+(\w+)\s+in\s+([\w;]+)\s*\?>(.*)<\?endforeach\?>)"
|
||||
matches = re.findall(foreach_regex, xml_str)
|
||||
for group_for, group_name, group_vars, group_text in matches:
|
||||
group_texts = ""
|
||||
for var in group_vars.split(";"):
|
||||
self.cus_vars[group_name] = var
|
||||
group_texts += self._pp_cus_var(group_text)
|
||||
xml_str = xml_str.replace(group_for, group_texts)
|
||||
return xml_str
|
||||
|
||||
def _pp_error_warning(self, xml_str: str) -> str:
|
||||
error_regex = r"<\?error\s*\"([^\"]+)\"\s*\?>"
|
||||
matches = re.findall(error_regex, xml_str)
|
||||
for group_var in matches:
|
||||
raise RuntimeError("[Error]: " + group_var)
|
||||
warning_regex = r"(<\?warning\s*\"([^\"]+)\"\s*\?>)"
|
||||
matches = re.findall(warning_regex, xml_str)
|
||||
for group_wrn, group_var in matches:
|
||||
print("[Warning]: " + group_var)
|
||||
xml_str = xml_str.replace(group_wrn, "")
|
||||
return xml_str
|
||||
|
||||
def _pp_if_eval(self, xml_str: str) -> str:
|
||||
ifelif_regex = (
|
||||
r"(<\?(if|elseif)\s*([^\"\s=<>!]+)\s*([!=<>]+)\s*\"*([^\"=<>!]+)\"*\s*\?>)"
|
||||
)
|
||||
matches = re.findall(ifelif_regex, xml_str)
|
||||
for ifelif, tag, left, operator, right in matches:
|
||||
if "<" in operator or ">" in operator:
|
||||
result = eval(f"{left} {operator} {right}")
|
||||
else:
|
||||
result = eval(f'"{left}" {operator} "{right}"')
|
||||
xml_str = xml_str.replace(ifelif, f"<?{tag} {result}?>")
|
||||
return xml_str
|
||||
|
||||
def _pp_ifdef_ifndef(self, xml_str: str) -> str:
|
||||
ifndef_regex = r"(<\?(ifdef|ifndef)\s*([\w]+)\s*\?>)"
|
||||
matches = re.findall(ifndef_regex, xml_str)
|
||||
for group_ifndef, group_tag, group_var in matches:
|
||||
if group_tag == "ifdef":
|
||||
result = group_var in self.cus_vars
|
||||
else:
|
||||
result = group_var not in self.cus_vars
|
||||
xml_str = xml_str.replace(group_ifndef, f"<?if {result}?>")
|
||||
return xml_str
|
||||
|
||||
def _pp_if_elseif(self, xml_str: str) -> str:
|
||||
if_elif_else_regex = (
|
||||
r"(<\?if\s(True|False)\?>"
|
||||
r"(.*?)"
|
||||
r"<\?elseif\s(True|False)\?>"
|
||||
r"(.*?)"
|
||||
r"<\?else\?>"
|
||||
r"(.*?)"
|
||||
r"<\?endif\?>)"
|
||||
)
|
||||
if_else_regex = (
|
||||
r"(<\?if\s(True|False)\?>"
|
||||
r"(.*?)"
|
||||
r"<\?else\?>"
|
||||
r"(.*?)"
|
||||
r"<\?endif\?>)"
|
||||
)
|
||||
if_regex = r"(<\?if\s(True|False)\?>(.*?)<\?endif\?>)"
|
||||
matches = re.findall(if_elif_else_regex, xml_str, re.DOTALL)
|
||||
for (group_full, group_if, group_if_elif, group_elif,
|
||||
group_elif_else, group_else) in matches:
|
||||
result = ""
|
||||
if group_if == "True":
|
||||
result = group_if_elif
|
||||
elif group_elif == "True":
|
||||
result = group_elif_else
|
||||
else:
|
||||
result = group_else
|
||||
xml_str = xml_str.replace(group_full, result)
|
||||
matches = re.findall(if_else_regex, xml_str, re.DOTALL)
|
||||
for group_full, group_if, group_if_else, group_else in matches:
|
||||
result = ""
|
||||
if group_if == "True":
|
||||
result = group_if_else
|
||||
else:
|
||||
result = group_else
|
||||
xml_str = xml_str.replace(group_full, result)
|
||||
matches = re.findall(if_regex, xml_str, re.DOTALL)
|
||||
for group_full, group_if, group_text in matches:
|
||||
result = ""
|
||||
if group_if == "True":
|
||||
result = group_text
|
||||
xml_str = xml_str.replace(group_full, result)
|
||||
return xml_str
|
||||
|
||||
def _pp_command(self, xml_str: str) -> str:
|
||||
cmd_regex = r"(<\?cmd\s*\"([^\"]+)\"\s*\?>)"
|
||||
matches = re.findall(cmd_regex, xml_str)
|
||||
for group_cmd, group_exec in matches:
|
||||
output = subprocess.check_output(
|
||||
group_exec, shell=True,
|
||||
text=True, stderr=subprocess.STDOUT
|
||||
)
|
||||
xml_str = xml_str.replace(group_cmd, output)
|
||||
return xml_str
|
||||
|
||||
def _pp_blanks(self, xml_str: str) -> str:
|
||||
right_blank_regex = r">[\n\s\t\r]*"
|
||||
left_blank_regex = r"[\n\s\t\r]*<"
|
||||
xml_str = re.sub(right_blank_regex, ">", xml_str)
|
||||
xml_str = re.sub(left_blank_regex, "<", xml_str)
|
||||
return xml_str
|
||||
|
||||
def preprocess(self, xml_str: str) -> str:
|
||||
fns = [
|
||||
self._pp_blanks,
|
||||
self._pp_include,
|
||||
self._pp_foreach,
|
||||
self._pp_env_var,
|
||||
self._pp_sys_var,
|
||||
self._pp_cus_var,
|
||||
self._pp_if_eval,
|
||||
self._pp_ifdef_ifndef,
|
||||
self._pp_if_elseif,
|
||||
self._pp_command,
|
||||
self._pp_error_warning,
|
||||
]
|
||||
|
||||
while True:
|
||||
changed = False
|
||||
for func in fns:
|
||||
out_xml = func(xml_str)
|
||||
if not changed and out_xml != xml_str:
|
||||
changed = True
|
||||
xml_str = out_xml
|
||||
if not changed:
|
||||
break
|
||||
|
||||
return xml_str
|
||||
|
||||
|
||||
def preprocess_xml(path: str) -> str:
|
||||
with open(path, "r", encoding="utf-8") as original_file:
|
||||
input_xml = original_file.read()
|
||||
|
||||
proc = Preprocessor()
|
||||
return proc.preprocess(input_xml)
|
||||
|
||||
|
||||
def save_xml(xml_str: str, path: Optional[str]):
|
||||
xml = minidom.parseString(xml_str)
|
||||
with open(path, "w", encoding="utf-8") if path else sys.stdout as output_file:
|
||||
output_file.write(xml.toprettyxml())
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: xml-preprocessor input.xml [output.xml]")
|
||||
sys.exit(1)
|
||||
|
||||
output_file = None
|
||||
if len(sys.argv) == 3:
|
||||
output_file = sys.argv[2]
|
||||
|
||||
input_file = sys.argv[1]
|
||||
output_xml = preprocess_xml(input_file)
|
||||
save_xml(output_xml, output_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/dbus.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include <gio/gio.h>
|
||||
@@ -14,7 +15,11 @@ test_dbus_p2p_from_fd(int fd)
|
||||
g_autoptr(GSocketConnection) socketc = NULL;
|
||||
GDBusConnection *conn;
|
||||
|
||||
#ifdef WIN32
|
||||
socket = g_socket_new_from_fd(_get_osfhandle(fd), &err);
|
||||
#else
|
||||
socket = g_socket_new_from_fd(fd, &err);
|
||||
#endif
|
||||
g_assert_no_error(err);
|
||||
|
||||
socketc = g_socket_connection_factory_create_connection(socket);
|
||||
@@ -126,7 +131,10 @@ test_dbus_console_registered(GObject *source_object,
|
||||
|
||||
qemu_dbus_display1_console_call_register_listener_finish(
|
||||
QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
|
||||
NULL, res, &err);
|
||||
#ifndef WIN32
|
||||
NULL,
|
||||
#endif
|
||||
res, &err);
|
||||
g_assert_no_error(err);
|
||||
|
||||
test->listener_conn = g_thread_join(test->thread);
|
||||
@@ -145,17 +153,25 @@ test_dbus_display_console(void)
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GDBusConnection) conn = NULL;
|
||||
g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
|
||||
g_autoptr(GUnixFDList) fd_list = NULL;
|
||||
g_autoptr(GMainLoop) loop = NULL;
|
||||
QTestState *qts = NULL;
|
||||
int pair[2], idx;
|
||||
int pair[2];
|
||||
TestDBusConsoleRegister test;
|
||||
#ifdef WIN32
|
||||
WSAPROTOCOL_INFOW info;
|
||||
g_autoptr(GVariant) listener = NULL;
|
||||
#else
|
||||
g_autoptr(GUnixFDList) fd_list = NULL;
|
||||
int idx;
|
||||
#endif
|
||||
|
||||
test_setup(&qts, &conn);
|
||||
|
||||
g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
|
||||
#ifndef WIN32
|
||||
fd_list = g_unix_fd_list_new();
|
||||
idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
|
||||
#endif
|
||||
|
||||
console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
|
||||
qemu_dbus_display1_console_proxy_new_sync(
|
||||
@@ -171,12 +187,33 @@ test_dbus_display_console(void)
|
||||
test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
|
||||
GINT_TO_POINTER(pair[0]));
|
||||
|
||||
#ifdef WIN32
|
||||
if (WSADuplicateSocketW(_get_osfhandle(pair[1]),
|
||||
GetProcessId((HANDLE) qtest_pid(qts)),
|
||||
&info) == SOCKET_ERROR)
|
||||
{
|
||||
g_autofree char *emsg = g_win32_error_message(WSAGetLastError());
|
||||
g_error("WSADuplicateSocket failed: %s", emsg);
|
||||
}
|
||||
close(pair[1]);
|
||||
listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
|
||||
&info,
|
||||
sizeof(info),
|
||||
1);
|
||||
#endif
|
||||
|
||||
qemu_dbus_display1_console_call_register_listener(
|
||||
QEMU_DBUS_DISPLAY1_CONSOLE(console),
|
||||
#ifdef WIN32
|
||||
listener,
|
||||
#else
|
||||
g_variant_new_handle(idx),
|
||||
#endif
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
#ifndef WIN32
|
||||
fd_list,
|
||||
#endif
|
||||
NULL,
|
||||
test_dbus_console_registered,
|
||||
&test);
|
||||
|
||||
@@ -142,6 +142,11 @@ static int socket_accept(int sock)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pid_t qtest_pid(QTestState *s)
|
||||
{
|
||||
return s->qemu_pid;
|
||||
}
|
||||
|
||||
bool qtest_probe_child(QTestState *s)
|
||||
{
|
||||
pid_t pid = s->qemu_pid;
|
||||
|
||||
@@ -985,4 +985,13 @@ void qtest_qom_set_bool(QTestState *s, const char *path, const char *property,
|
||||
* Returns: Value retrieved from property.
|
||||
*/
|
||||
bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property);
|
||||
|
||||
/**
|
||||
* qtest_pid:
|
||||
* @s: QTestState instance to operate on.
|
||||
*
|
||||
* Returns: the PID of the QEMU process, or <= 0
|
||||
*/
|
||||
pid_t qtest_pid(QTestState *s);
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user