* [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0)
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 14:35 ` Gerd Hoffmann
2013-12-10 4:05 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
` (8 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 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
v2.0: merge the SDL multihead patch into this, g_new the number of consoles
needed, wrap DCL inside per-console structure.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
configure | 23 +-
ui/Makefile.objs | 4 +-
ui/sdl.c | 3 +
ui/sdl2.c | 981 +++++++++++++++++++++++++++++++++++++++++++
ui/sdl2_scancode_translate.h | 260 ++++++++++++
ui/sdl_keysym.h | 3 +-
6 files changed, 1267 insertions(+), 7 deletions(-)
create mode 100644 ui/sdl2.c
create mode 100644 ui/sdl2_scancode_translate.h
diff --git a/configure b/configure
index 0666228..3b7490d 100755
--- a/configure
+++ b/configure
@@ -171,6 +171,7 @@ docs=""
fdt=""
pixman=""
sdl=""
+sdlabi="1.2"
virtfs=""
vnc="yes"
sparse="no"
@@ -322,6 +323,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}"
# If the user hasn't specified ARFLAGS, default to 'rv', just as make does.
ARFLAGS="${ARFLAGS-rv}"
@@ -727,6 +729,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"
@@ -1113,6 +1117,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"
@@ -1781,12 +1786,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..2eb3e9c
--- /dev/null
+++ b/ui/sdl2.c
@@ -0,0 +1,981 @@
+/*
+ * 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 int sdl2_num_outputs;
+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;
+
+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(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_num_outputs; 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)
+{
+ 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;
+
+ 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(struct sdl2_console_state *scon, int width, int height, int bpp)
+{
+ int flags;
+
+ 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;
+
+ 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 = scon->idx;
+ DisplaySurface *old_surface = scon->surface;
+
+ /* temporary hack: allows to call sdl_switch to handle scaling changes */
+ if (new_surface) {
+ scon->surface = new_surface;
+ }
+
+ if (!new_surface && idx > 0)
+ scon->surface = NULL;
+
+ 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 (old_surface && scon->texture) {
+ SDL_DestroyTexture(scon->texture);
+ scon->texture = NULL;
+ }
+
+ 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 */
+
+#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(struct sdl2_console_state *scon)
+{
+ 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-%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 (scon->real_window)
+ SDL_SetWindowTitle(scon->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(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(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
+ return;
+ }
+ if (guest_cursor) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled) {
+ SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
+ }
+ } else
+ sdl_hide_cursor();
+ SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
+ gui_grab = 1;
+ sdl_update_caption(scon);
+}
+
+static void sdl_grab_end(struct sdl2_console_state *scon)
+{
+ SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
+ gui_grab = 0;
+ sdl_show_cursor();
+ sdl_update_caption(scon);
+}
+
+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(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(scon);
+ }
+}
+
+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(&sdl2_console[0]);
+ }
+ }
+ } else if (absolute_enabled) {
+ if (!gui_fullscreen) {
+ sdl_grab_end(&sdl2_console[0]);
+ }
+ absolute_enabled = 0;
+ }
+}
+
+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;
+
+ 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;
+ 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_num_outputs; 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;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+}
+
+static void sdl_scale(struct sdl2_console_state *scon, int width, int height)
+{
+ int bpp = 0;
+ do_sdl_resize(scon, width, height, bpp);
+ scaling_active = 1;
+}
+
+static void toggle_full_screen(struct sdl2_console_state *scon)
+{
+ 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(scon->real_window, &gui_saved_width, &gui_saved_height);
+ gui_saved_scaling = scaling_active;
+
+ do_sdl_resize(scon, width, height, bpp);
+ scaling_active = 0;
+
+ gui_saved_grab = gui_grab;
+ sdl_grab_start(scon);
+ } else {
+ if (gui_saved_scaling) {
+ sdl_scale(scon, gui_saved_width, gui_saved_height);
+ } else {
+ do_sdl_resize(scon, width, height, 0);
+ }
+ if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
+ sdl_grab_end(scon);
+ }
+ }
+ 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);
+ } 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(scon);
+ gui_keysym = 1;
+ break;
+ case 0x16: /* 'u' key on US keyboard */
+ if (scaling_active) {
+ scaling_active = 0;
+ sdl_switch(&scon->dcl, NULL);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
+ }
+ 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(scon);
+ } else if (absolute_enabled) {
+ sdl_show_cursor();
+ }
+ } else if (absolute_enabled) {
+ sdl_hide_cursor();
+ absolute_mouse_grab(scon);
+ }
+ break;
+ case 0x1b: /* '+' */
+ case 0x35: /* '-' */
+ if (!gui_fullscreen) {
+ int scr_w, scr_h;
+ int width, height;
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+
+ width = MAX(scr_w + (keycode == 0x1b ? 50 : -50),
+ 160);
+ height = (surface_height(scon->surface) * width) /
+ surface_width(scon->surface);
+
+ sdl_scale(scon, 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;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
+
+ 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(scon);
+ }
+ } else if (!gui_fullscreen) {
+ sdl_grab_end(scon);
+ }
+ /* 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;
+ 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(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(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(scon);
+ }
+ }
+ if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ sdl_send_mouse_event(scon, 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;
+ struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID);
+ 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(scon);
+ }
+ } 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(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(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);
+ 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(scon);
+ }
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ if (gui_grab && !gui_fullscreen)
+ sdl_grab_end(scon);
+ 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)
+{
+ struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl);
+ SDL_Event ev1, *ev = &ev1;
+
+ if (scon->last_vm_running != runstate_is_running()) {
+ scon->last_vm_running = runstate_is_running();
+ sdl_update_caption(scon);
+ }
+
+ graphic_hw_update(dcl->con);
+
+ 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)
+{
+ 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(scon->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;
+ int i;
+#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);
+ }
+
+ for (i = 0;; i++) {
+ QemuConsole *con = qemu_console_lookup_by_index(i);
+ if (!con || !qemu_console_is_graphic(con))
+ break;
+ }
+ sdl2_num_outputs = i;
+ sdl2_console = g_new0(struct sdl2_console_state, sdl2_num_outputs);
+ for (i = 0; i < sdl2_num_outputs; i++) {
+ QemuConsole *con = qemu_console_lookup_by_index(i);
+ sdl2_console[i].dcl.ops = &dcl_ops;
+ sdl2_console[i].dcl.con = con;
+ register_displaychangelistener(&sdl2_console[i].dcl);
+ sdl2_console[i].idx = i;
+ }
+
+ /* 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(sdl2_console[0].real_window, image);
+ }
+ g_free(filename);
+ }
+
+ if (full_screen) {
+ gui_fullscreen = 1;
+ sdl_grab_start(0);
+ }
+
+ 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] 15+ messages in thread
* Re: [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0) Dave Airlie
@ 2013-12-10 14:35 ` Gerd Hoffmann
2013-12-11 1:12 ` Dave Airlie
0 siblings, 1 reply; 15+ messages in thread
From: Gerd Hoffmann @ 2013-12-10 14:35 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel
Hi,
> 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.
I've picked it up, cleaned up a bit (more to be done), switched over to
new input core. Simplified the keyboard mess, the new input support in
SDL2 allows us to reduce it to a single+simple lookup table. Pushed
here:
http://www.kraxel.org/cgit/qemu/log/?h=rebase/input-wip
cheers,
Gerd
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0)
2013-12-10 14:35 ` Gerd Hoffmann
@ 2013-12-11 1:12 ` Dave Airlie
2013-12-12 12:53 ` Gerd Hoffmann
0 siblings, 1 reply; 15+ messages in thread
From: Dave Airlie @ 2013-12-11 1:12 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Wed, Dec 11, 2013 at 12:35 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> Hi,
>
>> 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.
>
> I've picked it up, cleaned up a bit (more to be done), switched over to
> new input core. Simplified the keyboard mess, the new input support in
> SDL2 allows us to reduce it to a single+simple lookup table. Pushed
> here:
Oh thanks for that, looks a lot cleaner esp the input handling, let me
know if there
is anything you'd like me to look at.
I wasn't sure if we wanted to keep the old text console switching
stuff on console 0 or not,
I see you've ripped it out, I can't say I ever used it anyways.
I've also started reworking gtk for multi-head and have the basics
working there as well.
Dave.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0)
2013-12-11 1:12 ` Dave Airlie
@ 2013-12-12 12:53 ` Gerd Hoffmann
0 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2013-12-12 12:53 UTC (permalink / raw)
To: Dave Airlie; +Cc: qemu-devel@nongnu.org
On Mi, 2013-12-11 at 11:12 +1000, Dave Airlie wrote:
> On Wed, Dec 11, 2013 at 12:35 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > Hi,
> >
> >> 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.
> >
> > I've picked it up, cleaned up a bit (more to be done), switched over to
> > new input core. Simplified the keyboard mess, the new input support in
> > SDL2 allows us to reduce it to a single+simple lookup table. Pushed
> > here:
>
> Oh thanks for that, looks a lot cleaner esp the input handling, let me
> know if there
> is anything you'd like me to look at.
Testing would be nice (but take care a bit, the branch is a moving
target, i.e. rebases), other than that nothing for now.
I wanna get the input patch series out-of-the-door asap, probably with
the sdl2 bits sprinkled in at the correct places so the whole thing is
bisectable.
> I wasn't sure if we wanted to keep the old text console switching
> stuff on console 0 or not,
> I see you've ripped it out, I can't say I ever used it anyways.
It's pointless if we explicitly bind to a specific console. The
switching would change active_console, but you wouldn't see it.
cheers,
Gerd
^ permalink raw reply [flat|nested] 15+ messages in thread
* [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0) Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 3/8] sdl2: add display notify change support Dave Airlie
` (7 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 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 4156a87..f6e8957 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -173,6 +173,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 {
@@ -223,7 +226,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);
@@ -274,6 +278,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,
@@ -283,6 +288,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 502e160..def11ea 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)
{
@@ -1525,6 +1535,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] 15+ messages in thread
* [Qemu-devel] [PATCH 3/8] sdl2: add display notify change support
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 1/8] ui/sdl2 : initial port to SDL 2.0 (v2.0) Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 2/8] console: add state notifiers for ui<->display Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 4/8] sdl2: add UI to toggle head 1 on/off Dave Airlie
` (6 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
this adds support for the callbacks from the console layer, when the gpu
changes the layouts.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
ui/sdl2.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 2eb3e9c..dd8cd2b 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -880,6 +880,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)
@@ -894,6 +903,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)
--
1.8.3.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [PATCH 4/8] sdl2: add UI to toggle head 1 on/off
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (2 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 3/8] sdl2: add display notify change support Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 5/8] console: add ability to wrap a console Dave Airlie
` (5 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 UTC (permalink / raw)
To: qemu-devel
From: Dave Airlie <airlied@redhat.com>
This just adds ctrl-alt-n to toggle head 1 on/off for testing and demo purposes.
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
ui/sdl2.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/ui/sdl2.c b/ui/sdl2.c
index dd8cd2b..c52dcd9 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -522,6 +522,13 @@ 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(scon);
gui_keysym = 1;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [PATCH 5/8] console: add ability to wrap a console.
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (3 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 4/8] sdl2: add UI to toggle head 1 on/off Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.2 of the virtio based GPU code Dave Airlie
` (4 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 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 f6e8957..2ad9238 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -285,6 +285,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 def11ea..f2d6721 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1619,6 +1619,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] 15+ messages in thread
* [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.2 of the virtio based GPU code.
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (4 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 5/8] console: add ability to wrap a console Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
` (3 subsequent siblings)
9 siblings, 0 replies; 15+ 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 the basic virtio-gpu which is
multi-head capable,
ARGB cursor support,
unaccelerated.
Some more info is in docs/specs/virtio-gpu.txt.
changes since v0.1:
add reset handling
fix display info response
fix cursor generation issues
drop 3d stuff that snuck in
Signed-off-by: Dave Airlie <airlied@redhat.com>
---
default-configs/x86_64-softmmu.mak | 1 +
docs/specs/virtio-gpu.txt | 89 +++++
hw/display/Makefile.objs | 2 +
hw/display/virtgpu_hw.h | 142 ++++++++
hw/display/virtio-gpu.c | 649 +++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 49 +++
hw/virtio/virtio-pci.h | 15 +
include/hw/pci/pci.h | 1 +
include/hw/virtio/virtio-gpu.h | 89 +++++
9 files changed, 1037 insertions(+)
create mode 100644 docs/specs/virtio-gpu.txt
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/docs/specs/virtio-gpu.txt b/docs/specs/virtio-gpu.txt
new file mode 100644
index 0000000..987d807
--- /dev/null
+++ b/docs/specs/virtio-gpu.txt
@@ -0,0 +1,89 @@
+Initial info on virtio-gpu:
+
+virtio-gpu is a virtio based graphics adapter. The initial version provides support for:
+
+- ARGB Hardware cursors
+- multiple outputs
+
+There are no acceleration features available in the first release of this device, 3D acceleration features will be provided later via an interface to an external renderer.
+
+The virtio-gpu exposes 3 vqs to the guest,
+
+1) control vq - guest->host queue for sending commands and getting responses when required.
+2) cursor vq - guest->host queue for sending cursor position and resource updates
+3) event vq - host->guest queue for sending async events like display topography updates and errors (todo).
+
+How to use the virtio-gpu:
+
+The virtio-gpu is based around the concept of resources private to the host, the guest must DMA transfer into these resources. This is a design requirement in order to interface with future 3D rendering. In the unaccelerated there is no support for DMA transfers from resources, just to them.
+
+Resources are initially simple 2D resources, consisting of a width, height and format along with an identifier. The guest must then attach backing store to the resources in order for DMA transfers to work. This is like a GART in a real GPU.
+
+A typical guest user would create a 2D resource using VIRTGPU_CMD_RESOURCE_CREATE_2D, attach backing store using VIRTGPU_CMD_RESOURCE_ATTACH_BACKING, then attach the resource to a scanout using VIRTGPU_CMD_SET_SCANOUT, then use VIRTGPU_CMD_TRANSFER_SEND_2D to send updates to the resource, and finally VIRTGPU_CMD_RESOURCE_FLUSH to flush the scanout buffers to screen.
+
+Command queue:
+
+VIRTGPU_CMD_GET_DISPLAY_INFO:
+Retrieve the current output configuration.
+
+This sends a response in the same queue slot. The response contains the max number of scanouts the host can support,
+along with a list of per-scanout information. The info contains whether the
+scanout is enabled, what its preferred x, y, width and height are and some future flags.
+
+VIRTGPU_CMD_RESOURCE_CREATE_2D:
+Create a 2D resource on the host.
+
+This creates a 2D resource on the host with the specified width, height and format. Only a small subset of formats are support. The resource ids are generated by the guest.
+
+VIRTGPU_CMD_RESOURCE_UNREF:
+Destroy a resource.
+
+This informs the host that a resource is no longer required by the guest.
+
+VIRTGPU_CMD_SET_SCANOUT:
+Set the scanout parameters for a single output.
+
+This sets the scanout parameters for a single scanout. The resource_id is the
+resource to be scanned out from, along with a rectangle specified by x, y, width and height.
+
+VIRTGPU_CMD_RESOURCE_FLUSH:
+Flush a scanout resource
+
+This flushes a resource to screen, it takes a rectangle and a resource id,
+and flushes any scanouts the resource is being used on.
+
+VIRTGPU_CMD_TRANSFER_TO_HOST_2D:
+Transfer from guest memory to host resource.
+
+This takes a resource id along with an destination offset into the resource,
+and a box to transfer from the host backing for the resource.
+
+VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
+Assign backing pages to a resource.
+
+This assign an array of guest pages as the backing store for a resource. These
+pages are then used for the transfer operations for that resource from that
+point on.
+
+VIRTGPU_CMD_RESOURCE_DETACH_BACKING:
+Detach backing pages from a resource.
+
+This detaches any backing pages from a resource, to be used in case of
+guest swapping or object destruction.
+
+Cursor queue:
+The cursor queue accepts on structure per vq entry. The cursor is just a standard 2D resource that is 64x64 sized. Transfers are used to upload the cursor contents.
+
+For cursor movement, the cursor_x and cursor_y fields only need to be updated.
+
+For a cursor update where the guest has transferred a new cursor into the resource, the hotspot, cursor id, should be updated, but the generation_id should also be increased by one, so the host knows to refresh its copy of the cursor data from the resource.
+
+Event queue:
+The only current event passed is a message to denote the host wants to update the layout of the screens. It contains the same info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
+
+
+
+
+
+
+
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..f915510
--- /dev/null
+++ b/hw/display/virtgpu_hw.h
@@ -0,0 +1,142 @@
+#ifndef VIRTGPU_HW_H
+#define VIRTGPU_HW_H
+
+#define VIRTGPU_CMD_HAS_RESP (1 << 31)
+
+enum virtgpu_ctrl_cmd {
+ VIRTGPU_CMD_NOP,
+ VIRTGPU_CMD_GET_DISPLAY_INFO = (1 | VIRTGPU_CMD_HAS_RESP),
+ /* reserved */
+ VIRTGPU_CMD_RESOURCE_CREATE_2D = 3,
+ VIRTGPU_CMD_RESOURCE_UNREF = 4,
+ VIRTGPU_CMD_SET_SCANOUT = 5,
+ VIRTGPU_CMD_RESOURCE_FLUSH = 6,
+ VIRTGPU_CMD_TRANSFER_TO_HOST_2D = 7,
+ VIRTGPU_CMD_RESOURCE_ATTACH_BACKING = 8,
+ VIRTGPU_CMD_RESOURCE_INVAL_BACKING = 9,
+};
+
+enum virtgpu_ctrl_event {
+ VIRTGPU_EVENT_NOP,
+ 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;
+ uint32_t generation_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 to_host */
+struct virtgpu_transfer_to_host_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];
+};
+
+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_to_host_2d transfer_to_host_2d;
+ struct virtgpu_resource_attach_backing resource_attach_backing;
+ struct virtgpu_resource_inval_backing resource_inval_backing;
+ } 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..7bf2fbb
--- /dev/null
+++ b/hw/display/virtio-gpu.c
@@ -0,0 +1,649 @@
+/*
+ * 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;
+ }
+}
+
+static void update_cursor(VirtIOGPU *g, struct virtgpu_hw_cursor_page *cursor)
+{
+ if (g->cursor_generation_id != cursor->generation_id) {
+
+ if (!g->current_cursor)
+ g->current_cursor = cursor_alloc(64, 64);
+
+ g->current_cursor->hot_x = cursor->cursor_hot_x;
+ g->current_cursor->hot_y = cursor->cursor_hot_y;
+
+ if (cursor->cursor_id > 0) {
+ struct virtgpu_simple_resource *res;
+
+ res = virtgpu_find_resource(g, cursor->cursor_id);
+ if (res)
+ update_cursor_data(g, res);
+ }
+ g->cursor_generation_id = cursor->generation_id;
+ g->current_cursor_id = cursor->cursor_id;
+ dpy_cursor_define(g->con[0], g->current_cursor);
+ }
+
+ 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_response resp;
+
+ resp.type = VIRTGPU_CMD_GET_DISPLAY_INFO;
+ resp.flags = 0;
+ virtgpu_fill_display_info(g, &resp.u.display_info);
+
+ iov_from_buf(iov, iov_cnt, 0, &resp, sizeof(struct virtgpu_response));
+}
+
+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_destroy(struct virtgpu_simple_resource *res)
+{
+ pixman_image_unref(res->image);
+ QLIST_REMOVE(res, next);
+ free(res);
+}
+
+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;
+
+ virtgpu_resource_destroy(res);
+}
+
+static void virtgpu_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtgpu_transfer_to_host_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_TO_HOST_2D:
+ virtgpu_transfer_to_host_2d(g, &cmd->u.transfer_to_host_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_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->cursor_generation_id = 0xffffffff;
+ 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 void virtio_gpu_reset(VirtIODevice *vdev)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ int i;
+ struct virtgpu_simple_resource *res, *tmp;
+
+ g->cursor_generation_id = 0xffffffff;
+ g->current_cursor_id = 0;
+ g->enable = 0;
+
+ QLIST_FOREACH_SAFE(res, &g->reslist, next, tmp) {
+ virtgpu_resource_destroy(res);
+ }
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ g->req_state[i].x = 0;
+ g->req_state[i].y = 0;
+ if (i == 0) {
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+ } else {
+ g->req_state[i].width = 0;
+ g->req_state[i].height = 0;
+ }
+ g->scanout[i].resource_id = 0;
+ g->scanout[i].width = 0;
+ g->scanout[i].height = 0;
+ g->scanout[i].x = 0;
+ g->scanout[i].y = 0;
+ g->scanout[i].ds = NULL;
+ }
+ g->enabled_output_bitmask = 1;
+}
+
+static Property virtio_gpu_properties[] = {
+ DEFINE_VIRTIO_GPU_PROPERTIES(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;
+
+ vdc->reset = virtio_gpu_reset;
+
+ 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..1f04a9e 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_PROPERTIES(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..deb649b
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu.h
@@ -0,0 +1,89 @@
+#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 cursor_generation_id;
+
+ 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_PROPERTIES(_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_PROPERTIES(_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] 15+ 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
` (5 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 6/8] virtio-gpu: v0.2 of the virtio based GPU code Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:05 ` [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt Dave Airlie
` (2 subsequent siblings)
9 siblings, 0 replies; 15+ 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] 15+ messages in thread
* [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (6 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 7/8] virtio-vga: v1 Dave Airlie
@ 2013-12-10 4:05 ` Dave Airlie
2013-12-10 4:58 ` [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
2013-12-10 5:42 ` Stefan Weil
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:05 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] 15+ messages in thread
* Re: [Qemu-devel] [RFC] sdl2 + virtio-gpu repost
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (7 preceding siblings ...)
2013-12-10 4:05 ` [Qemu-devel] [PATCH 8/8] HACK: just to make things start easier with libvirt Dave Airlie
@ 2013-12-10 4:58 ` Dave Airlie
2013-12-10 5:42 ` Stefan Weil
9 siblings, 0 replies; 15+ messages in thread
From: Dave Airlie @ 2013-12-10 4:58 UTC (permalink / raw)
To: qemu-devel@nongnu.org
On Tue, Dec 10, 2013 at 2:05 PM, Dave Airlie <airlied@gmail.com> wrote:
> Hi,
>
> This is a repost of the latest SDL2 UI layer along with virtio-gpu,
>
> I've merged the SDL2 base and multi-head patches into one, and I've split
> out the display notifiers stuff and the sdl2 demo hack.
>
> The virtio-gpu/vga wrapping is still unresolved, I've failed to find a cleaner
> way yet, hopefully Gerd will advise further, but having 4 virtio-gpu- objects
> we getting ugly fast. I've also included a doc on the basic queues used in the
> virtio-gpu and what commands are sent on them.
Oh I added one more patch to use glib allocation routines in
virtio-gpu, I forgot that was on my list!
Dave.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [Qemu-devel] [RFC] sdl2 + virtio-gpu repost
2013-12-10 4:05 [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
` (8 preceding siblings ...)
2013-12-10 4:58 ` [Qemu-devel] [RFC] sdl2 + virtio-gpu repost Dave Airlie
@ 2013-12-10 5:42 ` Stefan Weil
2013-12-10 14:24 ` Gerd Hoffmann
9 siblings, 1 reply; 15+ messages in thread
From: Stefan Weil @ 2013-12-10 5:42 UTC (permalink / raw)
To: Dave Airlie, qemu-devel
Am 10.12.2013 05:05, schrieb Dave Airlie:
> Hi,
>
> This is a repost of the latest SDL2 UI layer along with virtio-gpu,
>
> I've merged the SDL2 base and multi-head patches into one, and I've split
> out the display notifiers stuff and the sdl2 demo hack.
>
> The virtio-gpu/vga wrapping is still unresolved, I've failed to find a cleaner
> way yet, hopefully Gerd will advise further, but having 4 virtio-gpu- objects
> we getting ugly fast. I've also included a doc on the basic queues used in the
> virtio-gpu and what commands are sent on them.
>
> I'd really like to get the sdl2 base patch merged at least,
>
> Dave.
I did not review all patches in the series, but stopped at the first one
which had style issues.
Please check your patches using scripts/checkpatch.pl.
Stefan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [Qemu-devel] [RFC] sdl2 + virtio-gpu repost
2013-12-10 5:42 ` Stefan Weil
@ 2013-12-10 14:24 ` Gerd Hoffmann
0 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2013-12-10 14:24 UTC (permalink / raw)
To: Stefan Weil; +Cc: Dave Airlie, qemu-devel
On Di, 2013-12-10 at 06:42 +0100, Stefan Weil wrote:
> Am 10.12.2013 05:05, schrieb Dave Airlie:
> > Hi,
> >
> > This is a repost of the latest SDL2 UI layer along with virtio-gpu,
> >
> > I've merged the SDL2 base and multi-head patches into one, and I've split
> > out the display notifiers stuff and the sdl2 demo hack.
> >
> > The virtio-gpu/vga wrapping is still unresolved, I've failed to find a cleaner
> > way yet, hopefully Gerd will advise further, but having 4 virtio-gpu- objects
> > we getting ugly fast. I've also included a doc on the basic queues used in the
> > virtio-gpu and what commands are sent on them.
> >
> > I'd really like to get the sdl2 base patch merged at least,
> >
> > Dave.
>
> I did not review all patches in the series, but stopped at the first one
> which had style issues.
>
> Please check your patches using scripts/checkpatch.pl.
To be fair most of this comes from ui/sdl.c not conforming to qemu code
style, because the code predates the current qemu code style.
Nevertheless it is a good idea to hook up scripts/checkpatch.pl in the
git pre-commit.
cheers,
Gerd
^ permalink raw reply [flat|nested] 15+ messages in thread