* [Qemu-devel] [RFC] virtio-gpu and sdl2 so far
@ 2013-11-20 5:52 Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2) Dave Airlie
` (7 more replies)
0 siblings, 8 replies; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
Hey,
I thought I should post this for a bit more feedback and show where
I've gone so far,
all available in git
http://cgit.freedesktop.org/~airlied/qemu/log/?h=virtio-gpu
The first patch is the sdl2 port with some minor changes I posted
before, then there are a bunch of console changes that I think
will make life easier for multi-head.
The sdl2 multi-head patch is why I'd like to keep SDL1.2 separate
from SDL2, there is a lot of changes in this, and it would be
messy to update SDL1 code at the same time and not cause breakages.
Then there is the virtio-gpu, virtio-vga initial patches, there
is still some future proofing work to be done, and I'd like to
try and sort out the 3d integration and threading before doing
much more with them.
The last patch you can ignore, I just included it because it was
in my tree, and its just a hack to keep libvirt happy on my system.
Dave.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2)
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 10:52 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
` (6 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
I've ported the SDL1.2 code over, and rewritten it to use the SDL2 interface.
The biggest changes were in the input handling, where SDL2 has done a major
overhaul, and I've had to include a generated translation file to get from
SDL2 codes back to qemu compatible ones. I'm still not sure how the keyboard
layout code works in qemu, so there may be further work if someone can point
me a test case that works with SDL1.2 and doesn't with SDL2.
Some SDL env vars we used to set are no longer used by SDL2,
Windows, OSX support is untested,
I don't think we can link to SDL1.2 and SDL2 at the same time, so I felt
using --with-sdlabi=2.0 to select the new code should be fine, like how
gtk does it.
v1.1: fix keys in text console
v1.2: fix shutdown, cleanups a bit of code, support ARGB cursor
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
configure | 23 +-
ui/Makefile.objs | 4 +-
ui/sdl.c | 3 +
ui/sdl2.c | 894 +++++++++++++++++++++++++++++++++++++++++++
ui/sdl2_scancode_translate.h | 260 +++++++++++++
ui/sdl_keysym.h | 3 +-
6 files changed, 1180 insertions(+), 7 deletions(-)
create mode 100644 ui/sdl2.c
create mode 100644 ui/sdl2_scancode_translate.h
diff --git a/configure b/configure
index 9addff1..bf3be37 100755
--- a/configure
+++ b/configure
@@ -158,6 +158,7 @@ docs=""
fdt=""
pixman=""
sdl=""
+sdlabi="1.2"
virtfs=""
vnc="yes"
sparse="no"
@@ -310,6 +311,7 @@ query_pkg_config() {
}
pkg_config=query_pkg_config
sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}"
+sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}"
# default flags for all hosts
QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS"
@@ -710,6 +712,8 @@ for opt do
;;
--enable-sdl) sdl="yes"
;;
+ --with-sdlabi=*) sdlabi="$optarg"
+ ;;
--disable-qom-cast-debug) qom_cast_debug="no"
;;
--enable-qom-cast-debug) qom_cast_debug="yes"
@@ -1092,6 +1096,7 @@ echo " --disable-strip disable stripping binaries"
echo " --disable-werror disable compilation abort on warning"
echo " --disable-sdl disable SDL"
echo " --enable-sdl enable SDL"
+echo " --with-sdlabi select preferred SDL ABI 1.2 or 2.0"
echo " --disable-gtk disable gtk UI"
echo " --enable-gtk enable gtk UI"
echo " --disable-virtfs disable VirtFS"
@@ -1751,12 +1756,22 @@ fi
# Look for sdl configuration program (pkg-config or sdl-config). Try
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
-if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then
- sdl_config=sdl-config
+
+if test $sdlabi == "2.0"; then
+ sdl_config=$sdl2_config
+ sdlname=sdl2
+ sdlconfigname=sdl2_config
+else
+ sdlname=sdl
+ sdlconfigname=sdl_config
+fi
+
+if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then
+ sdl_config=$sdlconfigname
fi
-if $pkg_config sdl --exists; then
- sdlconfig="$pkg_config sdl"
+if $pkg_config $sdlname --exists; then
+ sdlconfig="$pkg_config $sdlname"
_sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'`
elif has ${sdl_config}; then
sdlconfig="$sdl_config"
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index f33be47..721ad37 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -9,12 +9,12 @@ vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o
-$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS)
+$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS)
$(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS)
diff --git a/ui/sdl.c b/ui/sdl.c
index 9d8583c..736bb95 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -26,6 +26,8 @@
#undef WIN32_LEAN_AND_MEAN
#include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 1
#include <SDL_syswm.h>
#include "qemu-common.h"
@@ -966,3 +968,4 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
atexit(sdl_cleanup);
}
+#endif
diff --git a/ui/sdl2.c b/ui/sdl2.c
new file mode 100644
index 0000000..4ad7ce3
--- /dev/null
+++ b/ui/sdl2.c
@@ -0,0 +1,894 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+
+/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
+#undef WIN32_LEAN_AND_MEAN
+
+#include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 2
+#include <SDL_syswm.h>
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+#include "x_keymap.h"
+#include "sdl_zoom.h"
+
+#include "sdl2_scancode_translate.h"
+
+static DisplayChangeListener *dcl;
+static DisplaySurface *surface;
+static SDL_Window *real_window;
+static SDL_Renderer *real_renderer;
+static SDL_Texture *guest_texture;
+
+static SDL_Surface *guest_sprite_surface;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+
+static bool gui_saved_scaling;
+static int gui_saved_width;
+static int gui_saved_height;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_noframe;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[256];
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled = 0;
+static int guest_cursor = 0;
+static int guest_x, guest_y;
+static SDL_Cursor *guest_sprite = NULL;
+static int scaling_active = 0;
+static Notifier mouse_mode_notifier;
+
+static void sdl_update_caption(void);
+
+static void sdl_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ if (!surface)
+ return;
+
+ SDL_UpdateTexture(guest_texture, NULL, surface_data(surface),
+ surface_stride(surface));
+ SDL_RenderCopy(real_renderer, guest_texture, NULL, NULL);
+ SDL_RenderPresent(real_renderer);
+}
+
+static void do_sdl_resize(int width, int height, int bpp)
+{
+ int flags;
+
+ if (real_window) {
+ SDL_SetWindowSize(real_window, width, height);
+ SDL_RenderSetLogicalSize(real_renderer, width, height);
+ } else {
+ flags = 0;
+ if (gui_fullscreen)
+ flags |= SDL_WINDOW_FULLSCREEN;
+ else
+ flags |= SDL_WINDOW_RESIZABLE;
+
+ real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width, height, flags);
+ real_renderer = SDL_CreateRenderer(real_window, -1, 0);
+ sdl_update_caption();
+ }
+}
+
+static void sdl_switch(DisplayChangeListener *dcl,
+ DisplaySurface *new_surface)
+{
+ int format = 0;
+ /* temporary hack: allows to call sdl_switch to handle scaling changes */
+ if (new_surface) {
+ surface = new_surface;
+ }
+
+ if (!scaling_active) {
+ do_sdl_resize(surface_width(surface), surface_height(surface), 0);
+ } else
+ do_sdl_resize(surface_width(surface), surface_height(surface), 0);
+
+ if (guest_texture)
+ SDL_DestroyTexture(guest_texture);
+
+ if (surface_bits_per_pixel(surface) == 16)
+ format = SDL_PIXELFORMAT_RGB565;
+ else if (surface_bits_per_pixel(surface) == 32)
+ format = SDL_PIXELFORMAT_ARGB8888;
+
+ if (!format)
+ exit(1);
+ guest_texture = SDL_CreateTexture(real_renderer, format,
+ SDL_TEXTUREACCESS_STREAMING,
+ surface_width(surface), surface_height(surface));
+ SDL_RenderSetLogicalSize(real_renderer, surface_width(surface), surface_height(surface));
+}
+
+/* generic keyboard conversion */
+
+#include "sdl_keysym.h"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
+{
+ int keysym;
+ /* workaround for X11+SDL bug with AltGR */
+ keysym = ev->keysym.sym;
+ if (keysym == 0 && ev->keysym.scancode == 113)
+ keysym = SDLK_MODE;
+ /* For Japanese key '\' and '|' */
+ if (keysym == 92 && ev->keysym.scancode == 133) {
+ keysym = 0xa5;
+ }
+ return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
+}
+
+/* specific keyboard conversions from scan codes */
+
+#if defined(_WIN32)
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+
+ keycode = ev->keysym.scancode;
+ keycode = sdl2_scancode_to_keycode[keycode];
+ return keycode;
+}
+
+#else
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+
+ keycode = ev->keysym.scancode;
+
+ keycode = sdl2_scancode_to_keycode[keycode];
+ if (keycode >= 89 && keycode < 150) {
+ keycode = translate_evdev_keycode(keycode - 89);
+ }
+ return keycode;
+}
+
+#endif
+
+static void reset_keys(void)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (modifiers_state[i]) {
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
+ modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+ int keycode, v;
+
+ if (ev->keysym.sym == SDLK_PAUSE) {
+ /* specific case */
+ v = 0;
+ if (ev->type == SDL_KEYUP)
+ v |= SCANCODE_UP;
+ kbd_put_keycode(0xe1);
+ kbd_put_keycode(0x1d | v);
+ kbd_put_keycode(0x45 | v);
+ return;
+ }
+
+ if (kbd_layout) {
+ keycode = sdl_keyevent_to_keycode_generic(ev);
+ } else {
+ keycode = sdl_keyevent_to_keycode(ev);
+ }
+
+ switch(keycode) {
+ case 0x00:
+ /* sent when leaving window: reset the modifiers state */
+ reset_keys();
+ return;
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (ev->type == SDL_KEYUP)
+ modifiers_state[keycode] = 0;
+ else
+ modifiers_state[keycode] = 1;
+ break;
+ case 0x45: /* num lock */
+ case 0x3a: /* caps lock */
+ /* SDL does not send the key up event, so we generate it */
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ return;
+ }
+
+ /* now send the key code */
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ if (ev->type == SDL_KEYUP)
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ else
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+}
+
+static void sdl_update_caption(void)
+{
+ char win_title[1024];
+ char icon_title[1024];
+ const char *status = "";
+
+ if (!runstate_is_running())
+ status = " [Stopped]";
+ else if (gui_grab) {
+ if (alt_grab)
+ status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+ else if (ctrl_grab)
+ status = " - Press Right-Ctrl to exit mouse grab";
+ else
+ status = " - Press Ctrl-Alt to exit mouse grab";
+ }
+
+ if (qemu_name) {
+ snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+ } else {
+ snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU");
+ }
+
+ if (real_window)
+ SDL_SetWindowTitle(real_window, win_title);
+}
+
+static void sdl_hide_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+ } else {
+ SDL_ShowCursor(0);
+ }
+}
+
+static void sdl_show_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) {
+ SDL_ShowCursor(1);
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+ else
+ SDL_SetCursor(sdl_cursor_normal);
+ }
+}
+
+static void sdl_grab_start(void)
+{
+ /*
+ * If the application is not active, do not try to enter grab state. This
+ * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
+ * application (SDL bug).
+ */
+ if (!(SDL_GetWindowFlags(real_window) & SDL_WINDOW_INPUT_FOCUS)) {
+ return;
+ }
+ if (guest_cursor) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled) {
+ SDL_WarpMouseInWindow(real_window, guest_x, guest_y);
+ }
+ } else
+ sdl_hide_cursor();
+ SDL_SetWindowGrab(real_window, SDL_TRUE);
+ gui_grab = 1;
+ sdl_update_caption();
+}
+
+static void sdl_grab_end(void)
+{
+ SDL_SetWindowGrab(real_window, SDL_FALSE);
+ gui_grab = 0;
+ sdl_show_cursor();
+ sdl_update_caption();
+}
+
+static void absolute_mouse_grab(void)
+{
+ int mouse_x, mouse_y;
+ int scr_w, scr_h;
+ SDL_GetMouseState(&mouse_x, &mouse_y);
+ SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ if (mouse_x > 0 && mouse_x < scr_w - 1 &&
+ mouse_y > 0 && mouse_y < scr_h - 1) {
+ sdl_grab_start();
+ }
+}
+
+static void sdl_mouse_mode_change(Notifier *notify, void *data)
+{
+ if (kbd_mouse_is_absolute()) {
+ if (!absolute_enabled) {
+ absolute_enabled = 1;
+ if (qemu_console_is_graphic(NULL)) {
+ absolute_mouse_grab();
+ }
+ }
+ } else if (absolute_enabled) {
+ if (!gui_fullscreen) {
+ sdl_grab_end();
+ }
+ absolute_enabled = 0;
+ }
+}
+
+static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+{
+ int buttons = 0;
+
+ if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ }
+ if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) {
+ buttons |= MOUSE_EVENT_MBUTTON;
+ }
+
+ if (kbd_mouse_is_absolute()) {
+ int scr_w, scr_h;
+ SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ dx = x * 0x7FFF / (scr_w - 1);
+ dy = y * 0x7FFF / (scr_h - 1);
+ } else if (guest_cursor) {
+ x -= guest_x;
+ y -= guest_y;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+}
+
+static void sdl_scale(int width, int height)
+{
+ int bpp = 0;
+ do_sdl_resize(width, height, bpp);
+ scaling_active = 1;
+}
+
+static void toggle_full_screen(void)
+{
+ int width = surface_width(surface);
+ int height = surface_height(surface);
+ int bpp = surface_bits_per_pixel(surface);
+
+ gui_fullscreen = !gui_fullscreen;
+ if (gui_fullscreen) {
+ SDL_GetWindowSize(real_window, &gui_saved_width, &gui_saved_height);
+ gui_saved_scaling = scaling_active;
+
+ do_sdl_resize(width, height, bpp);
+ scaling_active = 0;
+
+ gui_saved_grab = gui_grab;
+ sdl_grab_start();
+ } else {
+ if (gui_saved_scaling) {
+ sdl_scale(gui_saved_width, gui_saved_height);
+ } else {
+ do_sdl_resize(width, height, 0);
+ }
+ if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
+ sdl_grab_end();
+ }
+ }
+ graphic_hw_invalidate(NULL);
+ graphic_hw_update(NULL);
+}
+
+static void handle_keydown(SDL_Event *ev)
+{
+ int mod_state;
+ int keycode;
+
+ if (alt_grab) {
+ mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
+ (gui_grab_code | KMOD_LSHIFT);
+ } else if (ctrl_grab) {
+ mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+ } else {
+ mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
+ }
+ gui_key_modifier_pressed = mod_state;
+
+ if (gui_key_modifier_pressed) {
+ keycode = sdl_keyevent_to_keycode(&ev->key);
+ switch (keycode) {
+ case 0x21: /* 'f' key on US keyboard */
+ toggle_full_screen();
+ gui_keysym = 1;
+ break;
+ case 0x16: /* 'u' key on US keyboard */
+ if (scaling_active) {
+ scaling_active = 0;
+ sdl_switch(dcl, NULL);
+ graphic_hw_invalidate(NULL);
+ graphic_hw_update(NULL);
+ }
+ gui_keysym = 1;
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ /* Reset the modifiers sent to the current console */
+ reset_keys();
+ console_select(keycode - 0x02);
+ gui_keysym = 1;
+ if (gui_fullscreen) {
+ break;
+ }
+ if (!qemu_console_is_graphic(NULL)) {
+ /* release grab if going to a text console */
+ if (gui_grab) {
+ sdl_grab_end();
+ } else if (absolute_enabled) {
+ sdl_show_cursor();
+ }
+ } else if (absolute_enabled) {
+ sdl_hide_cursor();
+ absolute_mouse_grab();
+ }
+ break;
+ case 0x1b: /* '+' */
+ case 0x35: /* '-' */
+ if (!gui_fullscreen) {
+ int scr_w, scr_h;
+ int width, height;
+ SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+
+ width = MAX(scr_w + (keycode == 0x1b ? 50 : -50),
+ 160);
+ height = (surface_height(surface) * width) /
+ surface_width(surface);
+
+ sdl_scale(width, height);
+ graphic_hw_invalidate(NULL);
+ graphic_hw_update(NULL);
+ gui_keysym = 1;
+ }
+ default:
+ break;
+ }
+ } else if (!qemu_console_is_graphic(NULL)) {
+ int keysym = ev->key.keysym.sym;
+
+ if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
+ switch (ev->key.keysym.sym) {
+ case SDLK_UP:
+ keysym = QEMU_KEY_CTRL_UP;
+ break;
+ case SDLK_DOWN:
+ keysym = QEMU_KEY_CTRL_DOWN;
+ break;
+ case SDLK_LEFT:
+ keysym = QEMU_KEY_CTRL_LEFT;
+ break;
+ case SDLK_RIGHT:
+ keysym = QEMU_KEY_CTRL_RIGHT;
+ break;
+ case SDLK_HOME:
+ keysym = QEMU_KEY_CTRL_HOME;
+ break;
+ case SDLK_END:
+ keysym = QEMU_KEY_CTRL_END;
+ break;
+ case SDLK_PAGEUP:
+ keysym = QEMU_KEY_CTRL_PAGEUP;
+ break;
+ case SDLK_PAGEDOWN:
+ keysym = QEMU_KEY_CTRL_PAGEDOWN;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (ev->key.keysym.sym) {
+ case SDLK_UP:
+ keysym = QEMU_KEY_UP;
+ break;
+ case SDLK_DOWN:
+ keysym = QEMU_KEY_DOWN;
+ break;
+ case SDLK_LEFT:
+ keysym = QEMU_KEY_LEFT;
+ break;
+ case SDLK_RIGHT:
+ keysym = QEMU_KEY_RIGHT;
+ break;
+ case SDLK_HOME:
+ keysym = QEMU_KEY_HOME;
+ break;
+ case SDLK_END:
+ keysym = QEMU_KEY_END;
+ break;
+ case SDLK_PAGEUP:
+ keysym = QEMU_KEY_PAGEUP;
+ break;
+ case SDLK_PAGEDOWN:
+ keysym = QEMU_KEY_PAGEDOWN;
+ break;
+ case SDLK_BACKSPACE:
+ keysym = QEMU_KEY_BACKSPACE;
+ break;
+ case SDLK_DELETE:
+ keysym = QEMU_KEY_DELETE;
+ break;
+ default:
+ break;
+ }
+ }
+ if (keysym) {
+ kbd_put_keysym(keysym);
+ }
+ }
+ if (qemu_console_is_graphic(NULL) && !gui_keysym) {
+ sdl_process_key(&ev->key);
+ }
+}
+
+static void handle_keyup(SDL_Event *ev)
+{
+ int mod_state;
+
+ if (!alt_grab) {
+ mod_state = (ev->key.keysym.mod & gui_grab_code);
+ } else {
+ mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
+ }
+ if (!mod_state && gui_key_modifier_pressed) {
+ gui_key_modifier_pressed = 0;
+ if (gui_keysym == 0) {
+ /* exit/enter grab if pressing Ctrl-Alt */
+ if (!gui_grab) {
+ if (qemu_console_is_graphic(NULL)) {
+ sdl_grab_start();
+ }
+ } else if (!gui_fullscreen) {
+ sdl_grab_end();
+ }
+ /* SDL does not send back all the modifiers key, so we must
+ * correct it. */
+ reset_keys();
+ return;
+ }
+ gui_keysym = 0;
+ }
+ if (qemu_console_is_graphic(NULL) && !gui_keysym) {
+ sdl_process_key(&ev->key);
+ }
+}
+
+static void handle_mousemotion(SDL_Event *ev)
+{
+ int max_x, max_y;
+
+ if (qemu_console_is_graphic(NULL) &&
+ (kbd_mouse_is_absolute() || absolute_enabled)) {
+ int scr_w, scr_h;
+ SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ max_x = scr_w - 1;
+ max_y = scr_h - 1;
+ if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
+ ev->motion.x == max_x || ev->motion.y == max_y)) {
+ sdl_grab_end();
+ }
+ if (!gui_grab &&
+ (ev->motion.x > 0 && ev->motion.x < max_x &&
+ ev->motion.y > 0 && ev->motion.y < max_y)) {
+ sdl_grab_start();
+ }
+ }
+ if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+ ev->motion.x, ev->motion.y, ev->motion.state);
+ }
+}
+
+static void handle_mousebutton(SDL_Event *ev)
+{
+ int buttonstate = SDL_GetMouseState(NULL, NULL);
+ SDL_MouseButtonEvent *bev;
+ int dz;
+
+ if (!qemu_console_is_graphic(NULL)) {
+ return;
+ }
+
+ bev = &ev->button;
+ if (!gui_grab && !kbd_mouse_is_absolute()) {
+ if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
+ /* start grabbing all events */
+ sdl_grab_start();
+ }
+ } else {
+ dz = 0;
+ if (ev->type == SDL_MOUSEBUTTONDOWN) {
+ buttonstate |= SDL_BUTTON(bev->button);
+ } else {
+ buttonstate &= ~SDL_BUTTON(bev->button);
+ }
+#ifdef SDL_BUTTON_WHEELUP
+ if (bev->button == SDL_BUTTON_WHEELUP &&
+ ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = -1;
+ } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
+ ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = 1;
+ }
+#endif
+ sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+ }
+}
+
+static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
+{
+ int w, h;
+
+ switch (ev->window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ sdl_scale(ev->window.data1, ev->window.data2);
+ graphic_hw_invalidate(NULL);
+ graphic_hw_update(NULL);
+ break;
+ case SDL_WINDOWEVENT_EXPOSED:
+ SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
+ sdl_update(dcl, 0, 0, w, h);
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ case SDL_WINDOWEVENT_ENTER:
+ if (!gui_grab && qemu_console_is_graphic(NULL) &&
+ (kbd_mouse_is_absolute() || absolute_enabled)) {
+ absolute_mouse_grab();
+ }
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ if (gui_grab && !gui_fullscreen)
+ sdl_grab_end();
+ break;
+ case SDL_WINDOWEVENT_RESTORED:
+ update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
+ break;
+ case SDL_WINDOWEVENT_MINIMIZED:
+ update_displaychangelistener(dcl, 500);
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ if (!no_quit) {
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+ }
+ break;
+ }
+}
+
+static void sdl_refresh(DisplayChangeListener *dcl)
+{
+ SDL_Event ev1, *ev = &ev1;
+
+ if (last_vm_running != runstate_is_running()) {
+ last_vm_running = runstate_is_running();
+ sdl_update_caption();
+ }
+
+ graphic_hw_update(NULL);
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_KEYDOWN:
+ handle_keydown(ev);
+ break;
+ case SDL_KEYUP:
+ handle_keyup(ev);
+ break;
+ case SDL_QUIT:
+ if (!no_quit) {
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ handle_mousemotion(ev);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ handle_mousebutton(ev);
+ break;
+ case SDL_WINDOWEVENT:
+ handle_windowevent(dcl, ev);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void sdl_mouse_warp(DisplayChangeListener *dcl,
+ int x, int y, int on)
+{
+ if (on) {
+ if (!guest_cursor)
+ sdl_show_cursor();
+ if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled) {
+ SDL_WarpMouseInWindow(real_window, x, y);
+ }
+ }
+ } else if (gui_grab)
+ sdl_hide_cursor();
+ guest_cursor = on;
+ guest_x = x, guest_y = y;
+}
+
+static void sdl_mouse_define(DisplayChangeListener *dcl,
+ QEMUCursor *c)
+{
+
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+
+ if (guest_sprite_surface)
+ SDL_FreeSurface(guest_sprite_surface);
+
+ guest_sprite_surface = SDL_CreateRGBSurfaceFrom(c->data,
+ c->width, c->height, 32, c->width * 4, 0xff0000,
+ 0x00ff00, 0xff, 0xff000000);
+
+ if (!guest_sprite_surface) {
+ fprintf(stderr, "Failed to make rgb surface from %p\n", c);
+ return;
+ }
+ guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
+ c->hot_x, c->hot_y);
+ if (!guest_sprite) {
+ fprintf(stderr, "Failed to make color cursor from %p\n", c);
+ return;
+ }
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+}
+
+static void sdl_cleanup(void)
+{
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+static const DisplayChangeListenerOps dcl_ops = {
+ .dpy_name = "sdl",
+ .dpy_gfx_update = sdl_update,
+ .dpy_gfx_switch = sdl_switch,
+ .dpy_refresh = sdl_refresh,
+ .dpy_mouse_set = sdl_mouse_warp,
+ .dpy_cursor_define = sdl_mouse_define,
+};
+
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
+{
+ int flags;
+ uint8_t data = 0;
+ char *filename;
+
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+
+ if (no_frame)
+ gui_noframe = 1;
+
+#ifdef __linux__
+ /* on Linux, SDL may use fbcon|directfb|svgalib when run without
+ * accessible $DISPLAY to open X11 window. This is often the case
+ * when qemu is run using sudo. But in this case, and when actually
+ * run in X11 environment, SDL fights with X11 for the video card,
+ * making current display unavailable, often until reboot.
+ * So make x11 the default SDL video driver if this variable is unset.
+ * This is a bit hackish but saves us from bigger problem.
+ * Maybe it's a good idea to fix this in SDL instead.
+ */
+ setenv("SDL_VIDEODRIVER", "x11", 0);
+#endif
+
+ flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+ if (SDL_Init (flags)) {
+ fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+ SDL_GetError());
+ exit(1);
+ }
+ /* Load a 32x32x4 image. White pixels are transparent. */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
+ if (filename) {
+ SDL_Surface *image = SDL_LoadBMP(filename);
+ if (image) {
+ uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
+ SDL_SetColorKey(image, SDL_TRUE, colorkey);
+ SDL_SetWindowIcon(real_window, image);
+ }
+ g_free(filename);
+ }
+
+ if (full_screen) {
+ gui_fullscreen = 1;
+ sdl_grab_start();
+ }
+
+ dcl = g_malloc0(sizeof(DisplayChangeListener));
+ dcl->ops = &dcl_ops;
+ register_displaychangelistener(dcl);
+
+ mouse_mode_notifier.notify = sdl_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
+
+
+ gui_grab = 0;
+
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ sdl_cursor_normal = SDL_GetCursor();
+
+ atexit(sdl_cleanup);
+}
+#endif
diff --git a/ui/sdl2_scancode_translate.h b/ui/sdl2_scancode_translate.h
new file mode 100644
index 0000000..1bd4953
--- /dev/null
+++ b/ui/sdl2_scancode_translate.h
@@ -0,0 +1,260 @@
+/* generated from a back translation of the SDL2 linux_scancodes file.
+ this translates SDL2 scancodes into Linux evdev keycodes,
+ these need to be translated again to go into qemu keys. */
+static uint8_t const sdl2_scancode_to_keycode[] = {
+235,
+0,
+0,
+0,
+30,
+48,
+46,
+32,
+18,
+33,
+34,
+35,
+23,
+36,
+37,
+38,
+50,
+49,
+24,
+25,
+16,
+19,
+31,
+20,
+22,
+47,
+17,
+45,
+21,
+44,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+28,
+1,
+14,
+15,
+57,
+12,
+13,
+26,
+27,
+43,
+0,
+39,
+40,
+41,
+51,
+52,
+53,
+58,
+59,
+60,
+61,
+62,
+63,
+64,
+65,
+66,
+67,
+68,
+87,
+88,
+0,
+70,
+119,
+110,
+102,
+104,
+111,
+107,
+109,
+106,
+105,
+108,
+103,
+69,
+98,
+55,
+74,
+78,
+96,
+79,
+80,
+81,
+75,
+76,
+77,
+71,
+72,
+73,
+82,
+83,
+86,
+0,
+116,
+117,
+183,
+184,
+185,
+186,
+187,
+188,
+189,
+190,
+191,
+192,
+193,
+194,
+0,
+138,
+139,
+0,
+128,
+129,
+131,
+137,
+133,
+135,
+136,
+113,
+115,
+114,
+0,
+0,
+0,
+121,
+0,
+89,
+93,
+124,
+92,
+95,
+0,
+0,
+0,
+0,
+122,
+123,
+90,
+91,
+0,
+0,
+0,
+0,
+0,
+221,
+99,
+222,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+179,
+180,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+118,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+29,
+42,
+56,
+125,
+97,
+54,
+100,
+126,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+};
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
index ee90480..599d9fc 100644
--- a/ui/sdl_keysym.h
+++ b/ui/sdl_keysym.h
@@ -200,6 +200,7 @@ static const name2keysym_t name2keysym[]={
{ "yacute", 0x0fd},
{ "thorn", 0x0fe},
{ "ydiaeresis", 0x0ff},
+#if SDL_MAJOR_VERSION == 1
{"EuroSign", SDLK_EURO},
/* modifiers */
@@ -272,6 +273,6 @@ static const name2keysym_t name2keysym[]={
{"Num_Lock", SDLK_NUMLOCK},
{"Pause", SDLK_PAUSE},
{"Escape", SDLK_ESCAPE},
-
+#endif
{NULL, 0},
};
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2) Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 11:04 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers Dave Airlie
` (5 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
These are to be used for the UI to signal the video display,
and vice-versa about changes in the state of a console, like
size and offsets in relation to other consoles for input handling.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
include/ui/console.h | 8 +++++++-
ui/console.c | 26 ++++++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/include/ui/console.h b/include/ui/console.h
index 98edf41..5731081 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -174,6 +174,9 @@ typedef struct DisplayChangeListenerOps {
int x, int y, int on);
void (*dpy_cursor_define)(DisplayChangeListener *dcl,
QEMUCursor *cursor);
+
+ void (*dpy_notify_state)(DisplayChangeListener *dcl,
+ int x, int y, uint32_t width, uint32_t height);
} DisplayChangeListenerOps;
struct DisplayChangeListener {
@@ -224,7 +227,8 @@ void dpy_text_resize(QemuConsole *con, int w, int h);
void dpy_mouse_set(QemuConsole *con, int x, int y, int on);
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor);
bool dpy_cursor_define_supported(QemuConsole *con);
-
+void dpy_notify_state(QemuConsole *con, int x, int y,
+ uint32_t width, uint32_t height);
static inline int surface_stride(DisplaySurface *s)
{
return pixman_image_get_stride(s->image);
@@ -275,6 +279,7 @@ typedef struct GraphicHwOps {
void (*gfx_update)(void *opaque);
void (*text_update)(void *opaque, console_ch_t *text);
void (*update_interval)(void *opaque, uint64_t interval);
+ void (*notify_state)(void *opaque, int idx, int x, int y, uint32_t width, uint32_t height);
} GraphicHwOps;
QemuConsole *graphic_console_init(DeviceState *dev,
@@ -284,6 +289,7 @@ QemuConsole *graphic_console_init(DeviceState *dev,
void graphic_hw_update(QemuConsole *con);
void graphic_hw_invalidate(QemuConsole *con);
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
+void graphic_hw_notify_state(QemuConsole *con, int x, int y, uint32_t width, uint32_t height);
QemuConsole *qemu_console_lookup_by_index(unsigned int index);
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev);
diff --git a/ui/console.c b/ui/console.c
index aad4fc9..c20e336 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -265,6 +265,16 @@ void graphic_hw_invalidate(QemuConsole *con)
}
}
+void graphic_hw_notify_state(QemuConsole *con, int x, int y, uint32_t width, uint32_t height)
+{
+ if (!con) {
+ con = active_console;
+ }
+ if (con && con->hw_ops->notify_state) {
+ con->hw_ops->notify_state(con->hw, con->index, x, y, width, height);
+ }
+}
+
static void ppm_save(const char *filename, struct DisplaySurface *ds,
Error **errp)
{
@@ -1562,6 +1572,22 @@ bool dpy_cursor_define_supported(QemuConsole *con)
return false;
}
+void dpy_notify_state(QemuConsole *con, int x, int y,
+ uint32_t width, uint32_t height)
+{
+ DisplayState *s = con->ds;
+ DisplayChangeListener *dcl;
+
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
+ if (dcl->ops->dpy_notify_state) {
+ dcl->ops->dpy_notify_state(dcl, x, y, width, height);
+ }
+ }
+}
+
/***********************************************************/
/* register display */
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2) Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 11:12 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 4/8] console: add ability to wrap a console Dave Airlie
` (4 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
We need to know how many graphics consoles are registered in the UI
code so it knows how many windows it should prepare for etc, also
so that it could potentially warn for cases it can't handle.
We also need to know the console index so we can add it to the list.
(maybe we don't).
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
include/ui/console.h | 3 +++
ui/console.c | 12 ++++++++++++
2 files changed, 15 insertions(+)
diff --git a/include/ui/console.h b/include/ui/console.h
index 5731081..be304fe 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -306,6 +306,9 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
DisplaySurface *qemu_console_surface(QemuConsole *con);
DisplayState *qemu_console_displaystate(QemuConsole *console);
+int qemu_get_console_index(QemuConsole *con);
+int qemu_get_number_graphical_consoles(void);
+
typedef CharDriverState *(VcHandler)(ChardevVC *vc);
CharDriverState *vc_init(ChardevVC *vc);
diff --git a/ui/console.c b/ui/console.c
index c20e336..4248a6f 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -175,6 +175,7 @@ static DisplayState *display_state;
static QemuConsole *active_console;
static QemuConsole *consoles[MAX_CONSOLES];
static int nb_consoles = 0;
+static int nb_graphics_consoles = 0;
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
static void dpy_refresh(DisplayState *s);
@@ -1247,6 +1248,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
s->index = i;
consoles[i] = s;
nb_consoles++;
+ nb_graphics_consoles++;
}
return s;
}
@@ -1873,6 +1875,16 @@ DisplayState *qemu_console_displaystate(QemuConsole *console)
return console->ds;
}
+int qemu_get_console_index(QemuConsole *console)
+{
+ return console->index;
+}
+
+int qemu_get_number_graphical_consoles(void)
+{
+ return nb_graphics_consoles;
+}
+
PixelFormat qemu_different_endianness_pixelformat(int bpp)
{
PixelFormat pf;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 4/8] console: add ability to wrap a console.
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
` (2 preceding siblings ...)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 5/8] sdl2: update for multihead support Dave Airlie
` (3 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
In order to implement virtio-vga on top of virtio-gpu we need
to be able to wrap the first console virtio-gpu registers from
inside virtio-vga which initialises after virtio-gpu. With this
interface virtio-vga can store the virtio-gpu interfaces, and
call them from its own ones.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
include/ui/console.h | 7 +++++++
ui/console.c | 13 +++++++++++++
2 files changed, 20 insertions(+)
diff --git a/include/ui/console.h b/include/ui/console.h
index be304fe..a143a0d 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -286,6 +286,13 @@ QemuConsole *graphic_console_init(DeviceState *dev,
const GraphicHwOps *ops,
void *opaque);
+void graphic_console_wrap(QemuConsole *con,
+ DeviceState *dev,
+ const GraphicHwOps *ops,
+ void *opaque,
+ const GraphicHwOps **orig_ops,
+ void **orig_opaque);
+
void graphic_hw_update(QemuConsole *con);
void graphic_hw_invalidate(QemuConsole *con);
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
diff --git a/ui/console.c b/ui/console.c
index 4248a6f..80e17e5 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1658,6 +1658,19 @@ QemuConsole *graphic_console_init(DeviceState *dev,
return s;
}
+void graphic_console_wrap(QemuConsole *con,
+ DeviceState *dev,
+ const GraphicHwOps *hw_ops,
+ void *opaque,
+ const GraphicHwOps **orig_ops,
+ void **orig_opaque)
+{
+ *orig_opaque = con->hw;
+ *orig_ops = con->hw_ops;
+ con->hw_ops = hw_ops;
+ con->hw = opaque;
+}
+
QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{
if (index >= MAX_CONSOLES) {
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 5/8] sdl2: update for multihead support.
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
` (3 preceding siblings ...)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 4/8] console: add ability to wrap a console Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code Dave Airlie
` (2 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
This reworks the complete SDL2 code to support multi-head,
by using DisplayChangeListeners wrapped inside a structure per-head,
containing the SDL2 information along with the console info.
This also adds a hack to allow Ctrl-Alt-n to toggle the first
console on/off.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
ui/sdl2.c | 322 ++++++++++++++++++++++++++++++++++++++++----------------------
1 file changed, 211 insertions(+), 111 deletions(-)
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 4ad7ce3..6f3a919 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -39,11 +39,18 @@
#include "sdl2_scancode_translate.h"
-static DisplayChangeListener *dcl;
-static DisplaySurface *surface;
-static SDL_Window *real_window;
-static SDL_Renderer *real_renderer;
-static SDL_Texture *guest_texture;
+#define SDL2_MAX_OUTPUT 4
+
+static struct sdl2_console_state {
+ DisplayChangeListener dcl;
+ DisplaySurface *surface;
+ SDL_Texture *texture;
+ SDL_Window *real_window;
+ SDL_Renderer *real_renderer;
+ int idx;
+ int last_vm_running; /* per console for caption reasons */
+ int x, y;
+} sdl2_console[SDL2_MAX_OUTPUT];
static SDL_Surface *guest_sprite_surface;
static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
@@ -67,70 +74,112 @@ static SDL_Cursor *guest_sprite = NULL;
static int scaling_active = 0;
static Notifier mouse_mode_notifier;
-static void sdl_update_caption(void);
+static void sdl_update_caption(struct sdl2_console_state *scon);
+
+static struct sdl2_console_state *get_scon_from_window(uint32_t window_id)
+{
+ int i;
+ for (i = 0; i < SDL2_MAX_OUTPUT; i++) {
+ if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id))
+ return &sdl2_console[i];
+ }
+ return NULL;
+}
static void sdl_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- if (!surface)
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
+ SDL_Rect rect;
+ DisplaySurface *surf = qemu_console_surface(dcl->con);
+
+ if (!surf)
+ return;
+ if (!scon->texture)
return;
- SDL_UpdateTexture(guest_texture, NULL, surface_data(surface),
- surface_stride(surface));
- SDL_RenderCopy(real_renderer, guest_texture, NULL, NULL);
- SDL_RenderPresent(real_renderer);
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
+ surface_stride(surf));
+ SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
+ SDL_RenderPresent(scon->real_renderer);
}
-static void do_sdl_resize(int width, int height, int bpp)
+static void do_sdl_resize(struct sdl2_console_state *scon, int width, int height, int bpp)
{
int flags;
- if (real_window) {
- SDL_SetWindowSize(real_window, width, height);
- SDL_RenderSetLogicalSize(real_renderer, width, height);
+ if (scon->real_window && scon->real_renderer) {
+ if (width && height) {
+ SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
+
+ SDL_SetWindowSize(scon->real_window, width, height);
+ } else {
+ SDL_DestroyRenderer(scon->real_renderer);
+ SDL_DestroyWindow(scon->real_window);
+ scon->real_renderer = NULL;
+ scon->real_window = NULL;
+ }
} else {
+ if (!width || !height) {
+ return;
+ }
flags = 0;
if (gui_fullscreen)
flags |= SDL_WINDOW_FULLSCREEN;
else
flags |= SDL_WINDOW_RESIZABLE;
- real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED,
- width, height, flags);
- real_renderer = SDL_CreateRenderer(real_window, -1, 0);
- sdl_update_caption();
+ scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width, height, flags);
+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
+ sdl_update_caption(scon);
}
}
static void sdl_switch(DisplayChangeListener *dcl,
DisplaySurface *new_surface)
{
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
int format = 0;
+ int idx = qemu_get_console_index(dcl->con);
+ DisplaySurface *old_surface = scon->surface;
+
/* temporary hack: allows to call sdl_switch to handle scaling changes */
if (new_surface) {
- surface = new_surface;
+ scon->surface = new_surface;
}
- if (!scaling_active) {
- do_sdl_resize(surface_width(surface), surface_height(surface), 0);
- } else
- do_sdl_resize(surface_width(surface), surface_height(surface), 0);
+ if (!new_surface && idx > 0)
+ scon->surface = NULL;
- if (guest_texture)
- SDL_DestroyTexture(guest_texture);
+ if (new_surface == NULL)
+ do_sdl_resize(scon, 0, 0, 0);
+ else
+ do_sdl_resize(scon, surface_width(scon->surface), surface_height(scon->surface), 0);
- if (surface_bits_per_pixel(surface) == 16)
- format = SDL_PIXELFORMAT_RGB565;
- else if (surface_bits_per_pixel(surface) == 32)
- format = SDL_PIXELFORMAT_ARGB8888;
+ if (old_surface && scon->texture) {
+ SDL_DestroyTexture(scon->texture);
+ scon->texture = NULL;
+ }
- if (!format)
- exit(1);
- guest_texture = SDL_CreateTexture(real_renderer, format,
- SDL_TEXTUREACCESS_STREAMING,
- surface_width(surface), surface_height(surface));
- SDL_RenderSetLogicalSize(real_renderer, surface_width(surface), surface_height(surface));
+ if (new_surface) {
+ if (!scon->texture) {
+ if (surface_bits_per_pixel(scon->surface) == 16)
+ format = SDL_PIXELFORMAT_RGB565;
+ else if (surface_bits_per_pixel(scon->surface) == 32)
+ format = SDL_PIXELFORMAT_ARGB8888;
+
+ scon->texture = SDL_CreateTexture(scon->real_renderer, format,
+ SDL_TEXTUREACCESS_STREAMING,
+ surface_width(new_surface), surface_height(new_surface));
+ }
+ }
}
/* generic keyboard conversion */
@@ -250,7 +299,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
}
-static void sdl_update_caption(void)
+static void sdl_update_caption(struct sdl2_console_state *scon)
{
char win_title[1024];
char icon_title[1024];
@@ -268,15 +317,15 @@ static void sdl_update_caption(void)
}
if (qemu_name) {
- snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
+ snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, scon->idx, status);
snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
} else {
snprintf(win_title, sizeof(win_title), "QEMU%s", status);
snprintf(icon_title, sizeof(icon_title), "QEMU");
}
- if (real_window)
- SDL_SetWindowTitle(real_window, win_title);
+ if (scon->real_window)
+ SDL_SetWindowTitle(scon->real_window, win_title);
}
static void sdl_hide_cursor(void)
@@ -300,52 +349,52 @@ static void sdl_show_cursor(void)
if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) {
SDL_ShowCursor(1);
if (guest_cursor &&
- (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
SDL_SetCursor(guest_sprite);
else
SDL_SetCursor(sdl_cursor_normal);
}
}
-static void sdl_grab_start(void)
+static void sdl_grab_start(struct sdl2_console_state *scon)
{
/*
* If the application is not active, do not try to enter grab state. This
* prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
* application (SDL bug).
*/
- if (!(SDL_GetWindowFlags(real_window) & SDL_WINDOW_INPUT_FOCUS)) {
+ if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
return;
}
if (guest_cursor) {
SDL_SetCursor(guest_sprite);
if (!kbd_mouse_is_absolute() && !absolute_enabled) {
- SDL_WarpMouseInWindow(real_window, guest_x, guest_y);
+ SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
}
} else
sdl_hide_cursor();
- SDL_SetWindowGrab(real_window, SDL_TRUE);
+ SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
gui_grab = 1;
- sdl_update_caption();
+ sdl_update_caption(scon);
}
-static void sdl_grab_end(void)
+static void sdl_grab_end(struct sdl2_console_state *scon)
{
- SDL_SetWindowGrab(real_window, SDL_FALSE);
+ SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
gui_grab = 0;
sdl_show_cursor();
- sdl_update_caption();
+ sdl_update_caption(scon);
}
-static void absolute_mouse_grab(void)
+static void absolute_mouse_grab(struct sdl2_console_state *scon)
{
int mouse_x, mouse_y;
int scr_w, scr_h;
SDL_GetMouseState(&mouse_x, &mouse_y);
- SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
if (mouse_x > 0 && mouse_x < scr_w - 1 &&
mouse_y > 0 && mouse_y < scr_h - 1) {
- sdl_grab_start();
+ sdl_grab_start(scon);
}
}
@@ -355,18 +404,18 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
if (!absolute_enabled) {
absolute_enabled = 1;
if (qemu_console_is_graphic(NULL)) {
- absolute_mouse_grab();
+ absolute_mouse_grab(&sdl2_console[0]);
}
}
} else if (absolute_enabled) {
if (!gui_fullscreen) {
- sdl_grab_end();
+ sdl_grab_end(&sdl2_console[0]);
}
absolute_enabled = 0;
}
}
-static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+static void sdl_send_mouse_event(struct sdl2_console_state *scon, int dx, int dy, int dz, int x, int y, int state)
{
int buttons = 0;
@@ -382,9 +431,30 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state
if (kbd_mouse_is_absolute()) {
int scr_w, scr_h;
- SDL_GetWindowSize(real_window, &scr_w, &scr_h);
- dx = x * 0x7FFF / (scr_w - 1);
- dy = y * 0x7FFF / (scr_h - 1);
+ int max_w = 0, max_h = 0;
+ int off_x = 0, off_y = 0;
+ int cur_off_x = 0, cur_off_y = 0;
+ int i;
+
+ for (i = 0; i < SDL2_MAX_OUTPUT; i++) {
+ struct sdl2_console_state *thiscon = &sdl2_console[i];
+ if (thiscon->real_window && thiscon->surface) {
+ SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
+ cur_off_x = thiscon->x;
+ cur_off_y = thiscon->y;
+ if (scr_w + cur_off_x > max_w)
+ max_w = scr_w + cur_off_x;
+ if (scr_h + cur_off_y > max_h)
+ max_h = scr_h + cur_off_y;
+ if (i == scon->idx) {
+ off_x = cur_off_x;
+ off_y = cur_off_y;
+ }
+ }
+ }
+
+ dx = (off_x + x) * 0x7FFF / (max_w - 1);
+ dy = (off_y + y) * 0x7FFF / (max_h - 1);
} else if (guest_cursor) {
x -= guest_x;
y -= guest_y;
@@ -397,51 +467,52 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state
kbd_mouse_event(dx, dy, dz, buttons);
}
-static void sdl_scale(int width, int height)
+static void sdl_scale(struct sdl2_console_state *scon, int width, int height)
{
int bpp = 0;
- do_sdl_resize(width, height, bpp);
+ do_sdl_resize(scon, width, height, bpp);
scaling_active = 1;
}
-static void toggle_full_screen(void)
+static void toggle_full_screen(struct sdl2_console_state *scon)
{
- int width = surface_width(surface);
- int height = surface_height(surface);
- int bpp = surface_bits_per_pixel(surface);
+ int width = surface_width(scon->surface);
+ int height = surface_height(scon->surface);
+ int bpp = surface_bits_per_pixel(scon->surface);
gui_fullscreen = !gui_fullscreen;
if (gui_fullscreen) {
- SDL_GetWindowSize(real_window, &gui_saved_width, &gui_saved_height);
+ SDL_GetWindowSize(scon->real_window, &gui_saved_width, &gui_saved_height);
gui_saved_scaling = scaling_active;
- do_sdl_resize(width, height, bpp);
+ do_sdl_resize(scon, width, height, bpp);
scaling_active = 0;
gui_saved_grab = gui_grab;
- sdl_grab_start();
+ sdl_grab_start(scon);
} else {
if (gui_saved_scaling) {
- sdl_scale(gui_saved_width, gui_saved_height);
+ sdl_scale(scon, gui_saved_width, gui_saved_height);
} else {
- do_sdl_resize(width, height, 0);
+ do_sdl_resize(scon, width, height, 0);
}
if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
- sdl_grab_end();
+ sdl_grab_end(scon);
}
}
- graphic_hw_invalidate(NULL);
- graphic_hw_update(NULL);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
}
static void handle_keydown(SDL_Event *ev)
{
int mod_state;
int keycode;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
if (alt_grab) {
mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
- (gui_grab_code | KMOD_LSHIFT);
+ (gui_grab_code | KMOD_LSHIFT);
} else if (ctrl_grab) {
mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
} else {
@@ -452,16 +523,23 @@ static void handle_keydown(SDL_Event *ev)
if (gui_key_modifier_pressed) {
keycode = sdl_keyevent_to_keycode(&ev->key);
switch (keycode) {
+ case 0x31:
+ /* spawn a new connected monitor if we have one */
+ if (sdl2_console[1].surface)
+ graphic_hw_notify_state(sdl2_console[1].dcl.con, 0, 0, 0, 0);
+ else
+ graphic_hw_notify_state(sdl2_console[1].dcl.con, 0, 0, 1024, 768);
+ break;
case 0x21: /* 'f' key on US keyboard */
- toggle_full_screen();
+ toggle_full_screen(scon);
gui_keysym = 1;
break;
case 0x16: /* 'u' key on US keyboard */
if (scaling_active) {
scaling_active = 0;
- sdl_switch(dcl, NULL);
- graphic_hw_invalidate(NULL);
- graphic_hw_update(NULL);
+ sdl_switch(&scon->dcl, NULL);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
}
gui_keysym = 1;
break;
@@ -476,13 +554,13 @@ static void handle_keydown(SDL_Event *ev)
if (!qemu_console_is_graphic(NULL)) {
/* release grab if going to a text console */
if (gui_grab) {
- sdl_grab_end();
+ sdl_grab_end(scon);
} else if (absolute_enabled) {
sdl_show_cursor();
}
} else if (absolute_enabled) {
sdl_hide_cursor();
- absolute_mouse_grab();
+ absolute_mouse_grab(scon);
}
break;
case 0x1b: /* '+' */
@@ -490,14 +568,14 @@ static void handle_keydown(SDL_Event *ev)
if (!gui_fullscreen) {
int scr_w, scr_h;
int width, height;
- SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
width = MAX(scr_w + (keycode == 0x1b ? 50 : -50),
- 160);
- height = (surface_height(surface) * width) /
- surface_width(surface);
+ 160);
+ height = (surface_height(scon->surface) * width) /
+ surface_width(scon->surface);
- sdl_scale(width, height);
+ sdl_scale(scon, width, height);
graphic_hw_invalidate(NULL);
graphic_hw_update(NULL);
gui_keysym = 1;
@@ -585,6 +663,7 @@ static void handle_keydown(SDL_Event *ev)
static void handle_keyup(SDL_Event *ev)
{
int mod_state;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
if (!alt_grab) {
mod_state = (ev->key.keysym.mod & gui_grab_code);
@@ -597,10 +676,10 @@ static void handle_keyup(SDL_Event *ev)
/* exit/enter grab if pressing Ctrl-Alt */
if (!gui_grab) {
if (qemu_console_is_graphic(NULL)) {
- sdl_grab_start();
+ sdl_grab_start(scon);
}
} else if (!gui_fullscreen) {
- sdl_grab_end();
+ sdl_grab_end(scon);
}
/* SDL does not send back all the modifiers key, so we must
* correct it. */
@@ -617,25 +696,26 @@ static void handle_keyup(SDL_Event *ev)
static void handle_mousemotion(SDL_Event *ev)
{
int max_x, max_y;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
if (qemu_console_is_graphic(NULL) &&
(kbd_mouse_is_absolute() || absolute_enabled)) {
int scr_w, scr_h;
- SDL_GetWindowSize(real_window, &scr_w, &scr_h);
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
max_x = scr_w - 1;
max_y = scr_h - 1;
if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
- ev->motion.x == max_x || ev->motion.y == max_y)) {
- sdl_grab_end();
+ ev->motion.x == max_x || ev->motion.y == max_y)) {
+ sdl_grab_end(scon);
}
if (!gui_grab &&
(ev->motion.x > 0 && ev->motion.x < max_x &&
- ev->motion.y > 0 && ev->motion.y < max_y)) {
- sdl_grab_start();
+ ev->motion.y > 0 && ev->motion.y < max_y)) {
+ sdl_grab_start(scon);
}
}
if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
- sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+ sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0,
ev->motion.x, ev->motion.y, ev->motion.state);
}
}
@@ -644,6 +724,7 @@ static void handle_mousebutton(SDL_Event *ev)
{
int buttonstate = SDL_GetMouseState(NULL, NULL);
SDL_MouseButtonEvent *bev;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
int dz;
if (!qemu_console_is_graphic(NULL)) {
@@ -654,7 +735,7 @@ static void handle_mousebutton(SDL_Event *ev)
if (!gui_grab && !kbd_mouse_is_absolute()) {
if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
/* start grabbing all events */
- sdl_grab_start();
+ sdl_grab_start(scon);
}
} else {
dz = 0;
@@ -672,19 +753,20 @@ static void handle_mousebutton(SDL_Event *ev)
dz = 1;
}
#endif
- sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+ sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate);
}
}
static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
{
int w, h;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
switch (ev->window.event) {
case SDL_WINDOWEVENT_RESIZED:
- sdl_scale(ev->window.data1, ev->window.data2);
- graphic_hw_invalidate(NULL);
- graphic_hw_update(NULL);
+ sdl_scale(scon, ev->window.data1, ev->window.data2);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
break;
case SDL_WINDOWEVENT_EXPOSED:
SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
@@ -694,12 +776,12 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
case SDL_WINDOWEVENT_ENTER:
if (!gui_grab && qemu_console_is_graphic(NULL) &&
(kbd_mouse_is_absolute() || absolute_enabled)) {
- absolute_mouse_grab();
+ absolute_mouse_grab(scon);
}
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (gui_grab && !gui_fullscreen)
- sdl_grab_end();
+ sdl_grab_end(scon);
break;
case SDL_WINDOWEVENT_RESTORED:
update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
@@ -718,14 +800,15 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
static void sdl_refresh(DisplayChangeListener *dcl)
{
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
SDL_Event ev1, *ev = &ev1;
- if (last_vm_running != runstate_is_running()) {
- last_vm_running = runstate_is_running();
- sdl_update_caption();
+ if (scon->last_vm_running != runstate_is_running()) {
+ scon->last_vm_running = runstate_is_running();
+ sdl_update_caption(scon);
}
- graphic_hw_update(NULL);
+ graphic_hw_update(dcl->con);
while (SDL_PollEvent(ev)) {
switch (ev->type) {
@@ -760,13 +843,14 @@ static void sdl_refresh(DisplayChangeListener *dcl)
static void sdl_mouse_warp(DisplayChangeListener *dcl,
int x, int y, int on)
{
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
if (on) {
if (!guest_cursor)
sdl_show_cursor();
if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
SDL_SetCursor(guest_sprite);
if (!kbd_mouse_is_absolute() && !absolute_enabled) {
- SDL_WarpMouseInWindow(real_window, x, y);
+ SDL_WarpMouseInWindow(scon->real_window, x, y);
}
}
} else if (gui_grab)
@@ -804,6 +888,15 @@ static void sdl_mouse_define(DisplayChangeListener *dcl,
SDL_SetCursor(guest_sprite);
}
+static void sdl_notify_state(DisplayChangeListener *dcl,
+ int x, int y, uint32_t width, uint32_t height)
+{
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
+
+ scon->x = x;
+ scon->y = y;
+}
+
static void sdl_cleanup(void)
{
if (guest_sprite)
@@ -818,6 +911,7 @@ static const DisplayChangeListenerOps dcl_ops = {
.dpy_refresh = sdl_refresh,
.dpy_mouse_set = sdl_mouse_warp,
.dpy_cursor_define = sdl_mouse_define,
+ .dpy_notify_state = sdl_notify_state,
};
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
@@ -825,7 +919,8 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
int flags;
uint8_t data = 0;
char *filename;
-
+ int nconsoles;
+ int i;
#if defined(__APPLE__)
/* always use generic keymaps */
if (!keyboard_layout)
@@ -866,19 +961,24 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
if (image) {
uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
SDL_SetColorKey(image, SDL_TRUE, colorkey);
- SDL_SetWindowIcon(real_window, image);
+ SDL_SetWindowIcon(sdl2_console[0].real_window, image);
}
g_free(filename);
}
if (full_screen) {
gui_fullscreen = 1;
- sdl_grab_start();
+ sdl_grab_start(0);
}
- dcl = g_malloc0(sizeof(DisplayChangeListener));
- dcl->ops = &dcl_ops;
- register_displaychangelistener(dcl);
+ nconsoles = qemu_get_number_graphical_consoles();
+
+ for (i = 0; i < nconsoles; i++) {
+ sdl2_console[i].dcl.ops = &dcl_ops;
+ sdl2_console[i].dcl.con = qemu_console_lookup_by_index(i);
+ register_displaychangelistener(&sdl2_console[i].dcl);
+ sdl2_console[i].idx = i;
+ }
mouse_mode_notifier.notify = sdl_mouse_mode_change;
qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code.
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
` (4 preceding siblings ...)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 5/8] sdl2: update for multihead support Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 11:26 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt Dave Airlie
7 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
This is the basic virtio-gpu which is
multi-head capable,
ARGB cursor support,
unaccelerated.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
default-configs/x86_64-softmmu.mak | 1 +
hw/display/Makefile.objs | 2 +
hw/display/virtgpu_hw.h | 225 ++++++++++++++
hw/display/virtio-gpu.c | 606 +++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 49 +++
hw/virtio/virtio-pci.h | 15 +
include/hw/pci/pci.h | 1 +
include/hw/virtio/virtio-gpu.h | 90 ++++++
8 files changed, 989 insertions(+)
create mode 100644 hw/display/virtgpu_hw.h
create mode 100644 hw/display/virtio-gpu.c
create mode 100644 include/hw/virtio/virtio-gpu.h
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 31bddce..1a00b78 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -9,6 +9,7 @@ CONFIG_VGA_PCI=y
CONFIG_VGA_ISA=y
CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
+CONFIG_VIRTIO_GPU=y
CONFIG_VMMOUSE=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 540df82..10e4066 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -32,3 +32,5 @@ obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
+
+obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
diff --git a/hw/display/virtgpu_hw.h b/hw/display/virtgpu_hw.h
new file mode 100644
index 0000000..81223de
--- /dev/null
+++ b/hw/display/virtgpu_hw.h
@@ -0,0 +1,225 @@
+#ifndef VIRTGPU_HW_H
+#define VIRTGPU_HW_H
+
+#define VIRTGPU_CMD_HAS_RESP (1 << 31)
+#define VIRTGPU_CMD_3D_ONLY (1 << 30)
+enum virtgpu_ctrl_cmd {
+ VIRTGPU_CMD_NOP,
+ VIRTGPU_CMD_GET_DISPLAY_INFO = (1 | VIRTGPU_CMD_HAS_RESP),
+ VIRTGPU_CMD_GET_CAPS = (2 | VIRTGPU_CMD_HAS_RESP),
+ VIRTGPU_CMD_RESOURCE_CREATE_2D = 3,
+ VIRTGPU_CMD_RESOURCE_UNREF = 4,
+ VIRTGPU_CMD_SET_SCANOUT = 5,
+ VIRTGPU_CMD_RESOURCE_FLUSH = 6,
+ VIRTGPU_CMD_TRANSFER_SEND_2D = 7,
+ VIRTGPU_CMD_RESOURCE_ATTACH_BACKING = 8,
+ VIRTGPU_CMD_RESOURCE_INVAL_BACKING = 9,
+
+ VIRTGPU_CMD_CTX_CREATE = (10 | VIRTGPU_CMD_3D_ONLY),
+ VIRTGPU_CMD_CTX_DESTROY = (11 | VIRTGPU_CMD_3D_ONLY),
+ VIRTGPU_CMD_CTX_ATTACH_RESOURCE = (12 | VIRTGPU_CMD_3D_ONLY),
+ VIRTGPU_CMD_CTX_DETACH_RESOURCE = (13 | VIRTGPU_CMD_3D_ONLY),
+
+ VIRTGPU_CMD_RESOURCE_CREATE_3D = (14 | VIRTGPU_CMD_3D_ONLY),
+
+ VIRTGPU_CMD_TRANSFER_SEND_3D = (15 | VIRTGPU_CMD_3D_ONLY),
+ VIRTGPU_CMD_TRANSFER_RECV_3D = (16 | VIRTGPU_CMD_3D_ONLY),
+
+ VIRTGPU_CMD_SUBMIT_3D = (17 | VIRTGPU_CMD_3D_ONLY),
+};
+
+enum virtgpu_ctrl_event {
+ VIRTGPU_EVENT_NOP,
+ VIRTGPU_EVENT_ERROR,
+ VIRTGPU_EVENT_DISPLAY_CHANGE,
+};
+
+/* data passed in the cursor vq */
+struct virtgpu_hw_cursor_page {
+ uint32_t cursor_x, cursor_y;
+ uint32_t cursor_hot_x, cursor_hot_y;
+ uint32_t cursor_id;
+};
+
+struct virtgpu_resource_unref {
+ uint32_t resource_id;
+};
+
+/* create a simple 2d resource with a format */
+struct virtgpu_resource_create_2d {
+ uint32_t resource_id;
+ uint32_t format;
+ uint32_t width;
+ uint32_t height;
+};
+
+struct virtgpu_set_scanout {
+ uint32_t scanout_id;
+ uint32_t resource_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t x;
+ uint32_t y;
+};
+
+struct virtgpu_resource_flush {
+ uint32_t resource_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t x;
+ uint32_t y;
+};
+
+/* simple transfer send */
+struct virtgpu_transfer_send_2d {
+ uint32_t resource_id;
+ uint32_t offset;
+ uint32_t width;
+ uint32_t height;
+ uint32_t x;
+ uint32_t y;
+};
+
+struct virtgpu_mem_entry {
+ uint64_t addr;
+ uint32_t length;
+ uint32_t pad;
+};
+
+struct virtgpu_resource_attach_backing {
+ uint32_t resource_id;
+ uint32_t nr_entries;
+};
+
+struct virtgpu_resource_inval_backing {
+ uint32_t resource_id;
+};
+
+#define VIRTGPU_MAX_SCANOUTS 16
+struct virtgpu_display_info {
+ uint32_t num_scanouts;
+ struct {
+ uint32_t enabled;
+ uint32_t width;
+ uint32_t height;
+ uint32_t x;
+ uint32_t y;
+ uint32_t flags;
+ } pmodes[VIRTGPU_MAX_SCANOUTS];
+};
+
+
+/* 3d related */
+struct virtgpu_box {
+ uint32_t x, y, z;
+ uint32_t w, h, d;
+};
+
+struct virtgpu_transfer_send_3d {
+ uint64_t data;
+ uint32_t resource_id;
+ uint32_t level;
+ struct virtgpu_box box;
+ uint32_t stride;
+ uint32_t layer_stride;
+ uint32_t ctx_id;
+};
+
+struct virtgpu_transfer_recv_3d {
+ uint64_t data;
+ uint32_t resource_id;
+ uint32_t level;
+ struct virtgpu_box box;
+ uint32_t stride;
+ uint32_t layer_stride;
+ uint32_t ctx_id;
+};
+
+#define VIRTGPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
+struct virtgpu_resource_create_3d {
+ uint32_t resource_id;
+ uint32_t target;
+ uint32_t format;
+ uint32_t bind;
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+ uint32_t array_size;
+ uint32_t last_level;
+ uint32_t nr_samples;
+ uint32_t flags;
+};
+
+struct virtgpu_ctx_create {
+ uint32_t ctx_id;
+ uint32_t nlen;
+ char debug_name[64];
+};
+
+struct virtgpu_ctx_destroy {
+ uint32_t ctx_id;
+};
+
+struct virtgpu_ctx_resource {
+ uint32_t resource_id;
+ uint32_t ctx_id;
+};
+
+struct virtgpu_cmd_submit {
+ uint64_t phy_addr;
+ uint32_t size;
+ uint32_t ctx_id;
+};
+
+struct virtgpu_command {
+ uint32_t type;
+ uint32_t flags;
+ uint64_t rsvd;
+ union virtgpu_cmds {
+ struct virtgpu_resource_create_2d resource_create_2d;
+ struct virtgpu_resource_unref resource_unref;
+ struct virtgpu_resource_flush resource_flush;
+ struct virtgpu_set_scanout set_scanout;
+ struct virtgpu_transfer_send_2d transfer_send_2d;
+ struct virtgpu_resource_attach_backing resource_attach_backing;
+ struct virtgpu_resource_inval_backing resource_inval_backing;
+
+ struct virtgpu_cmd_submit cmd_submit;
+ struct virtgpu_ctx_create ctx_create;
+ struct virtgpu_ctx_destroy ctx_destroy;
+ struct virtgpu_ctx_resource ctx_resource;
+ struct virtgpu_resource_create_3d resource_create_3d;
+ struct virtgpu_transfer_send_3d transfer_send_3d;
+ struct virtgpu_transfer_recv_3d transfer_recv_3d;
+ } u;
+};
+
+struct virtgpu_response {
+ uint32_t type;
+ uint32_t flags;
+ union virtgpu_resps {
+ struct virtgpu_display_info display_info;
+ } u;
+};
+
+struct virtgpu_event {
+ uint32_t type;
+ uint32_t err_code;
+ union virtgpu_events {
+ struct virtgpu_display_info display_info;
+ } u;
+};
+
+/* simple formats for fbcon/X use */
+enum virtgpu_formats {
+ VIRGL_FORMAT_B8G8R8A8_UNORM = 1,
+ VIRGL_FORMAT_B8G8R8X8_UNORM = 2,
+ VIRGL_FORMAT_A8R8G8B8_UNORM = 3,
+ VIRGL_FORMAT_X8R8G8B8_UNORM = 4,
+
+ VIRGL_FORMAT_B5G5R5A1_UNORM = 5,
+
+ VIRGL_FORMAT_R8_UNORM = 64,
+};
+
+#endif
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
new file mode 100644
index 0000000..392672d
--- /dev/null
+++ b/hw/display/virtio-gpu.c
@@ -0,0 +1,606 @@
+/*
+ * Virtio video device
+ *
+ * Copyright Red Hat
+ *
+ * Authors:
+ * Dave Airlie
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/iov.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-bus.h"
+
+#include "virtgpu_hw.h"
+
+static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
+ uint32_t resource_id);
+
+static void update_cursor_data(VirtIOGPU *g, struct virtgpu_simple_resource *res)
+{
+ uint32_t pixels;
+ int i;
+ pixels = g->current_cursor->width * g->current_cursor->height;
+ for (i = 0; i < pixels; i++) {
+ uint32_t value = ((uint32_t *)pixman_image_get_data(res->image))[i];
+ g->current_cursor->data[i] = value;
+ }
+ dpy_cursor_define(g->con[0], g->current_cursor);
+}
+
+static void update_cursor(VirtIOGPU *g, struct virtgpu_hw_cursor_page *cursor)
+{
+ if (g->current_cursor_id != cursor->cursor_id) {
+ if (cursor->cursor_id > 0) {
+ struct virtgpu_simple_resource *res;
+
+ res = virtgpu_find_resource(g, cursor->cursor_id);
+ if (res) {
+ if (!g->current_cursor)
+ g->current_cursor = cursor_alloc(res->width, res->height);
+ g->current_cursor->hot_x = cursor->cursor_hot_x;
+ g->current_cursor->hot_y = cursor->cursor_hot_y;
+
+ update_cursor_data(g, res);
+ }
+ }
+ g->current_cursor_id = cursor->cursor_id;
+ }
+
+ dpy_mouse_set(g->con[0], cursor->cursor_x, cursor->cursor_y, cursor->cursor_id ? 1 : 0);
+}
+
+static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+
+}
+
+static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+}
+
+static uint32_t virtio_gpu_get_features(VirtIODevice *vdev, uint32_t features)
+{
+ return features;
+}
+
+static void virtio_gpu_set_features(VirtIODevice *vdev, uint32_t features)
+{
+
+}
+
+static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
+ uint32_t resource_id)
+{
+ struct virtgpu_simple_resource *res;
+
+ QLIST_FOREACH(res, &g->reslist, next) {
+ if (res->resource_id == resource_id)
+ return res;
+ }
+ return NULL;
+}
+
+static void virtgpu_fill_display_info(VirtIOGPU *g,
+ struct virtgpu_display_info *dpy_info)
+{
+ int i;
+ memset(dpy_info, 0, sizeof(*dpy_info));
+ dpy_info->num_scanouts = g->conf.max_outputs;
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (g->enabled_output_bitmask & (1 << i)) {
+ dpy_info->pmodes[i].enabled = 1;
+ dpy_info->pmodes[i].width = g->req_state[i].width;
+ dpy_info->pmodes[i].height = g->req_state[i].height;
+ }
+ }
+}
+
+static void virtgpu_get_display_info(VirtIOGPU *g,
+ struct iovec *iov,
+ unsigned int iov_cnt)
+{
+ struct virtgpu_display_info dpy_info;
+
+ virtgpu_fill_display_info(g, &dpy_info);
+
+ iov_from_buf(iov, iov_cnt, 0, &dpy_info, sizeof(dpy_info));
+}
+
+static pixman_format_code_t get_pixman_format(uint32_t virtgpu_format)
+{
+ switch (virtgpu_format) {
+ case VIRGL_FORMAT_B8G8R8X8_UNORM:
+ return PIXMAN_x8r8g8b8;
+ case VIRGL_FORMAT_B8G8R8A8_UNORM:
+ return PIXMAN_a8r8g8b8;
+ default:
+ assert(0);
+ break;
+ }
+ return 0;
+}
+
+static void virtgpu_resource_create_2d(VirtIOGPU *g,
+ struct virtgpu_resource_create_2d *c2d)
+{
+ pixman_format_code_t pformat;
+ struct virtgpu_simple_resource *res;
+
+ res = calloc(1, sizeof(struct virtgpu_simple_resource));
+ if (!res)
+ return;
+
+ res->width = c2d->width;
+ res->height = c2d->height;
+ res->format = c2d->format;
+ res->resource_id = c2d->resource_id;
+
+ pformat = get_pixman_format(c2d->format);
+ res->image = pixman_image_create_bits(pformat,
+ c2d->width,
+ c2d->height,
+ NULL, 0);
+
+ QLIST_INSERT_HEAD(&g->reslist, res, next);
+}
+
+static void virtgpu_resource_unref(VirtIOGPU *g,
+ uint32_t resource_id)
+{
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, resource_id);
+
+ if (!res)
+ return;
+
+ pixman_image_unref(res->image);
+ QLIST_REMOVE(res, next);
+ free(res);
+}
+
+static void virtgpu_transfer_send_2d(VirtIOGPU *g,
+ struct virtgpu_transfer_send_2d *t2d)
+{
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, t2d->resource_id);
+ int h;
+ uint32_t src_offset, dst_offset, stride;
+ int bpp;
+ pixman_format_code_t format;
+ if (!res)
+ return;
+
+ if (!res->iov)
+ return;
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ stride = pixman_image_get_stride(res->image);
+
+ if (t2d->offset || t2d->x || t2d->y || t2d->width != pixman_image_get_width(res->image)) {
+ for (h = 0; h < t2d->height; h++) {
+ src_offset = t2d->offset + stride * h;
+ dst_offset = (t2d->y + h) * stride + (t2d->x * bpp);
+
+ iov_to_buf(res->iov, res->iov_cnt, src_offset, (uint8_t *)pixman_image_get_data(res->image) + dst_offset, t2d->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));
+ }
+}
+
+static void virtgpu_resource_flush(VirtIOGPU *g,
+ struct virtgpu_resource_flush *rf)
+{
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, rf->resource_id);
+ int i;
+ pixman_region16_t flush_region;
+
+ pixman_region_init_rect(&flush_region,
+ rf->x, rf->y, rf->width, rf->height);
+ for (i = 0; i < VIRTGPU_MAX_SCANOUT; i++) {
+ struct virtgpu_scanout *scanout;
+ pixman_region16_t region, finalregion;
+ pixman_box16_t *extents;
+
+ if (!(res->scanout_bitmask & (1 << i)))
+ continue;
+ scanout = &g->scanout[i];
+
+ pixman_region_init(&finalregion);
+ pixman_region_init_rect(®ion,
+ scanout->x, scanout->y, scanout->width, scanout->height);
+
+ pixman_region_intersect(&finalregion, &flush_region, ®ion);
+ pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
+ extents = pixman_region_extents(&finalregion);
+ /* work out the area we need to update for each console */
+ dpy_gfx_update(g->con[i], extents->x1, extents->y1, extents->x2 - extents->x1,
+ extents->y2 - extents->y1);
+
+ pixman_region_fini(®ion);
+ pixman_region_fini(&finalregion);
+ }
+ pixman_region_fini(&flush_region);
+}
+
+static void virtgpu_set_scanout(VirtIOGPU *g,
+ struct virtgpu_set_scanout *ss)
+{
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, ss->resource_id);
+ uint32_t offset;
+ int bpp;
+ pixman_format_code_t format;
+
+ g->enable = 1;
+ if (ss->resource_id == 0) {
+ if (g->scanout[ss->scanout_id].resource_id) {
+ res = virtgpu_find_resource(g, g->scanout[ss->scanout_id].resource_id);
+ if (res)
+ res->scanout_bitmask &= ~(1 << ss->scanout_id);
+ }
+ if (ss->scanout_id == 0)
+ return;
+ dpy_gfx_replace_surface(g->con[ss->scanout_id], NULL);
+ g->scanout[ss->scanout_id].ds = NULL;
+ g->scanout[ss->scanout_id].width = 0;
+ g->scanout[ss->scanout_id].height = 0;
+
+ dpy_notify_state(g->con[ss->scanout_id], 0, 0, 0, 0);
+ return;
+ }
+ /* create a surface for this scanout */
+
+ if (ss->scanout_id > 4) {
+ fprintf(stderr,"set scanout for non-0 surface %d\n", ss->scanout_id);
+ return;
+ }
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ offset = (ss->x * bpp) + ss->y * pixman_image_get_stride(res->image);
+
+ dpy_notify_state(g->con[ss->scanout_id], ss->x, ss->y, ss->width, ss->height);
+ if (!g->scanout[ss->scanout_id].ds ||
+ surface_data(g->scanout[ss->scanout_id].ds) != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
+ g->scanout[ss->scanout_id].width != ss->width ||
+ g->scanout[ss->scanout_id].height != ss->height) {
+ /* realloc the surface ptr */
+ g->scanout[ss->scanout_id].ds = qemu_create_displaysurface_from(ss->width, ss->height, 32, pixman_image_get_stride(res->image), (uint8_t *)pixman_image_get_data(res->image) + offset, FALSE);
+ if (!g->scanout[ss->scanout_id].ds)
+ return;
+
+ dpy_gfx_replace_surface(g->con[ss->scanout_id], g->scanout[ss->scanout_id].ds);
+ }
+
+ res->scanout_bitmask |= (1 << ss->scanout_id);
+
+
+ g->scanout[ss->scanout_id].resource_id = ss->resource_id;
+ g->scanout[ss->scanout_id].x = ss->x;
+ g->scanout[ss->scanout_id].y = ss->y;
+ g->scanout[ss->scanout_id].width = ss->width;
+ g->scanout[ss->scanout_id].height = ss->height;
+}
+
+static void virtgpu_resource_attach_backing(VirtIOGPU *g,
+ struct virtgpu_resource_attach_backing *att_rb,
+ struct iovec *iov,
+ unsigned int iov_cnt)
+{
+ uint32_t gsize = iov_size(iov, iov_cnt);
+ struct iovec *res_iovs;
+ int i;
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, att_rb->resource_id);
+ void *data;
+ if (!res)
+ return;
+
+ res_iovs = malloc(att_rb->nr_entries * sizeof(struct iovec));
+ if (!res_iovs)
+ return;
+
+ if (iov_cnt > 1) {
+ data = malloc(gsize);
+ iov_to_buf(iov, iov_cnt, 0, data, gsize);
+ } else
+ data = iov[0].iov_base;
+
+ for (i = 0; i < att_rb->nr_entries; i++) {
+ struct virtgpu_mem_entry *ent = ((struct virtgpu_mem_entry *)data) + i;
+ hwaddr len;
+ res_iovs[i].iov_len = ent->length;
+
+ len = ent->length;
+ res_iovs[i].iov_base = cpu_physical_memory_map(ent->addr, &len, 1);
+ if (!res_iovs[i].iov_base || len != ent->length) {
+ fprintf(stderr, "virtgp: trying to map MMIO memory");
+ exit(1);
+ }
+ }
+
+ res->iov = res_iovs;
+ res->iov_cnt = att_rb->nr_entries;
+
+ if (iov_cnt > 1)
+ free(data);
+}
+
+static void virtgpu_resource_inval_backing(VirtIOGPU *g,
+ uint32_t resource_id)
+{
+ int i;
+ struct virtgpu_simple_resource *res = virtgpu_find_resource(g, resource_id);
+
+ if (!res)
+ return;
+ if (!res->iov)
+ return;
+
+ for (i = 0; i < res->iov_cnt; i++) {
+ cpu_physical_memory_unmap(res->iov[i].iov_base, res->iov[i].iov_len, 1, res->iov[i].iov_len);
+ }
+ free(res->iov);
+ res->iov_cnt = 0;
+ res->iov = NULL;
+}
+
+static void virtio_gpu_process_cmd(VirtIOGPU *g,
+ struct virtgpu_command *cmd,
+ struct iovec *iov,
+ unsigned int iov_cnt)
+{
+ switch (cmd->type) {
+ case VIRTGPU_CMD_GET_DISPLAY_INFO:
+ if (iov_cnt < 1)
+ return;
+ virtgpu_get_display_info(g, iov, iov_cnt);
+ break;
+ case VIRTGPU_CMD_RESOURCE_CREATE_2D:
+ virtgpu_resource_create_2d(g, &cmd->u.resource_create_2d);
+ break;
+ case VIRTGPU_CMD_RESOURCE_UNREF:
+ virtgpu_resource_unref(g, cmd->u.resource_unref.resource_id);
+ break;
+ case VIRTGPU_CMD_RESOURCE_FLUSH:
+ virtgpu_resource_flush(g, &cmd->u.resource_flush);
+ break;
+ case VIRTGPU_CMD_TRANSFER_SEND_2D:
+ virtgpu_transfer_send_2d(g, &cmd->u.transfer_send_2d);
+ break;
+ case VIRTGPU_CMD_SET_SCANOUT:
+ virtgpu_set_scanout(g, &cmd->u.set_scanout);
+ break;
+ case VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
+ virtgpu_resource_attach_backing(g, &cmd->u.resource_attach_backing, iov, iov_cnt);
+ break;
+ case VIRTGPU_CMD_RESOURCE_INVAL_BACKING:
+ virtgpu_resource_inval_backing(g, cmd->u.resource_inval_backing.resource_id);
+ break;
+ case VIRTGPU_CMD_GET_CAPS:
+ case VIRTGPU_CMD_NOP:
+ break;
+ default:
+ fprintf(stderr,"got bad command from host %d\n", cmd->type);
+ break;
+ }
+}
+
+static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->ctrl_bh);
+}
+
+static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->cursor_bh);
+}
+
+static void virtio_gpu_handle_event_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+}
+
+
+static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct iovec *iov;
+ VirtQueueElement elem;
+ struct virtgpu_command cmd;
+ size_t s;
+ unsigned int iov_cnt;
+
+ if (!virtio_queue_ready(vq))
+ return;
+ while (virtqueue_pop(vq, &elem)) {
+
+ iov = elem.out_sg;
+ iov_cnt = elem.out_num;
+
+ s = iov_to_buf(iov, iov_cnt, 0, &cmd, sizeof(cmd));
+ if (s != sizeof(cmd))
+ fprintf(stderr,"error\n");
+ else {
+ if (elem.in_num > 0)
+ virtio_gpu_process_cmd(g, &cmd, elem.in_sg, elem.in_num);
+ else
+ virtio_gpu_process_cmd(g, &cmd, &iov[1], iov_cnt - 1);
+ }
+ virtqueue_push(vq, &elem, 0);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void virtio_gpu_ctrl_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
+}
+
+static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ VirtQueueElement elem;
+ size_t s;
+ struct virtgpu_hw_cursor_page cursor_info;
+
+ if (!virtio_queue_ready(vq))
+ return;
+ while (virtqueue_pop(vq, &elem)) {
+
+ s = iov_to_buf(elem.out_sg, elem.out_num, 0, &cursor_info, sizeof(cursor_info));
+ if (s != sizeof(cursor_info))
+ fprintf(stderr,"error\n");
+ else
+ update_cursor(g, &cursor_info);
+ virtqueue_push(vq, &elem, 0);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void virtio_gpu_cursor_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
+}
+
+static int virtio_gpu_send_event(VirtIOGPU *g, struct virtgpu_event *event)
+{
+ VirtQueueElement elem;
+
+ if (!virtio_queue_ready(g->event_vq))
+ return -1;
+
+ if (!virtqueue_pop(g->event_vq, &elem))
+ return -1;
+
+ iov_from_buf(elem.in_sg, elem.in_num, 0, event,
+ sizeof(struct virtgpu_event));
+
+ virtqueue_push(g->event_vq, &elem, sizeof(struct virtgpu_event));
+ virtio_notify(&g->parent_obj, g->event_vq);
+ return 0;
+}
+
+static void virtio_gpu_invalidate_display(void *opaque)
+{
+}
+
+static void virtio_gpu_update_display(void *opaque)
+{
+}
+
+static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static void virtio_gpu_notify_state(void *opaque, int idx, int x, int y, uint32_t width, uint32_t height)
+{
+ VirtIOGPU *g = opaque;
+ struct virtgpu_event event;
+
+ if (idx > g->conf.max_outputs)
+ return;
+
+ g->req_state[idx].x = x;
+ g->req_state[idx].y = y;
+ g->req_state[idx].width = width;
+ g->req_state[idx].height = height;
+
+ if (width && height)
+ g->enabled_output_bitmask |= (1 << idx);
+ else
+ g->enabled_output_bitmask &= ~(1 << idx);
+
+ /* send event to guest */
+ event.type = VIRTGPU_EVENT_DISPLAY_CHANGE;
+ event.err_code = 0;
+ virtgpu_fill_display_info(g, &event.u.display_info);
+ virtio_gpu_send_event(g, &event);
+}
+
+static const GraphicHwOps virtio_gpu_ops = {
+ .invalidate = virtio_gpu_invalidate_display,
+ .gfx_update = virtio_gpu_update_display,
+ .text_update = virtio_gpu_text_update,
+ .notify_state = virtio_gpu_notify_state,
+};
+
+static int virtio_gpu_device_init(VirtIODevice *vdev)
+{
+ DeviceState *qdev = DEVICE(vdev);
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ int i;
+
+ g->config_size = 0;
+ virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
+ g->config_size);
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (i == 0) {
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+ }
+ g->con[i] = graphic_console_init(DEVICE(vdev), &virtio_gpu_ops, g);
+ if (i > 0)
+ dpy_gfx_replace_surface(g->con[i], NULL);
+ }
+ g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_cursor_cb);
+
+ g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
+ g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
+
+ g->event_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_event_cb);
+
+ g->enabled_output_bitmask = 1;
+ g->qdev = qdev;
+ return 0;
+}
+
+static void virtio_gpu_instance_init(Object *obj)
+{
+
+}
+
+static Property virtio_gpu_properties[] = {
+ DEFINE_VIRTIO_GPU_FEATURES(VirtIOGPU, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ vdc->init = virtio_gpu_device_init;
+ vdc->get_config = virtio_gpu_get_config;
+ vdc->set_config = virtio_gpu_set_config;
+ vdc->get_features = virtio_gpu_get_features;
+ vdc->set_features = virtio_gpu_set_features;
+
+ // dc->reset = virtio_gpu_reset_handler;
+ dc->props = virtio_gpu_properties;
+}
+
+static const TypeInfo virtio_gpu_info = {
+ .name = TYPE_VIRTIO_GPU,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOGPU),
+ .instance_init = virtio_gpu_instance_init,
+ .class_init = virtio_gpu_class_init,
+};
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_gpu_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 7647be8..4b0e0e0 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1502,6 +1502,54 @@ static const TypeInfo virtio_rng_pci_info = {
.class_init = virtio_rng_pci_class_init,
};
+static Property virtio_gpu_pci_properties[] = {
+ DEFINE_VIRTIO_GPU_PCI_FEATURES(VirtIOPCIProxy),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static int virtio_gpu_pci_init(VirtIOPCIProxy *vpci_dev)
+{
+ VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vgpu->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ if (qdev_init(vdev) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_gpu_pci_properties;
+ k->init = virtio_gpu_pci_init;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
+}
+
+static void virtio_gpu_initfn(Object *obj)
+{
+ VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_gpu_pci_info = {
+ .name = TYPE_VIRTIO_GPU_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOGPUPCI),
+ .instance_init = virtio_gpu_initfn,
+ .class_init = virtio_gpu_pci_class_init,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1558,6 +1606,7 @@ static void virtio_pci_register_types(void)
#ifdef CONFIG_VHOST_SCSI
type_register_static(&vhost_scsi_pci_info);
#endif
+ type_register_static(&virtio_gpu_pci_info);
}
type_init(virtio_pci_register_types)
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 917bcc5..856046d 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -24,6 +24,7 @@
#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-9p.h"
+#include "hw/virtio/virtio-gpu.h"
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
#endif
@@ -39,6 +40,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
+typedef struct VirtIOGPUPCI VirtIOGPUPCI;
/* virtio-pci-bus */
@@ -200,6 +202,19 @@ struct VirtIORngPCI {
VirtIORNG vdev;
};
+/*
+ * virtio-gpu-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj) \
+ OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+};
+
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index b783e68..fee5d6b 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -80,6 +80,7 @@
#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005
#define PCI_DEVICE_ID_VIRTIO_9P 0x1009
+#define PCI_DEVICE_ID_VIRTIO_GPU 0x1010
#define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
new file mode 100644
index 0000000..c757457
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu.h
@@ -0,0 +1,90 @@
+#ifndef _QEMU_VIRTIO_VGA_H
+#define _QEMU_VIRTIO_VGA_H
+
+#include "qemu/queue.h"
+#include "ui/qemu-pixman.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio.h"
+#include "hw/pci/pci.h"
+
+#define TYPE_VIRTIO_GPU "virtio-gpu-device"
+#define VIRTIO_GPU(obj) \
+ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
+
+#define VIRTIO_ID_GPU 16
+
+#define VIRTGPU_MAX_RES 16
+
+#define VIRTGPU_MAX_SCANOUT 4
+
+struct virtgpu_simple_resource {
+ uint32_t resource_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ struct iovec *iov;
+ unsigned int iov_cnt;
+ uint32_t scanout_bitmask;
+ pixman_image_t *image;
+ QLIST_ENTRY(virtgpu_simple_resource) next;
+};
+
+struct virtgpu_scanout {
+ DisplaySurface *ds;
+ uint32_t width, height;
+ int x, y;
+ int invalidate;
+ uint32_t resource_id;
+};
+
+struct virtgpu_requested_state {
+ uint32_t width, height;
+ int x, y;
+};
+
+struct virtio_gpu_conf {
+ uint32_t max_outputs;
+};
+
+typedef struct VirtIOGPU {
+ VirtIODevice parent_obj;
+
+ /* qemu console for this GPU */
+ QemuConsole *con[VIRTGPU_MAX_SCANOUT];
+
+ QEMUBH *ctrl_bh;
+ QEMUBH *cursor_bh;
+ VirtQueue *ctrl_vq;
+ VirtQueue *cursor_vq;
+ VirtQueue *event_vq;
+
+ int enable;
+
+ uint32_t current_cursor_id;
+ uint32_t current_cursor_hotspot_info;
+ uint32_t current_cursor_x, current_cursor_y;
+
+ int config_size;
+ DeviceState *qdev;
+
+ QLIST_HEAD(, virtgpu_simple_resource) reslist;
+
+ struct virtgpu_scanout scanout[VIRTGPU_MAX_SCANOUT];
+ struct virtgpu_requested_state req_state[VIRTGPU_MAX_SCANOUT];
+ QEMUCursor *current_cursor;
+
+ struct virtio_gpu_conf conf;
+ int enabled_output_bitmask;
+} VirtIOGPU;
+
+/* to share between PCI and VGA */
+#define DEFINE_VIRTIO_GPU_PCI_FEATURES(_state) \
+ DEFINE_PROP_BIT("ioeventfd", _state, flags, \
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \
+ DEFINE_PROP_UINT32("vectors", _state, nvectors, 4)
+
+#define DEFINE_VIRTIO_GPU_FEATURES(_state, _conf_field) \
+ DEFINE_PROP_UINT32("max_outputs", _state, _conf_field.max_outputs, 2)
+
+#endif
+
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
` (5 preceding siblings ...)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
2013-11-20 12:02 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt Dave Airlie
7 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
This is a virtio-vga device built on top of the virtio-gpu device.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
Makefile | 2 +-
default-configs/x86_64-softmmu.mak | 1 +
hw/display/Makefile.objs | 1 +
hw/display/virtio-vga.c | 156 +++++++++++++++++++++++++++++++++++++
hw/pci/pci.c | 2 +
include/sysemu/sysemu.h | 2 +-
pc-bios/vgabios-virtio.bin | Bin 0 -> 40448 bytes
roms/Makefile | 2 +-
roms/config.vga.virtio | 6 ++
vl.c | 13 ++++
10 files changed, 182 insertions(+), 3 deletions(-)
create mode 100644 hw/display/virtio-vga.c
create mode 100644 pc-bios/vgabios-virtio.bin
create mode 100644 roms/config.vga.virtio
diff --git a/Makefile b/Makefile
index b15003f..093ff9a 100644
--- a/Makefile
+++ b/Makefile
@@ -284,7 +284,7 @@ bepo
ifdef INSTALL_BLOBS
BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
-vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
+vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
acpi-dsdt.aml q35-acpi-dsdt.aml \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 1a00b78..22d8587 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -10,6 +10,7 @@ CONFIG_VGA_ISA=y
CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
CONFIG_VIRTIO_GPU=y
+CONFIG_VIRTIO_VGA=y
CONFIG_VMMOUSE=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 10e4066..63427e9 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -34,3 +34,4 @@ obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
new file mode 100644
index 0000000..a6c9438
--- /dev/null
+++ b/hw/display/virtio-vga.c
@@ -0,0 +1,156 @@
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/virtio/virtio-pci.h"
+
+/*
+ * virtio-vga: This extends VirtIOGPUPCI
+ */
+#define TYPE_VIRTIO_VGA "virtio-vga"
+#define VIRTIO_VGA(obj) \
+ OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA VirtIOVGA;
+struct VirtIOVGA {
+ struct VirtIOGPUPCI gpu;
+ VGACommonState vga;
+ const struct GraphicHwOps *wrapped_ops;
+ void *wrapped_opaque;
+};
+
+static void virtio_vga_invalidate_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ vvga->vga.hw_ops->invalidate(&vvga->vga);
+ return;
+ }
+
+ vvga->wrapped_ops->invalidate(vvga->wrapped_opaque);
+}
+
+static void virtio_vga_update_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ vvga->vga.hw_ops->gfx_update(&vvga->vga);
+ }
+ vvga->wrapped_ops->gfx_update(vvga->wrapped_opaque);
+}
+
+static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ if (vvga->vga.hw_ops->text_update)
+ vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
+ }
+ vvga->wrapped_ops->text_update(vvga->wrapped_opaque, chardata);
+}
+
+static void virtio_vga_notify_state(void *opaque, int idx, int x, int y, uint32_t width, uint32_t height)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ if (vvga->vga.hw_ops->notify_state)
+ vvga->vga.hw_ops->notify_state(&vvga->vga, idx, x, y, width, height);
+ }
+ vvga->wrapped_ops->notify_state(vvga->wrapped_opaque, idx, x, y, width, height);
+}
+
+static const GraphicHwOps virtio_vga_ops = {
+ .invalidate = virtio_vga_invalidate_display,
+ .gfx_update = virtio_vga_update_display,
+ .text_update = virtio_vga_text_update,
+ .notify_state = virtio_vga_notify_state,
+};
+
+/* VGA device wrapper around PCI device around virtio GPU */
+static int virtio_vga_init(VirtIOPCIProxy *vpci_dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
+ DeviceState *vdev = DEVICE(&vvga->gpu.vdev);
+ VGACommonState *vga = &vvga->vga;
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ if (qdev_init(vdev) < 0) {
+ return -1;
+ }
+
+ graphic_console_wrap(vvga->gpu.vdev.con[0], DEVICE(vpci_dev), &virtio_vga_ops, vvga, &vvga->wrapped_ops, &vvga->wrapped_opaque);
+ vga->con = vvga->gpu.vdev.con[0];
+
+ vga_common_init(vga, OBJECT(vpci_dev));
+ vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true);
+
+ pci_register_bar(&vpci_dev->pci_dev, 2, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
+
+ if (!vpci_dev->pci_dev.rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev));
+ }
+
+ return 0;
+}
+
+static void virtio_vga_reset(DeviceState *dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(dev);
+ vvga->gpu.vdev.enable = 0;
+
+ vga_dirty_log_start(&vvga->vga);
+}
+
+static Property virtio_vga_properties[] = {
+ DEFINE_VIRTIO_GPU_FEATURES(VirtIOVGA, gpu.vdev.conf),
+ DEFINE_VIRTIO_GPU_PCI_FEATURES(VirtIOPCIProxy),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_UINT32("vgamem_mb", VirtIOVGA, vga.vram_size_mb, 16),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_vga_properties;
+ dc->reset = virtio_vga_reset;
+
+ k->init = virtio_vga_init;
+ pcidev_k->no_hotplug = 1;
+ pcidev_k->romfile = "vgabios-virtio.bin";
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
+}
+
+static void virtio_vga_inst_initfn(Object *obj)
+{
+ VirtIOVGA *dev = VIRTIO_VGA(obj);
+ object_initialize(&dev->gpu.vdev, sizeof(dev->gpu.vdev), TYPE_VIRTIO_GPU);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->gpu.vdev), NULL);
+}
+
+static TypeInfo virtio_vga_info = {
+ .name = TYPE_VIRTIO_VGA,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(struct VirtIOVGA),
+ .instance_init = virtio_vga_inst_initfn,
+ .class_init = virtio_vga_class_init,
+};
+
+static void virgl_register_types(void)
+{
+ type_register_static(&virtio_vga_info);
+}
+
+type_init(virgl_register_types)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index a98c8a0..32eb43f 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1673,6 +1673,8 @@ PCIDevice *pci_vga_init(PCIBus *bus)
return pci_create_simple(bus, -1, "VGA");
case VGA_VMWARE:
return pci_create_simple(bus, -1, "vmware-svga");
+ case VGA_VIRTIO:
+ return pci_create_simple(bus, -1, "virtio-vga");
case VGA_NONE:
default: /* Other non-PCI types. Checking for unsupported types is already
done in vl.c. */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index cd5791e..3523bc9 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -103,7 +103,7 @@ typedef enum DisplayType
extern int autostart;
typedef enum {
- VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
+ VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, VGA_VIRTIO
} VGAInterfaceType;
extern int vga_interface_type;
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
new file mode 100644
index 0000000000000000000000000000000000000000..2b70d272f2704e7839712a61613844321b93b465
GIT binary patch
literal 40448
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
AD*ylh
literal 0
HcmV?d00001
diff --git a/roms/Makefile b/roms/Makefile
index 10d5a65..882ac32 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -1,5 +1,5 @@
-vgabios_variants := stdvga cirrus vmware qxl isavga
+vgabios_variants := stdvga cirrus vmware qxl isavga virtio
vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants)))
pxerom_variants := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio
pxerom_targets := 8086100e 80861209 10500940 10222000 10ec8139 1af41000
diff --git a/roms/config.vga.virtio b/roms/config.vga.virtio
new file mode 100644
index 0000000..9a78983
--- /dev/null
+++ b/roms/config.vga.virtio
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1af4
+CONFIG_VGA_DID=0x1010
diff --git a/vl.c b/vl.c
index 4ad15b8..de5c40b 100644
--- a/vl.c
+++ b/vl.c
@@ -301,6 +301,7 @@ static struct {
{ .driver = "isa-cirrus-vga", .flag = &default_vga },
{ .driver = "vmware-svga", .flag = &default_vga },
{ .driver = "qxl-vga", .flag = &default_vga },
+ { .driver = "virtio-vga", .flag = &default_vga },
};
static QemuOptsList qemu_rtc_opts = {
@@ -2080,6 +2081,11 @@ static bool qxl_vga_available(void)
return object_class_by_name("qxl-vga");
}
+static bool virtio_vga_available(void)
+{
+ return object_class_by_name("virtio-vga");
+}
+
static void select_vgahw (const char *p)
{
const char *opts;
@@ -2106,6 +2112,13 @@ static void select_vgahw (const char *p)
fprintf(stderr, "Error: VMWare SVGA not available\n");
exit(0);
}
+ } else if (strstart(p, "virtio", &opts)) {
+ if (virtio_vga_available()) {
+ vga_interface_type = VGA_VIRTIO;
+ } else {
+ fprintf(stderr, "Error: Virtio VGA not available\n");
+ exit(0);
+ }
} else if (strstart(p, "xenfb", &opts)) {
vga_interface_type = VGA_XENFB;
} else if (strstart(p, "qxl", &opts)) {
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
` (6 preceding siblings ...)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
@ 2013-11-20 5:52 ` Dave Airlie
7 siblings, 0 replies; 23+ messages in thread
From: Dave Airlie @ 2013-11-20 5:52 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
---
hw/display/vga-pci.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index b3a45c8..e4bea17 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -146,6 +146,7 @@ static int pci_std_vga_initfn(PCIDevice *dev)
PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
VGACommonState *s = &d->vga;
+ return 0;
/* vga + console init */
vga_common_init(s, OBJECT(dev));
vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev),
@@ -195,7 +196,7 @@ static void vga_class_init(ObjectClass *klass, void *data)
k->romfile = "vgabios-stdvga.bin";
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = PCI_DEVICE_ID_QEMU_VGA;
- k->class_id = PCI_CLASS_DISPLAY_VGA;
+ k->class_id = 0;// PCI_CLASS_DISPLAY_VGA;
dc->vmsd = &vmstate_vga_pci;
dc->props = vga_pci_properties;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2)
2013-11-20 5:52 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2) Dave Airlie
@ 2013-11-20 10:52 ` Gerd Hoffmann
0 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-20 10:52 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> I've ported the SDL1.2 code over, and rewritten it to use the SDL2 interface.
Looks sane on a quick glance. Didn't look in detail yet. I would have
suggested to put stuff into structs instead of using global variables,
but I see you do that in patch #5. I think you can squash the two into
one.
> The biggest changes were in the input handling, where SDL2 has done a major
> overhaul, and I've had to include a generated translation file to get from
> SDL2 codes back to qemu compatible ones. I'm still not sure how the keyboard
> layout code works in qemu, so there may be further work if someone can point
> me a test case that works with SDL1.2 and doesn't with SDL2.
Can you describe what is has changed from sdl 1.2 -> 2.0?
Ideally qemu wants a scancode which it can feed into keyboard emulation.
How that'll get mapped into keysyms is guests bussiness.
When qemu can't get a scancode it will try to translate the keysym back
to a scancode, using a keymap (this is what the -k switch is for).
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display
2013-11-20 5:52 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
@ 2013-11-20 11:04 ` Gerd Hoffmann
0 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-20 11:04 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> These are to be used for the UI to signal the video display,
> and vice-versa about changes in the state of a console, like
> size and offsets in relation to other consoles for input handling.
See other mail from today morning. I think this is input layer's job,
not ui code job. Storing the info in QemuConsole is fine. Having a
QemuHeadInfo struct for it is probably useful, so the input layer can
query it easily, say using a to-be-written qemu_console_get_head_info()
function which returns a ptr to the struct.
Notification should also go to the input layer not ui code. Or maybe
use a notifier list (see include/qemu/notify.h) so anyone interested can
listen, ui code might want to know too (not for input processing, but
maybe to arrange window on the host).
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers
2013-11-20 5:52 ` [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers Dave Airlie
@ 2013-11-20 11:12 ` Gerd Hoffmann
0 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-20 11:12 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
Hi,
> +int qemu_get_console_index(QemuConsole *con);
Makes sense to have that. There are other retrival wrappers already
(qemu_console_is_*). I'd like to see the new one placed next to the
others, and follow the name convention (i.e. use
qemu_console_get_index).
> +int qemu_get_number_graphical_consoles(void);
Hmm, what is bad with qemu_console_is_graphic? Patch #5 uses it to loop
over the gfx consoles. As the code doesn't need to know the number of
consoles in advance you can do this instead (like spice):
for (i = 0;; i++) {
con = qemu_console_lookup_by_index(i);
if (!con || !qemu_console_is_graphic(con)) {
break;
}
[ ... ]
}
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code.
2013-11-20 5:52 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code Dave Airlie
@ 2013-11-20 11:26 ` Gerd Hoffmann
0 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-20 11:26 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> This is the basic virtio-gpu which is
>
> multi-head capable,
> ARGB cursor support,
> unaccelerated.
I'd like to see an overview on the design of the virtual hardware.
What is the purpose of the virtio commands?
What are the steps a guest is supposed to do to get something displayed
on the screen?
> + res = calloc(1, sizeof(struct virtgpu_simple_resource));
> + if (!res)
> + return;
qemu uses glib memory allocation functions (i.e. g_malloc0 for this
one). Also no need to check for NULL.
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-20 5:52 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
@ 2013-11-20 12:02 ` Gerd Hoffmann
2013-11-21 3:12 ` Dave Airlie
0 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-20 12:02 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> This is a virtio-vga device built on top of the virtio-gpu device.
Ah, I see what you use the wrapping for. Hmm. I think you should use a
common base class instead, i.e. something like virtio-gpu-base which
holds all the common stuff. Both virtio-gpu and virtio-vga can use that
as TypeInfo->parent then. This way virtio-vga doesn't have to muck with
virtio-gpu internals. virtio-gpu-base can be tagged as abstract class
(using .abstract = true) so it will not be instantiated directly.
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-20 12:02 ` Gerd Hoffmann
@ 2013-11-21 3:12 ` Dave Airlie
2013-11-21 6:17 ` Paolo Bonzini
2013-11-21 11:06 ` Gerd Hoffmann
0 siblings, 2 replies; 23+ messages in thread
From: Dave Airlie @ 2013-11-21 3:12 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Wed, Nov 20, 2013 at 10:02 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
>> From: Dave Airlie <airlied@redhat.com>
>>
>> This is a virtio-vga device built on top of the virtio-gpu device.
>
> Ah, I see what you use the wrapping for. Hmm. I think you should use a
> common base class instead, i.e. something like virtio-gpu-base which
> holds all the common stuff. Both virtio-gpu and virtio-vga can use that
> as TypeInfo->parent then. This way virtio-vga doesn't have to muck with
> virtio-gpu internals. virtio-gpu-base can be tagged as abstract class
> (using .abstract = true) so it will not be instantiated directly.
>
I'm not sure what that buys me here, I need the virtio-vga to attach
the vga ops the first console that the virtio-gpu registers, it can't
be a separate console, and since virtio-gpu initialises before
virtio-vga I can't tell it to not register the console.
Its no use attaching just the vga or just the gpu ops to the console I
need a wrapper and I can't see how having a common base class would
help. I already have a base class, that pci subclasses then vga
subclasses that.
Dave.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-21 3:12 ` Dave Airlie
@ 2013-11-21 6:17 ` Paolo Bonzini
2013-11-21 11:06 ` Gerd Hoffmann
1 sibling, 0 replies; 23+ messages in thread
From: Paolo Bonzini @ 2013-11-21 6:17 UTC (permalink / raw)
To: Dave Airlie; +Cc: Gerd Hoffmann, qemu-devel@nongnu.org
Il 21/11/2013 04:12, Dave Airlie ha scritto:
> I'm not sure what that buys me here, I need the virtio-vga to attach
> the vga ops the first console that the virtio-gpu registers, it can't
> be a separate console, and since virtio-gpu initialises before
> virtio-vga I can't tell it to not register the console.
If you make a base class, the ops can be registered in the base class
and they just call a method in the base class. The default
implementation of the method can be virtio-gpu's, but virtio-vga can
override them and make them go to either the virtio-gpu implementation
or the VGA implementation.
Paolo
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-21 3:12 ` Dave Airlie
2013-11-21 6:17 ` Paolo Bonzini
@ 2013-11-21 11:06 ` Gerd Hoffmann
2013-12-06 5:24 ` Dave Airlie
1 sibling, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2013-11-21 11:06 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel@nongnu.org
On Do, 2013-11-21 at 13:12 +1000, Dave Airlie wrote:
> On Wed, Nov 20, 2013 at 10:02 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
> >> From: Dave Airlie <airlied@redhat.com>
> >>
> >> This is a virtio-vga device built on top of the virtio-gpu device.
> >
> > Ah, I see what you use the wrapping for. Hmm. I think you should use a
> > common base class instead, i.e. something like virtio-gpu-base which
> > holds all the common stuff. Both virtio-gpu and virtio-vga can use that
> > as TypeInfo->parent then. This way virtio-vga doesn't have to muck with
> > virtio-gpu internals. virtio-gpu-base can be tagged as abstract class
> > (using .abstract = true) so it will not be instantiated directly.
> >
>
> I'm not sure what that buys me here, I need the virtio-vga to attach
> the vga ops the first console that the virtio-gpu registers, it can't
> be a separate console, and since virtio-gpu initialises before
> virtio-vga I can't tell it to not register the console.
virtio-gpu-core registers no consoles. It just export the hw_ops
functions. virtio-gpu-core inly initializes the stuff which is
identical for both virtio-gpu and virtio-vga, everything else is left to
the init functions of the subclasses.
virtio-gpu uses virtio-gpu-core as parent. Registers the the consoles,
using the hw_ops functions exported by virtio-gpu-core. Also sets the
pci class to DISPLAY_OTHER.
virtio-vga uses virtio-gpu-core as parent too. Registers the consoles,
using functions basically doing "if vgamode then call vga hw_ops else
call virtio-gpu-core hw_ops". Simliar to what you have today but
without the funky wrapping. Sets pci class to DISPLAY_VGA and
initializes vga stuff.
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-11-21 11:06 ` Gerd Hoffmann
@ 2013-12-06 5:24 ` Dave Airlie
2013-12-06 8:24 ` Gerd Hoffmann
0 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-12-06 5:24 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Thu, Nov 21, 2013 at 9:06 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Do, 2013-11-21 at 13:12 +1000, Dave Airlie wrote:
>> On Wed, Nov 20, 2013 at 10:02 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> > On Mi, 2013-11-20 at 15:52 +1000, Dave Airlie wrote:
>> >> From: Dave Airlie <airlied@redhat.com>
>> >>
>> >> This is a virtio-vga device built on top of the virtio-gpu device.
>> >
>> > Ah, I see what you use the wrapping for. Hmm. I think you should use a
>> > common base class instead, i.e. something like virtio-gpu-base which
>> > holds all the common stuff. Both virtio-gpu and virtio-vga can use that
>> > as TypeInfo->parent then. This way virtio-vga doesn't have to muck with
>> > virtio-gpu internals. virtio-gpu-base can be tagged as abstract class
>> > (using .abstract = true) so it will not be instantiated directly.
>> >
>>
>> I'm not sure what that buys me here, I need the virtio-vga to attach
>> the vga ops the first console that the virtio-gpu registers, it can't
>> be a separate console, and since virtio-gpu initialises before
>> virtio-vga I can't tell it to not register the console.
>
> virtio-gpu-core registers no consoles. It just export the hw_ops
> functions. virtio-gpu-core inly initializes the stuff which is
> identical for both virtio-gpu and virtio-vga, everything else is left to
> the init functions of the subclasses.
>
> virtio-gpu uses virtio-gpu-core as parent. Registers the the consoles,
> using the hw_ops functions exported by virtio-gpu-core. Also sets the
> pci class to DISPLAY_OTHER.
>
> virtio-vga uses virtio-gpu-core as parent too. Registers the consoles,
> using functions basically doing "if vgamode then call vga hw_ops else
> call virtio-gpu-core hw_ops". Simliar to what you have today but
> without the funky wrapping. Sets pci class to DISPLAY_VGA and
> initializes vga stuff.
>
> cheers,
> Gerd
Okay I'm really missing something here and I think I've confused
myself completely.
My plan was
virtio-gpu-base - VirtIOGPUBase object - VirtIODevice parent_obj -
abstract class - contains vqs + exposes ops
virtio-gpu - virtio-gpu-base wrapper with init sequence for non-VGA
virtio gpus (mmio + pci)
virtio-gpu-pci - VirtIOPCIProxy parent_obj - contains a VirtIOGPU vdev
that it instantiates in its instance init like all the PCI wrappers
Now the advice given was to have virtio-vga wrap virtio-gpu-base but
from what I can see it really can't. Since it needs to act and look
like a PCI device
virtio-vga: Also has a VirtIOPCIProxy parent_obj, however as
virtio-gpu-base is abstract I can't directly instantiate it, and I
can't instantiate virtio-gpu as its the wrong thing,
so do I really need to add another class? rename virtio-vga to
virtio-pci-vga and add a new virtio-vga that just wraps
virtio-gpu-base?
This is getting a lot messier than the code I had, and the benefits
are escaping me, which must mean I'm misinterpreting the instructions
given.
This then led to another question how do I call the virtio-gpu-base
init functions? directly? as I can't use qdev as they are abstract
from what I can see.
Dave.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-12-06 5:24 ` Dave Airlie
@ 2013-12-06 8:24 ` Gerd Hoffmann
2013-12-06 8:58 ` Dave Airlie
0 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2013-12-06 8:24 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel@nongnu.org
Hi,
> Now the advice given was to have virtio-vga wrap virtio-gpu-base but
> from what I can see it really can't. Since it needs to act and look
> like a PCI device
Oops, yes. VirtIOPCIProxy wasn't on my radar. That makes things a bit
more messy. Can you just push what you have now somewhere? I'll take a
closer look next week.
cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-12-06 8:24 ` Gerd Hoffmann
@ 2013-12-06 8:58 ` Dave Airlie
2014-01-07 23:35 ` Dave Airlie
0 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2013-12-06 8:58 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Fri, Dec 6, 2013 at 6:24 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> Hi,
>
>> Now the advice given was to have virtio-vga wrap virtio-gpu-base but
>> from what I can see it really can't. Since it needs to act and look
>> like a PCI device
>
> Oops, yes. VirtIOPCIProxy wasn't on my radar. That makes things a bit
> more messy. Can you just push what you have now somewhere? I'll take a
> closer look next week.
http://cgit.freedesktop.org/~airlied/qemu/log/?h=virtio-gpu-inherit
Well I didn't really get anything working, but the top commit in that
branch was where I was on my last random fail.
I think another object is probably required, or making the base one
not abstract.
Dave.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
0 siblings, 0 replies; 23+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
This is a virtio-vga device built on top of the virtio-gpu device.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
Makefile | 2 +-
default-configs/x86_64-softmmu.mak | 1 +
hw/display/Makefile.objs | 1 +
hw/display/virtio-vga.c | 156 +++++++++++++++++++++++++++++++++++++
hw/pci/pci.c | 2 +
include/sysemu/sysemu.h | 2 +-
pc-bios/vgabios-virtio.bin | Bin 0 -> 40448 bytes
roms/Makefile | 2 +-
roms/config.vga.virtio | 6 ++
vl.c | 13 ++++
10 files changed, 182 insertions(+), 3 deletions(-)
create mode 100644 hw/display/virtio-vga.c
create mode 100644 pc-bios/vgabios-virtio.bin
create mode 100644 roms/config.vga.virtio
diff --git a/Makefile b/Makefile
index bdff4e4..a7dabe4 100644
--- a/Makefile
+++ b/Makefile
@@ -291,7 +291,7 @@ bepo cz
ifdef INSTALL_BLOBS
BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
-vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
+vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
acpi-dsdt.aml q35-acpi-dsdt.aml \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 1a00b78..22d8587 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -10,6 +10,7 @@ CONFIG_VGA_ISA=y
CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
CONFIG_VIRTIO_GPU=y
+CONFIG_VIRTIO_VGA=y
CONFIG_VMMOUSE=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 10e4066..63427e9 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -34,3 +34,4 @@ obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
new file mode 100644
index 0000000..9e8eff9
--- /dev/null
+++ b/hw/display/virtio-vga.c
@@ -0,0 +1,156 @@
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/virtio/virtio-pci.h"
+
+/*
+ * virtio-vga: This extends VirtIOGPUPCI
+ */
+#define TYPE_VIRTIO_VGA "virtio-vga"
+#define VIRTIO_VGA(obj) \
+ OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA VirtIOVGA;
+struct VirtIOVGA {
+ struct VirtIOGPUPCI gpu;
+ VGACommonState vga;
+ const struct GraphicHwOps *wrapped_ops;
+ void *wrapped_opaque;
+};
+
+static void virtio_vga_invalidate_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ vvga->vga.hw_ops->invalidate(&vvga->vga);
+ return;
+ }
+
+ vvga->wrapped_ops->invalidate(vvga->wrapped_opaque);
+}
+
+static void virtio_vga_update_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ vvga->vga.hw_ops->gfx_update(&vvga->vga);
+ }
+ vvga->wrapped_ops->gfx_update(vvga->wrapped_opaque);
+}
+
+static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ if (vvga->vga.hw_ops->text_update)
+ vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
+ }
+ vvga->wrapped_ops->text_update(vvga->wrapped_opaque, chardata);
+}
+
+static void virtio_vga_notify_state(void *opaque, int idx, int x, int y, uint32_t width, uint32_t height)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (!vvga->gpu.vdev.enable) {
+ if (vvga->vga.hw_ops->notify_state)
+ vvga->vga.hw_ops->notify_state(&vvga->vga, idx, x, y, width, height);
+ }
+ vvga->wrapped_ops->notify_state(vvga->wrapped_opaque, idx, x, y, width, height);
+}
+
+static const GraphicHwOps virtio_vga_ops = {
+ .invalidate = virtio_vga_invalidate_display,
+ .gfx_update = virtio_vga_update_display,
+ .text_update = virtio_vga_text_update,
+ .notify_state = virtio_vga_notify_state,
+};
+
+/* VGA device wrapper around PCI device around virtio GPU */
+static int virtio_vga_init(VirtIOPCIProxy *vpci_dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
+ DeviceState *vdev = DEVICE(&vvga->gpu.vdev);
+ VGACommonState *vga = &vvga->vga;
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ if (qdev_init(vdev) < 0) {
+ return -1;
+ }
+
+ graphic_console_wrap(vvga->gpu.vdev.con[0], DEVICE(vpci_dev), &virtio_vga_ops, vvga, &vvga->wrapped_ops, &vvga->wrapped_opaque);
+ vga->con = vvga->gpu.vdev.con[0];
+
+ vga_common_init(vga, OBJECT(vpci_dev));
+ vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true);
+
+ pci_register_bar(&vpci_dev->pci_dev, 2, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
+
+ if (!vpci_dev->pci_dev.rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev));
+ }
+
+ return 0;
+}
+
+static void virtio_vga_reset(DeviceState *dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(dev);
+ vvga->gpu.vdev.enable = 0;
+
+ vga_dirty_log_start(&vvga->vga);
+}
+
+static Property virtio_vga_properties[] = {
+ DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOVGA, gpu.vdev.conf),
+ DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_UINT32("vgamem_mb", VirtIOVGA, vga.vram_size_mb, 16),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_vga_properties;
+ dc->reset = virtio_vga_reset;
+
+ k->init = virtio_vga_init;
+ pcidev_k->no_hotplug = 1;
+ pcidev_k->romfile = "vgabios-virtio.bin";
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
+}
+
+static void virtio_vga_inst_initfn(Object *obj)
+{
+ VirtIOVGA *dev = VIRTIO_VGA(obj);
+ object_initialize(&dev->gpu.vdev, sizeof(dev->gpu.vdev), TYPE_VIRTIO_GPU);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->gpu.vdev), NULL);
+}
+
+static TypeInfo virtio_vga_info = {
+ .name = TYPE_VIRTIO_VGA,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(struct VirtIOVGA),
+ .instance_init = virtio_vga_inst_initfn,
+ .class_init = virtio_vga_class_init,
+};
+
+static void virgl_register_types(void)
+{
+ type_register_static(&virtio_vga_info);
+}
+
+type_init(virgl_register_types)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 49eca95..9d92d92 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1655,6 +1655,8 @@ PCIDevice *pci_vga_init(PCIBus *bus)
return pci_create_simple(bus, -1, "VGA");
case VGA_VMWARE:
return pci_create_simple(bus, -1, "vmware-svga");
+ case VGA_VIRTIO:
+ return pci_create_simple(bus, -1, "virtio-vga");
case VGA_NONE:
default: /* Other non-PCI types. Checking for unsupported types is already
done in vl.c. */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 495dae8..1d6ec39 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -103,7 +103,7 @@ typedef enum DisplayType
extern int autostart;
typedef enum {
- VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
+ VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, VGA_VIRTIO
} VGAInterfaceType;
extern int vga_interface_type;
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
new file mode 100644
index 0000000000000000000000000000000000000000..2b70d272f2704e7839712a61613844321b93b465
GIT binary patch
literal 40448
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
AD*ylh
literal 0
HcmV?d00001
diff --git a/roms/Makefile b/roms/Makefile
index 1e04669..9e51a79 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -1,5 +1,5 @@
-vgabios_variants := stdvga cirrus vmware qxl isavga
+vgabios_variants := stdvga cirrus vmware qxl isavga virtio
vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants)))
pxerom_variants := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio
pxerom_targets := 8086100e 80861209 10500940 10222000 10ec8139 1af41000
diff --git a/roms/config.vga.virtio b/roms/config.vga.virtio
new file mode 100644
index 0000000..9a78983
--- /dev/null
+++ b/roms/config.vga.virtio
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1af4
+CONFIG_VGA_DID=0x1010
diff --git a/vl.c b/vl.c
index b0399de..1915ed6 100644
--- a/vl.c
+++ b/vl.c
@@ -299,6 +299,7 @@ static struct {
{ .driver = "isa-cirrus-vga", .flag = &default_vga },
{ .driver = "vmware-svga", .flag = &default_vga },
{ .driver = "qxl-vga", .flag = &default_vga },
+ { .driver = "virtio-vga", .flag = &default_vga },
};
static QemuOptsList qemu_rtc_opts = {
@@ -2082,6 +2083,11 @@ static bool qxl_vga_available(void)
return object_class_by_name("qxl-vga");
}
+static bool virtio_vga_available(void)
+{
+ return object_class_by_name("virtio-vga");
+}
+
static void select_vgahw (const char *p)
{
const char *opts;
@@ -2108,6 +2114,13 @@ static void select_vgahw (const char *p)
fprintf(stderr, "Error: VMWare SVGA not available\n");
exit(0);
}
+ } else if (strstart(p, "virtio", &opts)) {
+ if (virtio_vga_available()) {
+ vga_interface_type = VGA_VIRTIO;
+ } else {
+ fprintf(stderr, "Error: Virtio VGA not available\n");
+ exit(0);
+ }
} else if (strstart(p, "xenfb", &opts)) {
vga_interface_type = VGA_XENFB;
} else if (strstart(p, "qxl", &opts)) {
--
1.8.3.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2013-12-06 8:58 ` Dave Airlie
@ 2014-01-07 23:35 ` Dave Airlie
2014-01-13 8:01 ` Gerd Hoffmann
0 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2014-01-07 23:35 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Fri, Dec 6, 2013 at 6:58 PM, Dave Airlie <airlied@gmail.com> wrote:
> On Fri, Dec 6, 2013 at 6:24 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> Hi,
>>
>>> Now the advice given was to have virtio-vga wrap virtio-gpu-base but
>>> from what I can see it really can't. Since it needs to act and look
>>> like a PCI device
>>
>> Oops, yes. VirtIOPCIProxy wasn't on my radar. That makes things a bit
>> more messy. Can you just push what you have now somewhere? I'll take a
>> closer look next week.
>
> http://cgit.freedesktop.org/~airlied/qemu/log/?h=virtio-gpu-inherit
>
> Well I didn't really get anything working, but the top commit in that
> branch was where I was on my last random fail.
>
> I think another object is probably required, or making the base one
> not abstract.
>
Hi Gerd,
just repinging on this, not sure if you can see a solution that works
with VirtIOPCIProxy that avoids wrapping.
Dave.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Qemu-devel] [PATCH 7/8] virtio-vga: v1
2014-01-07 23:35 ` Dave Airlie
@ 2014-01-13 8:01 ` Gerd Hoffmann
0 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2014-01-13 8:01 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel@nongnu.org
On Mi, 2014-01-08 at 09:35 +1000, Dave Airlie wrote:
> On Fri, Dec 6, 2013 at 6:58 PM, Dave Airlie <airlied@gmail.com> wrote:
> > On Fri, Dec 6, 2013 at 6:24 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> >> Hi,
> >>
> >>> Now the advice given was to have virtio-vga wrap virtio-gpu-base but
> >>> from what I can see it really can't. Since it needs to act and look
> >>> like a PCI device
> >>
> >> Oops, yes. VirtIOPCIProxy wasn't on my radar. That makes things a bit
> >> more messy. Can you just push what you have now somewhere? I'll take a
> >> closer look next week.
> >
> > http://cgit.freedesktop.org/~airlied/qemu/log/?h=virtio-gpu-inherit
> >
> > Well I didn't really get anything working, but the top commit in that
> > branch was where I was on my last random fail.
> >
> > I think another object is probably required, or making the base one
> > not abstract.
> >
> Hi Gerd,
>
> just repinging on this, not sure if you can see a solution that works
> with VirtIOPCIProxy that avoids wrapping.
Havn't found time yet. Was sick over xmas/newyear, back online now.
So I have a big email backlog to wade through now, and there are also
the input/sdl2 bits which need some love to get it merged. Also not
checked yet bugzilla for urgent stuff. So I expect this has to wait at
least one more week ...
happy new years & cheers,
Gerd
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2014-01-13 8:02 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-20 5:52 [Qemu-devel] [RFC] virtio-gpu and sdl2 so far Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v1.2) Dave Airlie
2013-11-20 10:52 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
2013-11-20 11:04 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 3/8] console: add information retrival wrappers Dave Airlie
2013-11-20 11:12 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 4/8] console: add ability to wrap a console Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 5/8] sdl2: update for multihead support Dave Airlie
2013-11-20 5:52 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.1 of the virtio based GPU code Dave Airlie
2013-11-20 11:26 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
2013-11-20 12:02 ` Gerd Hoffmann
2013-11-21 3:12 ` Dave Airlie
2013-11-21 6:17 ` Paolo Bonzini
2013-11-21 11:06 ` Gerd Hoffmann
2013-12-06 5:24 ` Dave Airlie
2013-12-06 8:24 ` Gerd Hoffmann
2013-12-06 8:58 ` Dave Airlie
2014-01-07 23:35 ` Dave Airlie
2014-01-13 8:01 ` Gerd Hoffmann
2013-11-20 5:52 ` [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt Dave Airlie
-- strict thread matches above, loose matches on Subject: below --
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).