Commit-ID: 3e6f8fd5d5bd44b00a65485c68c4a4aad3fd1a21 Gitweb: http://git.kernel.org/tip/3e6f8fd5d5bd44b00a65485c68c4a4aad3fd1a21 Author: Pekka Enberg <penberg@xxxxxxxxxx> AuthorDate: Wed, 6 Feb 2013 17:52:57 +0200 Committer: Pekka Enberg <penberg@xxxxxxxxxx> CommitDate: Fri, 12 Apr 2013 10:05:31 +0300 kvm tools: Initial GTK+ 3.0 UI It's barely usable but it isn't getting any better sitting alone in a private git branch. You can start a new VM with the GTK UI like this: ./vm run --gtk It's rough around the edges: - Red and blue color channels are inverted. - Some keys do not work. - Mouse does not work. - GTK assertion failure pops up on shutdown. but I'm sure there's someone out there that's just dying to improve the user experience. Cc: Asias He <asias.hejun@xxxxxxxxx> Cc: Cyrill Gorcunov <gorcunov@xxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Sasha Levin <levinsasha928@xxxxxxxxx> Signed-off-by: Pekka Enberg <penberg@xxxxxxxxxx> --- tools/kvm/Makefile | 9 ++ tools/kvm/builtin-run.c | 3 +- tools/kvm/config/feature-tests.mak | 11 ++ tools/kvm/hw/vesa.c | 2 +- tools/kvm/include/kvm/gtk3.h | 28 ++++ tools/kvm/include/kvm/kvm-config.h | 1 + tools/kvm/ui/{sdl.c => gtk3.c} | 300 +++++++++++++++++++------------------ tools/kvm/x86/kvm.c | 2 +- 8 files changed, 204 insertions(+), 152 deletions(-) diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 0c59faa..a0a0a9b 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -207,6 +207,15 @@ ifeq ($(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD) -static),y) LIBS_STATOPT += -lbfd endif +FLAGS_GTK3 := $(CFLAGS) $(shell pkg-config --libs --cflags gtk+-3.0 2>/dev/null) +ifeq ($(call try-cc,$(SOURCE_GTK3),$(FLAGS_GTK3)),y) + OBJS_DYNOPT += ui/gtk3.o + CFLAGS_DYNOPT += -DCONFIG_HAS_GTK3 $(shell pkg-config --cflags gtk+-3.0 2>/dev/null) + LIBS_DYNOPT += $(shell pkg-config --libs gtk+-3.0 2>/dev/null) +else + $(warning warning GTK3 not found, disables GTK3 support. Please install gtk3-devel or libgtk3.0-dev) +endif + FLAGS_VNCSERVER := $(CFLAGS) -lvncserver ifeq ($(call try-cc,$(SOURCE_VNCSERVER),$(FLAGS_VNCSERVER)),y) OBJS_DYNOPT += ui/vnc.o diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c index d0b876a..c6f5862 100644 --- a/tools/kvm/builtin-run.c +++ b/tools/kvm/builtin-run.c @@ -112,6 +112,7 @@ void kvm_run_set_wrapper_sandbox(void) OPT_BOOLEAN('\0', "balloon", &(cfg)->balloon, "Enable virtio" \ " balloon"), \ OPT_BOOLEAN('\0', "vnc", &(cfg)->vnc, "Enable VNC framebuffer"),\ + OPT_BOOLEAN('\0', "gtk", &(cfg)->gtk, "Enable GTK framebuffer"),\ OPT_BOOLEAN('\0', "sdl", &(cfg)->sdl, "Enable SDL framebuffer"),\ OPT_BOOLEAN('\0', "rng", &(cfg)->virtio_rng, "Enable virtio" \ " Random Number Generator"), \ @@ -604,7 +605,7 @@ static struct kvm *kvm_cmd_run_init(int argc, const char **argv) kvm->cfg.network = DEFAULT_NETWORK; memset(real_cmdline, 0, sizeof(real_cmdline)); - kvm__arch_set_cmdline(real_cmdline, kvm->cfg.vnc || kvm->cfg.sdl); + kvm__arch_set_cmdline(real_cmdline, kvm->cfg.vnc || kvm->cfg.sdl || kvm->cfg.gtk); if (strlen(real_cmdline) > 0) strcat(real_cmdline, " "); diff --git a/tools/kvm/config/feature-tests.mak b/tools/kvm/config/feature-tests.mak index 4a81f56..927ac54 100644 --- a/tools/kvm/config/feature-tests.mak +++ b/tools/kvm/config/feature-tests.mak @@ -175,3 +175,14 @@ int main(void) return 0; } endef + +define SOURCE_GTK3 +#include <gtk/gtk.h> + +int main(void) +{ + gtk_main(); + + return 0; +} +endef diff --git a/tools/kvm/hw/vesa.c b/tools/kvm/hw/vesa.c index 33a675f..47e3a69 100644 --- a/tools/kvm/hw/vesa.c +++ b/tools/kvm/hw/vesa.c @@ -59,7 +59,7 @@ struct framebuffer *vesa__init(struct kvm *kvm) char *mem; int r; - if (!kvm->cfg.vnc && !kvm->cfg.sdl) + if (!kvm->cfg.vnc && !kvm->cfg.sdl && !kvm->cfg.gtk) return NULL; r = irq__register_device(PCI_DEVICE_ID_VESA, &pin, &line); diff --git a/tools/kvm/include/kvm/gtk3.h b/tools/kvm/include/kvm/gtk3.h new file mode 100644 index 0000000..b02dc13 --- /dev/null +++ b/tools/kvm/include/kvm/gtk3.h @@ -0,0 +1,28 @@ +#ifndef KVM__GTK3_H +#define KVM__GTK3_H + +#include "kvm/util.h" + +struct framebuffer; + +#ifdef CONFIG_HAS_GTK3 +int kvm_gtk_init(struct kvm *kvm); +int kvm_gtk_exit(struct kvm *kvm); +#else +static inline int kvm_gtk_init(struct kvm *kvm) +{ + if (kvm->cfg.gtk) + die("GTK3 support not compiled in. (install the gtk3-devel or libgtk3.0-dev package)"); + + return 0; +} +static inline int kvm_gtk_exit(struct kvm *kvm) +{ + if (kvm->cfg.gtk) + die("GTK3 support not compiled in. (install the gtk3-devel or libgtk3.0-dev package)"); + + return 0; +} +#endif + +#endif /* KVM__GTK3_H */ diff --git a/tools/kvm/include/kvm/kvm-config.h b/tools/kvm/include/kvm/kvm-config.h index c66f481..386fa8c 100644 --- a/tools/kvm/include/kvm/kvm-config.h +++ b/tools/kvm/include/kvm/kvm-config.h @@ -48,6 +48,7 @@ struct kvm_config { struct virtio_net_params *net_params; bool single_step; bool vnc; + bool gtk; bool sdl; bool balloon; bool using_rootfs; diff --git a/tools/kvm/ui/sdl.c b/tools/kvm/ui/gtk3.c similarity index 52% copy from tools/kvm/ui/sdl.c copy to tools/kvm/ui/gtk3.c index a260002..b2335bc 100644 --- a/tools/kvm/ui/sdl.c +++ b/tools/kvm/ui/gtk3.c @@ -1,39 +1,37 @@ -#include "kvm/sdl.h" +#include "kvm/gtk3.h" #include "kvm/framebuffer.h" -#include "kvm/i8042.h" -#include "kvm/util.h" -#include "kvm/kvm.h" #include "kvm/kvm-cpu.h" +#include "kvm/i8042.h" #include "kvm/vesa.h" +#include "kvm/kvm.h" -#include <SDL/SDL.h> +#include <gtk/gtk.h> #include <pthread.h> -#include <signal.h> #include <linux/err.h> -#define FRAME_RATE 25 +#define FRAME_RATE 25 -#define SCANCODE_UNKNOWN 0 -#define SCANCODE_NORMAL 1 -#define SCANCODE_ESCAPED 2 -#define SCANCODE_KEY_PAUSE 3 -#define SCANCODE_KEY_PRNTSCRN 4 +#define SCANCODE_UNKNOWN 0 +#define SCANCODE_NORMAL 1 +#define SCANCODE_ESCAPED 2 +#define SCANCODE_KEY_PAUSE 3 +#define SCANCODE_KEY_PRNTSCRN 4 struct set2_scancode { u8 code; u8 type; }; -#define DEFINE_SC(_code) {\ - .code = _code,\ - .type = SCANCODE_NORMAL,\ +#define DEFINE_SC(_code) { \ + .code = _code, \ + .type = SCANCODE_NORMAL, \ } /* escaped scancodes */ -#define DEFINE_ESC(_code) {\ - .code = _code,\ - .type = SCANCODE_ESCAPED,\ +#define DEFINE_ESC(_code) { \ + .code = _code, \ + .type = SCANCODE_ESCAPED, \ } static const struct set2_scancode const keymap[256] = { @@ -139,169 +137,172 @@ static const struct set2_scancode const keymap[256] = { [118] = DEFINE_ESC(0x70), /* <ins> */ [119] = DEFINE_ESC(0x71), /* <delete> */ }; -static bool running, done; + +static cairo_surface_t *surface; +static bool done; static const struct set2_scancode *to_code(u8 scancode) { - return &keymap[scancode]; + return &keymap[scancode]; } -static void key_press(const struct set2_scancode *sc) +static gboolean +kvm_gtk_configure_event(GtkWidget * widget, GdkEventConfigure * event, gpointer data) { - switch (sc->type) { - case SCANCODE_ESCAPED: - kbd_queue(0xe0); - /* fallthrough */ - case SCANCODE_NORMAL: - kbd_queue(sc->code); - break; - case SCANCODE_KEY_PAUSE: - kbd_queue(0xe1); - kbd_queue(0x14); - kbd_queue(0x77); - kbd_queue(0xe1); - kbd_queue(0xf0); - kbd_queue(0x14); - kbd_queue(0x77); - break; - case SCANCODE_KEY_PRNTSCRN: - kbd_queue(0xe0); - kbd_queue(0x12); - kbd_queue(0xe0); - kbd_queue(0x7c); - break; - } + struct framebuffer *fb = data; + int stride; + + if (surface) + cairo_surface_destroy(surface); + + stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, fb->width); + + surface = + cairo_image_surface_create_for_data((void *) fb->mem, + CAIRO_FORMAT_RGB24, + fb->width, + fb->height, + stride); + + return TRUE; } -static void key_release(const struct set2_scancode *sc) +static gboolean kvm_gtk_draw(GtkWidget *widget, cairo_t *cr, gpointer data) { - switch (sc->type) { - case SCANCODE_ESCAPED: - kbd_queue(0xe0); - /* fallthrough */ - case SCANCODE_NORMAL: - kbd_queue(0xf0); - kbd_queue(sc->code); - break; - case SCANCODE_KEY_PAUSE: - /* nothing to do */ - break; - case SCANCODE_KEY_PRNTSCRN: - kbd_queue(0xe0); - kbd_queue(0xf0); - kbd_queue(0x7c); - kbd_queue(0xe0); - kbd_queue(0xf0); - kbd_queue(0x12); - break; - } + cairo_set_source_surface(cr, surface, 0, 0); + + cairo_paint(cr); + + return FALSE; +} + +static void kvm_gtk_destroy(void) +{ + if (surface) + cairo_surface_destroy(surface); + + gtk_main_quit(); } -static void *sdl__thread(void *p) +static gboolean kvm_gtk_redraw(GtkWidget * window) +{ + gtk_widget_queue_draw(window); + + return TRUE; +} + +static gboolean +kvm_gtk_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + const struct set2_scancode *sc = to_code(event->hardware_keycode); + + switch (sc->type) { + case SCANCODE_ESCAPED: + kbd_queue(0xe0); + /* fallthrough */ + case SCANCODE_NORMAL: + kbd_queue(sc->code); + break; + case SCANCODE_KEY_PAUSE: + kbd_queue(0xe1); + kbd_queue(0x14); + kbd_queue(0x77); + kbd_queue(0xe1); + kbd_queue(0xf0); + kbd_queue(0x14); + kbd_queue(0x77); + break; + case SCANCODE_KEY_PRNTSCRN: + kbd_queue(0xe0); + kbd_queue(0x12); + kbd_queue(0xe0); + kbd_queue(0x7c); + break; + } + + return TRUE; +} + +static void *kvm_gtk_thread(void *p) { - Uint32 rmask, gmask, bmask, amask; struct framebuffer *fb = p; - SDL_Surface *guest_screen; - SDL_Surface *screen; - SDL_Event ev; - Uint32 flags; - - kvm__set_thread_name("kvm-sdl-worker"); - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - die("Unable to initialize SDL"); - - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0x00000000; - - guest_screen = SDL_CreateRGBSurfaceFrom(fb->mem, fb->width, fb->height, fb->depth, fb->width * fb->depth / 8, rmask, gmask, bmask, amask); - if (!guest_screen) - die("Unable to create SDL RBG surface"); - - flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_DOUBLEBUF; - - SDL_WM_SetCaption("KVM tool", "KVM tool"); - - screen = SDL_SetVideoMode(fb->width, fb->height, fb->depth, flags); - if (!screen) - die("Unable to set SDL video mode"); - - SDL_EnableKeyRepeat(200, 50); - - while (running) { - SDL_BlitSurface(guest_screen, NULL, screen, NULL); - SDL_Flip(screen); - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_KEYDOWN: { - const struct set2_scancode *sc = to_code(ev.key.keysym.scancode); - if (sc->type == SCANCODE_UNKNOWN) { - pr_warning("key '%d' not found in keymap", ev.key.keysym.scancode); - break; - } - key_press(sc); - break; - } - case SDL_KEYUP: { - const struct set2_scancode *sc = to_code(ev.key.keysym.scancode); - if (sc->type == SCANCODE_UNKNOWN) - break; - key_release(sc); - break; - } - case SDL_QUIT: - goto exit; - } - } - - SDL_Delay(1000 / FRAME_RATE); - } + GtkWidget *window; + GtkWidget *frame; + GtkWidget *da; + + gtk_init(NULL, NULL); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(window), "VM"); + + g_signal_connect(window, "destroy", G_CALLBACK(kvm_gtk_destroy), NULL); + + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + + frame = gtk_frame_new(NULL); + + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(window), frame); + + da = gtk_drawing_area_new(); + + gtk_widget_set_size_request(da, 100, 100); + + gtk_container_add(GTK_CONTAINER(frame), da); + + g_signal_connect(da, "draw", G_CALLBACK(kvm_gtk_draw), NULL); + g_signal_connect(da, "configure-event", + G_CALLBACK(kvm_gtk_configure_event), fb); + g_signal_connect(G_OBJECT (window), "key_press_event", G_CALLBACK(kvm_gtk_key_press), NULL); + + + gtk_widget_set_events(da, gtk_widget_get_events(da) + | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + gtk_widget_show_all(window); + + g_timeout_add(1000 / FRAME_RATE, (GSourceFunc) kvm_gtk_redraw, window); + + gtk_main(); - if (running == false && done == false) { - done = true; - return NULL; - } -exit: done = true; - kvm_cpu__reboot(fb->kvm); return NULL; } -static int sdl__start(struct framebuffer *fb) +static int kvm_gtk_start(struct framebuffer *fb) { pthread_t thread; - running = true; - - if (pthread_create(&thread, NULL, sdl__thread, fb) != 0) + if (pthread_create(&thread, NULL, kvm_gtk_thread, fb) != 0) return -1; return 0; } -static int sdl__stop(struct framebuffer *fb) +static int kvm_gtk_stop(struct framebuffer *fb) { - running = false; - while (done == false) + gtk_main_quit(); + + while (!done) sleep(0); return 0; } -static struct fb_target_operations sdl_ops = { - .start = sdl__start, - .stop = sdl__stop, +static struct fb_target_operations kvm_gtk_ops = { + .start = kvm_gtk_start, + .stop = kvm_gtk_stop, }; -int sdl__init(struct kvm *kvm) +int kvm_gtk_init(struct kvm *kvm) { struct framebuffer *fb; - if (!kvm->cfg.sdl) + if (!kvm->cfg.gtk) return 0; fb = vesa__init(kvm); @@ -310,15 +311,16 @@ int sdl__init(struct kvm *kvm) return PTR_ERR(fb); } - return fb__attach(fb, &sdl_ops); + return fb__attach(fb, &kvm_gtk_ops); } -dev_init(sdl__init); -int sdl__exit(struct kvm *kvm) +int kvm_gtk_exit(struct kvm *kvm) { - if (kvm->cfg.sdl) - return sdl__stop(NULL); + if (kvm->cfg.gtk) + return kvm_gtk_stop(NULL); return 0; } -dev_exit(sdl__exit); + +dev_init(kvm_gtk_init); +dev_exit(kvm_gtk_exit); diff --git a/tools/kvm/x86/kvm.c b/tools/kvm/x86/kvm.c index 2ba1db0..9b46772 100644 --- a/tools/kvm/x86/kvm.c +++ b/tools/kvm/x86/kvm.c @@ -296,7 +296,7 @@ bool load_bzimage(struct kvm *kvm, int fd_kernel, int fd_initrd, /* vidmode should be either specified or set by default */ - if (kvm->cfg.vnc || kvm->cfg.sdl) { + if (kvm->cfg.vnc || kvm->cfg.sdl || kvm->cfg.gtk) { if (!kvm->cfg.arch.vidmode) vidmode = 0x312; else -- To unsubscribe from this list: send the line "unsubscribe linux-tip-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html