Gerd Hoffmann <kraxel@redhat.com>
KVM Forum 2014, Düsseldorf, Germany
qemu -enable-kvm $memory $disk $whatever \ -display gtk \ -vga std -usb -device usb-tablet \ -device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \ -device secondary-vga,bus=head.2,addr=02.0,id=video.2 \ -device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \ -device usb-kbd,bus=usb.2.0,port=1,display=video.2 \ -device usb-tablet,bus=usb.2.0,port=2,display=video.2
In the guest:
[root@fedora ~]# cat /etc/udev/rules.d/70-qemu-autoseat.rules SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:12.0", TAG+="seat", ENV{ID_AUTOSEAT}="1"
More documentation is in docs/multiseat.txt.
static const GraphicHwOps qxl_ops = {
.gfx_update = qxl_hw_update, // called by graphic_hw_update();
};
static int qxl_init_primary(PCIDevice *dev)
{
QemuConsole *con;
/* ... */
con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
/* ... */
}
static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "vnc",
.dpy_refresh = vnc_refresh, // called by timer
.dpy_gfx_switch = vnc_dpy_switch, // dpy_gfx_replace_surface();
.dpy_gfx_update = vnc_dpy_update, // dpy_gfx_update();
/* ... */
};
void vnc_display_init(DisplayState *ds)
{
VncDisplay *vs = g_malloc0(sizeof(*vs));
/* ... */
vs->dcl.ops = &dcl_ops;
register_displaychangelistener(&vs->dcl);
}
void sdl_display_init(/* ... */)
{
/* ... */
for (i = 0; i < sdl2_num_outputs; i++) {
QemuConsole *con = qemu_console_lookup_by_index(i);
if (!qemu_console_is_graphic(con)) {
sdl2_console[i].hidden = true;
}
sdl2_console[i].idx = i;
sdl2_console[i].dcl.ops = &dcl_ops;
sdl2_console[i].dcl.con = con;
register_displaychangelistener(&sdl2_console[i].dcl);
}
/* ... */
}
struct DisplaySurface {
pixman_format_code_t format;
pixman_image_t *image;
uint8_t flags;
};
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface);
/* backed by host memory (vga text mode) */
DisplaySurface *qemu_create_displaysurface(int width, int height);
/* backed by device (vga) memory */
DisplaySurface *qemu_create_displaysurface_from
(int width, int height, pixman_format_code_t format,
int linesize, uint8_t *data);
/* backed by guest main memory */
DisplaySurface *qemu_create_displaysurface_guestmem
(int width, int height, pixman_format_code_t format,
int linesize, uint64_t addr);
/* setup input routing (hw) */
void qemu_input_handler_bind(QemuInputHandlerState *s,
const char *device_id, int head,
Error **errp);
/* core input event function (ui) */
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
/* various helper functions for specific events (ui) */
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
/* ... */
Quoting m-labs.hk/m1.html:
The Milkymist One is an experimental hardware appliance for live video effects. [ ... ]
The LM32 microprocessor is assisted by a texture mapping unit and a programmable floating point VLIW coprocessor [ ... ]
QEMU emulates the texture mapping unit today by rendering into a texture using opengl, then copy back the data from the texture. Requires X11 server access for glx.
/* opengl context management */
qemu_gl_context dpy_gl_ctx_create(QemuConsole *con, bool shared);
void dpy_gl_ctx_destroy(QemuConsole *con, qemu_gl_context ctx);
int dpy_gl_ctx_make_current(QemuConsole *con, qemu_gl_context ctx);
qemu_gl_context dpy_gl_ctx_get_current(QemuConsole *con);
/* define and update guest display */
void dpy_gl_scanout(QemuConsole *con,
uint32_t backing_id, bool backing_y_0_top,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
With "working" as in "demoable patches exist"
.Hardware emulation:
In the ui code:
Simple approach:
Offload to the GPU (better sapproach?):
Other ideas?
Slides
git repos
Documentation
/